feat: add bounded Quantum cabinet extraction

This commit is contained in:
2026-07-22 19:23:00 +02:00
parent 531612829d
commit a7b1cd7d16
20 changed files with 1099 additions and 66 deletions

View File

@@ -67,6 +67,19 @@ const MULTI_BLOCK_MSZIP_CAB = Uint8Array.from(
)
);
/*
* Generated one-folder CAB around the pinned compcol qtm.txt CFDATA payload.
* Provenance and hashes are recorded in tests/fixtures/README.md.
*/
const QUANTUM_CAB = Uint8Array.from(
Buffer.from(
'TVNDRgAAAAB8AAAAAAAAACwAAAAAAAAAAwEBAAEAAAAAAAAARAAAAAEAIhI7AAAAAAAA' +
'AAAAAAAAACAAcXRtLnR4dAD9LexLMAA7ANYGaQvLR/AsKjqPLKu7PLkzAYvYWEt7Abpv' +
'bVFuOsNnQkvrAjZD1mZWyp5yzDAAAA==',
'base64'
)
);
const LZX_CAB = binary(
'\x4d\x53\x43\x46\x00\x00\x00\x00\x97\x00\x00\x00\x00\x00\x00' +
'\x00\x2c\x00\x00\x00\x00\x00\x00\x00\x03\x01\x01\x00\x02\x00' +
@@ -226,6 +239,69 @@ describe('CAB enumeration and extraction', () => {
});
});
it('enumerates and extracts the pinned Quantum CAB payload', async () => {
expect(QUANTUM_CAB).toHaveLength(124);
expect(createHash('sha256').update(QUANTUM_CAB).digest('hex')).toBe(
'f622c5a8f5a000dfaddc5569750344f5a6e35d0de9fbd3c4f0c8720c7d94d291'
);
const listing = enumerateCabinet(QUANTUM_CAB);
expect(listing.folders[0]).toMatchObject({
dataBlockCount: 1,
compressedSize: 48,
uncompressedSize: 59,
compression: {
kind: 'quantum',
raw: 0x1222,
level: 2,
memoryBits: 18,
windowBytes: 262_144,
},
});
expect(listing.diagnostics).not.toEqual(
expect.arrayContaining([
expect.objectContaining({ code: 'cab-compression-unsupported' }),
])
);
const extracted = await extractCabinet(QUANTUM_CAB);
expect(extracted.extractedEntries[0]!.normalizedPath).toBe('qtm.txt');
expect(new TextDecoder().decode(extracted.extractedEntries[0]!.bytes)).toBe(
'If you can read this, the Quantum decompressor is working!\n'
);
});
it('enforces Quantum type, window, and operation limits', () => {
const reservedBits = QUANTUM_CAB.slice();
new DataView(
reservedBits.buffer,
reservedBits.byteOffset,
reservedBits.byteLength
).setUint16(42, 0x3222, true);
expectCode(() => enumerateCabinet(reservedBits), 'cab-invalid-compression');
expectCode(
() =>
enumerateCabinet(QUANTUM_CAB, {
limits: { maxQuantumWindowBytes: 128 * 1024 },
}),
'cab-quantum-window-limit'
);
expectCode(
() =>
enumerateCabinet(QUANTUM_CAB, {
limits: { maxDecodeOperations: 58 },
}),
'quantum-operation-limit'
);
});
it('rejects a corrupt Quantum CFDATA payload before decoding', async () => {
const corrupted = QUANTUM_CAB.slice();
corrupted[corrupted.length - 1] = corrupted[corrupted.length - 1]! ^ 1;
await expect(extractCabinet(corrupted)).rejects.toMatchObject({
diagnostic: { code: 'cab-checksum-mismatch' },
});
});
it('retains an uncompressed LZX block across CAB chunks', () => {
const decoder = new LzxDecoder(18, { maxOperations: 100 });
const first = binary(

View File

@@ -15,6 +15,7 @@ import { fail, warning } from './errors.js';
import { LzxDecoder } from './lzx/decoder.js';
import { MsZipDecoder } from './mszip/decoder.js';
import { decodeCabFileName, normalizeCabPath } from './path.js';
import { QuantumDecoder } from './quantum/decoder.js';
import {
resolveLimits,
type CabCompressionMethod,
@@ -60,6 +61,27 @@ interface ParsedCabinet {
result: CabEnumerationResult;
}
function verifyDataBlockChecksum(bytes: Uint8Array, block: CabDataBlock): void {
if (block.checksum === 0) return;
const compressed = bytes.subarray(
block.dataOffset,
block.dataOffset + block.compressedSize
);
const actual = calculateDataBlockChecksum(
block.reserve,
compressed,
block.compressedSize,
block.uncompressedSize
);
if (actual !== block.checksum) {
fail(
'cab-checksum-mismatch',
`CAB data block checksum mismatch: expected 0x${block.checksum.toString(16).padStart(8, '0')}, got 0x${actual.toString(16).padStart(8, '0')}.`,
{ offset: block.headerOffset, structure: 'CFDATA.csum' }
);
}
}
function checkedAdd(
left: number,
right: number,
@@ -108,7 +130,13 @@ function parseCompression(
if (kind === 2) {
const level = (raw & 0x00f0) >>> 4;
const memoryBits = (raw & 0x1f00) >>> 8;
if (level < 1 || level > 7 || memoryBits < 10 || memoryBits > 21) {
if (
(raw & ~0x1ff2) !== 0 ||
level < 1 ||
level > 7 ||
memoryBits < 10 ||
memoryBits > 21
) {
fail(
'cab-invalid-compression',
`Invalid Quantum type 0x${raw.toString(16)}.`,
@@ -118,7 +146,22 @@ function parseCompression(
}
);
}
return { kind: 'quantum', raw, label: 'Quantum', level, memoryBits };
const windowBytes = 2 ** memoryBits;
if (windowBytes > limits.maxQuantumWindowBytes) {
fail(
'cab-quantum-window-limit',
`Quantum window ${windowBytes} exceeds the configured ${limits.maxQuantumWindowBytes}-byte limit.`,
{ offset, structure: 'CFFOLDER.typeCompress' }
);
}
return {
kind: 'quantum',
raw,
label: 'Quantum',
level,
memoryBits,
windowBytes,
};
}
if (kind === 3) {
if ((raw & ~0x1f0f) !== 0) {
@@ -404,6 +447,7 @@ function parseCabinet(
let totalFolderOutput = 0;
let totalLzxOutput = 0;
let totalQuantumOutput = 0;
for (const folder of folders) {
if (folder.firstDataBlockOffset > declaredSize) {
fail(
@@ -533,6 +577,14 @@ function parseCabinet(
'CFDATA.cbUncomp'
);
}
if (folder.compression.kind === 'quantum') {
totalQuantumOutput = checkedAdd(
totalQuantumOutput,
folder.uncompressedSize,
'quantum-operation-limit',
'CFDATA.cbUncomp'
);
}
}
if (totalFolderOutput > limits.maxTotalUncompressedBytes) {
fail(
@@ -548,6 +600,13 @@ function parseCabinet(
{ structure: 'CFDATA.cbUncomp' }
);
}
if (totalQuantumOutput > limits.maxDecodeOperations) {
fail(
'quantum-operation-limit',
`Quantum output exceeds the configured ${limits.maxDecodeOperations}-operation limit.`,
{ structure: 'CFDATA.cbUncomp' }
);
}
const dataRanges = folders
.filter((folder) => folder.dataBlockCount > 0)
@@ -600,18 +659,6 @@ function parseCabinet(
)
);
}
for (const folder of folders) {
if (folder.compression.kind === 'quantum') {
diagnostics.push(
warning(
'cab-compression-unsupported',
`${folder.compression.label} metadata can be listed, but this build cannot extract it.`,
{ structure: `CFFOLDER[${folder.index}].typeCompress` }
)
);
}
}
return {
bytes,
limits,
@@ -655,14 +702,6 @@ export async function extractCabinet(
const extractedEntries = [];
for (const folder of parsed.folders) {
checkCancellation(options.cancellation);
if (folder.compression.kind === 'quantum') {
fail(
'cab-compression-unsupported',
`${folder.compression.label} extraction is not implemented.`,
{ structure: `CFFOLDER[${folder.index}].typeCompress` }
);
}
const folderOutput = new Uint8Array(folder.uncompressedSize);
const decoder =
folder.compression.kind === 'lzx'
@@ -673,34 +712,50 @@ export async function extractCabinet(
: undefined;
const msZipDecoder =
folder.compression.kind === 'mszip' ? new MsZipDecoder() : undefined;
const quantumDecoder =
folder.compression.kind === 'quantum'
? new QuantumDecoder(folder.compression.memoryBits, {
maxOperations: parsed.limits.maxDecodeOperations,
checkCancellation: () => checkCancellation(options.cancellation),
})
: undefined;
let quantumQueuedBlock = -1;
let outputOffset = 0;
for (const block of folder.blocks) {
for (const [blockIndex, block] of folder.blocks.entries()) {
checkCancellation(options.cancellation);
if (quantumDecoder !== undefined) {
const lookahead = Math.min(blockIndex + 1, folder.blocks.length - 1);
while (quantumQueuedBlock < lookahead) {
quantumQueuedBlock += 1;
const queued = folder.blocks[quantumQueuedBlock]!;
verifyDataBlockChecksum(parsed.bytes, queued);
quantumDecoder.appendCabBlock(
parsed.bytes.subarray(
queued.dataOffset,
queued.dataOffset + queued.compressedSize
),
quantumQueuedBlock === folder.blocks.length - 1
);
}
}
const compressed = parsed.bytes.subarray(
block.dataOffset,
block.dataOffset + block.compressedSize
);
if (block.checksum !== 0) {
const actual = calculateDataBlockChecksum(
block.reserve,
compressed,
block.compressedSize,
block.uncompressedSize
);
if (actual !== block.checksum) {
fail(
'cab-checksum-mismatch',
`CAB data block checksum mismatch: expected 0x${block.checksum.toString(16).padStart(8, '0')}, got 0x${actual.toString(16).padStart(8, '0')}.`,
{ offset: block.headerOffset, structure: 'CFDATA.csum' }
);
}
if (quantumDecoder === undefined) {
verifyDataBlockChecksum(parsed.bytes, block);
}
const output =
decoder !== undefined
? decoder.decompressNext(compressed, block.uncompressedSize)
: msZipDecoder !== undefined
? msZipDecoder.decompressNext(compressed, block.uncompressedSize)
: compressed;
: quantumDecoder !== undefined
? quantumDecoder.decompressNext(
block.uncompressedSize,
blockIndex === folder.blocks.length - 1
)
: compressed;
if (output.length !== block.uncompressedSize) {
fail(
'cab-output-size-mismatch',

View File

@@ -0,0 +1,154 @@
/*
* SPDX-License-Identifier: MIT
*
* TypeScript port of compcol `src/quantum/bits.rs` at commit
* 04a6db2aa7bd487a89c559631d79d1b384139f50.
* Copyright (c) 2026 Karpeles Lab Inc., MIT License.
* Modified for a synchronous, bounded CAB-block decoder.
*/
import { fail } from '../errors.js';
import { QuantumBudget } from './budget.js';
const BIT_BUFFER_WIDTH = 32;
export class QuantumBitReader {
private bitBuffer = 0;
private bitsLeft = 0;
private byteOffset = 0;
private endOfInput = false;
private eofPaddingUsed = false;
constructor(private readonly budget: QuantumBudget) {}
get offset(): number {
return this.byteOffset;
}
get bufferedBitCount(): number {
return this.bitsLeft;
}
setEndOfInput(): void {
this.endOfInput = true;
}
ensureBits(bitCount: number, input: Uint8Array): void {
if (!Number.isInteger(bitCount) || bitCount < 1 || bitCount > 16) {
fail('quantum-invalid-bit-count', 'Invalid Quantum bit-read width.', {
structure: 'Quantum bitstream',
});
}
while (this.bitsLeft < bitCount) this.readPair(input);
}
peekBits(bitCount: number): number {
if (
!Number.isInteger(bitCount) ||
bitCount < 1 ||
bitCount > BIT_BUFFER_WIDTH ||
bitCount > this.bitsLeft
) {
fail('quantum-invalid-bit-state', 'Invalid Quantum bit-buffer access.', {
structure: 'Quantum bitstream',
});
}
return bitCount === BIT_BUFFER_WIDTH
? this.bitBuffer
: this.bitBuffer >>> (BIT_BUFFER_WIDTH - bitCount);
}
removeBits(bitCount: number): void {
if (
!Number.isInteger(bitCount) ||
bitCount < 0 ||
bitCount > this.bitsLeft
) {
fail('quantum-invalid-bit-state', 'Invalid Quantum bit-buffer advance.', {
structure: 'Quantum bitstream',
});
}
this.bitBuffer =
bitCount === BIT_BUFFER_WIDTH ? 0 : (this.bitBuffer << bitCount) >>> 0;
this.bitsLeft -= bitCount;
}
readBits(bitCount: number, input: Uint8Array): number {
this.ensureBits(bitCount, input);
const value = this.peekBits(bitCount);
this.removeBits(bitCount);
return value;
}
readManyBits(bitCount: number, input: Uint8Array): number {
if (!Number.isInteger(bitCount) || bitCount < 0 || bitCount > 32) {
fail('quantum-invalid-bit-count', 'Invalid Quantum bit-read width.', {
structure: 'Quantum bitstream',
});
}
let needed = bitCount;
let value = 0;
while (needed > 0) {
if (this.bitsLeft <= BIT_BUFFER_WIDTH - 16) this.readPair(input);
const run = Math.min(this.bitsLeft, needed);
value = (value * 2 ** run + this.peekBits(run)) >>> 0;
this.removeBits(run);
needed -= run;
this.budget.consume();
}
return value;
}
alignToByte(): void {
this.removeBits(this.bitsLeft & 7);
}
rebase(consumedBytes: number): void {
if (consumedBytes < 0 || consumedBytes > this.byteOffset) {
fail('quantum-invalid-bit-state', 'Invalid Quantum input rebase.', {
structure: 'Quantum bitstream',
});
}
this.byteOffset -= consumedBytes;
}
private readPair(input: Uint8Array): void {
if (this.bitsLeft > BIT_BUFFER_WIDTH - 16) {
fail('quantum-invalid-bit-state', 'Quantum bit buffer overflow.', {
offset: this.byteOffset,
structure: 'Quantum bitstream',
});
}
if (this.byteOffset + 2 > input.length) {
if (this.endOfInput && !this.eofPaddingUsed) {
// libmspack supplies two zero bytes once at folder EOF. Preserve a
// final odd byte (normally the synthetic CAB sentinel) as the high
// byte of that refill instead of discarding it.
const remainingByte =
this.byteOffset < input.length ? input[this.byteOffset++]! : 0;
this.bitBuffer =
(this.bitBuffer |
(remainingByte << (BIT_BUFFER_WIDTH - 16 - this.bitsLeft))) >>>
0;
this.eofPaddingUsed = true;
this.bitsLeft += 16;
this.budget.consume();
return;
}
fail(
'quantum-unexpected-eof',
'Quantum stream ended while reading compressed bits.',
{ offset: this.byteOffset, structure: 'Quantum bitstream' }
);
}
const combined =
(input[this.byteOffset]! << 8) | input[this.byteOffset + 1]!;
this.byteOffset += 2;
this.bitBuffer =
(this.bitBuffer |
(combined << (BIT_BUFFER_WIDTH - 16 - this.bitsLeft))) >>>
0;
this.bitsLeft += 16;
this.budget.consume();
}
}

View File

@@ -0,0 +1,44 @@
/*
* SPDX-License-Identifier: MIT
*
* Quantum decoder support adapted from compcol `src/quantum/*` at commit
* 04a6db2aa7bd487a89c559631d79d1b384139f50.
* Copyright (c) 2026 Karpeles Lab Inc., MIT License.
*/
import { fail } from '../errors.js';
export interface QuantumDecoderOptions {
maxOperations: number;
checkCancellation?: () => void;
}
export class QuantumBudget {
private operations = 0;
private nextCancellationCheck = 4_096;
constructor(private readonly options: QuantumDecoderOptions) {}
consume(count = 1): void {
if (!Number.isSafeInteger(count) || count < 0) {
fail('quantum-operation-overflow', 'Invalid Quantum operation count.', {
structure: 'Quantum stream',
});
}
this.operations += count;
if (
!Number.isSafeInteger(this.operations) ||
this.operations > this.options.maxOperations
) {
fail(
'quantum-operation-limit',
`Quantum decoding exceeded the configured ${this.options.maxOperations}-operation limit.`,
{ structure: 'Quantum stream' }
);
}
if (this.operations >= this.nextCancellationCheck) {
this.options.checkCancellation?.();
this.nextCancellationCheck = this.operations + 4_096;
}
}
}

View File

@@ -0,0 +1,121 @@
import { describe, expect, it } from 'vitest';
import { OnePkgError } from '../errors.js';
import { QuantumDecoder } from './decoder.js';
/*
* Pinned compcol MIT fixture from tests/quantum.rs at commit
* 04a6db2aa7bd487a89c559631d79d1b384139f50. The source bytes are the
* Quantum CFDATA payload from libmspack's mszip_lzx_qtm.cab; the compcol test
* appends 0xFF, while QuantumDecoder appends that CAB sentinel itself.
*/
const QTM_TEXT = Uint8Array.from([
0xd6, 0x06, 0x69, 0x0b, 0xcb, 0x47, 0xf0, 0x2c, 0x2a, 0x3a, 0x8f, 0x2c, 0xab,
0xbb, 0x3c, 0xb9, 0x33, 0x01, 0x8b, 0xd8, 0x58, 0x4b, 0x7b, 0x01, 0xba, 0x6f,
0x6d, 0x51, 0x6e, 0x3a, 0xc3, 0x67, 0x42, 0x4b, 0xeb, 0x02, 0x36, 0x43, 0xd6,
0x66, 0x56, 0xca, 0x9e, 0x72, 0xcc, 0x30, 0x00, 0x00,
]);
const EXPECTED_TEXT =
'If you can read this, the Quantum decompressor is working!\n';
const ZERO_FRAME = Uint8Array.from([
0xff, 0x6d, 0xda, 0x34, 0x62, 0x1a, 0x9b, 0xa9, 0x92, 0x04, 0xd2, 0x80, 0x00,
0x20,
]);
const SECOND_ZERO_FRAME = Uint8Array.from([0x69, 0x33, 0x90, 0x00, 0x06, 0x00]);
function expectCode(action: () => unknown, code: string): void {
try {
action();
} catch (error) {
expect(error).toBeInstanceOf(OnePkgError);
expect((error as OnePkgError).diagnostic.code).toBe(code);
return;
}
throw new Error(`Expected OnePkgError ${code}`);
}
describe('Quantum decoder', () => {
it('decodes the pinned compcol qtm.txt CFDATA payload', () => {
const decoder = new QuantumDecoder(18, { maxOperations: 1_000_000 });
decoder.appendCabBlock(QTM_TEXT, true);
const output = decoder.decompressNext(EXPECTED_TEXT.length, true);
expect(new TextDecoder().decode(output)).toBe(EXPECTED_TEXT);
});
it('rejects truncated streams and enforces the operation budget', () => {
expectCode(() => {
const decoder = new QuantumDecoder(18, {
maxOperations: 1_000_000,
});
decoder.appendCabBlock(QTM_TEXT.subarray(0, 4), true);
decoder.decompressNext(EXPECTED_TEXT.length, true);
}, 'quantum-unexpected-eof');
expectCode(() => {
const decoder = new QuantumDecoder(18, { maxOperations: 10 });
decoder.appendCabBlock(QTM_TEXT, true);
decoder.decompressNext(EXPECTED_TEXT.length, true);
}, 'quantum-operation-limit');
});
it('checks cancellation during a Quantum frame', () => {
const cancelled = new Error('cancelled');
const decoder = new QuantumDecoder(18, {
maxOperations: 10_000_000,
checkCancellation: () => {
throw cancelled;
},
});
decoder.appendCabBlock(ZERO_FRAME, true);
expect(() => decoder.decompressNext(32_768, true)).toThrow(cancelled);
});
it('appends the CAB sentinel and preserves state across frames', () => {
const decoder = new QuantumDecoder(18, { maxOperations: 10_000_000 });
decoder.appendCabBlock(ZERO_FRAME, false);
decoder.appendCabBlock(SECOND_ZERO_FRAME, true);
const first = decoder.decompressNext(32_768, false);
const second = decoder.decompressNext(32_768, true);
expect(first.every((value) => value === 0)).toBe(true);
expect(second.every((value) => value === 0)).toBe(true);
});
it('bounds and validates Quantum frame padding before the sentinel', () => {
const invalid = new QuantumDecoder(18, {
maxOperations: 10_000_000,
});
invalid.appendCabBlock(Uint8Array.from([...ZERO_FRAME, 0x42]), true);
expectCode(
() => invalid.decompressNext(32_768, true),
'quantum-invalid-frame-trailer'
);
const tooMuchPadding = new QuantumDecoder(18, {
maxOperations: 10_000_000,
});
tooMuchPadding.appendCabBlock(
Uint8Array.from([...ZERO_FRAME, 0, 0, 0, 0, 0]),
true
);
expectCode(
() => tooMuchPadding.decompressNext(32_768, true),
'quantum-invalid-frame-trailer'
);
});
it('rejects partial non-final frames and data after the final block', () => {
const decoder = new QuantumDecoder(18, { maxOperations: 1_000_000 });
decoder.appendCabBlock(QTM_TEXT, true);
expectCode(
() => decoder.decompressNext(EXPECTED_TEXT.length, false),
'quantum-partial-intermediate-frame'
);
decoder.decompressNext(EXPECTED_TEXT.length, true);
expectCode(
() => decoder.decompressNext(EXPECTED_TEXT.length, true),
'quantum-data-after-final-block'
);
});
});

View File

@@ -0,0 +1,296 @@
/*
* SPDX-License-Identifier: MIT
*
* TypeScript port of compcol `src/quantum/decoder.rs` at commit
* 04a6db2aa7bd487a89c559631d79d1b384139f50.
* Copyright (c) 2026 Karpeles Lab Inc., MIT License.
*
* Modified for synchronous CAB CFDATA chunks, fixed output buffers, explicit
* operation/cancellation budgets, strict frame/output boundaries, and one
* synthetic 0xFF trailer byte per CAB block as required by the reference.
*/
import { fail } from '../errors.js';
import { QuantumBitReader } from './bit-reader.js';
import { QuantumBudget, type QuantumDecoderOptions } from './budget.js';
import { QuantumArithmeticDecoder, QuantumModel } from './model.js';
import {
EXTRA_BITS,
LENGTH_BASE,
LENGTH_EXTRA,
POSITION_BASE,
} from './tables.js';
const FRAME_SIZE = 32 * 1024;
const MIN_WINDOW_BITS = 10;
const MAX_WINDOW_BITS = 21;
const MAX_TRAILER_ZERO_BYTES = 4;
const CAB_BLOCK_SENTINEL = 0xff;
export class QuantumDecoder {
private readonly window: Uint8Array;
private readonly windowMask: number;
private readonly budget: QuantumBudget;
private readonly bitReader: QuantumBitReader;
private readonly arithmetic: QuantumArithmeticDecoder;
private input = new Uint8Array(0);
private windowPosition = 0;
private headerRead = false;
private frameRemaining = FRAME_SIZE;
private finished = false;
private inputFinished = false;
private readonly model0: QuantumModel;
private readonly model1: QuantumModel;
private readonly model2: QuantumModel;
private readonly model3: QuantumModel;
private readonly model4: QuantumModel;
private readonly model5: QuantumModel;
private readonly model6: QuantumModel;
private readonly model6Length: QuantumModel;
private readonly model7: QuantumModel;
constructor(windowBits: number, options: QuantumDecoderOptions) {
if (
!Number.isInteger(windowBits) ||
windowBits < MIN_WINDOW_BITS ||
windowBits > MAX_WINDOW_BITS
) {
fail(
'quantum-invalid-window',
`Quantum window size 2^${windowBits} is outside 2^${MIN_WINDOW_BITS}..2^${MAX_WINDOW_BITS}.`,
{ structure: 'CFFOLDER.typeCompress' }
);
}
const windowSize = 2 ** windowBits;
this.window = new Uint8Array(windowSize);
this.windowMask = windowSize - 1;
this.budget = new QuantumBudget(options);
this.bitReader = new QuantumBitReader(this.budget);
this.arithmetic = new QuantumArithmeticDecoder(this.budget);
const positionModelSize = windowBits * 2;
this.model0 = new QuantumModel(0, 64, this.budget);
this.model1 = new QuantumModel(64, 64, this.budget);
this.model2 = new QuantumModel(128, 64, this.budget);
this.model3 = new QuantumModel(192, 64, this.budget);
this.model4 = new QuantumModel(
0,
Math.min(positionModelSize, 24),
this.budget
);
this.model5 = new QuantumModel(
0,
Math.min(positionModelSize, 36),
this.budget
);
this.model6 = new QuantumModel(0, positionModelSize, this.budget);
this.model6Length = new QuantumModel(0, 27, this.budget);
this.model7 = new QuantumModel(0, 7, this.budget);
}
appendCabBlock(compressed: Uint8Array, isLastBlock: boolean): void {
if (this.inputFinished) {
fail(
'quantum-data-after-final-block',
'Quantum decoder received data after the final CAB block.',
{ structure: 'Quantum stream' }
);
}
this.appendBlockBytes(compressed);
if (isLastBlock) {
this.inputFinished = true;
this.bitReader.setEndOfInput();
}
}
decompressNext(outputLength: number, isLastBlock: boolean): Uint8Array {
if (this.finished) {
fail(
'quantum-data-after-final-block',
'Quantum decoder received data after the final CAB block.',
{ structure: 'Quantum stream' }
);
}
if (
!Number.isInteger(outputLength) ||
outputLength <= 0 ||
outputLength > FRAME_SIZE
) {
fail(
'quantum-invalid-chunk-size',
`Quantum CAB chunk output size ${outputLength} is outside 1..${FRAME_SIZE}.`,
{ structure: 'CFDATA.cbUncomp' }
);
}
if (!isLastBlock && outputLength !== FRAME_SIZE) {
fail(
'quantum-partial-intermediate-frame',
'A non-final Quantum CAB block must produce exactly 32 KiB.',
{ structure: 'CFDATA.cbUncomp' }
);
}
const output = new Uint8Array(outputLength);
let outputOffset = 0;
while (outputOffset < output.length) {
this.budget.consume();
if (this.frameRemaining === 0) this.completeFrameTrailer();
if (!this.headerRead) {
this.arithmetic.initializeFrame(this.bitReader, this.input);
this.headerRead = true;
}
outputOffset = this.decodePacket(output, outputOffset);
}
// CAB blocks before the final partial block are complete Quantum frames.
// Consume their alignment and synthetic sentinel now, matching qtmd.c,
// so a malformed final frame cannot evade trailer validation.
if (this.frameRemaining === 0) this.completeFrameTrailer();
this.compactInput();
if (isLastBlock) this.finished = true;
return output;
}
private decodePacket(output: Uint8Array, outputOffset: number): number {
const selector = this.arithmetic.getSymbol(
this.model7,
this.bitReader,
this.input
);
if (selector < 4) {
const model = [this.model0, this.model1, this.model2, this.model3][
selector
]!;
const symbol = this.arithmetic.getSymbol(
model,
this.bitReader,
this.input
);
this.window[this.windowPosition & this.windowMask] = symbol & 0xff;
output[outputOffset] = symbol & 0xff;
this.windowPosition += 1;
this.frameRemaining -= 1;
this.budget.consume();
return outputOffset + 1;
}
let matchLength: number;
let matchOffset: number;
if (selector === 4 || selector === 5) {
const model = selector === 4 ? this.model4 : this.model5;
const slot = this.arithmetic.getSymbol(model, this.bitReader, this.input);
matchOffset = this.readPosition(slot);
matchLength = selector === 4 ? 3 : 4;
} else if (selector === 6) {
const lengthSlot = this.arithmetic.getSymbol(
this.model6Length,
this.bitReader,
this.input
);
if (lengthSlot >= LENGTH_EXTRA.length) {
fail('quantum-invalid-length-slot', 'Invalid Quantum length slot.', {
structure: 'Quantum match',
});
}
const lengthExtraBits = LENGTH_EXTRA[lengthSlot]!;
const lengthExtra = this.bitReader.readManyBits(
lengthExtraBits,
this.input
);
matchLength = LENGTH_BASE[lengthSlot]! + lengthExtra + 5;
const positionSlot = this.arithmetic.getSymbol(
this.model6,
this.bitReader,
this.input
);
matchOffset = this.readPosition(positionSlot);
} else {
fail('quantum-invalid-selector', 'Invalid Quantum packet selector.', {
structure: 'Quantum packet',
});
}
if (matchOffset <= 0 || matchOffset > this.window.length) {
fail(
'quantum-invalid-match-offset',
`Quantum match offset ${matchOffset} exceeds its ${this.window.length}-byte window.`,
{ structure: 'Quantum match' }
);
}
if (matchLength > this.frameRemaining) {
fail(
'quantum-frame-overrun',
'Quantum match crosses its 32 KiB frame boundary.',
{ structure: 'Quantum match' }
);
}
if (matchLength > output.length - outputOffset) {
fail(
'quantum-output-overrun',
'Quantum match exceeds the CAB block output declaration.',
{ structure: 'CFDATA.cbUncomp' }
);
}
for (let index = 0; index < matchLength; index += 1) {
const source =
(this.windowPosition - matchOffset + this.window.length) &
this.windowMask;
const value = this.window[source]!;
this.window[this.windowPosition & this.windowMask] = value;
output[outputOffset + index] = value;
this.windowPosition += 1;
}
this.frameRemaining -= matchLength;
this.budget.consume(matchLength);
return outputOffset + matchLength;
}
private readPosition(slot: number): number {
if (slot >= EXTRA_BITS.length) {
fail('quantum-invalid-position-slot', 'Invalid Quantum position slot.', {
structure: 'Quantum match',
});
}
const extra = this.bitReader.readManyBits(EXTRA_BITS[slot]!, this.input);
return POSITION_BASE[slot]! + extra + 1;
}
private completeFrameTrailer(): void {
this.bitReader.alignToByte();
let zeroBytes = 0;
while (true) {
const value = this.bitReader.readBits(8, this.input);
if (value === CAB_BLOCK_SENTINEL) break;
if (value !== 0 || zeroBytes >= MAX_TRAILER_ZERO_BYTES) {
fail(
'quantum-invalid-frame-trailer',
'Quantum frame trailer is not zero padding followed by 0xFF.',
{ structure: 'Quantum frame trailer' }
);
}
zeroBytes += 1;
this.budget.consume();
}
this.headerRead = false;
this.frameRemaining = FRAME_SIZE;
}
private appendBlockBytes(compressed: Uint8Array): void {
this.compactInput();
const appended = new Uint8Array(this.input.length + compressed.length + 1);
appended.set(this.input);
appended.set(compressed, this.input.length);
appended[appended.length - 1] = CAB_BLOCK_SENTINEL;
this.input = appended;
}
private compactInput(): void {
const consumed = this.bitReader.offset;
if (consumed === 0) return;
this.input = this.input.slice(consumed);
this.bitReader.rebase(consumed);
}
}

View File

@@ -0,0 +1,168 @@
/*
* SPDX-License-Identifier: MIT
*
* TypeScript port of compcol `src/quantum/model.rs` at commit
* 04a6db2aa7bd487a89c559631d79d1b384139f50.
* Copyright (c) 2026 Karpeles Lab Inc., MIT License.
* Modified to account for decoder work and report structured CAB errors.
*/
import { fail } from '../errors.js';
import { QuantumBitReader } from './bit-reader.js';
import { QuantumBudget } from './budget.js';
export class QuantumModel {
shiftsLeft = 4;
readonly symbols = new Uint16Array(65);
readonly cumulativeFrequencies = new Uint16Array(65);
constructor(
start: number,
readonly entries: number,
private readonly budget: QuantumBudget
) {
if (!Number.isInteger(entries) || entries < 1 || entries > 64) {
fail('quantum-invalid-model', 'Quantum model size is out of range.', {
structure: 'Quantum probability model',
});
}
for (let index = 0; index <= entries; index += 1) {
this.symbols[index] = start + index;
this.cumulativeFrequencies[index] = entries - index;
}
}
update(): void {
this.shiftsLeft -= 1;
if (this.shiftsLeft !== 0) {
for (let index = this.entries - 1; index >= 0; index -= 1) {
let frequency = this.cumulativeFrequencies[index]! >>> 1;
if (frequency <= this.cumulativeFrequencies[index + 1]!) {
frequency = this.cumulativeFrequencies[index + 1]! + 1;
}
this.cumulativeFrequencies[index] = frequency;
this.budget.consume();
}
return;
}
this.shiftsLeft = 50;
for (let index = 0; index < this.entries; index += 1) {
this.cumulativeFrequencies[index] =
(this.cumulativeFrequencies[index]! -
this.cumulativeFrequencies[index + 1]! +
1) >>>
1;
this.budget.consume();
}
// Preserve compcol/libmspack's selection-sort tie behavior exactly.
for (let left = 0; left < this.entries - 1; left += 1) {
for (let right = left + 1; right < this.entries; right += 1) {
if (
this.cumulativeFrequencies[left]! < this.cumulativeFrequencies[right]!
) {
const frequency = this.cumulativeFrequencies[left]!;
this.cumulativeFrequencies[left] = this.cumulativeFrequencies[right]!;
this.cumulativeFrequencies[right] = frequency;
const symbol = this.symbols[left]!;
this.symbols[left] = this.symbols[right]!;
this.symbols[right] = symbol;
}
this.budget.consume();
}
}
for (let index = this.entries - 1; index >= 0; index -= 1) {
this.cumulativeFrequencies[index] =
this.cumulativeFrequencies[index]! +
this.cumulativeFrequencies[index + 1]!;
this.budget.consume();
}
}
}
export class QuantumArithmeticDecoder {
private high = 0;
private low = 0;
private current = 0;
constructor(private readonly budget: QuantumBudget) {}
initializeFrame(reader: QuantumBitReader, input: Uint8Array): void {
this.high = 0xffff;
this.low = 0;
this.current = reader.readBits(16, input);
}
getSymbol(
model: QuantumModel,
reader: QuantumBitReader,
input: Uint8Array
): number {
const high = this.high >>> 0;
const low = this.low >>> 0;
const current = this.current >>> 0;
const range = (((high - low) >>> 0) & 0xffff) + 1;
const total = model.cumulativeFrequencies[0]!;
if (total === 0) {
fail('quantum-invalid-model', 'Quantum probability model is exhausted.', {
structure: 'Quantum probability model',
});
}
const scaledCurrent = (current - low + 1) >>> 0;
const numerator = (Math.imul(scaledCurrent, total) - 1) >>> 0;
const symbolFrequency = Math.floor(numerator / range) & 0xffff;
let index = 1;
while (
index < model.entries &&
model.cumulativeFrequencies[index]! > symbolFrequency
) {
index += 1;
this.budget.consume();
}
const symbol = model.symbols[index - 1]!;
const range2 = (high - low + 1) >>> 0;
const frequencyLow = model.cumulativeFrequencies[index - 1]!;
const frequencyHigh = model.cumulativeFrequencies[index]!;
let nextHigh =
(low + Math.floor((Math.imul(frequencyLow, range2) >>> 0) / total) - 1) &
0xffff;
let nextLow =
(low + Math.floor((Math.imul(frequencyHigh, range2) >>> 0) / total)) &
0xffff;
for (let update = index - 1; update >= 0; update -= 1) {
model.cumulativeFrequencies[update] =
(model.cumulativeFrequencies[update]! + 8) & 0xffff;
this.budget.consume();
}
if (model.cumulativeFrequencies[0]! > 3_800) model.update();
let nextCurrent = current & 0xffff;
while (true) {
if ((nextLow & 0x8000) !== (nextHigh & 0x8000)) {
if ((nextLow & 0x4000) !== 0 && (nextHigh & 0x4000) === 0) {
nextCurrent ^= 0x4000;
nextLow &= 0x3fff;
nextHigh |= 0x4000;
} else {
break;
}
}
nextLow = (nextLow << 1) & 0xffff;
nextHigh = ((nextHigh << 1) | 1) & 0xffff;
reader.ensureBits(1, input);
const bit = reader.peekBits(1);
reader.removeBits(1);
nextCurrent = ((nextCurrent << 1) | bit) & 0xffff;
this.budget.consume();
}
this.high = nextHigh;
this.low = nextLow;
this.current = nextCurrent;
this.budget.consume();
return symbol;
}
}

View File

@@ -0,0 +1,29 @@
/*
* SPDX-License-Identifier: MIT
*
* Direct TypeScript translation of compcol `src/quantum/tables.rs` at commit
* 04a6db2aa7bd487a89c559631d79d1b384139f50.
* Copyright (c) 2026 Karpeles Lab Inc., MIT License.
*/
export const POSITION_BASE = Uint32Array.from([
0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512, 768,
1_024, 1_536, 2_048, 3_072, 4_096, 6_144, 8_192, 12_288, 16_384, 24_576,
32_768, 49_152, 65_536, 98_304, 131_072, 196_608, 262_144, 393_216, 524_288,
786_432, 1_048_576, 1_572_864,
]);
export const EXTRA_BITS = Uint8Array.from([
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11,
11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19,
]);
export const LENGTH_BASE = Uint8Array.from([
0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 14, 18, 22, 26, 30, 38, 46, 54, 62, 78, 94,
110, 126, 158, 190, 222, 254,
]);
export const LENGTH_EXTRA = Uint8Array.from([
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5,
0,
]);

View File

@@ -9,6 +9,7 @@ export type CabCompressionMethod =
label: 'Quantum';
level: number;
memoryBits: number;
windowBytes: number;
}
| {
kind: 'lzx';
@@ -75,6 +76,7 @@ export interface OnePkgLimits {
maxExtractedBytes: number;
/** Maximum per-folder uncompressed CFDATA bytes / compressed payload bytes. */
maxCompressionRatio: number;
maxQuantumWindowBytes: number;
maxLzxWindowBytes: number;
maxDecodeOperations: number;
}
@@ -92,6 +94,7 @@ export const DEFAULT_ONEPKG_LIMITS: Readonly<OnePkgLimits> = {
maxTotalUncompressedBytes: 512 * 1024 * 1024,
maxExtractedBytes: 512 * 1024 * 1024,
maxCompressionRatio: 200,
maxQuantumWindowBytes: 2 * 1024 * 1024,
maxLzxWindowBytes: 8 * 1024 * 1024,
maxDecodeOperations: 1_000_000_000,
};