xz-compat 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/lzma/lib/Lzma2ChunkParser.js.map +1 -0
- package/dist/cjs/lzma/stream/transforms.js +18 -6
- package/dist/cjs/lzma/stream/transforms.js.map +1 -1
- package/dist/cjs/lzma/sync/Lzma2Decoder.js +11 -7
- package/dist/cjs/lzma/sync/Lzma2Decoder.js.map +1 -1
- package/dist/cjs/native.d.cts +0 -3
- package/dist/cjs/native.d.ts +0 -3
- package/dist/cjs/native.js +13 -66
- package/dist/cjs/native.js.map +1 -1
- package/dist/cjs/sevenz.js +11 -8
- package/dist/cjs/sevenz.js.map +1 -1
- package/dist/cjs/xz/Decoder.js +5 -11
- package/dist/cjs/xz/Decoder.js.map +1 -1
- package/dist/esm/lzma/lib/Lzma2ChunkParser.js.map +1 -0
- package/dist/esm/lzma/stream/transforms.js +18 -6
- package/dist/esm/lzma/stream/transforms.js.map +1 -1
- package/dist/esm/lzma/sync/Lzma2Decoder.js +11 -7
- package/dist/esm/lzma/sync/Lzma2Decoder.js.map +1 -1
- package/dist/esm/native.d.ts +0 -3
- package/dist/esm/native.js +15 -70
- package/dist/esm/native.js.map +1 -1
- package/dist/esm/sevenz.js +11 -8
- package/dist/esm/sevenz.js.map +1 -1
- package/dist/esm/xz/Decoder.js +5 -11
- package/dist/esm/xz/Decoder.js.map +1 -1
- package/package.json +1 -1
- package/dist/cjs/lzma/Lzma2ChunkParser.js.map +0 -1
- package/dist/esm/lzma/Lzma2ChunkParser.js.map +0 -1
- /package/dist/cjs/lzma/{Lzma2ChunkParser.d.cts → lib/Lzma2ChunkParser.d.cts} +0 -0
- /package/dist/cjs/lzma/{Lzma2ChunkParser.d.ts → lib/Lzma2ChunkParser.d.ts} +0 -0
- /package/dist/cjs/lzma/{Lzma2ChunkParser.js → lib/Lzma2ChunkParser.js} +0 -0
- /package/dist/esm/lzma/{Lzma2ChunkParser.d.ts → lib/Lzma2ChunkParser.d.ts} +0 -0
- /package/dist/esm/lzma/{Lzma2ChunkParser.js → lib/Lzma2ChunkParser.js} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/iterators/xz-compat/src/xz/Decoder.ts"],"sourcesContent":["/**\n * XZ Decompression Module\n *\n * XZ is a container format that wraps LZMA2 compressed data.\n * This module provides both synchronous and streaming XZ decoders.\n *\n * Pure JavaScript implementation, works on Node.js 0.8+\n *\n * IMPORTANT: Buffer Management Pattern\n *\n * When calling decodeLzma2(), use the direct return pattern:\n *\n * ✅ CORRECT - Fast path:\n * const output = decodeLzma2(data, props, size) as Buffer;\n *\n * ❌ WRONG - Slow path (do NOT buffer):\n * const chunks: Buffer[] = [];\n * decodeLzma2(data, props, size, { write: c => chunks.push(c) });\n * return Buffer.concat(chunks); // ← Unnecessary copies!\n */\n\nimport { Transform } from 'extract-base-iterator';\nimport type { Transform as TransformType } from 'stream';\nimport { decodeBcj } from '../filters/bcj/Bcj.ts';\nimport { decodeBcjArm } from '../filters/bcj/BcjArm.ts';\nimport { decodeBcjArm64 } from '../filters/bcj/BcjArm64.ts';\nimport { decodeBcjArmt } from '../filters/bcj/BcjArmt.ts';\nimport { decodeBcjIa64 } from '../filters/bcj/BcjIa64.ts';\nimport { decodeBcjPpc } from '../filters/bcj/BcjPpc.ts';\nimport { decodeBcjSparc } from '../filters/bcj/BcjSparc.ts';\nimport { decodeDelta } from '../filters/delta/Delta.ts';\nimport { decodeLzma2 } from '../lzma/index.ts';\nimport { tryLoadNative } from '../native.ts';\n\n// XZ magic bytes\nconst XZ_MAGIC = [0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00];\nconst XZ_FOOTER_MAGIC = [0x59, 0x5a]; // \"YZ\"\n\n// Filter IDs (from XZ specification)\nconst FILTER_DELTA = 0x03;\nconst FILTER_BCJ_X86 = 0x04;\nconst FILTER_BCJ_PPC = 0x05;\nconst FILTER_BCJ_IA64 = 0x06;\nconst FILTER_BCJ_ARM = 0x07;\nconst FILTER_BCJ_ARMT = 0x08;\nconst FILTER_BCJ_SPARC = 0x09;\nconst FILTER_BCJ_ARM64 = 0x0a;\nconst FILTER_LZMA2 = 0x21;\n\n// Filter info for parsing\ninterface FilterInfo {\n id: number;\n props: Buffer;\n}\n\n/**\n * Simple buffer comparison\n */\nfunction bufferEquals(buf: Buffer, offset: number, expected: number[]): boolean {\n if (offset + expected.length > buf.length) {\n return false;\n }\n for (let i = 0; i < expected.length; i++) {\n if (buf[offset + i] !== expected[i]) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * Decode variable-length integer (XZ multibyte encoding)\n * Returns number, but limits to 32-bit to work on Node 0.8+\n */\nfunction decodeMultibyte(buf: Buffer, offset: number): { value: number; bytesRead: number } {\n let value = 0;\n let i = 0;\n let byte: number;\n do {\n if (offset + i >= buf.length) {\n throw new Error('Truncated multibyte integer');\n }\n byte = buf[offset + i];\n value |= (byte & 0x7f) << (i * 7);\n i++;\n if (i > 4) {\n // Reduced to prevent overflow on Node 0.8\n throw new Error('Multibyte integer too large');\n }\n } while (byte & 0x80);\n return { value, bytesRead: i };\n}\n\n/**\n * Apply a preprocessing filter (BCJ/Delta) to decompressed data\n */\nfunction applyFilter(data: Buffer, filter: FilterInfo): Buffer {\n switch (filter.id) {\n case FILTER_BCJ_X86:\n return decodeBcj(data, filter.props);\n case FILTER_BCJ_ARM:\n return decodeBcjArm(data, filter.props);\n case FILTER_BCJ_ARM64:\n return decodeBcjArm64(data, filter.props);\n case FILTER_BCJ_ARMT:\n return decodeBcjArmt(data, filter.props);\n case FILTER_BCJ_PPC:\n return decodeBcjPpc(data, filter.props);\n case FILTER_BCJ_SPARC:\n return decodeBcjSparc(data, filter.props);\n case FILTER_BCJ_IA64:\n return decodeBcjIa64(data, filter.props);\n case FILTER_DELTA:\n return decodeDelta(data, filter.props);\n default:\n throw new Error(`Unsupported filter: 0x${filter.id.toString(16)}`);\n }\n}\n\n/**\n * Parse XZ Block Header to extract filters and LZMA2 properties\n */\nfunction parseBlockHeader(\n input: Buffer,\n offset: number,\n _checkSize: number\n): {\n filters: FilterInfo[];\n lzma2Props: Buffer;\n headerSize: number;\n dataStart: number;\n dataEnd: number;\n nextOffset: number;\n} {\n // Block header size\n const blockHeaderSizeRaw = input[offset];\n if (blockHeaderSizeRaw === 0) {\n throw new Error('Invalid block header size (index indicator found instead of block)');\n }\n const blockHeaderSize = (blockHeaderSizeRaw + 1) * 4;\n\n // Parse block header\n const blockHeaderStart = offset;\n offset++; // skip size byte\n\n const blockFlags = input[offset++];\n const numFilters = (blockFlags & 0x03) + 1;\n const hasCompressedSize = (blockFlags & 0x40) !== 0;\n const hasUncompressedSize = (blockFlags & 0x80) !== 0;\n\n // Skip optional sizes\n if (hasCompressedSize) {\n const result = decodeMultibyte(input, offset);\n offset += result.bytesRead;\n }\n\n if (hasUncompressedSize) {\n const result = decodeMultibyte(input, offset);\n offset += result.bytesRead;\n }\n\n // Parse all filters\n const filters: FilterInfo[] = [];\n let lzma2Props: Buffer | null = null;\n\n for (let i = 0; i < numFilters; i++) {\n const filterIdResult = decodeMultibyte(input, offset);\n const filterId = filterIdResult.value;\n offset += filterIdResult.bytesRead;\n\n const propsSizeResult = decodeMultibyte(input, offset);\n offset += propsSizeResult.bytesRead;\n\n const filterProps = input.slice(offset, offset + propsSizeResult.value);\n offset += propsSizeResult.value;\n\n if (filterId === FILTER_LZMA2) {\n // LZMA2 must be the last filter\n lzma2Props = filterProps;\n } else if (filterId === FILTER_DELTA || (filterId >= FILTER_BCJ_X86 && filterId <= FILTER_BCJ_ARM64)) {\n // Preprocessing filter - store for later application\n filters.push({ id: filterId, props: filterProps });\n } else {\n throw new Error(`Unsupported filter: 0x${filterId.toString(16)}`);\n }\n }\n\n if (!lzma2Props) {\n throw new Error('No LZMA2 filter found in XZ block');\n }\n\n // Skip to end of block header (must be aligned to 4 bytes)\n const blockDataStart = blockHeaderStart + blockHeaderSize;\n\n return {\n filters,\n lzma2Props,\n headerSize: blockHeaderSize,\n dataStart: blockDataStart,\n dataEnd: input.length,\n nextOffset: blockDataStart,\n };\n}\n\n/**\n * Parse XZ Index to get block positions\n *\n * XZ Index stores \"Unpadded Size\" for each block which equals:\n * Block Header Size + Compressed Data Size + Check Size\n * (does NOT include padding to 4-byte boundary)\n */\nfunction parseIndex(\n input: Buffer,\n indexStart: number,\n checkSize: number\n): Array<{\n compressedPos: number;\n compressedDataSize: number;\n uncompressedSize: number;\n}> {\n let offset = indexStart;\n\n // Index indicator (0x00)\n if (input[offset] !== 0x00) {\n throw new Error('Invalid index indicator');\n }\n offset++;\n\n // Number of records\n const countResult = decodeMultibyte(input, offset);\n const recordCount = countResult.value;\n offset += countResult.bytesRead;\n\n const records: Array<{\n compressedPos: number;\n unpaddedSize: number;\n compressedDataSize: number;\n uncompressedSize: number;\n }> = [];\n\n // Parse each record\n for (let i = 0; i < recordCount; i++) {\n // Unpadded Size (header + compressed data + check)\n const unpaddedResult = decodeMultibyte(input, offset);\n offset += unpaddedResult.bytesRead;\n\n // Uncompressed size\n const uncompressedResult = decodeMultibyte(input, offset);\n offset += uncompressedResult.bytesRead;\n\n records.push({\n compressedPos: 0, // will be calculated\n unpaddedSize: unpaddedResult.value,\n compressedDataSize: 0, // will be calculated\n uncompressedSize: uncompressedResult.value,\n });\n }\n\n // Calculate actual positions by walking through blocks\n let currentPos = 12; // After stream header\n for (let i = 0; i < records.length; i++) {\n const record = records[i];\n // Record where this block's header starts\n record.compressedPos = currentPos;\n\n // Get block header size from the actual data\n const headerSizeRaw = input[currentPos];\n const headerSize = (headerSizeRaw + 1) * 4;\n\n // Calculate compressed data size from unpadded size\n // unpaddedSize = headerSize + compressedDataSize + checkSize\n record.compressedDataSize = record.unpaddedSize - headerSize - checkSize;\n\n // Move to next block: unpaddedSize + padding to 4-byte boundary\n const paddedSize = Math.ceil(record.unpaddedSize / 4) * 4;\n currentPos += paddedSize;\n }\n\n return records;\n}\n\n/**\n * Pure JS XZ decompression (handles all XZ spec features)\n */\nfunction decodeXZPure(input: Buffer): Buffer {\n // Verify XZ magic\n if (input.length < 12 || !bufferEquals(input, 0, XZ_MAGIC)) {\n throw new Error('Invalid XZ magic bytes');\n }\n\n // Stream flags at offset 6-7\n const checkType = input[7] & 0x0f;\n\n // Check sizes based on check type\n const checkSizes: { [key: number]: number } = {\n 0: 0, // None\n 1: 4, // CRC32\n 4: 8, // CRC64\n 10: 32, // SHA-256\n };\n const checkSize = checkSizes[checkType] ?? 0;\n\n // Find footer by skipping stream padding (null bytes at end before footer)\n // Stream padding must be multiple of 4 bytes\n let footerEnd = input.length;\n while (footerEnd > 12 && input[footerEnd - 1] === 0x00) {\n footerEnd--;\n }\n // Align to 4-byte boundary (stream padding rules)\n while (footerEnd % 4 !== 0 && footerEnd > 12) {\n footerEnd++;\n }\n\n // Verify footer magic (at footerEnd - 2)\n if (!bufferEquals(input, footerEnd - 2, XZ_FOOTER_MAGIC)) {\n throw new Error('Invalid XZ footer magic');\n }\n\n // Get backward size (tells us where index starts) - at footerEnd - 8\n const backwardSize = (input.readUInt32LE(footerEnd - 8) + 1) * 4;\n const indexStart = footerEnd - 12 - backwardSize;\n\n // Parse Index to get block information\n const blockRecords = parseIndex(input, indexStart, checkSize);\n\n // Decompress each block\n const outputChunks: Buffer[] = [];\n let _totalOutputSize = 0;\n\n for (let i = 0; i < blockRecords.length; i++) {\n const record = blockRecords[i];\n const recordStart = record.compressedPos;\n\n // Parse block header\n const blockInfo = parseBlockHeader(input, recordStart, checkSize);\n\n // Extract compressed data for this block\n const dataStart = recordStart + blockInfo.headerSize;\n // compressedDataSize is calculated from the Index's Unpadded Size minus header and check\n const dataEnd = dataStart + record.compressedDataSize;\n\n // Note: XZ blocks have padding AFTER the check field to align to 4 bytes,\n // but the compressedSize from index is exact - no need to strip padding.\n // LZMA2 data includes a 0x00 end marker which must NOT be stripped.\n const compressedData = input.slice(dataStart, dataEnd);\n\n // Decompress this block with LZMA2 (fast path, no buffering)\n let blockOutput = decodeLzma2(compressedData, blockInfo.lzma2Props, record.uncompressedSize) as Buffer;\n\n // Apply preprocessing filters in reverse order (BCJ/Delta applied after LZMA2)\n // Filters are stored in order they were applied during compression,\n // so we need to reverse for decompression\n for (let j = blockInfo.filters.length - 1; j >= 0; j--) {\n blockOutput = applyFilter(blockOutput, blockInfo.filters[j]) as Buffer;\n }\n\n outputChunks.push(blockOutput);\n _totalOutputSize += blockOutput.length;\n }\n\n return Buffer.concat(outputChunks);\n}\n\n/**\n * Decompress XZ data synchronously\n * Uses @napi-rs/lzma if available on Node 14+, falls back to pure JS\n * Properly handles multi-block XZ files, stream padding, and concatenated streams\n * @param input - XZ compressed data\n * @returns Decompressed data\n */\nexport function decodeXZ(input: Buffer): Buffer {\n // Try native acceleration first (Node 14+ with @napi-rs/lzma installed)\n const native = tryLoadNative();\n if (native) {\n try {\n return native.xz.decompressSync(input);\n } catch (nativeErr) {\n // Native failed - try pure JS (handles more edge cases like\n // stream padding, concatenated streams, SHA-256 checksums)\n try {\n return decodeXZPure(input);\n } catch {\n // Both failed - throw the native error (usually more informative)\n throw nativeErr;\n }\n }\n }\n return decodeXZPure(input);\n}\n\n/**\n * Parse XZ stream to get block information (without decompressing)\n * This allows streaming decompression by processing blocks one at a time.\n */\nfunction parseXZIndex(input: Buffer): Array<{\n compressedPos: number;\n compressedDataSize: number;\n uncompressedSize: number;\n checkSize: number;\n}> {\n // Stream header validation\n if (input.length < 12) {\n throw new Error('XZ file too small');\n }\n\n // Stream magic bytes (0xFD, '7zXZ', 0x00)\n if (input[0] !== 0xfd || input[1] !== 0x37 || input[2] !== 0x7a || input[3] !== 0x58 || input[4] !== 0x5a || input[5] !== 0x00) {\n throw new Error('Invalid XZ magic bytes');\n }\n\n // Stream flags at offset 6-7\n const checkType = input[7] & 0x0f;\n\n // Check sizes based on check type\n const checkSizes: { [key: number]: number } = {\n 0: 0, // None\n 1: 4, // CRC32\n 4: 8, // CRC64\n 10: 32, // SHA-256\n };\n const checkSize = checkSizes[checkType] ?? 0;\n\n // Find footer by skipping stream padding\n let footerEnd = input.length;\n while (footerEnd > 12 && input[footerEnd - 1] === 0x00) {\n footerEnd--;\n }\n while (footerEnd % 4 !== 0 && footerEnd > 12) {\n footerEnd++;\n }\n\n // Verify footer magic\n if (!bufferEquals(input, footerEnd - 2, XZ_FOOTER_MAGIC)) {\n throw new Error('Invalid XZ footer magic');\n }\n\n // Get backward size\n const backwardSize = (input.readUInt32LE(footerEnd - 8) + 1) * 4;\n const indexStart = footerEnd - 12 - backwardSize;\n\n // Parse Index to get block information\n return parseIndex(input, indexStart, checkSize).map((record) => ({\n ...record,\n checkSize,\n }));\n}\n\n/**\n * Create an XZ decompression Transform stream\n * @returns Transform stream that decompresses XZ data\n */\nexport function createXZDecoder(): TransformType {\n const chunks: Buffer[] = [];\n\n return new Transform({\n transform(chunk: Buffer, _encoding: string, callback: (error?: Error | null) => void) {\n chunks.push(chunk);\n callback();\n },\n\n flush(callback: (error?: Error | null) => void) {\n try {\n const input = Buffer.concat(chunks);\n\n // Stream decode each block instead of buffering all output\n const blockRecords = parseXZIndex(input);\n\n for (let i = 0; i < blockRecords.length; i++) {\n const record = blockRecords[i];\n const recordStart = record.compressedPos;\n\n // Parse block header\n const blockInfo = parseBlockHeader(input, recordStart, blockRecords[i].checkSize);\n\n // Extract compressed data for this block\n const dataStart = recordStart + blockInfo.headerSize;\n const dataEnd = dataStart + record.compressedDataSize;\n const compressedData = input.slice(dataStart, dataEnd);\n\n // Decompress this block\n let blockOutput = decodeLzma2(compressedData, blockInfo.lzma2Props, record.uncompressedSize) as Buffer;\n\n // Apply preprocessing filters in reverse order\n for (let j = blockInfo.filters.length - 1; j >= 0; j--) {\n blockOutput = applyFilter(blockOutput, blockInfo.filters[j]) as Buffer;\n }\n\n // Push block output immediately instead of buffering\n this.push(blockOutput);\n }\n\n callback();\n } catch (err) {\n callback(err as Error);\n }\n },\n });\n}\n"],"names":["createXZDecoder","decodeXZ","XZ_MAGIC","XZ_FOOTER_MAGIC","FILTER_DELTA","FILTER_BCJ_X86","FILTER_BCJ_PPC","FILTER_BCJ_IA64","FILTER_BCJ_ARM","FILTER_BCJ_ARMT","FILTER_BCJ_SPARC","FILTER_BCJ_ARM64","FILTER_LZMA2","bufferEquals","buf","offset","expected","length","i","decodeMultibyte","value","byte","Error","bytesRead","applyFilter","data","filter","id","decodeBcj","props","decodeBcjArm","decodeBcjArm64","decodeBcjArmt","decodeBcjPpc","decodeBcjSparc","decodeBcjIa64","decodeDelta","toString","parseBlockHeader","input","_checkSize","blockHeaderSizeRaw","blockHeaderSize","blockHeaderStart","blockFlags","numFilters","hasCompressedSize","hasUncompressedSize","result","filters","lzma2Props","filterIdResult","filterId","propsSizeResult","filterProps","slice","push","blockDataStart","headerSize","dataStart","dataEnd","nextOffset","parseIndex","indexStart","checkSize","countResult","recordCount","records","unpaddedResult","uncompressedResult","compressedPos","unpaddedSize","compressedDataSize","uncompressedSize","currentPos","record","headerSizeRaw","paddedSize","Math","ceil","decodeXZPure","checkSizes","checkType","footerEnd","backwardSize","readUInt32LE","blockRecords","outputChunks","_totalOutputSize","recordStart","blockInfo","compressedData","blockOutput","decodeLzma2","j","Buffer","concat","native","tryLoadNative","xz","decompressSync","nativeErr","parseXZIndex","map","chunks","Transform","transform","chunk","_encoding","callback","flush","err"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/iterators/xz-compat/src/xz/Decoder.ts"],"sourcesContent":["/**\n * XZ Decompression Module\n *\n * XZ is a container format that wraps LZMA2 compressed data.\n * This module provides both synchronous and streaming XZ decoders.\n *\n * Pure JavaScript implementation, works on Node.js 0.8+\n *\n * IMPORTANT: Buffer Management Pattern\n *\n * When calling decodeLzma2(), use the direct return pattern:\n *\n * ✅ CORRECT - Fast path:\n * const output = decodeLzma2(data, props, size) as Buffer;\n *\n * ❌ WRONG - Slow path (do NOT buffer):\n * const chunks: Buffer[] = [];\n * decodeLzma2(data, props, size, { write: c => chunks.push(c) });\n * return Buffer.concat(chunks); // ← Unnecessary copies!\n */\n\nimport { Transform } from 'extract-base-iterator';\nimport type { Transform as TransformType } from 'stream';\nimport { decodeBcj } from '../filters/bcj/Bcj.ts';\nimport { decodeBcjArm } from '../filters/bcj/BcjArm.ts';\nimport { decodeBcjArm64 } from '../filters/bcj/BcjArm64.ts';\nimport { decodeBcjArmt } from '../filters/bcj/BcjArmt.ts';\nimport { decodeBcjIa64 } from '../filters/bcj/BcjIa64.ts';\nimport { decodeBcjPpc } from '../filters/bcj/BcjPpc.ts';\nimport { decodeBcjSparc } from '../filters/bcj/BcjSparc.ts';\nimport { decodeDelta } from '../filters/delta/Delta.ts';\nimport { decodeLzma2 } from '../lzma/index.ts';\nimport { tryLoadNative } from '../native.ts';\n\n// XZ magic bytes\nconst XZ_MAGIC = [0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00];\nconst XZ_FOOTER_MAGIC = [0x59, 0x5a]; // \"YZ\"\n\n// Filter IDs (from XZ specification)\nconst FILTER_DELTA = 0x03;\nconst FILTER_BCJ_X86 = 0x04;\nconst FILTER_BCJ_PPC = 0x05;\nconst FILTER_BCJ_IA64 = 0x06;\nconst FILTER_BCJ_ARM = 0x07;\nconst FILTER_BCJ_ARMT = 0x08;\nconst FILTER_BCJ_SPARC = 0x09;\nconst FILTER_BCJ_ARM64 = 0x0a;\nconst FILTER_LZMA2 = 0x21;\n\n// Filter info for parsing\ninterface FilterInfo {\n id: number;\n props: Buffer;\n}\n\n/**\n * Simple buffer comparison\n */\nfunction bufferEquals(buf: Buffer, offset: number, expected: number[]): boolean {\n if (offset + expected.length > buf.length) {\n return false;\n }\n for (let i = 0; i < expected.length; i++) {\n if (buf[offset + i] !== expected[i]) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * Decode variable-length integer (XZ multibyte encoding)\n * Returns number, but limits to 32-bit to work on Node 0.8+\n */\nfunction decodeMultibyte(buf: Buffer, offset: number): { value: number; bytesRead: number } {\n let value = 0;\n let i = 0;\n let byte: number;\n do {\n if (offset + i >= buf.length) {\n throw new Error('Truncated multibyte integer');\n }\n byte = buf[offset + i];\n value |= (byte & 0x7f) << (i * 7);\n i++;\n if (i > 4) {\n // Reduced to prevent overflow on Node 0.8\n throw new Error('Multibyte integer too large');\n }\n } while (byte & 0x80);\n return { value, bytesRead: i };\n}\n\n/**\n * Apply a preprocessing filter (BCJ/Delta) to decompressed data\n */\nfunction applyFilter(data: Buffer, filter: FilterInfo): Buffer {\n switch (filter.id) {\n case FILTER_BCJ_X86:\n return decodeBcj(data, filter.props);\n case FILTER_BCJ_ARM:\n return decodeBcjArm(data, filter.props);\n case FILTER_BCJ_ARM64:\n return decodeBcjArm64(data, filter.props);\n case FILTER_BCJ_ARMT:\n return decodeBcjArmt(data, filter.props);\n case FILTER_BCJ_PPC:\n return decodeBcjPpc(data, filter.props);\n case FILTER_BCJ_SPARC:\n return decodeBcjSparc(data, filter.props);\n case FILTER_BCJ_IA64:\n return decodeBcjIa64(data, filter.props);\n case FILTER_DELTA:\n return decodeDelta(data, filter.props);\n default:\n throw new Error(`Unsupported filter: 0x${filter.id.toString(16)}`);\n }\n}\n\n/**\n * Parse XZ Block Header to extract filters and LZMA2 properties\n */\nfunction parseBlockHeader(\n input: Buffer,\n offset: number,\n _checkSize: number\n): {\n filters: FilterInfo[];\n lzma2Props: Buffer;\n headerSize: number;\n dataStart: number;\n dataEnd: number;\n nextOffset: number;\n} {\n // Block header size\n const blockHeaderSizeRaw = input[offset];\n if (blockHeaderSizeRaw === 0) {\n throw new Error('Invalid block header size (index indicator found instead of block)');\n }\n const blockHeaderSize = (blockHeaderSizeRaw + 1) * 4;\n\n // Parse block header\n const blockHeaderStart = offset;\n offset++; // skip size byte\n\n const blockFlags = input[offset++];\n const numFilters = (blockFlags & 0x03) + 1;\n const hasCompressedSize = (blockFlags & 0x40) !== 0;\n const hasUncompressedSize = (blockFlags & 0x80) !== 0;\n\n // Skip optional sizes\n if (hasCompressedSize) {\n const result = decodeMultibyte(input, offset);\n offset += result.bytesRead;\n }\n\n if (hasUncompressedSize) {\n const result = decodeMultibyte(input, offset);\n offset += result.bytesRead;\n }\n\n // Parse all filters\n const filters: FilterInfo[] = [];\n let lzma2Props: Buffer | null = null;\n\n for (let i = 0; i < numFilters; i++) {\n const filterIdResult = decodeMultibyte(input, offset);\n const filterId = filterIdResult.value;\n offset += filterIdResult.bytesRead;\n\n const propsSizeResult = decodeMultibyte(input, offset);\n offset += propsSizeResult.bytesRead;\n\n const filterProps = input.slice(offset, offset + propsSizeResult.value);\n offset += propsSizeResult.value;\n\n if (filterId === FILTER_LZMA2) {\n // LZMA2 must be the last filter\n lzma2Props = filterProps;\n } else if (filterId === FILTER_DELTA || (filterId >= FILTER_BCJ_X86 && filterId <= FILTER_BCJ_ARM64)) {\n // Preprocessing filter - store for later application\n filters.push({ id: filterId, props: filterProps });\n } else {\n throw new Error(`Unsupported filter: 0x${filterId.toString(16)}`);\n }\n }\n\n if (!lzma2Props) {\n throw new Error('No LZMA2 filter found in XZ block');\n }\n\n // Skip to end of block header (must be aligned to 4 bytes)\n const blockDataStart = blockHeaderStart + blockHeaderSize;\n\n return {\n filters,\n lzma2Props,\n headerSize: blockHeaderSize,\n dataStart: blockDataStart,\n dataEnd: input.length,\n nextOffset: blockDataStart,\n };\n}\n\n/**\n * Parse XZ Index to get block positions\n *\n * XZ Index stores \"Unpadded Size\" for each block which equals:\n * Block Header Size + Compressed Data Size + Check Size\n * (does NOT include padding to 4-byte boundary)\n */\nfunction parseIndex(\n input: Buffer,\n indexStart: number,\n checkSize: number\n): Array<{\n compressedPos: number;\n compressedDataSize: number;\n uncompressedSize: number;\n}> {\n let offset = indexStart;\n\n // Index indicator (0x00)\n if (input[offset] !== 0x00) {\n throw new Error('Invalid index indicator');\n }\n offset++;\n\n // Number of records\n const countResult = decodeMultibyte(input, offset);\n const recordCount = countResult.value;\n offset += countResult.bytesRead;\n\n const records: Array<{\n compressedPos: number;\n unpaddedSize: number;\n compressedDataSize: number;\n uncompressedSize: number;\n }> = [];\n\n // Parse each record\n for (let i = 0; i < recordCount; i++) {\n // Unpadded Size (header + compressed data + check)\n const unpaddedResult = decodeMultibyte(input, offset);\n offset += unpaddedResult.bytesRead;\n\n // Uncompressed size\n const uncompressedResult = decodeMultibyte(input, offset);\n offset += uncompressedResult.bytesRead;\n\n records.push({\n compressedPos: 0, // will be calculated\n unpaddedSize: unpaddedResult.value,\n compressedDataSize: 0, // will be calculated\n uncompressedSize: uncompressedResult.value,\n });\n }\n\n // Calculate actual positions by walking through blocks\n let currentPos = 12; // After stream header\n for (let i = 0; i < records.length; i++) {\n const record = records[i];\n // Record where this block's header starts\n record.compressedPos = currentPos;\n\n // Get block header size from the actual data\n const headerSizeRaw = input[currentPos];\n const headerSize = (headerSizeRaw + 1) * 4;\n\n // Calculate compressed data size from unpadded size\n // unpaddedSize = headerSize + compressedDataSize + checkSize\n record.compressedDataSize = record.unpaddedSize - headerSize - checkSize;\n\n // Move to next block: unpaddedSize + padding to 4-byte boundary\n const paddedSize = Math.ceil(record.unpaddedSize / 4) * 4;\n currentPos += paddedSize;\n }\n\n return records;\n}\n\n/**\n * Pure JS XZ decompression (handles all XZ spec features)\n */\nfunction decodeXZPure(input: Buffer): Buffer {\n // Verify XZ magic\n if (input.length < 12 || !bufferEquals(input, 0, XZ_MAGIC)) {\n throw new Error('Invalid XZ magic bytes');\n }\n\n // Stream flags at offset 6-7\n const checkType = input[7] & 0x0f;\n\n // Check sizes based on check type\n const checkSizes: { [key: number]: number } = {\n 0: 0, // None\n 1: 4, // CRC32\n 4: 8, // CRC64\n 10: 32, // SHA-256\n };\n const checkSize = checkSizes[checkType] ?? 0;\n\n // Find footer by skipping stream padding (null bytes at end before footer)\n // Stream padding must be multiple of 4 bytes\n let footerEnd = input.length;\n while (footerEnd > 12 && input[footerEnd - 1] === 0x00) {\n footerEnd--;\n }\n // Align to 4-byte boundary (stream padding rules)\n while (footerEnd % 4 !== 0 && footerEnd > 12) {\n footerEnd++;\n }\n\n // Verify footer magic (at footerEnd - 2)\n if (!bufferEquals(input, footerEnd - 2, XZ_FOOTER_MAGIC)) {\n throw new Error('Invalid XZ footer magic');\n }\n\n // Get backward size (tells us where index starts) - at footerEnd - 8\n const backwardSize = (input.readUInt32LE(footerEnd - 8) + 1) * 4;\n const indexStart = footerEnd - 12 - backwardSize;\n\n // Parse Index to get block information\n const blockRecords = parseIndex(input, indexStart, checkSize);\n\n // Decompress each block\n const outputChunks: Buffer[] = [];\n let _totalOutputSize = 0;\n\n for (let i = 0; i < blockRecords.length; i++) {\n const record = blockRecords[i];\n const recordStart = record.compressedPos;\n\n // Parse block header\n const blockInfo = parseBlockHeader(input, recordStart, checkSize);\n\n // Extract compressed data for this block\n const dataStart = recordStart + blockInfo.headerSize;\n // compressedDataSize is calculated from the Index's Unpadded Size minus header and check\n const dataEnd = dataStart + record.compressedDataSize;\n\n // Note: XZ blocks have padding AFTER the check field to align to 4 bytes,\n // but the compressedSize from index is exact - no need to strip padding.\n // LZMA2 data includes a 0x00 end marker which must NOT be stripped.\n const compressedData = input.slice(dataStart, dataEnd);\n\n // Decompress this block with LZMA2 (fast path, no buffering)\n let blockOutput = decodeLzma2(compressedData, blockInfo.lzma2Props, record.uncompressedSize) as Buffer;\n\n // Apply preprocessing filters in reverse order (BCJ/Delta applied after LZMA2)\n // Filters are stored in order they were applied during compression,\n // so we need to reverse for decompression\n for (let j = blockInfo.filters.length - 1; j >= 0; j--) {\n blockOutput = applyFilter(blockOutput, blockInfo.filters[j]) as Buffer;\n }\n\n outputChunks.push(blockOutput);\n _totalOutputSize += blockOutput.length;\n }\n\n return Buffer.concat(outputChunks);\n}\n\n/**\n * Decompress XZ data synchronously\n * Uses @napi-rs/lzma if available on Node 14+, falls back to pure JS\n * Properly handles multi-block XZ files, stream padding, and concatenated streams\n * @param input - XZ compressed data\n * @returns Decompressed data\n */\nexport function decodeXZ(input: Buffer): Buffer {\n // Try native acceleration first (Node 14+ with @napi-rs/lzma installed)\n const native = tryLoadNative();\n if (native) {\n try {\n const result = native.xz.decompressSync(input);\n if (result.length > 0) return result;\n } catch {}\n // Fall back to pure JS if native fails (e.g., format mismatch)\n // console.log('Native decodeXZ failed. Defaulting to JavaScript');\n }\n return decodeXZPure(input);\n}\n\n/**\n * Parse XZ stream to get block information (without decompressing)\n * This allows streaming decompression by processing blocks one at a time.\n */\nfunction parseXZIndex(input: Buffer): Array<{\n compressedPos: number;\n compressedDataSize: number;\n uncompressedSize: number;\n checkSize: number;\n}> {\n // Stream header validation\n if (input.length < 12) {\n throw new Error('XZ file too small');\n }\n\n // Stream magic bytes (0xFD, '7zXZ', 0x00)\n if (input[0] !== 0xfd || input[1] !== 0x37 || input[2] !== 0x7a || input[3] !== 0x58 || input[4] !== 0x5a || input[5] !== 0x00) {\n throw new Error('Invalid XZ magic bytes');\n }\n\n // Stream flags at offset 6-7\n const checkType = input[7] & 0x0f;\n\n // Check sizes based on check type\n const checkSizes: { [key: number]: number } = {\n 0: 0, // None\n 1: 4, // CRC32\n 4: 8, // CRC64\n 10: 32, // SHA-256\n };\n const checkSize = checkSizes[checkType] ?? 0;\n\n // Find footer by skipping stream padding\n let footerEnd = input.length;\n while (footerEnd > 12 && input[footerEnd - 1] === 0x00) {\n footerEnd--;\n }\n while (footerEnd % 4 !== 0 && footerEnd > 12) {\n footerEnd++;\n }\n\n // Verify footer magic\n if (!bufferEquals(input, footerEnd - 2, XZ_FOOTER_MAGIC)) {\n throw new Error('Invalid XZ footer magic');\n }\n\n // Get backward size\n const backwardSize = (input.readUInt32LE(footerEnd - 8) + 1) * 4;\n const indexStart = footerEnd - 12 - backwardSize;\n\n // Parse Index to get block information\n return parseIndex(input, indexStart, checkSize).map((record) => ({\n ...record,\n checkSize,\n }));\n}\n\n/**\n * Create an XZ decompression Transform stream\n * @returns Transform stream that decompresses XZ data\n */\nexport function createXZDecoder(): TransformType {\n const chunks: Buffer[] = [];\n\n return new Transform({\n transform(chunk: Buffer, _encoding: string, callback: (error?: Error | null) => void) {\n chunks.push(chunk);\n callback();\n },\n\n flush(callback: (error?: Error | null) => void) {\n try {\n const input = Buffer.concat(chunks);\n\n // Stream decode each block instead of buffering all output\n const blockRecords = parseXZIndex(input);\n\n for (let i = 0; i < blockRecords.length; i++) {\n const record = blockRecords[i];\n const recordStart = record.compressedPos;\n\n // Parse block header\n const blockInfo = parseBlockHeader(input, recordStart, blockRecords[i].checkSize);\n\n // Extract compressed data for this block\n const dataStart = recordStart + blockInfo.headerSize;\n const dataEnd = dataStart + record.compressedDataSize;\n const compressedData = input.slice(dataStart, dataEnd);\n\n // Decompress this block\n let blockOutput = decodeLzma2(compressedData, blockInfo.lzma2Props, record.uncompressedSize) as Buffer;\n\n // Apply preprocessing filters in reverse order\n for (let j = blockInfo.filters.length - 1; j >= 0; j--) {\n blockOutput = applyFilter(blockOutput, blockInfo.filters[j]) as Buffer;\n }\n\n // Push block output immediately instead of buffering\n this.push(blockOutput);\n }\n\n callback();\n } catch (err) {\n callback(err as Error);\n }\n },\n });\n}\n"],"names":["createXZDecoder","decodeXZ","XZ_MAGIC","XZ_FOOTER_MAGIC","FILTER_DELTA","FILTER_BCJ_X86","FILTER_BCJ_PPC","FILTER_BCJ_IA64","FILTER_BCJ_ARM","FILTER_BCJ_ARMT","FILTER_BCJ_SPARC","FILTER_BCJ_ARM64","FILTER_LZMA2","bufferEquals","buf","offset","expected","length","i","decodeMultibyte","value","byte","Error","bytesRead","applyFilter","data","filter","id","decodeBcj","props","decodeBcjArm","decodeBcjArm64","decodeBcjArmt","decodeBcjPpc","decodeBcjSparc","decodeBcjIa64","decodeDelta","toString","parseBlockHeader","input","_checkSize","blockHeaderSizeRaw","blockHeaderSize","blockHeaderStart","blockFlags","numFilters","hasCompressedSize","hasUncompressedSize","result","filters","lzma2Props","filterIdResult","filterId","propsSizeResult","filterProps","slice","push","blockDataStart","headerSize","dataStart","dataEnd","nextOffset","parseIndex","indexStart","checkSize","countResult","recordCount","records","unpaddedResult","uncompressedResult","compressedPos","unpaddedSize","compressedDataSize","uncompressedSize","currentPos","record","headerSizeRaw","paddedSize","Math","ceil","decodeXZPure","checkSizes","checkType","footerEnd","backwardSize","readUInt32LE","blockRecords","outputChunks","_totalOutputSize","recordStart","blockInfo","compressedData","blockOutput","decodeLzma2","j","Buffer","concat","native","tryLoadNative","xz","decompressSync","parseXZIndex","map","chunks","Transform","transform","chunk","_encoding","callback","flush","err"],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;CAmBC;;;;;;;;;;;QA0aeA;eAAAA;;QA3EAC;eAAAA;;;mCA7VU;qBAEA;wBACG;0BACE;yBACD;yBACA;wBACD;0BACE;uBACH;uBACA;wBACE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAE9B,iBAAiB;AACjB,IAAMC,WAAW;IAAC;IAAM;IAAM;IAAM;IAAM;IAAM;CAAK;AACrD,IAAMC,kBAAkB;IAAC;IAAM;CAAK,EAAE,OAAO;AAE7C,qCAAqC;AACrC,IAAMC,eAAe;AACrB,IAAMC,iBAAiB;AACvB,IAAMC,iBAAiB;AACvB,IAAMC,kBAAkB;AACxB,IAAMC,iBAAiB;AACvB,IAAMC,kBAAkB;AACxB,IAAMC,mBAAmB;AACzB,IAAMC,mBAAmB;AACzB,IAAMC,eAAe;AAQrB;;CAEC,GACD,SAASC,aAAaC,GAAW,EAAEC,MAAc,EAAEC,QAAkB;IACnE,IAAID,SAASC,SAASC,MAAM,GAAGH,IAAIG,MAAM,EAAE;QACzC,OAAO;IACT;IACA,IAAK,IAAIC,IAAI,GAAGA,IAAIF,SAASC,MAAM,EAAEC,IAAK;QACxC,IAAIJ,GAAG,CAACC,SAASG,EAAE,KAAKF,QAAQ,CAACE,EAAE,EAAE;YACnC,OAAO;QACT;IACF;IACA,OAAO;AACT;AAEA;;;CAGC,GACD,SAASC,gBAAgBL,GAAW,EAAEC,MAAc;IAClD,IAAIK,QAAQ;IACZ,IAAIF,IAAI;IACR,IAAIG;IACJ,GAAG;QACD,IAAIN,SAASG,KAAKJ,IAAIG,MAAM,EAAE;YAC5B,MAAM,IAAIK,MAAM;QAClB;QACAD,OAAOP,GAAG,CAACC,SAASG,EAAE;QACtBE,SAAS,AAACC,CAAAA,OAAO,IAAG,KAAOH,IAAI;QAC/BA;QACA,IAAIA,IAAI,GAAG;YACT,0CAA0C;YAC1C,MAAM,IAAII,MAAM;QAClB;IACF,QAASD,OAAO,MAAM;IACtB,OAAO;QAAED,OAAAA;QAAOG,WAAWL;IAAE;AAC/B;AAEA;;CAEC,GACD,SAASM,YAAYC,IAAY,EAAEC,MAAkB;IACnD,OAAQA,OAAOC,EAAE;QACf,KAAKtB;YACH,OAAOuB,IAAAA,gBAAS,EAACH,MAAMC,OAAOG,KAAK;QACrC,KAAKrB;YACH,OAAOsB,IAAAA,sBAAY,EAACL,MAAMC,OAAOG,KAAK;QACxC,KAAKlB;YACH,OAAOoB,IAAAA,0BAAc,EAACN,MAAMC,OAAOG,KAAK;QAC1C,KAAKpB;YACH,OAAOuB,IAAAA,wBAAa,EAACP,MAAMC,OAAOG,KAAK;QACzC,KAAKvB;YACH,OAAO2B,IAAAA,sBAAY,EAACR,MAAMC,OAAOG,KAAK;QACxC,KAAKnB;YACH,OAAOwB,IAAAA,0BAAc,EAACT,MAAMC,OAAOG,KAAK;QAC1C,KAAKtB;YACH,OAAO4B,IAAAA,wBAAa,EAACV,MAAMC,OAAOG,KAAK;QACzC,KAAKzB;YACH,OAAOgC,IAAAA,oBAAW,EAACX,MAAMC,OAAOG,KAAK;QACvC;YACE,MAAM,IAAIP,MAAM,AAAC,yBAA+C,OAAvBI,OAAOC,EAAE,CAACU,QAAQ,CAAC;IAChE;AACF;AAEA;;CAEC,GACD,SAASC,iBACPC,KAAa,EACbxB,MAAc,EACdyB,UAAkB;IASlB,oBAAoB;IACpB,IAAMC,qBAAqBF,KAAK,CAACxB,OAAO;IACxC,IAAI0B,uBAAuB,GAAG;QAC5B,MAAM,IAAInB,MAAM;IAClB;IACA,IAAMoB,kBAAkB,AAACD,CAAAA,qBAAqB,CAAA,IAAK;IAEnD,qBAAqB;IACrB,IAAME,mBAAmB5B;IACzBA,UAAU,iBAAiB;IAE3B,IAAM6B,aAAaL,KAAK,CAACxB,SAAS;IAClC,IAAM8B,aAAa,AAACD,CAAAA,aAAa,IAAG,IAAK;IACzC,IAAME,oBAAoB,AAACF,CAAAA,aAAa,IAAG,MAAO;IAClD,IAAMG,sBAAsB,AAACH,CAAAA,aAAa,IAAG,MAAO;IAEpD,sBAAsB;IACtB,IAAIE,mBAAmB;QACrB,IAAME,SAAS7B,gBAAgBoB,OAAOxB;QACtCA,UAAUiC,OAAOzB,SAAS;IAC5B;IAEA,IAAIwB,qBAAqB;QACvB,IAAMC,UAAS7B,gBAAgBoB,OAAOxB;QACtCA,UAAUiC,QAAOzB,SAAS;IAC5B;IAEA,oBAAoB;IACpB,IAAM0B,UAAwB,EAAE;IAChC,IAAIC,aAA4B;IAEhC,IAAK,IAAIhC,IAAI,GAAGA,IAAI2B,YAAY3B,IAAK;QACnC,IAAMiC,iBAAiBhC,gBAAgBoB,OAAOxB;QAC9C,IAAMqC,WAAWD,eAAe/B,KAAK;QACrCL,UAAUoC,eAAe5B,SAAS;QAElC,IAAM8B,kBAAkBlC,gBAAgBoB,OAAOxB;QAC/CA,UAAUsC,gBAAgB9B,SAAS;QAEnC,IAAM+B,cAAcf,MAAMgB,KAAK,CAACxC,QAAQA,SAASsC,gBAAgBjC,KAAK;QACtEL,UAAUsC,gBAAgBjC,KAAK;QAE/B,IAAIgC,aAAaxC,cAAc;YAC7B,gCAAgC;YAChCsC,aAAaI;QACf,OAAO,IAAIF,aAAahD,gBAAiBgD,YAAY/C,kBAAkB+C,YAAYzC,kBAAmB;YACpG,qDAAqD;YACrDsC,QAAQO,IAAI,CAAC;gBAAE7B,IAAIyB;gBAAUvB,OAAOyB;YAAY;QAClD,OAAO;YACL,MAAM,IAAIhC,MAAM,AAAC,yBAA8C,OAAtB8B,SAASf,QAAQ,CAAC;QAC7D;IACF;IAEA,IAAI,CAACa,YAAY;QACf,MAAM,IAAI5B,MAAM;IAClB;IAEA,2DAA2D;IAC3D,IAAMmC,iBAAiBd,mBAAmBD;IAE1C,OAAO;QACLO,SAAAA;QACAC,YAAAA;QACAQ,YAAYhB;QACZiB,WAAWF;QACXG,SAASrB,MAAMtB,MAAM;QACrB4C,YAAYJ;IACd;AACF;AAEA;;;;;;CAMC,GACD,SAASK,WACPvB,KAAa,EACbwB,UAAkB,EAClBC,SAAiB;IAMjB,IAAIjD,SAASgD;IAEb,yBAAyB;IACzB,IAAIxB,KAAK,CAACxB,OAAO,KAAK,MAAM;QAC1B,MAAM,IAAIO,MAAM;IAClB;IACAP;IAEA,oBAAoB;IACpB,IAAMkD,cAAc9C,gBAAgBoB,OAAOxB;IAC3C,IAAMmD,cAAcD,YAAY7C,KAAK;IACrCL,UAAUkD,YAAY1C,SAAS;IAE/B,IAAM4C,UAKD,EAAE;IAEP,oBAAoB;IACpB,IAAK,IAAIjD,IAAI,GAAGA,IAAIgD,aAAahD,IAAK;QACpC,mDAAmD;QACnD,IAAMkD,iBAAiBjD,gBAAgBoB,OAAOxB;QAC9CA,UAAUqD,eAAe7C,SAAS;QAElC,oBAAoB;QACpB,IAAM8C,qBAAqBlD,gBAAgBoB,OAAOxB;QAClDA,UAAUsD,mBAAmB9C,SAAS;QAEtC4C,QAAQX,IAAI,CAAC;YACXc,eAAe;YACfC,cAAcH,eAAehD,KAAK;YAClCoD,oBAAoB;YACpBC,kBAAkBJ,mBAAmBjD,KAAK;QAC5C;IACF;IAEA,uDAAuD;IACvD,IAAIsD,aAAa,IAAI,sBAAsB;IAC3C,IAAK,IAAIxD,KAAI,GAAGA,KAAIiD,QAAQlD,MAAM,EAAEC,KAAK;QACvC,IAAMyD,SAASR,OAAO,CAACjD,GAAE;QACzB,0CAA0C;QAC1CyD,OAAOL,aAAa,GAAGI;QAEvB,6CAA6C;QAC7C,IAAME,gBAAgBrC,KAAK,CAACmC,WAAW;QACvC,IAAMhB,aAAa,AAACkB,CAAAA,gBAAgB,CAAA,IAAK;QAEzC,oDAAoD;QACpD,6DAA6D;QAC7DD,OAAOH,kBAAkB,GAAGG,OAAOJ,YAAY,GAAGb,aAAaM;QAE/D,gEAAgE;QAChE,IAAMa,aAAaC,KAAKC,IAAI,CAACJ,OAAOJ,YAAY,GAAG,KAAK;QACxDG,cAAcG;IAChB;IAEA,OAAOV;AACT;AAEA;;CAEC,GACD,SAASa,aAAazC,KAAa;QAgBf0C;IAflB,kBAAkB;IAClB,IAAI1C,MAAMtB,MAAM,GAAG,MAAM,CAACJ,aAAa0B,OAAO,GAAGrC,WAAW;QAC1D,MAAM,IAAIoB,MAAM;IAClB;IAEA,6BAA6B;IAC7B,IAAM4D,YAAY3C,KAAK,CAAC,EAAE,GAAG;IAE7B,kCAAkC;IAClC,IAAM0C,aAAwC;QAC5C,GAAG;QACH,GAAG;QACH,GAAG;QACH,IAAI;IACN;IACA,IAAMjB,aAAYiB,wBAAAA,UAAU,CAACC,UAAU,cAArBD,mCAAAA,wBAAyB;IAE3C,2EAA2E;IAC3E,6CAA6C;IAC7C,IAAIE,YAAY5C,MAAMtB,MAAM;IAC5B,MAAOkE,YAAY,MAAM5C,KAAK,CAAC4C,YAAY,EAAE,KAAK,KAAM;QACtDA;IACF;IACA,kDAAkD;IAClD,MAAOA,YAAY,MAAM,KAAKA,YAAY,GAAI;QAC5CA;IACF;IAEA,yCAAyC;IACzC,IAAI,CAACtE,aAAa0B,OAAO4C,YAAY,GAAGhF,kBAAkB;QACxD,MAAM,IAAImB,MAAM;IAClB;IAEA,qEAAqE;IACrE,IAAM8D,eAAe,AAAC7C,CAAAA,MAAM8C,YAAY,CAACF,YAAY,KAAK,CAAA,IAAK;IAC/D,IAAMpB,aAAaoB,YAAY,KAAKC;IAEpC,uCAAuC;IACvC,IAAME,eAAexB,WAAWvB,OAAOwB,YAAYC;IAEnD,wBAAwB;IACxB,IAAMuB,eAAyB,EAAE;IACjC,IAAIC,mBAAmB;IAEvB,IAAK,IAAItE,IAAI,GAAGA,IAAIoE,aAAarE,MAAM,EAAEC,IAAK;QAC5C,IAAMyD,SAASW,YAAY,CAACpE,EAAE;QAC9B,IAAMuE,cAAcd,OAAOL,aAAa;QAExC,qBAAqB;QACrB,IAAMoB,YAAYpD,iBAAiBC,OAAOkD,aAAazB;QAEvD,yCAAyC;QACzC,IAAML,YAAY8B,cAAcC,UAAUhC,UAAU;QACpD,yFAAyF;QACzF,IAAME,UAAUD,YAAYgB,OAAOH,kBAAkB;QAErD,0EAA0E;QAC1E,yEAAyE;QACzE,oEAAoE;QACpE,IAAMmB,iBAAiBpD,MAAMgB,KAAK,CAACI,WAAWC;QAE9C,6DAA6D;QAC7D,IAAIgC,cAAcC,IAAAA,oBAAW,EAACF,gBAAgBD,UAAUxC,UAAU,EAAEyB,OAAOF,gBAAgB;QAE3F,+EAA+E;QAC/E,oEAAoE;QACpE,0CAA0C;QAC1C,IAAK,IAAIqB,IAAIJ,UAAUzC,OAAO,CAAChC,MAAM,GAAG,GAAG6E,KAAK,GAAGA,IAAK;YACtDF,cAAcpE,YAAYoE,aAAaF,UAAUzC,OAAO,CAAC6C,EAAE;QAC7D;QAEAP,aAAa/B,IAAI,CAACoC;QAClBJ,oBAAoBI,YAAY3E,MAAM;IACxC;IAEA,OAAO8E,OAAOC,MAAM,CAACT;AACvB;AASO,SAAStF,SAASsC,KAAa;IACpC,wEAAwE;IACxE,IAAM0D,SAASC,IAAAA,uBAAa;IAC5B,IAAID,QAAQ;QACV,IAAI;YACF,IAAMjD,SAASiD,OAAOE,EAAE,CAACC,cAAc,CAAC7D;YACxC,IAAIS,OAAO/B,MAAM,GAAG,GAAG,OAAO+B;QAChC,EAAE,eAAM,CAAC;IACT,+DAA+D;IAC/D,mEAAmE;IACrE;IACA,OAAOgC,aAAazC;AACtB;AAEA;;;CAGC,GACD,SAAS8D,aAAa9D,KAAa;QA0Bf0C;IApBlB,2BAA2B;IAC3B,IAAI1C,MAAMtB,MAAM,GAAG,IAAI;QACrB,MAAM,IAAIK,MAAM;IAClB;IAEA,0CAA0C;IAC1C,IAAIiB,KAAK,CAAC,EAAE,KAAK,QAAQA,KAAK,CAAC,EAAE,KAAK,QAAQA,KAAK,CAAC,EAAE,KAAK,QAAQA,KAAK,CAAC,EAAE,KAAK,QAAQA,KAAK,CAAC,EAAE,KAAK,QAAQA,KAAK,CAAC,EAAE,KAAK,MAAM;QAC9H,MAAM,IAAIjB,MAAM;IAClB;IAEA,6BAA6B;IAC7B,IAAM4D,YAAY3C,KAAK,CAAC,EAAE,GAAG;IAE7B,kCAAkC;IAClC,IAAM0C,aAAwC;QAC5C,GAAG;QACH,GAAG;QACH,GAAG;QACH,IAAI;IACN;IACA,IAAMjB,aAAYiB,wBAAAA,UAAU,CAACC,UAAU,cAArBD,mCAAAA,wBAAyB;IAE3C,yCAAyC;IACzC,IAAIE,YAAY5C,MAAMtB,MAAM;IAC5B,MAAOkE,YAAY,MAAM5C,KAAK,CAAC4C,YAAY,EAAE,KAAK,KAAM;QACtDA;IACF;IACA,MAAOA,YAAY,MAAM,KAAKA,YAAY,GAAI;QAC5CA;IACF;IAEA,sBAAsB;IACtB,IAAI,CAACtE,aAAa0B,OAAO4C,YAAY,GAAGhF,kBAAkB;QACxD,MAAM,IAAImB,MAAM;IAClB;IAEA,oBAAoB;IACpB,IAAM8D,eAAe,AAAC7C,CAAAA,MAAM8C,YAAY,CAACF,YAAY,KAAK,CAAA,IAAK;IAC/D,IAAMpB,aAAaoB,YAAY,KAAKC;IAEpC,uCAAuC;IACvC,OAAOtB,WAAWvB,OAAOwB,YAAYC,WAAWsC,GAAG,CAAC,SAAC3B;eAAY,wCAC5DA;YACHX,WAAAA;;;AAEJ;AAMO,SAAShE;IACd,IAAMuG,SAAmB,EAAE;IAE3B,OAAO,IAAIC,8BAAS,CAAC;QACnBC,WAAAA,SAAAA,UAAUC,KAAa,EAAEC,SAAiB,EAAEC,QAAwC;YAClFL,OAAO/C,IAAI,CAACkD;YACZE;QACF;QAEAC,OAAAA,SAAAA,MAAMD,QAAwC;YAC5C,IAAI;gBACF,IAAMrE,QAAQwD,OAAOC,MAAM,CAACO;gBAE5B,2DAA2D;gBAC3D,IAAMjB,eAAee,aAAa9D;gBAElC,IAAK,IAAIrB,IAAI,GAAGA,IAAIoE,aAAarE,MAAM,EAAEC,IAAK;oBAC5C,IAAMyD,SAASW,YAAY,CAACpE,EAAE;oBAC9B,IAAMuE,cAAcd,OAAOL,aAAa;oBAExC,qBAAqB;oBACrB,IAAMoB,YAAYpD,iBAAiBC,OAAOkD,aAAaH,YAAY,CAACpE,EAAE,CAAC8C,SAAS;oBAEhF,yCAAyC;oBACzC,IAAML,YAAY8B,cAAcC,UAAUhC,UAAU;oBACpD,IAAME,UAAUD,YAAYgB,OAAOH,kBAAkB;oBACrD,IAAMmB,iBAAiBpD,MAAMgB,KAAK,CAACI,WAAWC;oBAE9C,wBAAwB;oBACxB,IAAIgC,cAAcC,IAAAA,oBAAW,EAACF,gBAAgBD,UAAUxC,UAAU,EAAEyB,OAAOF,gBAAgB;oBAE3F,+CAA+C;oBAC/C,IAAK,IAAIqB,IAAIJ,UAAUzC,OAAO,CAAChC,MAAM,GAAG,GAAG6E,KAAK,GAAGA,IAAK;wBACtDF,cAAcpE,YAAYoE,aAAaF,UAAUzC,OAAO,CAAC6C,EAAE;oBAC7D;oBAEA,qDAAqD;oBACrD,IAAI,CAACtC,IAAI,CAACoC;gBACZ;gBAEAgB;YACF,EAAE,OAAOE,KAAK;gBACZF,SAASE;YACX;QACF;IACF;AACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/iterators/xz-compat/src/lzma/lib/Lzma2ChunkParser.ts"],"sourcesContent":["/**\n * LZMA2 Chunk Parser\n *\n * Shared parsing logic for LZMA2 chunk headers.\n * Used by both synchronous and streaming decoders.\n *\n * LZMA2 control byte ranges:\n * 0x00 = End of stream\n * 0x01 = Uncompressed chunk, dictionary reset\n * 0x02 = Uncompressed chunk, no dictionary reset\n * 0x80-0x9F = LZMA chunk, no reset (solid mode)\n * 0xA0-0xBF = LZMA chunk, reset state (probabilities)\n * 0xC0-0xDF = LZMA chunk, reset state + new properties\n * 0xE0-0xFF = LZMA chunk, reset dictionary + state + new properties\n */\n\n/**\n * LZMA properties extracted from chunk header\n */\nexport interface LzmaChunkProps {\n lc: number;\n lp: number;\n pb: number;\n}\n\n/**\n * Parsed LZMA2 chunk information\n */\nexport interface Lzma2Chunk {\n /** Chunk type */\n type: 'end' | 'uncompressed' | 'lzma';\n /** Total bytes consumed by header (including control byte) */\n headerSize: number;\n /** Whether to reset dictionary */\n dictReset: boolean;\n /** Whether to reset state/probabilities */\n stateReset: boolean;\n /** New LZMA properties (only for control >= 0xC0) */\n newProps: LzmaChunkProps | null;\n /** Uncompressed data size */\n uncompSize: number;\n /** Compressed data size (0 for uncompressed chunks) */\n compSize: number;\n}\n\n/**\n * Result of parsing attempt\n */\nexport type ParseResult = { success: true; chunk: Lzma2Chunk } | { success: false; needBytes: number };\n\n/**\n * Parse an LZMA2 chunk header\n *\n * @param input - Input buffer\n * @param offset - Offset to start parsing\n * @returns Parsed chunk info or number of bytes needed\n */\nexport function parseLzma2ChunkHeader(input: Buffer, offset: number): ParseResult {\n if (offset >= input.length) {\n return { success: false, needBytes: 1 };\n }\n\n const control = input[offset];\n\n // End of stream\n if (control === 0x00) {\n return {\n success: true,\n chunk: {\n type: 'end',\n headerSize: 1,\n dictReset: false,\n stateReset: false,\n newProps: null,\n uncompSize: 0,\n compSize: 0,\n },\n };\n }\n\n // Uncompressed chunk\n if (control === 0x01 || control === 0x02) {\n // Need 3 bytes: control + 2 size bytes\n if (offset + 3 > input.length) {\n return { success: false, needBytes: 3 - (input.length - offset) };\n }\n\n const uncompSize = ((input[offset + 1] << 8) | input[offset + 2]) + 1;\n\n return {\n success: true,\n chunk: {\n type: 'uncompressed',\n headerSize: 3,\n dictReset: control === 0x01,\n stateReset: false,\n newProps: null,\n uncompSize,\n compSize: 0,\n },\n };\n }\n\n // LZMA compressed chunk\n if (control >= 0x80) {\n const hasNewProps = control >= 0xc0;\n const minHeaderSize = hasNewProps ? 6 : 5; // control + 2 uncomp + 2 comp + (1 props)\n\n if (offset + minHeaderSize > input.length) {\n return { success: false, needBytes: minHeaderSize - (input.length - offset) };\n }\n\n // Parse sizes\n const uncompHigh = control & 0x1f;\n const uncompSize = ((uncompHigh << 16) | (input[offset + 1] << 8) | input[offset + 2]) + 1;\n const compSize = ((input[offset + 3] << 8) | input[offset + 4]) + 1;\n\n // Parse properties if present\n let newProps: LzmaChunkProps | null = null;\n if (hasNewProps) {\n const propsByte = input[offset + 5];\n const lc = propsByte % 9;\n const remainder = ~~(propsByte / 9);\n const lp = remainder % 5;\n const pb = ~~(remainder / 5);\n newProps = { lc, lp, pb };\n }\n\n return {\n success: true,\n chunk: {\n type: 'lzma',\n headerSize: minHeaderSize,\n dictReset: control >= 0xe0,\n stateReset: control >= 0xa0,\n newProps,\n uncompSize,\n compSize,\n },\n };\n }\n\n // Invalid control byte\n throw new Error(`Invalid LZMA2 control byte: 0x${control.toString(16)}`);\n}\n\n/** Result type for hasCompleteChunk with totalSize included on success */\nexport type CompleteChunkResult = { success: true; chunk: Lzma2Chunk; totalSize: number } | { success: false; needBytes: number };\n\n/**\n * Check if we have enough data for the complete chunk (header + data)\n */\nexport function hasCompleteChunk(input: Buffer, offset: number): CompleteChunkResult {\n const result = parseLzma2ChunkHeader(input, offset);\n\n if (result.success === false) {\n return { success: false, needBytes: result.needBytes };\n }\n\n const { chunk } = result;\n const dataSize = chunk.type === 'uncompressed' ? chunk.uncompSize : chunk.compSize;\n const totalSize = chunk.headerSize + dataSize;\n\n if (offset + totalSize > input.length) {\n return { success: false, needBytes: totalSize - (input.length - offset) };\n }\n\n return { success: true, chunk, totalSize };\n}\n"],"names":["parseLzma2ChunkHeader","input","offset","length","success","needBytes","control","chunk","type","headerSize","dictReset","stateReset","newProps","uncompSize","compSize","hasNewProps","minHeaderSize","uncompHigh","propsByte","lc","remainder","lp","pb","Error","toString","hasCompleteChunk","result","dataSize","totalSize"],"mappings":"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"}
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* decoder with continuation-passing style, which is complex and not worth
|
|
17
17
|
* the effort given LZMA2's chunked format.
|
|
18
18
|
*/ import { allocBufferUnsafe, Transform } from 'extract-base-iterator';
|
|
19
|
-
import { hasCompleteChunk } from '../Lzma2ChunkParser.js';
|
|
19
|
+
import { hasCompleteChunk } from '../lib/Lzma2ChunkParser.js';
|
|
20
20
|
import { LzmaDecoder } from '../sync/LzmaDecoder.js';
|
|
21
21
|
import { parseLzma2DictionarySize } from '../types.js';
|
|
22
22
|
/**
|
|
@@ -37,6 +37,10 @@ import { parseLzma2DictionarySize } from '../types.js';
|
|
|
37
37
|
decoder.setDictionarySize(dictSize);
|
|
38
38
|
// Track current LZMA properties
|
|
39
39
|
let propsSet = false;
|
|
40
|
+
// Store lc/lp/pb for reuse in stream decoder
|
|
41
|
+
let currentLc;
|
|
42
|
+
let currentLp;
|
|
43
|
+
let currentPb;
|
|
40
44
|
// Buffer for incomplete chunk data
|
|
41
45
|
let pending = null;
|
|
42
46
|
let finished = false;
|
|
@@ -90,13 +94,19 @@ import { parseLzma2DictionarySize } from '../types.js';
|
|
|
90
94
|
// Apply new properties if present
|
|
91
95
|
if (chunkInfo.newProps) {
|
|
92
96
|
({ lc, lp, pb } = chunkInfo.newProps);
|
|
97
|
+
// Store properties for reuse in stream decoder
|
|
98
|
+
currentLc = lc;
|
|
99
|
+
currentLp = lp;
|
|
100
|
+
currentPb = pb;
|
|
93
101
|
if (!decoder.setLcLpPb(lc, lp, pb)) {
|
|
94
102
|
throw new Error(`Invalid LZMA properties: lc=${lc} lp=${lp} pb=${pb}`);
|
|
95
103
|
}
|
|
96
104
|
propsSet = true;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
|
|
105
|
+
} else {
|
|
106
|
+
// No new properties, check if we already have them
|
|
107
|
+
if (!propsSet) {
|
|
108
|
+
throw new Error('LZMA chunk without properties');
|
|
109
|
+
}
|
|
100
110
|
}
|
|
101
111
|
// Reset probabilities if state reset
|
|
102
112
|
if (chunkInfo.stateReset) {
|
|
@@ -111,8 +121,10 @@ import { parseLzma2DictionarySize } from '../types.js';
|
|
|
111
121
|
write: (chunk)=>this.push(chunk)
|
|
112
122
|
});
|
|
113
123
|
streamDecoder.setDictionarySize(dictSize);
|
|
114
|
-
//
|
|
115
|
-
|
|
124
|
+
// Set properties from current values (from first chunk or newProps)
|
|
125
|
+
if (currentLc !== undefined && currentLp !== undefined && currentPb !== undefined) {
|
|
126
|
+
streamDecoder.setLcLpPb(currentLc, currentLp, currentPb);
|
|
127
|
+
}
|
|
116
128
|
// Use solid mode based on chunk properties
|
|
117
129
|
streamDecoder.decodeWithSink(compData, 0, chunkInfo.uncompSize, useSolid);
|
|
118
130
|
// Flush any remaining data in the OutWindow
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/iterators/xz-compat/src/lzma/stream/transforms.ts"],"sourcesContent":["/**\n * LZMA Transform Stream Wrappers\n *\n * Provides Transform streams for LZMA1 and LZMA2 decompression.\n *\n * LZMA2 streaming works by buffering until a complete chunk is available,\n * then decoding synchronously. LZMA2 chunks are bounded in size (~2MB max\n * uncompressed), so memory usage is predictable and bounded.\n *\n * Performance Optimization:\n * - Uses OutputSink pattern for zero-copy output during decode\n * - Each decoded byte written directly to stream (not buffered then copied)\n * - ~4x faster than previous buffering approach\n *\n * True byte-by-byte async LZMA streaming would require rewriting the entire\n * decoder with continuation-passing style, which is complex and not worth\n * the effort given LZMA2's chunked format.\n */\n\nimport { allocBufferUnsafe, Transform } from 'extract-base-iterator';\nimport { hasCompleteChunk } from '../Lzma2ChunkParser.ts';\nimport { LzmaDecoder } from '../sync/LzmaDecoder.ts';\nimport { parseLzma2DictionarySize } from '../types.ts';\n\n/**\n * Create an LZMA2 decoder Transform stream\n *\n * This is a streaming decoder that processes LZMA2 chunks incrementally.\n * Memory usage is O(dictionary_size + max_chunk_size) instead of O(folder_size).\n *\n * @param properties - 1-byte LZMA2 properties (dictionary size)\n * @returns Transform stream that decompresses LZMA2 data\n */\nexport function createLzma2Decoder(properties: Buffer | Uint8Array): InstanceType<typeof Transform> {\n if (!properties || properties.length < 1) {\n throw new Error('LZMA2 requires properties byte');\n }\n\n const dictSize = parseLzma2DictionarySize(properties[0]);\n\n // LZMA decoder instance - reused across chunks for solid mode\n const decoder = new LzmaDecoder();\n decoder.setDictionarySize(dictSize);\n\n // Track current LZMA properties\n let propsSet = false;\n\n // Buffer for incomplete chunk data\n let pending: Buffer | null = null;\n let finished = false;\n\n return new Transform({\n transform: function (this: InstanceType<typeof Transform>, chunk: Buffer, _encoding: string, callback: (err?: Error | null) => void) {\n if (finished) {\n callback(null);\n return;\n }\n\n // Combine with pending data\n let input: Buffer;\n if (pending && pending.length > 0) {\n input = Buffer.concat([pending, chunk]);\n pending = null;\n } else {\n input = chunk;\n }\n\n let offset = 0;\n\n try {\n while (offset < input.length && !finished) {\n const result = hasCompleteChunk(input, offset);\n\n if (!result.success) {\n // Need more data\n pending = input.slice(offset);\n break;\n }\n\n const { chunk: chunkInfo, totalSize } = result;\n\n if (chunkInfo.type === 'end') {\n finished = true;\n break;\n }\n\n // Handle dictionary reset\n if (chunkInfo.dictReset) {\n decoder.resetDictionary();\n }\n\n const dataOffset = offset + chunkInfo.headerSize;\n\n if (chunkInfo.type === 'uncompressed') {\n const uncompData = input.slice(dataOffset, dataOffset + chunkInfo.uncompSize);\n this.push(uncompData);\n\n // Feed uncompressed data to dictionary for subsequent LZMA chunks\n decoder.feedUncompressed(uncompData);\n } else {\n // LZMA compressed chunk\n\n // Variables to store properties (used for both decoders)\n let lc: number;\n let lp: number;\n let pb: number;\n\n // Apply new properties if present\n if (chunkInfo.newProps) {\n ({ lc, lp, pb } = chunkInfo.newProps);\n if (!decoder.setLcLpPb(lc, lp, pb)) {\n throw new Error(`Invalid LZMA properties: lc=${lc} lp=${lp} pb=${pb}`);\n }\n propsSet = true;\n }\n\n if (!propsSet) {\n throw new Error('LZMA chunk without properties');\n }\n\n // Reset probabilities if state reset\n if (chunkInfo.stateReset) {\n decoder.resetProbabilities();\n }\n\n // Determine solid mode - preserve dictionary if not resetting state or if only resetting state (not dict)\n const useSolid = !chunkInfo.stateReset || (chunkInfo.stateReset && !chunkInfo.dictReset);\n\n const compData = input.slice(dataOffset, dataOffset + chunkInfo.compSize);\n\n // Enhanced: Use OutputSink for direct emission (zero-copy)\n // Create a decoder with direct stream emission\n const streamDecoder = new LzmaDecoder({\n write: (chunk: Buffer) => this.push(chunk),\n });\n streamDecoder.setDictionarySize(dictSize);\n // Preserve properties from main decoder\n streamDecoder.setLcLpPb(lc, lp, pb);\n\n // Use solid mode based on chunk properties\n streamDecoder.decodeWithSink(compData, 0, chunkInfo.uncompSize, useSolid);\n\n // Flush any remaining data in the OutWindow\n streamDecoder.flushOutWindow();\n }\n\n offset += totalSize;\n }\n\n callback(null);\n } catch (err) {\n callback(err as Error);\n }\n },\n\n flush: function (this: InstanceType<typeof Transform>, callback: (err?: Error | null) => void) {\n if (pending && pending.length > 0 && !finished) {\n callback(new Error('Truncated LZMA2 stream'));\n } else {\n callback(null);\n }\n },\n });\n}\n\n/**\n * Create an LZMA1 decoder Transform stream\n *\n * Note: LZMA1 has no chunk boundaries, so this requires knowing the\n * uncompressed size upfront. The stream buffers all input, then\n * decompresses when complete.\n *\n * For true streaming, use LZMA2 which has built-in chunking.\n *\n * Optimization: Pre-allocates input buffer and copies chunks once,\n * avoiding the double-buffering of Buffer.concat().\n *\n * @param properties - 5-byte LZMA properties\n * @param unpackSize - Expected uncompressed size\n * @returns Transform stream that decompresses LZMA1 data\n */\nexport function createLzmaDecoder(properties: Buffer | Uint8Array, unpackSize: number): InstanceType<typeof Transform> {\n const decoder = new LzmaDecoder();\n decoder.setDecoderProperties(properties);\n\n const chunks: Buffer[] = [];\n let totalSize = 0;\n\n return new Transform({\n transform: function (this: InstanceType<typeof Transform>, chunk: Buffer, _encoding: string, callback: (err?: Error | null) => void) {\n chunks.push(chunk);\n totalSize += chunk.length;\n callback(null);\n },\n\n flush: function (this: InstanceType<typeof Transform>, callback: (err?: Error | null) => void) {\n try {\n // Optimization: Pre-allocate single buffer instead of Buffer.concat()\n // This reduces peak memory usage by ~50% during concatenation\n const input = allocBufferUnsafe(totalSize);\n let offset = 0;\n\n // Copy each chunk into the pre-allocated buffer\n for (let i = 0; i < chunks.length; i++) {\n const chunk = chunks[i];\n chunk.copy(input, offset);\n offset += chunk.length;\n }\n\n // Enhanced: Use OutputSink for direct emission (zero-copy)\n // Create a decoder with direct stream emission\n const streamDecoder = new LzmaDecoder({\n write: (chunk: Buffer) => this.push(chunk),\n });\n streamDecoder.setDecoderProperties(properties);\n streamDecoder.decodeWithSink(input, 0, unpackSize, false);\n\n // Flush any remaining data in the OutWindow\n streamDecoder.flushOutWindow();\n\n callback(null);\n } catch (err) {\n callback(err as Error);\n }\n },\n });\n}\n"],"names":["allocBufferUnsafe","Transform","hasCompleteChunk","LzmaDecoder","parseLzma2DictionarySize","createLzma2Decoder","properties","length","Error","dictSize","decoder","setDictionarySize","propsSet","pending","finished","transform","chunk","_encoding","callback","input","Buffer","concat","offset","result","success","slice","chunkInfo","totalSize","type","dictReset","resetDictionary","dataOffset","headerSize","uncompData","uncompSize","push","feedUncompressed","lc","lp","pb","newProps","setLcLpPb","stateReset","resetProbabilities","useSolid","compData","compSize","streamDecoder","write","decodeWithSink","flushOutWindow","err","flush","createLzmaDecoder","unpackSize","setDecoderProperties","chunks","i","copy"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/iterators/xz-compat/src/lzma/stream/transforms.ts"],"sourcesContent":["/**\n * LZMA Transform Stream Wrappers\n *\n * Provides Transform streams for LZMA1 and LZMA2 decompression.\n *\n * LZMA2 streaming works by buffering until a complete chunk is available,\n * then decoding synchronously. LZMA2 chunks are bounded in size (~2MB max\n * uncompressed), so memory usage is predictable and bounded.\n *\n * Performance Optimization:\n * - Uses OutputSink pattern for zero-copy output during decode\n * - Each decoded byte written directly to stream (not buffered then copied)\n * - ~4x faster than previous buffering approach\n *\n * True byte-by-byte async LZMA streaming would require rewriting the entire\n * decoder with continuation-passing style, which is complex and not worth\n * the effort given LZMA2's chunked format.\n */\n\nimport { allocBufferUnsafe, Transform } from 'extract-base-iterator';\nimport { hasCompleteChunk } from '../lib/Lzma2ChunkParser.ts';\nimport { LzmaDecoder } from '../sync/LzmaDecoder.ts';\nimport { parseLzma2DictionarySize } from '../types.ts';\n\n/**\n * Create an LZMA2 decoder Transform stream\n *\n * This is a streaming decoder that processes LZMA2 chunks incrementally.\n * Memory usage is O(dictionary_size + max_chunk_size) instead of O(folder_size).\n *\n * @param properties - 1-byte LZMA2 properties (dictionary size)\n * @returns Transform stream that decompresses LZMA2 data\n */\nexport function createLzma2Decoder(properties: Buffer | Uint8Array): InstanceType<typeof Transform> {\n if (!properties || properties.length < 1) {\n throw new Error('LZMA2 requires properties byte');\n }\n\n const dictSize = parseLzma2DictionarySize(properties[0]);\n\n // LZMA decoder instance - reused across chunks for solid mode\n const decoder = new LzmaDecoder();\n decoder.setDictionarySize(dictSize);\n\n // Track current LZMA properties\n let propsSet = false;\n\n // Store lc/lp/pb for reuse in stream decoder\n let currentLc: number | undefined;\n let currentLp: number | undefined;\n let currentPb: number | undefined;\n\n // Buffer for incomplete chunk data\n let pending: Buffer | null = null;\n let finished = false;\n\n return new Transform({\n transform: function (this: InstanceType<typeof Transform>, chunk: Buffer, _encoding: string, callback: (err?: Error | null) => void) {\n if (finished) {\n callback(null);\n return;\n }\n\n // Combine with pending data\n let input: Buffer;\n if (pending && pending.length > 0) {\n input = Buffer.concat([pending, chunk]);\n pending = null;\n } else {\n input = chunk;\n }\n\n let offset = 0;\n\n try {\n while (offset < input.length && !finished) {\n const result = hasCompleteChunk(input, offset);\n\n if (!result.success) {\n // Need more data\n pending = input.slice(offset);\n break;\n }\n\n const { chunk: chunkInfo, totalSize } = result;\n\n if (chunkInfo.type === 'end') {\n finished = true;\n break;\n }\n\n // Handle dictionary reset\n if (chunkInfo.dictReset) {\n decoder.resetDictionary();\n }\n\n const dataOffset = offset + chunkInfo.headerSize;\n\n if (chunkInfo.type === 'uncompressed') {\n const uncompData = input.slice(dataOffset, dataOffset + chunkInfo.uncompSize);\n this.push(uncompData);\n\n // Feed uncompressed data to dictionary for subsequent LZMA chunks\n decoder.feedUncompressed(uncompData);\n } else {\n // LZMA compressed chunk\n\n // Variables to store properties (used for both decoders)\n let lc: number;\n let lp: number;\n let pb: number;\n\n // Apply new properties if present\n if (chunkInfo.newProps) {\n ({ lc, lp, pb } = chunkInfo.newProps);\n // Store properties for reuse in stream decoder\n currentLc = lc;\n currentLp = lp;\n currentPb = pb;\n if (!decoder.setLcLpPb(lc, lp, pb)) {\n throw new Error(`Invalid LZMA properties: lc=${lc} lp=${lp} pb=${pb}`);\n }\n propsSet = true;\n } else {\n // No new properties, check if we already have them\n if (!propsSet) {\n throw new Error('LZMA chunk without properties');\n }\n }\n\n // Reset probabilities if state reset\n if (chunkInfo.stateReset) {\n decoder.resetProbabilities();\n }\n\n // Determine solid mode - preserve dictionary if not resetting state or if only resetting state (not dict)\n const useSolid = !chunkInfo.stateReset || (chunkInfo.stateReset && !chunkInfo.dictReset);\n\n const compData = input.slice(dataOffset, dataOffset + chunkInfo.compSize);\n\n // Enhanced: Use OutputSink for direct emission (zero-copy)\n // Create a decoder with direct stream emission\n const streamDecoder = new LzmaDecoder({\n write: (chunk: Buffer) => this.push(chunk),\n });\n streamDecoder.setDictionarySize(dictSize);\n // Set properties from current values (from first chunk or newProps)\n if (currentLc !== undefined && currentLp !== undefined && currentPb !== undefined) {\n streamDecoder.setLcLpPb(currentLc, currentLp, currentPb);\n }\n\n // Use solid mode based on chunk properties\n streamDecoder.decodeWithSink(compData, 0, chunkInfo.uncompSize, useSolid);\n\n // Flush any remaining data in the OutWindow\n streamDecoder.flushOutWindow();\n }\n\n offset += totalSize;\n }\n\n callback(null);\n } catch (err) {\n callback(err as Error);\n }\n },\n\n flush: function (this: InstanceType<typeof Transform>, callback: (err?: Error | null) => void) {\n if (pending && pending.length > 0 && !finished) {\n callback(new Error('Truncated LZMA2 stream'));\n } else {\n callback(null);\n }\n },\n });\n}\n\n/**\n * Create an LZMA1 decoder Transform stream\n *\n * Note: LZMA1 has no chunk boundaries, so this requires knowing the\n * uncompressed size upfront. The stream buffers all input, then\n * decompresses when complete.\n *\n * For true streaming, use LZMA2 which has built-in chunking.\n *\n * Optimization: Pre-allocates input buffer and copies chunks once,\n * avoiding the double-buffering of Buffer.concat().\n *\n * @param properties - 5-byte LZMA properties\n * @param unpackSize - Expected uncompressed size\n * @returns Transform stream that decompresses LZMA1 data\n */\nexport function createLzmaDecoder(properties: Buffer | Uint8Array, unpackSize: number): InstanceType<typeof Transform> {\n const decoder = new LzmaDecoder();\n decoder.setDecoderProperties(properties);\n\n const chunks: Buffer[] = [];\n let totalSize = 0;\n\n return new Transform({\n transform: function (this: InstanceType<typeof Transform>, chunk: Buffer, _encoding: string, callback: (err?: Error | null) => void) {\n chunks.push(chunk);\n totalSize += chunk.length;\n callback(null);\n },\n\n flush: function (this: InstanceType<typeof Transform>, callback: (err?: Error | null) => void) {\n try {\n // Optimization: Pre-allocate single buffer instead of Buffer.concat()\n // This reduces peak memory usage by ~50% during concatenation\n const input = allocBufferUnsafe(totalSize);\n let offset = 0;\n\n // Copy each chunk into the pre-allocated buffer\n for (let i = 0; i < chunks.length; i++) {\n const chunk = chunks[i];\n chunk.copy(input, offset);\n offset += chunk.length;\n }\n\n // Enhanced: Use OutputSink for direct emission (zero-copy)\n // Create a decoder with direct stream emission\n const streamDecoder = new LzmaDecoder({\n write: (chunk: Buffer) => this.push(chunk),\n });\n streamDecoder.setDecoderProperties(properties);\n streamDecoder.decodeWithSink(input, 0, unpackSize, false);\n\n // Flush any remaining data in the OutWindow\n streamDecoder.flushOutWindow();\n\n callback(null);\n } catch (err) {\n callback(err as Error);\n }\n },\n });\n}\n"],"names":["allocBufferUnsafe","Transform","hasCompleteChunk","LzmaDecoder","parseLzma2DictionarySize","createLzma2Decoder","properties","length","Error","dictSize","decoder","setDictionarySize","propsSet","currentLc","currentLp","currentPb","pending","finished","transform","chunk","_encoding","callback","input","Buffer","concat","offset","result","success","slice","chunkInfo","totalSize","type","dictReset","resetDictionary","dataOffset","headerSize","uncompData","uncompSize","push","feedUncompressed","lc","lp","pb","newProps","setLcLpPb","stateReset","resetProbabilities","useSolid","compData","compSize","streamDecoder","write","undefined","decodeWithSink","flushOutWindow","err","flush","createLzmaDecoder","unpackSize","setDecoderProperties","chunks","i","copy"],"mappings":"AAAA;;;;;;;;;;;;;;;;;CAiBC,GAED,SAASA,iBAAiB,EAAEC,SAAS,QAAQ,wBAAwB;AACrE,SAASC,gBAAgB,QAAQ,6BAA6B;AAC9D,SAASC,WAAW,QAAQ,yBAAyB;AACrD,SAASC,wBAAwB,QAAQ,cAAc;AAEvD;;;;;;;;CAQC,GACD,OAAO,SAASC,mBAAmBC,UAA+B;IAChE,IAAI,CAACA,cAAcA,WAAWC,MAAM,GAAG,GAAG;QACxC,MAAM,IAAIC,MAAM;IAClB;IAEA,MAAMC,WAAWL,yBAAyBE,UAAU,CAAC,EAAE;IAEvD,8DAA8D;IAC9D,MAAMI,UAAU,IAAIP;IACpBO,QAAQC,iBAAiB,CAACF;IAE1B,gCAAgC;IAChC,IAAIG,WAAW;IAEf,6CAA6C;IAC7C,IAAIC;IACJ,IAAIC;IACJ,IAAIC;IAEJ,mCAAmC;IACnC,IAAIC,UAAyB;IAC7B,IAAIC,WAAW;IAEf,OAAO,IAAIhB,UAAU;QACnBiB,WAAW,SAAgDC,KAAa,EAAEC,SAAiB,EAAEC,QAAsC;YACjI,IAAIJ,UAAU;gBACZI,SAAS;gBACT;YACF;YAEA,4BAA4B;YAC5B,IAAIC;YACJ,IAAIN,WAAWA,QAAQT,MAAM,GAAG,GAAG;gBACjCe,QAAQC,OAAOC,MAAM,CAAC;oBAACR;oBAASG;iBAAM;gBACtCH,UAAU;YACZ,OAAO;gBACLM,QAAQH;YACV;YAEA,IAAIM,SAAS;YAEb,IAAI;gBACF,MAAOA,SAASH,MAAMf,MAAM,IAAI,CAACU,SAAU;oBACzC,MAAMS,SAASxB,iBAAiBoB,OAAOG;oBAEvC,IAAI,CAACC,OAAOC,OAAO,EAAE;wBACnB,iBAAiB;wBACjBX,UAAUM,MAAMM,KAAK,CAACH;wBACtB;oBACF;oBAEA,MAAM,EAAEN,OAAOU,SAAS,EAAEC,SAAS,EAAE,GAAGJ;oBAExC,IAAIG,UAAUE,IAAI,KAAK,OAAO;wBAC5Bd,WAAW;wBACX;oBACF;oBAEA,0BAA0B;oBAC1B,IAAIY,UAAUG,SAAS,EAAE;wBACvBtB,QAAQuB,eAAe;oBACzB;oBAEA,MAAMC,aAAaT,SAASI,UAAUM,UAAU;oBAEhD,IAAIN,UAAUE,IAAI,KAAK,gBAAgB;wBACrC,MAAMK,aAAad,MAAMM,KAAK,CAACM,YAAYA,aAAaL,UAAUQ,UAAU;wBAC5E,IAAI,CAACC,IAAI,CAACF;wBAEV,kEAAkE;wBAClE1B,QAAQ6B,gBAAgB,CAACH;oBAC3B,OAAO;wBACL,wBAAwB;wBAExB,yDAAyD;wBACzD,IAAII;wBACJ,IAAIC;wBACJ,IAAIC;wBAEJ,kCAAkC;wBAClC,IAAIb,UAAUc,QAAQ,EAAE;4BACrB,CAAA,EAAEH,EAAE,EAAEC,EAAE,EAAEC,EAAE,EAAE,GAAGb,UAAUc,QAAQ,AAAD;4BACnC,+CAA+C;4BAC/C9B,YAAY2B;4BACZ1B,YAAY2B;4BACZ1B,YAAY2B;4BACZ,IAAI,CAAChC,QAAQkC,SAAS,CAACJ,IAAIC,IAAIC,KAAK;gCAClC,MAAM,IAAIlC,MAAM,CAAC,4BAA4B,EAAEgC,GAAG,IAAI,EAAEC,GAAG,IAAI,EAAEC,IAAI;4BACvE;4BACA9B,WAAW;wBACb,OAAO;4BACL,mDAAmD;4BACnD,IAAI,CAACA,UAAU;gCACb,MAAM,IAAIJ,MAAM;4BAClB;wBACF;wBAEA,qCAAqC;wBACrC,IAAIqB,UAAUgB,UAAU,EAAE;4BACxBnC,QAAQoC,kBAAkB;wBAC5B;wBAEA,0GAA0G;wBAC1G,MAAMC,WAAW,CAAClB,UAAUgB,UAAU,IAAKhB,UAAUgB,UAAU,IAAI,CAAChB,UAAUG,SAAS;wBAEvF,MAAMgB,WAAW1B,MAAMM,KAAK,CAACM,YAAYA,aAAaL,UAAUoB,QAAQ;wBAExE,2DAA2D;wBAC3D,+CAA+C;wBAC/C,MAAMC,gBAAgB,IAAI/C,YAAY;4BACpCgD,OAAO,CAAChC,QAAkB,IAAI,CAACmB,IAAI,CAACnB;wBACtC;wBACA+B,cAAcvC,iBAAiB,CAACF;wBAChC,oEAAoE;wBACpE,IAAII,cAAcuC,aAAatC,cAAcsC,aAAarC,cAAcqC,WAAW;4BACjFF,cAAcN,SAAS,CAAC/B,WAAWC,WAAWC;wBAChD;wBAEA,2CAA2C;wBAC3CmC,cAAcG,cAAc,CAACL,UAAU,GAAGnB,UAAUQ,UAAU,EAAEU;wBAEhE,4CAA4C;wBAC5CG,cAAcI,cAAc;oBAC9B;oBAEA7B,UAAUK;gBACZ;gBAEAT,SAAS;YACX,EAAE,OAAOkC,KAAK;gBACZlC,SAASkC;YACX;QACF;QAEAC,OAAO,SAAgDnC,QAAsC;YAC3F,IAAIL,WAAWA,QAAQT,MAAM,GAAG,KAAK,CAACU,UAAU;gBAC9CI,SAAS,IAAIb,MAAM;YACrB,OAAO;gBACLa,SAAS;YACX;QACF;IACF;AACF;AAEA;;;;;;;;;;;;;;;CAeC,GACD,OAAO,SAASoC,kBAAkBnD,UAA+B,EAAEoD,UAAkB;IACnF,MAAMhD,UAAU,IAAIP;IACpBO,QAAQiD,oBAAoB,CAACrD;IAE7B,MAAMsD,SAAmB,EAAE;IAC3B,IAAI9B,YAAY;IAEhB,OAAO,IAAI7B,UAAU;QACnBiB,WAAW,SAAgDC,KAAa,EAAEC,SAAiB,EAAEC,QAAsC;YACjIuC,OAAOtB,IAAI,CAACnB;YACZW,aAAaX,MAAMZ,MAAM;YACzBc,SAAS;QACX;QAEAmC,OAAO,SAAgDnC,QAAsC;YAC3F,IAAI;gBACF,sEAAsE;gBACtE,8DAA8D;gBAC9D,MAAMC,QAAQtB,kBAAkB8B;gBAChC,IAAIL,SAAS;gBAEb,gDAAgD;gBAChD,IAAK,IAAIoC,IAAI,GAAGA,IAAID,OAAOrD,MAAM,EAAEsD,IAAK;oBACtC,MAAM1C,QAAQyC,MAAM,CAACC,EAAE;oBACvB1C,MAAM2C,IAAI,CAACxC,OAAOG;oBAClBA,UAAUN,MAAMZ,MAAM;gBACxB;gBAEA,2DAA2D;gBAC3D,+CAA+C;gBAC/C,MAAM2C,gBAAgB,IAAI/C,YAAY;oBACpCgD,OAAO,CAAChC,QAAkB,IAAI,CAACmB,IAAI,CAACnB;gBACtC;gBACA+B,cAAcS,oBAAoB,CAACrD;gBACnC4C,cAAcG,cAAc,CAAC/B,OAAO,GAAGoC,YAAY;gBAEnD,4CAA4C;gBAC5CR,cAAcI,cAAc;gBAE5BjC,SAAS;YACX,EAAE,OAAOkC,KAAK;gBACZlC,SAASkC;YACX;QACF;IACF;AACF"}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* LZMA2 is a container format that wraps LZMA chunks with framing.
|
|
5
5
|
* Decodes LZMA2 data from a buffer.
|
|
6
6
|
*/ import { allocBufferUnsafe } from 'extract-base-iterator';
|
|
7
|
-
import { parseLzma2ChunkHeader } from '../Lzma2ChunkParser.js';
|
|
7
|
+
import { parseLzma2ChunkHeader } from '../lib/Lzma2ChunkParser.js';
|
|
8
8
|
import { parseLzma2DictionarySize } from '../types.js';
|
|
9
9
|
import { LzmaDecoder } from './LzmaDecoder.js';
|
|
10
10
|
/**
|
|
@@ -81,9 +81,11 @@ import { LzmaDecoder } from './LzmaDecoder.js';
|
|
|
81
81
|
throw new Error(`Invalid LZMA properties: lc=${lc} lp=${lp} pb=${pb}`);
|
|
82
82
|
}
|
|
83
83
|
this.propsSet = true;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
} else {
|
|
85
|
+
// No new properties, check if we already have them
|
|
86
|
+
if (!this.propsSet) {
|
|
87
|
+
throw new Error('LZMA chunk without properties');
|
|
88
|
+
}
|
|
87
89
|
}
|
|
88
90
|
// Reset probabilities if state reset
|
|
89
91
|
if (chunk.stateReset) {
|
|
@@ -154,9 +156,11 @@ import { LzmaDecoder } from './LzmaDecoder.js';
|
|
|
154
156
|
throw new Error(`Invalid LZMA properties: lc=${lc} lp=${lp} pb=${pb}`);
|
|
155
157
|
}
|
|
156
158
|
this.propsSet = true;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
|
|
159
|
+
} else {
|
|
160
|
+
// No new properties, check if we already have them
|
|
161
|
+
if (!this.propsSet) {
|
|
162
|
+
throw new Error('LZMA chunk without properties');
|
|
163
|
+
}
|
|
160
164
|
}
|
|
161
165
|
// Reset probabilities if state reset
|
|
162
166
|
if (chunk.stateReset) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/iterators/xz-compat/src/lzma/sync/Lzma2Decoder.ts"],"sourcesContent":["/**\n * Synchronous LZMA2 Decoder\n *\n * LZMA2 is a container format that wraps LZMA chunks with framing.\n * Decodes LZMA2 data from a buffer.\n */\n\nimport { allocBufferUnsafe } from 'extract-base-iterator';\nimport { parseLzma2ChunkHeader } from '../Lzma2ChunkParser.ts';\nimport { type OutputSink, parseLzma2DictionarySize } from '../types.ts';\nimport { LzmaDecoder } from './LzmaDecoder.ts';\n\n/**\n * Synchronous LZMA2 decoder\n */\nexport class Lzma2Decoder {\n private lzmaDecoder: LzmaDecoder;\n private dictionarySize: number;\n private propsSet: boolean;\n\n constructor(properties: Buffer | Uint8Array, outputSink?: OutputSink) {\n if (!properties || properties.length < 1) {\n throw new Error('LZMA2 requires properties byte');\n }\n\n this.dictionarySize = parseLzma2DictionarySize(properties[0]);\n this.lzmaDecoder = new LzmaDecoder(outputSink);\n this.lzmaDecoder.setDictionarySize(this.dictionarySize);\n this.propsSet = false;\n }\n\n /**\n * Reset the dictionary (for stream boundaries)\n */\n resetDictionary(): void {\n this.lzmaDecoder.resetDictionary();\n }\n\n /**\n * Reset all probability models (for stream boundaries)\n */\n resetProbabilities(): void {\n this.lzmaDecoder.resetProbabilities();\n }\n\n /**\n * Set LZMA properties\n */\n setLcLpPb(lc: number, lp: number, pb: number): boolean {\n return this.lzmaDecoder.setLcLpPb(lc, lp, pb);\n }\n\n /**\n * Feed uncompressed data to the dictionary (for subsequent LZMA chunks)\n */\n feedUncompressed(data: Buffer): void {\n this.lzmaDecoder.feedUncompressed(data);\n }\n\n /**\n * Decode raw LZMA data (used internally for LZMA2 chunks)\n * @param input - LZMA compressed data\n * @param offset - Input offset\n * @param outSize - Expected output size\n * @param solid - Use solid mode\n * @returns Decompressed data\n */\n decodeLzmaData(input: Buffer, offset: number, outSize: number, solid = false): Buffer {\n return this.lzmaDecoder.decode(input, offset, outSize, solid);\n }\n\n /**\n * Decode LZMA2 data with streaming output\n * @param input - LZMA2 compressed data\n * @returns Total number of bytes written to sink\n */\n decodeWithSink(input: Buffer): number {\n let totalBytes = 0;\n let offset = 0;\n\n while (offset < input.length) {\n const result = parseLzma2ChunkHeader(input, offset);\n\n if (!result.success) {\n throw new Error('Truncated LZMA2 chunk header');\n }\n\n const chunk = result.chunk;\n\n if (chunk.type === 'end') {\n break;\n }\n\n // Validate we have enough data for the chunk\n const dataSize = chunk.type === 'uncompressed' ? chunk.uncompSize : chunk.compSize;\n if (offset + chunk.headerSize + dataSize > input.length) {\n throw new Error(`Truncated LZMA2 ${chunk.type} data`);\n }\n\n // Handle dictionary reset\n if (chunk.dictReset) {\n this.lzmaDecoder.resetDictionary();\n }\n\n const dataOffset = offset + chunk.headerSize;\n\n if (chunk.type === 'uncompressed') {\n const uncompData = input.slice(dataOffset, dataOffset + chunk.uncompSize);\n\n // Feed uncompressed data to dictionary so subsequent LZMA chunks can reference it\n this.lzmaDecoder.feedUncompressed(uncompData);\n\n totalBytes += uncompData.length;\n offset = dataOffset + chunk.uncompSize;\n } else {\n // LZMA compressed chunk\n\n // Apply new properties if present\n if (chunk.newProps) {\n const { lc, lp, pb } = chunk.newProps;\n if (!this.lzmaDecoder.setLcLpPb(lc, lp, pb)) {\n throw new Error(`Invalid LZMA properties: lc=${lc} lp=${lp} pb=${pb}`);\n }\n this.propsSet = true;\n }\n\n if (!this.propsSet) {\n throw new Error('LZMA chunk without properties');\n }\n\n // Reset probabilities if state reset\n if (chunk.stateReset) {\n this.lzmaDecoder.resetProbabilities();\n }\n\n // Determine solid mode\n const useSolid = !chunk.stateReset || (chunk.stateReset && !chunk.dictReset);\n\n // Decode LZMA chunk directly to sink\n totalBytes += this.lzmaDecoder.decodeWithSink(input, dataOffset, chunk.uncompSize, useSolid);\n\n offset = dataOffset + chunk.compSize;\n }\n }\n\n // Flush any remaining data in the OutWindow\n this.lzmaDecoder.flushOutWindow();\n\n return totalBytes;\n }\n\n /**\n * Decode LZMA2 data\n * @param input - LZMA2 compressed data\n * @param unpackSize - Expected output size (optional, for pre-allocation)\n * @returns Decompressed data\n */\n decode(input: Buffer, unpackSize?: number): Buffer {\n // Pre-allocate output buffer if size is known\n let outputBuffer: Buffer | null = null;\n let outputPos = 0;\n const outputChunks: Buffer[] = [];\n\n if (unpackSize && unpackSize > 0) {\n outputBuffer = allocBufferUnsafe(unpackSize);\n }\n\n let offset = 0;\n\n while (offset < input.length) {\n const result = parseLzma2ChunkHeader(input, offset);\n\n if (!result.success) {\n throw new Error('Truncated LZMA2 chunk header');\n }\n\n const chunk = result.chunk;\n\n if (chunk.type === 'end') {\n break;\n }\n\n // Validate we have enough data for the chunk\n const dataSize = chunk.type === 'uncompressed' ? chunk.uncompSize : chunk.compSize;\n if (offset + chunk.headerSize + dataSize > input.length) {\n throw new Error(`Truncated LZMA2 ${chunk.type} data`);\n }\n\n // Handle dictionary reset\n if (chunk.dictReset) {\n this.lzmaDecoder.resetDictionary();\n }\n\n const dataOffset = offset + chunk.headerSize;\n\n if (chunk.type === 'uncompressed') {\n const uncompData = input.slice(dataOffset, dataOffset + chunk.uncompSize);\n\n // Copy to output\n if (outputBuffer) {\n uncompData.copy(outputBuffer, outputPos);\n outputPos += uncompData.length;\n } else {\n outputChunks.push(uncompData);\n }\n\n // Feed uncompressed data to dictionary so subsequent LZMA chunks can reference it\n this.lzmaDecoder.feedUncompressed(uncompData);\n\n offset = dataOffset + chunk.uncompSize;\n } else {\n // LZMA compressed chunk\n\n // Apply new properties if present\n if (chunk.newProps) {\n const { lc, lp, pb } = chunk.newProps;\n if (!this.lzmaDecoder.setLcLpPb(lc, lp, pb)) {\n throw new Error(`Invalid LZMA properties: lc=${lc} lp=${lp} pb=${pb}`);\n }\n this.propsSet = true;\n }\n\n if (!this.propsSet) {\n throw new Error('LZMA chunk without properties');\n }\n\n // Reset probabilities if state reset\n if (chunk.stateReset) {\n this.lzmaDecoder.resetProbabilities();\n }\n\n // Determine solid mode - preserve dictionary if not resetting state or if only resetting state (not dict)\n const useSolid = !chunk.stateReset || (chunk.stateReset && !chunk.dictReset);\n\n // Decode LZMA chunk - use zero-copy when we have pre-allocated buffer\n if (outputBuffer) {\n // Zero-copy: decode directly into caller's buffer\n const bytesWritten = this.lzmaDecoder.decodeToBuffer(input, dataOffset, chunk.uncompSize, outputBuffer, outputPos, useSolid);\n outputPos += bytesWritten;\n } else {\n // No pre-allocation: decode to new buffer and collect chunks\n const chunkData = input.slice(dataOffset, dataOffset + chunk.compSize);\n const decoded = this.lzmaDecoder.decode(chunkData, 0, chunk.uncompSize, useSolid);\n outputChunks.push(decoded);\n }\n\n offset = dataOffset + chunk.compSize;\n }\n }\n\n // Return pre-allocated buffer or concatenated chunks\n if (outputBuffer) {\n return outputPos < outputBuffer.length ? outputBuffer.slice(0, outputPos) : outputBuffer;\n }\n return Buffer.concat(outputChunks);\n }\n}\n\n/**\n * Decode LZMA2 data synchronously\n * @param input - LZMA2 compressed data\n * @param properties - 1-byte properties (dictionary size)\n * @param unpackSize - Expected output size (optional, autodetects if not provided)\n * @param outputSink - Optional output sink with write callback for streaming (returns bytes written)\n * @returns Decompressed data (or bytes written if outputSink provided)\n */\nexport function decodeLzma2(input: Buffer, properties: Buffer | Uint8Array, unpackSize?: number, outputSink?: { write(buffer: Buffer): void }): Buffer | number {\n const decoder = new Lzma2Decoder(properties, outputSink as OutputSink);\n if (outputSink) {\n // Zero-copy mode: write to sink during decode\n return decoder.decodeWithSink(input);\n }\n // Buffering mode: returns Buffer (zero-copy)\n return decoder.decode(input, unpackSize);\n}\n"],"names":["allocBufferUnsafe","parseLzma2ChunkHeader","parseLzma2DictionarySize","LzmaDecoder","Lzma2Decoder","resetDictionary","lzmaDecoder","resetProbabilities","setLcLpPb","lc","lp","pb","feedUncompressed","data","decodeLzmaData","input","offset","outSize","solid","decode","decodeWithSink","totalBytes","length","result","success","Error","chunk","type","dataSize","uncompSize","compSize","headerSize","dictReset","dataOffset","uncompData","slice","newProps","propsSet","stateReset","useSolid","flushOutWindow","unpackSize","outputBuffer","outputPos","outputChunks","copy","push","bytesWritten","decodeToBuffer","chunkData","decoded","Buffer","concat","properties","outputSink","dictionarySize","setDictionarySize","decodeLzma2","decoder"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/iterators/xz-compat/src/lzma/sync/Lzma2Decoder.ts"],"sourcesContent":["/**\n * Synchronous LZMA2 Decoder\n *\n * LZMA2 is a container format that wraps LZMA chunks with framing.\n * Decodes LZMA2 data from a buffer.\n */\n\nimport { allocBufferUnsafe } from 'extract-base-iterator';\nimport { parseLzma2ChunkHeader } from '../lib/Lzma2ChunkParser.ts';\nimport { type OutputSink, parseLzma2DictionarySize } from '../types.ts';\nimport { LzmaDecoder } from './LzmaDecoder.ts';\n\n/**\n * Synchronous LZMA2 decoder\n */\nexport class Lzma2Decoder {\n private lzmaDecoder: LzmaDecoder;\n private dictionarySize: number;\n private propsSet: boolean;\n\n constructor(properties: Buffer | Uint8Array, outputSink?: OutputSink) {\n if (!properties || properties.length < 1) {\n throw new Error('LZMA2 requires properties byte');\n }\n\n this.dictionarySize = parseLzma2DictionarySize(properties[0]);\n this.lzmaDecoder = new LzmaDecoder(outputSink);\n this.lzmaDecoder.setDictionarySize(this.dictionarySize);\n this.propsSet = false;\n }\n\n /**\n * Reset the dictionary (for stream boundaries)\n */\n resetDictionary(): void {\n this.lzmaDecoder.resetDictionary();\n }\n\n /**\n * Reset all probability models (for stream boundaries)\n */\n resetProbabilities(): void {\n this.lzmaDecoder.resetProbabilities();\n }\n\n /**\n * Set LZMA properties\n */\n setLcLpPb(lc: number, lp: number, pb: number): boolean {\n return this.lzmaDecoder.setLcLpPb(lc, lp, pb);\n }\n\n /**\n * Feed uncompressed data to the dictionary (for subsequent LZMA chunks)\n */\n feedUncompressed(data: Buffer): void {\n this.lzmaDecoder.feedUncompressed(data);\n }\n\n /**\n * Decode raw LZMA data (used internally for LZMA2 chunks)\n * @param input - LZMA compressed data\n * @param offset - Input offset\n * @param outSize - Expected output size\n * @param solid - Use solid mode\n * @returns Decompressed data\n */\n decodeLzmaData(input: Buffer, offset: number, outSize: number, solid = false): Buffer {\n return this.lzmaDecoder.decode(input, offset, outSize, solid);\n }\n\n /**\n * Decode LZMA2 data with streaming output\n * @param input - LZMA2 compressed data\n * @returns Total number of bytes written to sink\n */\n decodeWithSink(input: Buffer): number {\n let totalBytes = 0;\n let offset = 0;\n\n while (offset < input.length) {\n const result = parseLzma2ChunkHeader(input, offset);\n\n if (!result.success) {\n throw new Error('Truncated LZMA2 chunk header');\n }\n\n const chunk = result.chunk;\n\n if (chunk.type === 'end') {\n break;\n }\n\n // Validate we have enough data for the chunk\n const dataSize = chunk.type === 'uncompressed' ? chunk.uncompSize : chunk.compSize;\n if (offset + chunk.headerSize + dataSize > input.length) {\n throw new Error(`Truncated LZMA2 ${chunk.type} data`);\n }\n\n // Handle dictionary reset\n if (chunk.dictReset) {\n this.lzmaDecoder.resetDictionary();\n }\n\n const dataOffset = offset + chunk.headerSize;\n\n if (chunk.type === 'uncompressed') {\n const uncompData = input.slice(dataOffset, dataOffset + chunk.uncompSize);\n\n // Feed uncompressed data to dictionary so subsequent LZMA chunks can reference it\n this.lzmaDecoder.feedUncompressed(uncompData);\n\n totalBytes += uncompData.length;\n offset = dataOffset + chunk.uncompSize;\n } else {\n // LZMA compressed chunk\n\n // Apply new properties if present\n if (chunk.newProps) {\n const { lc, lp, pb } = chunk.newProps;\n if (!this.lzmaDecoder.setLcLpPb(lc, lp, pb)) {\n throw new Error(`Invalid LZMA properties: lc=${lc} lp=${lp} pb=${pb}`);\n }\n this.propsSet = true;\n } else {\n // No new properties, check if we already have them\n if (!this.propsSet) {\n throw new Error('LZMA chunk without properties');\n }\n }\n\n // Reset probabilities if state reset\n if (chunk.stateReset) {\n this.lzmaDecoder.resetProbabilities();\n }\n\n // Determine solid mode\n const useSolid = !chunk.stateReset || (chunk.stateReset && !chunk.dictReset);\n\n // Decode LZMA chunk directly to sink\n totalBytes += this.lzmaDecoder.decodeWithSink(input, dataOffset, chunk.uncompSize, useSolid);\n\n offset = dataOffset + chunk.compSize;\n }\n }\n\n // Flush any remaining data in the OutWindow\n this.lzmaDecoder.flushOutWindow();\n\n return totalBytes;\n }\n\n /**\n * Decode LZMA2 data\n * @param input - LZMA2 compressed data\n * @param unpackSize - Expected output size (optional, for pre-allocation)\n * @returns Decompressed data\n */\n decode(input: Buffer, unpackSize?: number): Buffer {\n // Pre-allocate output buffer if size is known\n let outputBuffer: Buffer | null = null;\n let outputPos = 0;\n const outputChunks: Buffer[] = [];\n\n if (unpackSize && unpackSize > 0) {\n outputBuffer = allocBufferUnsafe(unpackSize);\n }\n\n let offset = 0;\n\n while (offset < input.length) {\n const result = parseLzma2ChunkHeader(input, offset);\n\n if (!result.success) {\n throw new Error('Truncated LZMA2 chunk header');\n }\n\n const chunk = result.chunk;\n\n if (chunk.type === 'end') {\n break;\n }\n\n // Validate we have enough data for the chunk\n const dataSize = chunk.type === 'uncompressed' ? chunk.uncompSize : chunk.compSize;\n if (offset + chunk.headerSize + dataSize > input.length) {\n throw new Error(`Truncated LZMA2 ${chunk.type} data`);\n }\n\n // Handle dictionary reset\n if (chunk.dictReset) {\n this.lzmaDecoder.resetDictionary();\n }\n\n const dataOffset = offset + chunk.headerSize;\n\n if (chunk.type === 'uncompressed') {\n const uncompData = input.slice(dataOffset, dataOffset + chunk.uncompSize);\n\n // Copy to output\n if (outputBuffer) {\n uncompData.copy(outputBuffer, outputPos);\n outputPos += uncompData.length;\n } else {\n outputChunks.push(uncompData);\n }\n\n // Feed uncompressed data to dictionary so subsequent LZMA chunks can reference it\n this.lzmaDecoder.feedUncompressed(uncompData);\n\n offset = dataOffset + chunk.uncompSize;\n } else {\n // LZMA compressed chunk\n\n // Apply new properties if present\n if (chunk.newProps) {\n const { lc, lp, pb } = chunk.newProps;\n if (!this.lzmaDecoder.setLcLpPb(lc, lp, pb)) {\n throw new Error(`Invalid LZMA properties: lc=${lc} lp=${lp} pb=${pb}`);\n }\n this.propsSet = true;\n } else {\n // No new properties, check if we already have them\n if (!this.propsSet) {\n throw new Error('LZMA chunk without properties');\n }\n }\n\n // Reset probabilities if state reset\n if (chunk.stateReset) {\n this.lzmaDecoder.resetProbabilities();\n }\n\n // Determine solid mode - preserve dictionary if not resetting state or if only resetting state (not dict)\n const useSolid = !chunk.stateReset || (chunk.stateReset && !chunk.dictReset);\n\n // Decode LZMA chunk - use zero-copy when we have pre-allocated buffer\n if (outputBuffer) {\n // Zero-copy: decode directly into caller's buffer\n const bytesWritten = this.lzmaDecoder.decodeToBuffer(input, dataOffset, chunk.uncompSize, outputBuffer, outputPos, useSolid);\n outputPos += bytesWritten;\n } else {\n // No pre-allocation: decode to new buffer and collect chunks\n const chunkData = input.slice(dataOffset, dataOffset + chunk.compSize);\n const decoded = this.lzmaDecoder.decode(chunkData, 0, chunk.uncompSize, useSolid);\n outputChunks.push(decoded);\n }\n\n offset = dataOffset + chunk.compSize;\n }\n }\n\n // Return pre-allocated buffer or concatenated chunks\n if (outputBuffer) {\n return outputPos < outputBuffer.length ? outputBuffer.slice(0, outputPos) : outputBuffer;\n }\n return Buffer.concat(outputChunks);\n }\n}\n\n/**\n * Decode LZMA2 data synchronously\n * @param input - LZMA2 compressed data\n * @param properties - 1-byte properties (dictionary size)\n * @param unpackSize - Expected output size (optional, autodetects if not provided)\n * @param outputSink - Optional output sink with write callback for streaming (returns bytes written)\n * @returns Decompressed data (or bytes written if outputSink provided)\n */\nexport function decodeLzma2(input: Buffer, properties: Buffer | Uint8Array, unpackSize?: number, outputSink?: { write(buffer: Buffer): void }): Buffer | number {\n const decoder = new Lzma2Decoder(properties, outputSink as OutputSink);\n if (outputSink) {\n // Zero-copy mode: write to sink during decode\n return decoder.decodeWithSink(input);\n }\n // Buffering mode: returns Buffer (zero-copy)\n return decoder.decode(input, unpackSize);\n}\n"],"names":["allocBufferUnsafe","parseLzma2ChunkHeader","parseLzma2DictionarySize","LzmaDecoder","Lzma2Decoder","resetDictionary","lzmaDecoder","resetProbabilities","setLcLpPb","lc","lp","pb","feedUncompressed","data","decodeLzmaData","input","offset","outSize","solid","decode","decodeWithSink","totalBytes","length","result","success","Error","chunk","type","dataSize","uncompSize","compSize","headerSize","dictReset","dataOffset","uncompData","slice","newProps","propsSet","stateReset","useSolid","flushOutWindow","unpackSize","outputBuffer","outputPos","outputChunks","copy","push","bytesWritten","decodeToBuffer","chunkData","decoded","Buffer","concat","properties","outputSink","dictionarySize","setDictionarySize","decodeLzma2","decoder"],"mappings":"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"}
|
package/dist/esm/native.d.ts
CHANGED
|
@@ -24,8 +24,5 @@ interface NativeModule {
|
|
|
24
24
|
* Returns null if not available or Node version is too old
|
|
25
25
|
*/
|
|
26
26
|
export declare function tryLoadNative(): NativeModule | null;
|
|
27
|
-
/**
|
|
28
|
-
* Check if native acceleration is available
|
|
29
|
-
*/
|
|
30
27
|
export declare function isNativeAvailable(): boolean;
|
|
31
28
|
export {};
|
package/dist/esm/native.js
CHANGED
|
@@ -12,88 +12,33 @@ const _require = typeof require === 'undefined' ? Module.createRequire(import.me
|
|
|
12
12
|
const __dirname = path.dirname(typeof __filename !== 'undefined' ? __filename : url.fileURLToPath(import.meta.url));
|
|
13
13
|
// Get node_modules path (go up from dist/cjs to package root, then to node_modules)
|
|
14
14
|
const nodeModulesPath = path.join(__dirname, '..', '..', 'node_modules');
|
|
15
|
+
const major = +process.versions.node.split('.')[0];
|
|
15
16
|
// Cache for native module loading result
|
|
16
|
-
let nativeModule;
|
|
17
|
-
let nodeVersionChecked = false;
|
|
18
|
-
let nodeVersionSupported = false;
|
|
17
|
+
let nativeModule = null;
|
|
19
18
|
let installationAttempted = false;
|
|
20
|
-
/**
|
|
21
|
-
* Check if Node.js version supports native module (14+)
|
|
22
|
-
*/ function checkNodeVersion() {
|
|
23
|
-
if (nodeVersionChecked) return nodeVersionSupported;
|
|
24
|
-
nodeVersionChecked = true;
|
|
25
|
-
try {
|
|
26
|
-
const version = process.versions.node;
|
|
27
|
-
const major = parseInt(version.split('.')[0], 10);
|
|
28
|
-
nodeVersionSupported = major >= 14;
|
|
29
|
-
} catch {
|
|
30
|
-
nodeVersionSupported = false;
|
|
31
|
-
}
|
|
32
|
-
return nodeVersionSupported;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Install @napi-rs/lzma using install-module-linked
|
|
36
|
-
*/ function installNativeModule(callback) {
|
|
37
|
-
// Only attempt installation once
|
|
38
|
-
if (installationAttempted) {
|
|
39
|
-
callback(new Error('Installation already attempted'));
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
installationAttempted = true;
|
|
43
|
-
try {
|
|
44
|
-
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
45
|
-
const installModule = _require('install-module-linked').default;
|
|
46
|
-
console.log('Installing @napi-rs/lzma for native acceleration...');
|
|
47
|
-
installModule('@napi-rs/lzma', nodeModulesPath, {}, (err)=>{
|
|
48
|
-
if (err) {
|
|
49
|
-
console.warn('Failed to install @napi-rs/lzma:', err.message);
|
|
50
|
-
} else {
|
|
51
|
-
console.log('Successfully installed @napi-rs/lzma');
|
|
52
|
-
}
|
|
53
|
-
callback(err);
|
|
54
|
-
});
|
|
55
|
-
} catch (err) {
|
|
56
|
-
callback(err);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
19
|
/**
|
|
60
20
|
* Try to load the native @napi-rs/lzma module
|
|
61
21
|
* Returns null if not available or Node version is too old
|
|
62
22
|
*/ export function tryLoadNative() {
|
|
63
|
-
|
|
64
|
-
if (
|
|
65
|
-
|
|
66
|
-
if
|
|
67
|
-
nativeModule = null;
|
|
68
|
-
return null;
|
|
69
|
-
}
|
|
70
|
-
// Try to load native module (it should be installed at module load time)
|
|
23
|
+
if (major < 14) return null;
|
|
24
|
+
if (installationAttempted) return nativeModule;
|
|
25
|
+
installationAttempted = true;
|
|
26
|
+
// check if installed already
|
|
71
27
|
try {
|
|
72
|
-
|
|
28
|
+
nativeModule = _require('@napi-rs/lzma');
|
|
29
|
+
return nativeModule;
|
|
30
|
+
} catch {}
|
|
31
|
+
// try to install
|
|
32
|
+
try {
|
|
33
|
+
console.log('Installing @napi-rs/lzma for native acceleration...');
|
|
34
|
+
const installModule = _require('install-module-linked').default;
|
|
35
|
+
installModule.sync('@napi-rs/lzma', nodeModulesPath, {});
|
|
73
36
|
nativeModule = _require('@napi-rs/lzma');
|
|
74
37
|
return nativeModule;
|
|
75
38
|
} catch {
|
|
76
|
-
// Module not installed yet - return null
|
|
77
|
-
nativeModule = null;
|
|
78
39
|
return null;
|
|
79
40
|
}
|
|
80
41
|
}
|
|
81
|
-
|
|
82
|
-
// This is done asynchronously so it doesn't block module initialization
|
|
83
|
-
if (checkNodeVersion()) {
|
|
84
|
-
installNativeModule(()=>{
|
|
85
|
-
// Installation complete - clear cache and try to load
|
|
86
|
-
nativeModule = undefined; // Clear cache to force re-check
|
|
87
|
-
try {
|
|
88
|
-
nativeModule = _require('@napi-rs/lzma');
|
|
89
|
-
} catch {
|
|
90
|
-
// Module still not available
|
|
91
|
-
nativeModule = null;
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
}
|
|
95
|
-
/**
|
|
96
|
-
* Check if native acceleration is available
|
|
97
|
-
*/ export function isNativeAvailable() {
|
|
42
|
+
export function isNativeAvailable() {
|
|
98
43
|
return tryLoadNative() !== null;
|
|
99
44
|
}
|
package/dist/esm/native.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/iterators/xz-compat/src/native.ts"],"sourcesContent":["/**\n * Native Acceleration Module\n *\n * Provides optional native acceleration via @napi-rs/lzma on Node.js 14+.\n * Falls back gracefully to pure JS implementation on older Node versions\n * or when the native module is not available.\n */\n\nimport Module from 'module';\nimport path from 'path';\nimport url from 'url';\n\n// Get __dirname for ES modules\nconst _require = typeof require === 'undefined' ? Module.createRequire(import.meta.url) : require;\nconst __dirname = path.dirname(typeof __filename !== 'undefined' ? __filename : url.fileURLToPath(import.meta.url));\n\n// Get node_modules path (go up from dist/cjs to package root, then to node_modules)\nconst nodeModulesPath = path.join(__dirname, '..', '..', 'node_modules');\n\n// Cache for native module loading result\nlet nativeModule: NativeModule | null
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/iterators/xz-compat/src/native.ts"],"sourcesContent":["/**\n * Native Acceleration Module\n *\n * Provides optional native acceleration via @napi-rs/lzma on Node.js 14+.\n * Falls back gracefully to pure JS implementation on older Node versions\n * or when the native module is not available.\n */\n\nimport Module from 'module';\nimport path from 'path';\nimport url from 'url';\n\n// Get __dirname for ES modules\nconst _require = typeof require === 'undefined' ? Module.createRequire(import.meta.url) : require;\nconst __dirname = path.dirname(typeof __filename !== 'undefined' ? __filename : url.fileURLToPath(import.meta.url));\n\n// Get node_modules path (go up from dist/cjs to package root, then to node_modules)\nconst nodeModulesPath = path.join(__dirname, '..', '..', 'node_modules');\nconst major = +process.versions.node.split('.')[0];\n\n// Cache for native module loading result\nlet nativeModule: NativeModule | null = null;\nlet installationAttempted = false;\n\ninterface NativeModule {\n xz: {\n decompressSync(input: Uint8Array): Buffer;\n decompress(input: Uint8Array, signal?: AbortSignal | null): Promise<Buffer>;\n };\n lzma: {\n decompressSync(input: Uint8Array): Buffer;\n decompress(input: Uint8Array, signal?: AbortSignal | null): Promise<Buffer>;\n };\n lzma2: {\n decompressSync(input: Uint8Array): Buffer;\n decompress(input: Uint8Array, signal?: AbortSignal | null): Promise<Buffer>;\n };\n}\n\n/**\n * Try to load the native @napi-rs/lzma module\n * Returns null if not available or Node version is too old\n */\nexport function tryLoadNative(): NativeModule | null {\n if (major < 14) return null;\n if (installationAttempted) return nativeModule;\n installationAttempted = true;\n\n // check if installed already\n try {\n nativeModule = _require('@napi-rs/lzma') as NativeModule;\n return nativeModule;\n } catch {}\n\n // try to install\n try {\n console.log('Installing @napi-rs/lzma for native acceleration...');\n const installModule = _require('install-module-linked').default;\n installModule.sync('@napi-rs/lzma', nodeModulesPath, {});\n nativeModule = _require('@napi-rs/lzma') as NativeModule;\n return nativeModule;\n } catch {\n return null;\n }\n}\n\nexport function isNativeAvailable(): boolean {\n return tryLoadNative() !== null;\n}\n"],"names":["Module","path","url","_require","require","createRequire","__dirname","dirname","__filename","fileURLToPath","nodeModulesPath","join","major","process","versions","node","split","nativeModule","installationAttempted","tryLoadNative","console","log","installModule","default","sync","isNativeAvailable"],"mappings":"AAAA;;;;;;CAMC,GAED,OAAOA,YAAY,SAAS;AAC5B,OAAOC,UAAU,OAAO;AACxB,OAAOC,SAAS,MAAM;AAEtB,+BAA+B;AAC/B,MAAMC,WAAW,OAAOC,YAAY,cAAcJ,OAAOK,aAAa,CAAC,YAAYH,GAAG,IAAIE;AAC1F,MAAME,YAAYL,KAAKM,OAAO,CAAC,OAAOC,eAAe,cAAcA,aAAaN,IAAIO,aAAa,CAAC,YAAYP,GAAG;AAEjH,oFAAoF;AACpF,MAAMQ,kBAAkBT,KAAKU,IAAI,CAACL,WAAW,MAAM,MAAM;AACzD,MAAMM,QAAQ,CAACC,QAAQC,QAAQ,CAACC,IAAI,CAACC,KAAK,CAAC,IAAI,CAAC,EAAE;AAElD,yCAAyC;AACzC,IAAIC,eAAoC;AACxC,IAAIC,wBAAwB;AAiB5B;;;CAGC,GACD,OAAO,SAASC;IACd,IAAIP,QAAQ,IAAI,OAAO;IACvB,IAAIM,uBAAuB,OAAOD;IAClCC,wBAAwB;IAExB,6BAA6B;IAC7B,IAAI;QACFD,eAAed,SAAS;QACxB,OAAOc;IACT,EAAE,OAAM,CAAC;IAET,iBAAiB;IACjB,IAAI;QACFG,QAAQC,GAAG,CAAC;QACZ,MAAMC,gBAAgBnB,SAAS,yBAAyBoB,OAAO;QAC/DD,cAAcE,IAAI,CAAC,iBAAiBd,iBAAiB,CAAC;QACtDO,eAAed,SAAS;QACxB,OAAOc;IACT,EAAE,OAAM;QACN,OAAO;IACT;AACF;AAEA,OAAO,SAASQ;IACd,OAAON,oBAAoB;AAC7B"}
|
package/dist/esm/sevenz.js
CHANGED
|
@@ -46,10 +46,11 @@ import { tryLoadNative } from './native.js';
|
|
|
46
46
|
properties,
|
|
47
47
|
data
|
|
48
48
|
]);
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
const result = native.lzma2.decompressSync(selfDescribing);
|
|
50
|
+
if (result.length > 0) return result;
|
|
51
|
+
} catch {}
|
|
52
|
+
// Fall back to pure JS if native fails (e.g., format mismatch)
|
|
53
|
+
// console.log('Native decode7zLzma failed. Defaulting to JavaScript');
|
|
53
54
|
}
|
|
54
55
|
// Pure JS fallback - use fast path directly (no sink wrapper for buffering)
|
|
55
56
|
return decodeLzma(data, properties, unpackSize);
|
|
@@ -71,10 +72,12 @@ import { tryLoadNative } from './native.js';
|
|
|
71
72
|
properties,
|
|
72
73
|
data
|
|
73
74
|
]);
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
//
|
|
77
|
-
}
|
|
75
|
+
const result = native.lzma2.decompressSync(selfDescribing);
|
|
76
|
+
if (result.length > 0) return result;
|
|
77
|
+
// Empty result from native - fall through to JS decoder
|
|
78
|
+
} catch {}
|
|
79
|
+
// Fall back to pure JS if native fails (e.g., format mismatch)
|
|
80
|
+
// console.log('Native decode7zLzma2 failed. Defaulting to JavaScript');
|
|
78
81
|
}
|
|
79
82
|
// Pure JS fallback - use fast path directly (no sink wrapper for buffering)
|
|
80
83
|
return decodeLzma2(data, properties, unpackSize);
|
package/dist/esm/sevenz.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/iterators/xz-compat/src/sevenz.ts"],"sourcesContent":["/**\n * High-Level 7z-Specific Decoders\n *\n * These functions accept properties separately (matching 7z format structure)\n * and internally wrap them with the data to use @napi-rs/lzma when available.\n *\n * This provides automatic native acceleration for 7z files while maintaining\n * the API that 7z-iterator expects.\n *\n * IMPORTANT: Buffer Management Pattern\n *\n * ❌ SLOW - DO NOT use OutputSink with buffering:\n * const chunks: Buffer[] = [];\n * decodeLzma2(data, props, size, { write: c => chunks.push(c) });\n * return Buffer.concat(chunks); // ← 3 copies: push + concat + return\n *\n * OutWindow → chunks.push(chunk) → Buffer.concat(chunks) → result\n * COPY TO ARRAY COPY ALL FINAL BUFFER\n *\n * ✅ FAST - Direct return (let decoder manage buffer):\n * return decodeLzma2(data, props, size) as Buffer; // ← 1 copy\n *\n * OutWindow → pre-allocated buffer → result\n * DIRECT WRITE\n *\n * The decodeLzma2() function internally pre-allocates the exact output size\n * and writes directly to it. Wrapping with an OutputSink that buffers to an\n * array defeats this optimization by creating unnecessary intermediate copies.\n */\n\nimport { decodeLzma2 } from './lzma/sync/Lzma2Decoder.ts';\nimport { decodeLzma } from './lzma/sync/LzmaDecoder.ts';\nimport { tryLoadNative } from './native.ts';\n\n/**\n * Decode LZMA-compressed data from a 7z file\n *\n * @param data - LZMA compressed data (without properties)\n * @param properties - 5-byte LZMA properties (lc/lp/pb + dictionary size)\n * @param unpackSize - Expected output size\n * @returns Decompressed data\n */\nexport function decode7zLzma(data: Buffer, properties: Buffer, unpackSize: number): Buffer {\n // Try native acceleration first\n const native = tryLoadNative();\n if (native) {\n try {\n // @napi-rs/lzma expects properties embedded at the start of the data\n const selfDescribing = Buffer.concat([properties, data]);\n
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/iterators/xz-compat/src/sevenz.ts"],"sourcesContent":["/**\n * High-Level 7z-Specific Decoders\n *\n * These functions accept properties separately (matching 7z format structure)\n * and internally wrap them with the data to use @napi-rs/lzma when available.\n *\n * This provides automatic native acceleration for 7z files while maintaining\n * the API that 7z-iterator expects.\n *\n * IMPORTANT: Buffer Management Pattern\n *\n * ❌ SLOW - DO NOT use OutputSink with buffering:\n * const chunks: Buffer[] = [];\n * decodeLzma2(data, props, size, { write: c => chunks.push(c) });\n * return Buffer.concat(chunks); // ← 3 copies: push + concat + return\n *\n * OutWindow → chunks.push(chunk) → Buffer.concat(chunks) → result\n * COPY TO ARRAY COPY ALL FINAL BUFFER\n *\n * ✅ FAST - Direct return (let decoder manage buffer):\n * return decodeLzma2(data, props, size) as Buffer; // ← 1 copy\n *\n * OutWindow → pre-allocated buffer → result\n * DIRECT WRITE\n *\n * The decodeLzma2() function internally pre-allocates the exact output size\n * and writes directly to it. Wrapping with an OutputSink that buffers to an\n * array defeats this optimization by creating unnecessary intermediate copies.\n */\n\nimport { decodeLzma2 } from './lzma/sync/Lzma2Decoder.ts';\nimport { decodeLzma } from './lzma/sync/LzmaDecoder.ts';\nimport { tryLoadNative } from './native.ts';\n\n/**\n * Decode LZMA-compressed data from a 7z file\n *\n * @param data - LZMA compressed data (without properties)\n * @param properties - 5-byte LZMA properties (lc/lp/pb + dictionary size)\n * @param unpackSize - Expected output size\n * @returns Decompressed data\n */\nexport function decode7zLzma(data: Buffer, properties: Buffer, unpackSize: number): Buffer {\n // Try native acceleration first\n const native = tryLoadNative();\n if (native) {\n try {\n // @napi-rs/lzma expects properties embedded at the start of the data\n const selfDescribing = Buffer.concat([properties, data]);\n const result = native.lzma2.decompressSync(selfDescribing);\n if (result.length > 0) return result;\n } catch {}\n // Fall back to pure JS if native fails (e.g., format mismatch)\n // console.log('Native decode7zLzma failed. Defaulting to JavaScript');\n }\n\n // Pure JS fallback - use fast path directly (no sink wrapper for buffering)\n return decodeLzma(data, properties, unpackSize) as Buffer;\n}\n\n/**\n * Decode LZMA2-compressed data from a 7z file\n *\n * @param data - LZMA2 compressed data (without properties)\n * @param properties - 1-byte LZMA2 properties (dictionary size)\n * @param unpackSize - Expected output size (optional)\n * @returns Decompressed data\n */\nexport function decode7zLzma2(data: Buffer, properties: Buffer, unpackSize?: number): Buffer {\n // Try native acceleration first\n const native = tryLoadNative();\n if (native) {\n try {\n // @napi-rs/lzma expects properties embedded at the start of the data\n const selfDescribing = Buffer.concat([properties, data]);\n const result = native.lzma2.decompressSync(selfDescribing);\n if (result.length > 0) return result;\n // Empty result from native - fall through to JS decoder\n } catch {}\n // Fall back to pure JS if native fails (e.g., format mismatch)\n // console.log('Native decode7zLzma2 failed. Defaulting to JavaScript');\n }\n\n // Pure JS fallback - use fast path directly (no sink wrapper for buffering)\n return decodeLzma2(data, properties, unpackSize) as Buffer;\n}\n"],"names":["decodeLzma2","decodeLzma","tryLoadNative","decode7zLzma","data","properties","unpackSize","native","selfDescribing","Buffer","concat","result","lzma2","decompressSync","length","decode7zLzma2"],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4BC,GAED,SAASA,WAAW,QAAQ,8BAA8B;AAC1D,SAASC,UAAU,QAAQ,6BAA6B;AACxD,SAASC,aAAa,QAAQ,cAAc;AAE5C;;;;;;;CAOC,GACD,OAAO,SAASC,aAAaC,IAAY,EAAEC,UAAkB,EAAEC,UAAkB;IAC/E,gCAAgC;IAChC,MAAMC,SAASL;IACf,IAAIK,QAAQ;QACV,IAAI;YACF,qEAAqE;YACrE,MAAMC,iBAAiBC,OAAOC,MAAM,CAAC;gBAACL;gBAAYD;aAAK;YACvD,MAAMO,SAASJ,OAAOK,KAAK,CAACC,cAAc,CAACL;YAC3C,IAAIG,OAAOG,MAAM,GAAG,GAAG,OAAOH;QAChC,EAAE,OAAM,CAAC;IACT,+DAA+D;IAC/D,uEAAuE;IACzE;IAEA,4EAA4E;IAC5E,OAAOV,WAAWG,MAAMC,YAAYC;AACtC;AAEA;;;;;;;CAOC,GACD,OAAO,SAASS,cAAcX,IAAY,EAAEC,UAAkB,EAAEC,UAAmB;IACjF,gCAAgC;IAChC,MAAMC,SAASL;IACf,IAAIK,QAAQ;QACV,IAAI;YACF,qEAAqE;YACrE,MAAMC,iBAAiBC,OAAOC,MAAM,CAAC;gBAACL;gBAAYD;aAAK;YACvD,MAAMO,SAASJ,OAAOK,KAAK,CAACC,cAAc,CAACL;YAC3C,IAAIG,OAAOG,MAAM,GAAG,GAAG,OAAOH;QAC9B,wDAAwD;QAC1D,EAAE,OAAM,CAAC;IACT,+DAA+D;IAC/D,wEAAwE;IAC1E;IAEA,4EAA4E;IAC5E,OAAOX,YAAYI,MAAMC,YAAYC;AACvC"}
|