QIC-122 compression¶
QIC-122 is the Stac LZS compression format used by most QIC-80 backup software. QIC-113 §9 defines how a compressed volume packs it into segments: each segment holds one compression extent, which starts with the uncompressed byte offset of its data and contains one or more independently compressed frames. Because every segment says where its output belongs, Tapewyrm can decompress a damaged volume segment by segment and leave a hole of exactly the right size where a segment was lost.
Sources: QIC-122 Rev B, 06 Feb 91 (qic122b.pdf); QIC-113 Rev G §9 (qic113g.pdf); QIC-123 Rev D (qic123d.pdf) for algorithm codes.
When a volume is compressed¶
Volume table byte 124 bit 7 is set. Bits 0–5 name the algorithm, from the QIC-123 registry:
| Code | Algorithm | Tapewyrm |
|---|---|---|
| 1 | QIC-122 (Stac LZS) | Supported |
| 2 | QIC-130 (DCLZ) | Not implemented |
| 3, 4, 5 | QIC-154 (ALDC), 512, 1,024 and 2,048-byte history | Not implemented |
63 (3F) |
Vendor specific | Not implemented |
QIC-80-MC Rev N §8 allows only code 1 for a compliant tape.
The compressed stream¶
QIC-122 Rev B §5.4 defines the stream with a grammar. Bits are read most significant first from each byte.
| Element | Encoding |
|---|---|
| Stream | Zero or more strings, then an end marker |
| Literal byte | 0 followed by 8 bits |
| Back-reference | 1, an offset, then a length |
| Offset | 1 + 7 bits (1–127), or 0 + 11 bits (1–2,047) |
| End marker | 1 1 0000000: a 7-bit offset of zero |
Lengths:
| Bits | Length |
|---|---|
00 |
2 |
01 |
3 |
10 |
4 |
11 00 |
5 |
11 01 |
6 |
11 10 |
7 |
11 11 then 4-bit groups |
8 + the groups' sum: each 1111 adds 15 and continues; the first other value ends it |
A back-reference copies length bytes starting offset bytes back in a
2,048-byte history of the output (QIC-122 Rev B §3). The source may overlap
the bytes being written, so an offset of 1 repeats the last byte.
Extents and frames¶
QIC-113 Rev G §9. In a volume that does not span segments, each segment holds exactly one compression extent, starting at byte 0 of the segment's data:
Compression extent (Rev G §9.1.3):
| Offset | Size | Field |
|---|---|---|
| 0 | 8 | Uncompressed volume byte offset: total uncompressed bytes in all earlier segments |
| 8 | … | Compression frames, back to back |
Compression frame (Rev G §9.1.4):
| Offset | Size | Field |
|---|---|---|
| 0 | 2 | Frame size n. Bit 15 set: the data is stored raw, not compressed. |
| 2 | n | Data |
Each frame is an independent QIC-122 stream; its history starts empty. A frame holds at most 63,488 uncompressed bytes. If a frame ends within the last 18 bytes of a segment's data area, those bytes are null fill and the extent ends.
Segment spanning (volume table byte 56, bit 4; Rev G §9.1.2). Frames may continue into the next segment, and every segment starts with a 2-byte next extent offset: 2 means a new extent starts at byte 2; a larger value is where the next extent starts in this segment; 0 means the whole segment continues the previous frame.
Directory-last volumes. The uncompressed offset restarts at zero where the directory is written (Rev G §9.1.3).
Vendor widths¶
The offset and frame-size widths are not the same on every tape. Each volume
profile sets them in its [extent] table:
| Writer | Offset field | Frame size field | Evidence |
|---|---|---|---|
| QIC-113 Rev G | 8 bytes | 2 bytes | Standard |
| MTN backup software | 4 bytes | 2 bytes | Bench: with 8, almost every segment fails; with 4, the offsets chain exactly |
| NovaStor | 8 bytes | 4 bytes | Another reader's source code only; untested |
Tapewyrm notes¶
Modules. qiclib.volume.qic122 decodes streams (decompress) and
extents (decode_extent). qiclib.extract runs it over every segment of a
volume for qicsilver extract. It was written from the two standards; no
mature QIC-122 decoder exists to depend on.
Holes, not shifts. Each extent's output is placed at its own uncompressed offset. A lost or undecodable segment leaves a zero-filled hole recorded in the TWVL header, and nothing after it moves, so every later file is still where the directory says it is.
Only non-spanning volumes are supported. Every compressed volume seen so far has segment spanning clear. The decoder assumes one extent at byte 0 of each segment and does not read a next extent offset, so a spanning volume would not decode correctly.
Truncated tokens fail the segment. A token cut off by the end of a frame, or a back-reference before the start of the history, raises an error and the segment is treated as lost.