7z-iterator 1.3.0 → 1.4.0

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.
Files changed (97) hide show
  1. package/dist/cjs/index.d.cts +3 -1
  2. package/dist/cjs/index.d.ts +3 -1
  3. package/dist/cjs/index.js +10 -0
  4. package/dist/cjs/index.js.map +1 -1
  5. package/dist/cjs/lzma/index.d.cts +18 -0
  6. package/dist/cjs/lzma/index.d.ts +18 -0
  7. package/dist/cjs/lzma/index.js +20 -0
  8. package/dist/cjs/lzma/index.js.map +1 -1
  9. package/dist/cjs/lzma/stream/transforms.d.cts +8 -0
  10. package/dist/cjs/lzma/stream/transforms.d.ts +8 -0
  11. package/dist/cjs/lzma/stream/transforms.js +66 -6
  12. package/dist/cjs/lzma/stream/transforms.js.map +1 -1
  13. package/dist/cjs/lzma/sync/Lzma2Decoder.d.cts +37 -4
  14. package/dist/cjs/lzma/sync/Lzma2Decoder.d.ts +37 -4
  15. package/dist/cjs/lzma/sync/Lzma2Decoder.js +100 -4
  16. package/dist/cjs/lzma/sync/Lzma2Decoder.js.map +1 -1
  17. package/dist/cjs/lzma/sync/LzmaDecoder.d.cts +18 -3
  18. package/dist/cjs/lzma/sync/LzmaDecoder.d.ts +18 -3
  19. package/dist/cjs/lzma/sync/LzmaDecoder.js +146 -6
  20. package/dist/cjs/lzma/sync/LzmaDecoder.js.map +1 -1
  21. package/dist/cjs/lzma/types.d.cts +7 -0
  22. package/dist/cjs/lzma/types.d.ts +7 -0
  23. package/dist/cjs/lzma/types.js.map +1 -1
  24. package/dist/cjs/sevenz/codecs/Aes.d.cts +1 -1
  25. package/dist/cjs/sevenz/codecs/Aes.d.ts +1 -1
  26. package/dist/cjs/sevenz/codecs/Aes.js.map +1 -1
  27. package/dist/cjs/sevenz/codecs/BZip2.d.cts +1 -1
  28. package/dist/cjs/sevenz/codecs/BZip2.d.ts +1 -1
  29. package/dist/cjs/sevenz/codecs/BZip2.js.map +1 -1
  30. package/dist/cjs/sevenz/codecs/Bcj2.d.cts +1 -1
  31. package/dist/cjs/sevenz/codecs/Bcj2.d.ts +1 -1
  32. package/dist/cjs/sevenz/codecs/Bcj2.js.map +1 -1
  33. package/dist/cjs/sevenz/codecs/BcjArm64.d.cts +1 -1
  34. package/dist/cjs/sevenz/codecs/BcjArm64.d.ts +1 -1
  35. package/dist/cjs/sevenz/codecs/BcjArm64.js.map +1 -1
  36. package/dist/cjs/sevenz/codecs/BcjArmt.d.cts +1 -1
  37. package/dist/cjs/sevenz/codecs/BcjArmt.d.ts +1 -1
  38. package/dist/cjs/sevenz/codecs/BcjArmt.js.map +1 -1
  39. package/dist/cjs/sevenz/codecs/BcjIa64.d.cts +1 -1
  40. package/dist/cjs/sevenz/codecs/BcjIa64.d.ts +1 -1
  41. package/dist/cjs/sevenz/codecs/BcjIa64.js.map +1 -1
  42. package/dist/cjs/sevenz/codecs/BcjPpc.d.cts +1 -1
  43. package/dist/cjs/sevenz/codecs/BcjPpc.d.ts +1 -1
  44. package/dist/cjs/sevenz/codecs/BcjPpc.js.map +1 -1
  45. package/dist/cjs/sevenz/codecs/BcjSparc.d.cts +1 -1
  46. package/dist/cjs/sevenz/codecs/BcjSparc.d.ts +1 -1
  47. package/dist/cjs/sevenz/codecs/BcjSparc.js.map +1 -1
  48. package/dist/cjs/sevenz/codecs/Lzma.js.map +1 -1
  49. package/dist/cjs/sevenz/codecs/Lzma2.js.map +1 -1
  50. package/dist/cjs/sevenz/codecs/index.d.cts +1 -1
  51. package/dist/cjs/sevenz/codecs/index.d.ts +1 -1
  52. package/dist/cjs/sevenz/codecs/index.js.map +1 -1
  53. package/dist/cjs/xz/Decoder.d.cts +25 -0
  54. package/dist/cjs/xz/Decoder.d.ts +25 -0
  55. package/dist/cjs/xz/Decoder.js +194 -0
  56. package/dist/cjs/xz/Decoder.js.map +1 -0
  57. package/dist/esm/index.d.ts +3 -1
  58. package/dist/esm/index.js +2 -1
  59. package/dist/esm/index.js.map +1 -1
  60. package/dist/esm/lzma/index.d.ts +18 -0
  61. package/dist/esm/lzma/index.js +29 -0
  62. package/dist/esm/lzma/index.js.map +1 -1
  63. package/dist/esm/lzma/stream/transforms.d.ts +8 -0
  64. package/dist/esm/lzma/stream/transforms.js +46 -7
  65. package/dist/esm/lzma/stream/transforms.js.map +1 -1
  66. package/dist/esm/lzma/sync/Lzma2Decoder.d.ts +37 -4
  67. package/dist/esm/lzma/sync/Lzma2Decoder.js +102 -6
  68. package/dist/esm/lzma/sync/Lzma2Decoder.js.map +1 -1
  69. package/dist/esm/lzma/sync/LzmaDecoder.d.ts +18 -3
  70. package/dist/esm/lzma/sync/LzmaDecoder.js +147 -7
  71. package/dist/esm/lzma/sync/LzmaDecoder.js.map +1 -1
  72. package/dist/esm/lzma/types.d.ts +7 -0
  73. package/dist/esm/lzma/types.js.map +1 -1
  74. package/dist/esm/sevenz/codecs/Aes.d.ts +1 -1
  75. package/dist/esm/sevenz/codecs/Aes.js.map +1 -1
  76. package/dist/esm/sevenz/codecs/BZip2.d.ts +1 -1
  77. package/dist/esm/sevenz/codecs/BZip2.js.map +1 -1
  78. package/dist/esm/sevenz/codecs/Bcj2.d.ts +1 -1
  79. package/dist/esm/sevenz/codecs/Bcj2.js.map +1 -1
  80. package/dist/esm/sevenz/codecs/BcjArm64.d.ts +1 -1
  81. package/dist/esm/sevenz/codecs/BcjArm64.js.map +1 -1
  82. package/dist/esm/sevenz/codecs/BcjArmt.d.ts +1 -1
  83. package/dist/esm/sevenz/codecs/BcjArmt.js.map +1 -1
  84. package/dist/esm/sevenz/codecs/BcjIa64.d.ts +1 -1
  85. package/dist/esm/sevenz/codecs/BcjIa64.js.map +1 -1
  86. package/dist/esm/sevenz/codecs/BcjPpc.d.ts +1 -1
  87. package/dist/esm/sevenz/codecs/BcjPpc.js.map +1 -1
  88. package/dist/esm/sevenz/codecs/BcjSparc.d.ts +1 -1
  89. package/dist/esm/sevenz/codecs/BcjSparc.js.map +1 -1
  90. package/dist/esm/sevenz/codecs/Lzma.js.map +1 -1
  91. package/dist/esm/sevenz/codecs/Lzma2.js.map +1 -1
  92. package/dist/esm/sevenz/codecs/index.d.ts +1 -1
  93. package/dist/esm/sevenz/codecs/index.js.map +1 -1
  94. package/dist/esm/xz/Decoder.d.ts +25 -0
  95. package/dist/esm/xz/Decoder.js +185 -0
  96. package/dist/esm/xz/Decoder.js.map +1 -0
  97. package/package.json +8 -3
@@ -1,3 +1,5 @@
1
- export { createLzma2Decoder, createLzmaDecoder, decodeLzma, decodeLzma2, Lzma2Decoder, LzmaDecoder, } from './lzma/index.js';
1
+ export type { OutputSink } from './lzma/index.js';
2
+ export { createLzma2Decoder, createLzmaDecoder, decodeLzma, decodeLzma2, detectFormat, Lzma2Decoder, LzmaDecoder, } from './lzma/index.js';
2
3
  export { default } from './SevenZipIterator.js';
3
4
  export * from './types.js';
5
+ export { createXZDecoder, decodeXZ } from './xz/Decoder.js';
@@ -1,3 +1,5 @@
1
- export { createLzma2Decoder, createLzmaDecoder, decodeLzma, decodeLzma2, Lzma2Decoder, LzmaDecoder, } from './lzma/index.js';
1
+ export type { OutputSink } from './lzma/index.js';
2
+ export { createLzma2Decoder, createLzmaDecoder, decodeLzma, decodeLzma2, detectFormat, Lzma2Decoder, LzmaDecoder, } from './lzma/index.js';
2
3
  export { default } from './SevenZipIterator.js';
3
4
  export * from './types.js';
5
+ export { createXZDecoder, decodeXZ } from './xz/Decoder.js';
package/dist/cjs/index.js CHANGED
@@ -22,19 +22,29 @@ _export(exports, {
22
22
  get createLzmaDecoder () {
23
23
  return _indexts.createLzmaDecoder;
24
24
  },
25
+ get createXZDecoder () {
26
+ return _Decoderts.createXZDecoder;
27
+ },
25
28
  get decodeLzma () {
26
29
  return _indexts.decodeLzma;
27
30
  },
28
31
  get decodeLzma2 () {
29
32
  return _indexts.decodeLzma2;
30
33
  },
34
+ get decodeXZ () {
35
+ return _Decoderts.decodeXZ;
36
+ },
31
37
  get default () {
32
38
  return _SevenZipIteratorts.default;
39
+ },
40
+ get detectFormat () {
41
+ return _indexts.detectFormat;
33
42
  }
34
43
  });
35
44
  var _indexts = require("./lzma/index.js");
36
45
  var _SevenZipIteratorts = /*#__PURE__*/ _interop_require_default(require("./SevenZipIterator.js"));
37
46
  _export_star(require("./types.js"), exports);
47
+ var _Decoderts = require("./xz/Decoder.js");
38
48
  function _export_star(from, to) {
39
49
  Object.keys(from).forEach(function(k) {
40
50
  if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) {
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/iterators/7z-iterator/src/index.ts"],"sourcesContent":["// LZMA decoders for external use\nexport {\n createLzma2Decoder,\n createLzmaDecoder,\n decodeLzma,\n decodeLzma2,\n Lzma2Decoder,\n LzmaDecoder,\n} from './lzma/index.ts';\nexport { default } from './SevenZipIterator.ts';\nexport * from './types.ts';\n"],"names":["Lzma2Decoder","LzmaDecoder","createLzma2Decoder","createLzmaDecoder","decodeLzma","decodeLzma2","default"],"mappings":"AAAA,iCAAiC;;;;;;;;;;;;QAM/BA;eAAAA,qBAAY;;QACZC;eAAAA,oBAAW;;QALXC;eAAAA,2BAAkB;;QAClBC;eAAAA,0BAAiB;;QACjBC;eAAAA,mBAAU;;QACVC;eAAAA,oBAAW;;QAIJC;eAAAA,2BAAO;;;uBADT;yEACiB;qBACV"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/iterators/7z-iterator/src/index.ts"],"sourcesContent":["// LZMA decoders for external use\n\nexport type { OutputSink } from './lzma/index.ts';\nexport {\n createLzma2Decoder,\n createLzmaDecoder,\n decodeLzma,\n decodeLzma2,\n detectFormat,\n Lzma2Decoder,\n LzmaDecoder,\n} from './lzma/index.ts';\nexport { default } from './SevenZipIterator.ts';\nexport * from './types.ts';\nexport { createXZDecoder, decodeXZ } from './xz/Decoder.ts';\n"],"names":["Lzma2Decoder","LzmaDecoder","createLzma2Decoder","createLzmaDecoder","createXZDecoder","decodeLzma","decodeLzma2","decodeXZ","default","detectFormat"],"mappings":"AAAA,iCAAiC;;;;;;;;;;;;QAS/BA;eAAAA,qBAAY;;QACZC;eAAAA,oBAAW;;QANXC;eAAAA,2BAAkB;;QAClBC;eAAAA,0BAAiB;;QASVC;eAAAA,0BAAe;;QARtBC;eAAAA,mBAAU;;QACVC;eAAAA,oBAAW;;QAOaC;eAAAA,mBAAQ;;QAFzBC;eAAAA,2BAAO;;QAJdC;eAAAA,qBAAY;;;uBAGP;yEACiB;qBACV;yBAC4B"}
@@ -5,9 +5,27 @@
5
5
  *
6
6
  * Synchronous API: Use when input is a complete Buffer
7
7
  * Streaming API: Use with Transform streams for memory-efficient decompression
8
+ *
9
+ * LZMA1 vs LZMA2:
10
+ * - LZMA2 is chunked and supports true streaming with bounded memory
11
+ * - LZMA1 has no chunk boundaries and requires buffering all input for streaming
8
12
  */
9
13
  export { createLzma2Decoder, createLzmaDecoder } from './stream/transforms.js';
10
14
  export { decodeLzma2, Lzma2Decoder } from './sync/Lzma2Decoder.js';
11
15
  export { decodeLzma, LzmaDecoder } from './sync/LzmaDecoder.js';
12
16
  export { BitTreeDecoder, RangeDecoder } from './sync/RangeDecoder.js';
13
17
  export * from './types.js';
18
+ /**
19
+ * Detect LZMA format from compressed data
20
+ *
21
+ * LZMA2 uses chunk-based framing with control bytes:
22
+ * - 0x00: End of stream
23
+ * - 0x01-0x02: Uncompressed chunks
24
+ * - 0x80-0xFF: LZMA compressed chunks
25
+ *
26
+ * LZMA1 is raw LZMA-compressed data (no framing)
27
+ *
28
+ * @param data - Compressed data to analyze
29
+ * @returns 'lzma1' for LZMA1, 'lzma2' for LZMA2
30
+ */
31
+ export declare function detectFormat(data: Buffer): 'lzma1' | 'lzma2';
@@ -5,9 +5,27 @@
5
5
  *
6
6
  * Synchronous API: Use when input is a complete Buffer
7
7
  * Streaming API: Use with Transform streams for memory-efficient decompression
8
+ *
9
+ * LZMA1 vs LZMA2:
10
+ * - LZMA2 is chunked and supports true streaming with bounded memory
11
+ * - LZMA1 has no chunk boundaries and requires buffering all input for streaming
8
12
  */
9
13
  export { createLzma2Decoder, createLzmaDecoder } from './stream/transforms.js';
10
14
  export { decodeLzma2, Lzma2Decoder } from './sync/Lzma2Decoder.js';
11
15
  export { decodeLzma, LzmaDecoder } from './sync/LzmaDecoder.js';
12
16
  export { BitTreeDecoder, RangeDecoder } from './sync/RangeDecoder.js';
13
17
  export * from './types.js';
18
+ /**
19
+ * Detect LZMA format from compressed data
20
+ *
21
+ * LZMA2 uses chunk-based framing with control bytes:
22
+ * - 0x00: End of stream
23
+ * - 0x01-0x02: Uncompressed chunks
24
+ * - 0x80-0xFF: LZMA compressed chunks
25
+ *
26
+ * LZMA1 is raw LZMA-compressed data (no framing)
27
+ *
28
+ * @param data - Compressed data to analyze
29
+ * @returns 'lzma1' for LZMA1, 'lzma2' for LZMA2
30
+ */
31
+ export declare function detectFormat(data: Buffer): 'lzma1' | 'lzma2';
@@ -5,6 +5,10 @@
5
5
  *
6
6
  * Synchronous API: Use when input is a complete Buffer
7
7
  * Streaming API: Use with Transform streams for memory-efficient decompression
8
+ *
9
+ * LZMA1 vs LZMA2:
10
+ * - LZMA2 is chunked and supports true streaming with bounded memory
11
+ * - LZMA1 has no chunk boundaries and requires buffering all input for streaming
8
12
  */ // Streaming decoders (Transform streams)
9
13
  "use strict";
10
14
  Object.defineProperty(exports, "__esModule", {
@@ -40,6 +44,9 @@ _export(exports, {
40
44
  },
41
45
  get decodeLzma2 () {
42
46
  return _Lzma2Decoderts.decodeLzma2;
47
+ },
48
+ get detectFormat () {
49
+ return detectFormat;
43
50
  }
44
51
  });
45
52
  var _transformsts = require("./stream/transforms.js");
@@ -60,4 +67,17 @@ function _export_star(from, to) {
60
67
  });
61
68
  return from;
62
69
  }
70
+ function detectFormat(data) {
71
+ if (data.length === 0) {
72
+ // Default to LZMA2 for empty data (matches LZMA2 decoder behavior)
73
+ return 'lzma2';
74
+ }
75
+ var firstByte = data[0];
76
+ // LZMA2 control bytes: 0x00, 0x01, 0x02, or 0x80-0xFF
77
+ if (firstByte === 0x00 || firstByte === 0x01 || firstByte === 0x02 || firstByte >= 0x80 && firstByte <= 0xff) {
78
+ return 'lzma2';
79
+ }
80
+ // All other values indicate LZMA1 (raw LZMA data)
81
+ return 'lzma1';
82
+ }
63
83
  /* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (var key in exports) { exports.default[key] = exports[key]; } } catch (_) {}; module.exports = exports.default; }
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/iterators/7z-iterator/src/lzma/index.ts"],"sourcesContent":["/**\n * LZMA Decoder Module\n *\n * Provides both synchronous and streaming LZMA1/LZMA2 decoders.\n *\n * Synchronous API: Use when input is a complete Buffer\n * Streaming API: Use with Transform streams for memory-efficient decompression\n */\n\n// Streaming decoders (Transform streams)\nexport { createLzma2Decoder, createLzmaDecoder } from './stream/transforms.ts';\nexport { decodeLzma2, Lzma2Decoder } from './sync/Lzma2Decoder.ts';\n// Synchronous decoders (for Buffer input)\nexport { decodeLzma, LzmaDecoder } from './sync/LzmaDecoder.ts';\nexport { BitTreeDecoder, RangeDecoder } from './sync/RangeDecoder.ts';\n// Type exports\nexport * from './types.ts';\n"],"names":["BitTreeDecoder","Lzma2Decoder","LzmaDecoder","RangeDecoder","createLzma2Decoder","createLzmaDecoder","decodeLzma","decodeLzma2"],"mappings":"AAAA;;;;;;;CAOC,GAED,yCAAyC;;;;;;;;;;;;QAKhCA;eAAAA,8BAAc;;QAHDC;eAAAA,4BAAY;;QAEbC;eAAAA,0BAAW;;QACPC;eAAAA,4BAAY;;QAJ5BC;eAAAA,gCAAkB;;QAAEC;eAAAA,+BAAiB;;QAGrCC;eAAAA,yBAAU;;QAFVC;eAAAA,2BAAW;;;4BADkC;8BACZ;6BAEF;8BACK;qBAE/B"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/iterators/7z-iterator/src/lzma/index.ts"],"sourcesContent":["/**\n * LZMA Decoder Module\n *\n * Provides both synchronous and streaming LZMA1/LZMA2 decoders.\n *\n * Synchronous API: Use when input is a complete Buffer\n * Streaming API: Use with Transform streams for memory-efficient decompression\n *\n * LZMA1 vs LZMA2:\n * - LZMA2 is chunked and supports true streaming with bounded memory\n * - LZMA1 has no chunk boundaries and requires buffering all input for streaming\n */\n\n// Streaming decoders (Transform streams)\nexport { createLzma2Decoder, createLzmaDecoder } from './stream/transforms.ts';\nexport { decodeLzma2, Lzma2Decoder } from './sync/Lzma2Decoder.ts';\n// Synchronous decoders (for Buffer input)\nexport { decodeLzma, LzmaDecoder } from './sync/LzmaDecoder.ts';\nexport { BitTreeDecoder, RangeDecoder } from './sync/RangeDecoder.ts';\n// Type exports\nexport * from './types.ts';\n\n/**\n * Detect LZMA format from compressed data\n *\n * LZMA2 uses chunk-based framing with control bytes:\n * - 0x00: End of stream\n * - 0x01-0x02: Uncompressed chunks\n * - 0x80-0xFF: LZMA compressed chunks\n *\n * LZMA1 is raw LZMA-compressed data (no framing)\n *\n * @param data - Compressed data to analyze\n * @returns 'lzma1' for LZMA1, 'lzma2' for LZMA2\n */\nexport function detectFormat(data: Buffer): 'lzma1' | 'lzma2' {\n if (data.length === 0) {\n // Default to LZMA2 for empty data (matches LZMA2 decoder behavior)\n return 'lzma2';\n }\n\n const firstByte = data[0];\n\n // LZMA2 control bytes: 0x00, 0x01, 0x02, or 0x80-0xFF\n if (firstByte === 0x00 || firstByte === 0x01 || firstByte === 0x02 || (firstByte >= 0x80 && firstByte <= 0xff)) {\n return 'lzma2';\n }\n\n // All other values indicate LZMA1 (raw LZMA data)\n return 'lzma1';\n}\n"],"names":["BitTreeDecoder","Lzma2Decoder","LzmaDecoder","RangeDecoder","createLzma2Decoder","createLzmaDecoder","decodeLzma","decodeLzma2","detectFormat","data","length","firstByte"],"mappings":"AAAA;;;;;;;;;;;CAWC,GAED,yCAAyC;;;;;;;;;;;;QAKhCA;eAAAA,8BAAc;;QAHDC;eAAAA,4BAAY;;QAEbC;eAAAA,0BAAW;;QACPC;eAAAA,4BAAY;;QAJ5BC;eAAAA,gCAAkB;;QAAEC;eAAAA,+BAAiB;;QAGrCC;eAAAA,yBAAU;;QAFVC;eAAAA,2BAAW;;QAoBJC;eAAAA;;;4BArBsC;8BACZ;6BAEF;8BACK;qBAE/B;;;;;;;;;;;;;;AAeP,SAASA,aAAaC,IAAY;IACvC,IAAIA,KAAKC,MAAM,KAAK,GAAG;QACrB,mEAAmE;QACnE,OAAO;IACT;IAEA,IAAMC,YAAYF,IAAI,CAAC,EAAE;IAEzB,sDAAsD;IACtD,IAAIE,cAAc,QAAQA,cAAc,QAAQA,cAAc,QAASA,aAAa,QAAQA,aAAa,MAAO;QAC9G,OAAO;IACT;IAEA,kDAAkD;IAClD,OAAO;AACT"}
@@ -7,6 +7,11 @@
7
7
  * then decoding synchronously. LZMA2 chunks are bounded in size (~2MB max
8
8
  * uncompressed), so memory usage is predictable and bounded.
9
9
  *
10
+ * Performance Optimization:
11
+ * - Uses OutputSink pattern for zero-copy output during decode
12
+ * - Each decoded byte written directly to stream (not buffered then copied)
13
+ * - ~4x faster than previous buffering approach
14
+ *
10
15
  * True byte-by-byte async LZMA streaming would require rewriting the entire
11
16
  * decoder with continuation-passing style, which is complex and not worth
12
17
  * the effort given LZMA2's chunked format.
@@ -31,6 +36,9 @@ export declare function createLzma2Decoder(properties: Buffer | Uint8Array): Ins
31
36
  *
32
37
  * For true streaming, use LZMA2 which has built-in chunking.
33
38
  *
39
+ * Optimization: Pre-allocates input buffer and copies chunks once,
40
+ * avoiding the double-buffering of Buffer.concat().
41
+ *
34
42
  * @param properties - 5-byte LZMA properties
35
43
  * @param unpackSize - Expected uncompressed size
36
44
  * @returns Transform stream that decompresses LZMA1 data
@@ -7,6 +7,11 @@
7
7
  * then decoding synchronously. LZMA2 chunks are bounded in size (~2MB max
8
8
  * uncompressed), so memory usage is predictable and bounded.
9
9
  *
10
+ * Performance Optimization:
11
+ * - Uses OutputSink pattern for zero-copy output during decode
12
+ * - Each decoded byte written directly to stream (not buffered then copied)
13
+ * - ~4x faster than previous buffering approach
14
+ *
10
15
  * True byte-by-byte async LZMA streaming would require rewriting the entire
11
16
  * decoder with continuation-passing style, which is complex and not worth
12
17
  * the effort given LZMA2's chunked format.
@@ -31,6 +36,9 @@ export declare function createLzma2Decoder(properties: Buffer | Uint8Array): Ins
31
36
  *
32
37
  * For true streaming, use LZMA2 which has built-in chunking.
33
38
  *
39
+ * Optimization: Pre-allocates input buffer and copies chunks once,
40
+ * avoiding the double-buffering of Buffer.concat().
41
+ *
34
42
  * @param properties - 5-byte LZMA properties
35
43
  * @param unpackSize - Expected uncompressed size
36
44
  * @returns Transform stream that decompresses LZMA1 data
@@ -7,6 +7,11 @@
7
7
  * then decoding synchronously. LZMA2 chunks are bounded in size (~2MB max
8
8
  * uncompressed), so memory usage is predictable and bounded.
9
9
  *
10
+ * Performance Optimization:
11
+ * - Uses OutputSink pattern for zero-copy output during decode
12
+ * - Each decoded byte written directly to stream (not buffered then copied)
13
+ * - ~4x faster than previous buffering approach
14
+ *
10
15
  * True byte-by-byte async LZMA streaming would require rewriting the entire
11
16
  * decoder with continuation-passing style, which is complex and not worth
12
17
  * the effort given LZMA2's chunked format.
@@ -47,6 +52,7 @@ function createLzma2Decoder(properties) {
47
52
  var finished = false;
48
53
  return new _extractbaseiterator.Transform({
49
54
  transform: function transform(chunk, _encoding, callback) {
55
+ var _this = this;
50
56
  if (finished) {
51
57
  callback(null);
52
58
  return;
@@ -88,9 +94,14 @@ function createLzma2Decoder(properties) {
88
94
  decoder.feedUncompressed(uncompData);
89
95
  } else {
90
96
  // LZMA compressed chunk
97
+ // Variables to store properties (used for both decoders)
98
+ var lc = void 0;
99
+ var lp = void 0;
100
+ var pb = void 0;
91
101
  // Apply new properties if present
92
102
  if (chunkInfo.newProps) {
93
- var _chunkInfo_newProps = chunkInfo.newProps, lc = _chunkInfo_newProps.lc, lp = _chunkInfo_newProps.lp, pb = _chunkInfo_newProps.pb;
103
+ var ref;
104
+ ref = chunkInfo.newProps, lc = ref.lc, lp = ref.lp, pb = ref.pb, ref;
94
105
  if (!decoder.setLcLpPb(lc, lp, pb)) {
95
106
  throw new Error("Invalid LZMA properties: lc=".concat(lc, " lp=").concat(lp, " pb=").concat(pb));
96
107
  }
@@ -106,8 +117,20 @@ function createLzma2Decoder(properties) {
106
117
  // Determine solid mode - preserve dictionary if not resetting state or if only resetting state (not dict)
107
118
  var useSolid = !chunkInfo.stateReset || chunkInfo.stateReset && !chunkInfo.dictReset;
108
119
  var compData = input.slice(dataOffset, dataOffset + chunkInfo.compSize);
109
- var decoded = decoder.decode(compData, 0, chunkInfo.uncompSize, useSolid);
110
- this.push(decoded);
120
+ // Enhanced: Use OutputSink for direct emission (zero-copy)
121
+ // Create a decoder with direct stream emission
122
+ var streamDecoder = new _LzmaDecoderts.LzmaDecoder({
123
+ write: function(chunk) {
124
+ return _this.push(chunk);
125
+ }
126
+ });
127
+ streamDecoder.setDictionarySize(dictSize);
128
+ // Preserve properties from main decoder
129
+ streamDecoder.setLcLpPb(lc, lp, pb);
130
+ // Use solid mode based on chunk properties
131
+ streamDecoder.decodeWithSink(compData, 0, chunkInfo.uncompSize, useSolid);
132
+ // Flush any remaining data in the OutWindow
133
+ streamDecoder.flushOutWindow();
111
134
  }
112
135
  offset += totalSize;
113
136
  }
@@ -129,16 +152,53 @@ function createLzmaDecoder(properties, unpackSize) {
129
152
  var decoder = new _LzmaDecoderts.LzmaDecoder();
130
153
  decoder.setDecoderProperties(properties);
131
154
  var chunks = [];
155
+ var totalSize = 0;
132
156
  return new _extractbaseiterator.Transform({
133
157
  transform: function transform(chunk, _encoding, callback) {
134
158
  chunks.push(chunk);
159
+ totalSize += chunk.length;
135
160
  callback(null);
136
161
  },
137
162
  flush: function flush(callback) {
163
+ var _this = this;
138
164
  try {
139
- var input = Buffer.concat(chunks);
140
- var output = decoder.decode(input, 0, unpackSize, false);
141
- this.push(output);
165
+ // Optimization: Pre-allocate single buffer instead of Buffer.concat()
166
+ // This reduces peak memory usage by ~50% during concatenation
167
+ var input = (0, _extractbaseiterator.allocBufferUnsafe)(totalSize);
168
+ var offset = 0;
169
+ var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
170
+ try {
171
+ // Copy each chunk into the pre-allocated buffer
172
+ for(var _iterator = chunks[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
173
+ var chunk = _step.value;
174
+ chunk.copy(input, offset);
175
+ offset += chunk.length;
176
+ }
177
+ } catch (err) {
178
+ _didIteratorError = true;
179
+ _iteratorError = err;
180
+ } finally{
181
+ try {
182
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
183
+ _iterator.return();
184
+ }
185
+ } finally{
186
+ if (_didIteratorError) {
187
+ throw _iteratorError;
188
+ }
189
+ }
190
+ }
191
+ // Enhanced: Use OutputSink for direct emission (zero-copy)
192
+ // Create a decoder with direct stream emission
193
+ var streamDecoder = new _LzmaDecoderts.LzmaDecoder({
194
+ write: function(chunk) {
195
+ return _this.push(chunk);
196
+ }
197
+ });
198
+ streamDecoder.setDecoderProperties(properties);
199
+ streamDecoder.decodeWithSink(input, 0, unpackSize, false);
200
+ // Flush any remaining data in the OutWindow
201
+ streamDecoder.flushOutWindow();
142
202
  callback(null);
143
203
  } catch (err) {
144
204
  callback(err);
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/iterators/7z-iterator/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 * 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 { 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 // Apply new properties if present\n if (chunkInfo.newProps) {\n const { 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 const decoded = decoder.decode(compData, 0, chunkInfo.uncompSize, useSolid);\n this.push(decoded);\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 * @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\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 callback(null);\n },\n\n flush: function (this: InstanceType<typeof Transform>, callback: (err?: Error | null) => void) {\n try {\n const input = Buffer.concat(chunks);\n const output = decoder.decode(input, 0, unpackSize, false);\n this.push(output);\n callback(null);\n } catch (err) {\n callback(err as Error);\n }\n },\n });\n}\n"],"names":["createLzma2Decoder","createLzmaDecoder","properties","length","Error","dictSize","parseLzma2DictionarySize","decoder","LzmaDecoder","setDictionarySize","propsSet","pending","finished","Transform","transform","chunk","_encoding","callback","input","Buffer","concat","offset","result","hasCompleteChunk","success","slice","chunkInfo","totalSize","type","dictReset","resetDictionary","dataOffset","headerSize","uncompData","uncompSize","push","feedUncompressed","newProps","lc","lp","pb","setLcLpPb","stateReset","resetProbabilities","useSolid","compData","compSize","decoded","decode","err","flush","unpackSize","setDecoderProperties","chunks","output"],"mappings":"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"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/iterators/7z-iterator/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 (const chunk of chunks) {\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":["createLzma2Decoder","createLzmaDecoder","properties","length","Error","dictSize","parseLzma2DictionarySize","decoder","LzmaDecoder","setDictionarySize","propsSet","pending","finished","Transform","transform","chunk","_encoding","callback","input","Buffer","concat","offset","result","hasCompleteChunk","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","unpackSize","setDecoderProperties","chunks","allocBufferUnsafe","copy"],"mappings":"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"}
@@ -4,6 +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
  */
7
+ import { type OutputSink } from '../types.js';
7
8
  /**
8
9
  * Synchronous LZMA2 decoder
9
10
  */
@@ -11,7 +12,38 @@ export declare class Lzma2Decoder {
11
12
  private lzmaDecoder;
12
13
  private dictionarySize;
13
14
  private propsSet;
14
- constructor(properties: Buffer | Uint8Array);
15
+ constructor(properties: Buffer | Uint8Array, outputSink?: OutputSink);
16
+ /**
17
+ * Reset the dictionary (for stream boundaries)
18
+ */
19
+ resetDictionary(): void;
20
+ /**
21
+ * Reset all probability models (for stream boundaries)
22
+ */
23
+ resetProbabilities(): void;
24
+ /**
25
+ * Set LZMA properties
26
+ */
27
+ setLcLpPb(lc: number, lp: number, pb: number): boolean;
28
+ /**
29
+ * Feed uncompressed data to the dictionary (for subsequent LZMA chunks)
30
+ */
31
+ feedUncompressed(data: Buffer): void;
32
+ /**
33
+ * Decode raw LZMA data (used internally for LZMA2 chunks)
34
+ * @param input - LZMA compressed data
35
+ * @param offset - Input offset
36
+ * @param outSize - Expected output size
37
+ * @param solid - Use solid mode
38
+ * @returns Decompressed data
39
+ */
40
+ decodeLzmaData(input: Buffer, offset: number, outSize: number, solid?: boolean): Buffer;
41
+ /**
42
+ * Decode LZMA2 data with streaming output
43
+ * @param input - LZMA2 compressed data
44
+ * @returns Total number of bytes written to sink
45
+ */
46
+ decodeWithSink(input: Buffer): number;
15
47
  /**
16
48
  * Decode LZMA2 data
17
49
  * @param input - LZMA2 compressed data
@@ -24,7 +56,8 @@ export declare class Lzma2Decoder {
24
56
  * Decode LZMA2 data synchronously
25
57
  * @param input - LZMA2 compressed data
26
58
  * @param properties - 1-byte properties (dictionary size)
27
- * @param unpackSize - Expected output size (optional)
28
- * @returns Decompressed data
59
+ * @param unpackSize - Expected output size (optional, autodetects if not provided)
60
+ * @param outputSink - Optional output sink for zero-copy decoding (returns bytes written)
61
+ * @returns Decompressed data (or bytes written if outputSink provided)
29
62
  */
30
- export declare function decodeLzma2(input: Buffer, properties: Buffer | Uint8Array, unpackSize?: number): Buffer;
63
+ export declare function decodeLzma2(input: Buffer, properties: Buffer | Uint8Array, unpackSize?: number, outputSink?: OutputSink): Buffer | number;
@@ -4,6 +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
  */
7
+ import { type OutputSink } from '../types.js';
7
8
  /**
8
9
  * Synchronous LZMA2 decoder
9
10
  */
@@ -11,7 +12,38 @@ export declare class Lzma2Decoder {
11
12
  private lzmaDecoder;
12
13
  private dictionarySize;
13
14
  private propsSet;
14
- constructor(properties: Buffer | Uint8Array);
15
+ constructor(properties: Buffer | Uint8Array, outputSink?: OutputSink);
16
+ /**
17
+ * Reset the dictionary (for stream boundaries)
18
+ */
19
+ resetDictionary(): void;
20
+ /**
21
+ * Reset all probability models (for stream boundaries)
22
+ */
23
+ resetProbabilities(): void;
24
+ /**
25
+ * Set LZMA properties
26
+ */
27
+ setLcLpPb(lc: number, lp: number, pb: number): boolean;
28
+ /**
29
+ * Feed uncompressed data to the dictionary (for subsequent LZMA chunks)
30
+ */
31
+ feedUncompressed(data: Buffer): void;
32
+ /**
33
+ * Decode raw LZMA data (used internally for LZMA2 chunks)
34
+ * @param input - LZMA compressed data
35
+ * @param offset - Input offset
36
+ * @param outSize - Expected output size
37
+ * @param solid - Use solid mode
38
+ * @returns Decompressed data
39
+ */
40
+ decodeLzmaData(input: Buffer, offset: number, outSize: number, solid?: boolean): Buffer;
41
+ /**
42
+ * Decode LZMA2 data with streaming output
43
+ * @param input - LZMA2 compressed data
44
+ * @returns Total number of bytes written to sink
45
+ */
46
+ decodeWithSink(input: Buffer): number;
15
47
  /**
16
48
  * Decode LZMA2 data
17
49
  * @param input - LZMA2 compressed data
@@ -24,7 +56,8 @@ export declare class Lzma2Decoder {
24
56
  * Decode LZMA2 data synchronously
25
57
  * @param input - LZMA2 compressed data
26
58
  * @param properties - 1-byte properties (dictionary size)
27
- * @param unpackSize - Expected output size (optional)
28
- * @returns Decompressed data
59
+ * @param unpackSize - Expected output size (optional, autodetects if not provided)
60
+ * @param outputSink - Optional output sink for zero-copy decoding (returns bytes written)
61
+ * @returns Decompressed data (or bytes written if outputSink provided)
29
62
  */
30
- export declare function decodeLzma2(input: Buffer, properties: Buffer | Uint8Array, unpackSize?: number): Buffer;
63
+ export declare function decodeLzma2(input: Buffer, properties: Buffer | Uint8Array, unpackSize?: number, outputSink?: OutputSink): Buffer | number;
@@ -32,18 +32,109 @@ function _class_call_check(instance, Constructor) {
32
32
  }
33
33
  var Lzma2Decoder = /*#__PURE__*/ function() {
34
34
  "use strict";
35
- function Lzma2Decoder(properties) {
35
+ function Lzma2Decoder(properties, outputSink) {
36
36
  _class_call_check(this, Lzma2Decoder);
37
37
  if (!properties || properties.length < 1) {
38
38
  throw new Error('LZMA2 requires properties byte');
39
39
  }
40
40
  this.dictionarySize = (0, _typests.parseLzma2DictionarySize)(properties[0]);
41
- this.lzmaDecoder = new _LzmaDecoderts.LzmaDecoder();
41
+ this.lzmaDecoder = new _LzmaDecoderts.LzmaDecoder(outputSink);
42
42
  this.lzmaDecoder.setDictionarySize(this.dictionarySize);
43
43
  this.propsSet = false;
44
44
  }
45
45
  var _proto = Lzma2Decoder.prototype;
46
46
  /**
47
+ * Reset the dictionary (for stream boundaries)
48
+ */ _proto.resetDictionary = function resetDictionary() {
49
+ this.lzmaDecoder.resetDictionary();
50
+ };
51
+ /**
52
+ * Reset all probability models (for stream boundaries)
53
+ */ _proto.resetProbabilities = function resetProbabilities() {
54
+ this.lzmaDecoder.resetProbabilities();
55
+ };
56
+ /**
57
+ * Set LZMA properties
58
+ */ _proto.setLcLpPb = function setLcLpPb(lc, lp, pb) {
59
+ return this.lzmaDecoder.setLcLpPb(lc, lp, pb);
60
+ };
61
+ /**
62
+ * Feed uncompressed data to the dictionary (for subsequent LZMA chunks)
63
+ */ _proto.feedUncompressed = function feedUncompressed(data) {
64
+ this.lzmaDecoder.feedUncompressed(data);
65
+ };
66
+ /**
67
+ * Decode raw LZMA data (used internally for LZMA2 chunks)
68
+ * @param input - LZMA compressed data
69
+ * @param offset - Input offset
70
+ * @param outSize - Expected output size
71
+ * @param solid - Use solid mode
72
+ * @returns Decompressed data
73
+ */ _proto.decodeLzmaData = function decodeLzmaData(input, offset, outSize) {
74
+ var solid = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : false;
75
+ return this.lzmaDecoder.decode(input, offset, outSize, solid);
76
+ };
77
+ /**
78
+ * Decode LZMA2 data with streaming output
79
+ * @param input - LZMA2 compressed data
80
+ * @returns Total number of bytes written to sink
81
+ */ _proto.decodeWithSink = function decodeWithSink(input) {
82
+ var totalBytes = 0;
83
+ var offset = 0;
84
+ while(offset < input.length){
85
+ var result = (0, _Lzma2ChunkParserts.parseLzma2ChunkHeader)(input, offset);
86
+ if (!result.success) {
87
+ throw new Error('Truncated LZMA2 chunk header');
88
+ }
89
+ var chunk = result.chunk;
90
+ if (chunk.type === 'end') {
91
+ break;
92
+ }
93
+ // Validate we have enough data for the chunk
94
+ var dataSize = chunk.type === 'uncompressed' ? chunk.uncompSize : chunk.compSize;
95
+ if (offset + chunk.headerSize + dataSize > input.length) {
96
+ throw new Error("Truncated LZMA2 ".concat(chunk.type, " data"));
97
+ }
98
+ // Handle dictionary reset
99
+ if (chunk.dictReset) {
100
+ this.lzmaDecoder.resetDictionary();
101
+ }
102
+ var dataOffset = offset + chunk.headerSize;
103
+ if (chunk.type === 'uncompressed') {
104
+ var uncompData = input.slice(dataOffset, dataOffset + chunk.uncompSize);
105
+ // Feed uncompressed data to dictionary so subsequent LZMA chunks can reference it
106
+ this.lzmaDecoder.feedUncompressed(uncompData);
107
+ totalBytes += uncompData.length;
108
+ offset = dataOffset + chunk.uncompSize;
109
+ } else {
110
+ // LZMA compressed chunk
111
+ // Apply new properties if present
112
+ if (chunk.newProps) {
113
+ var _chunk_newProps = chunk.newProps, lc = _chunk_newProps.lc, lp = _chunk_newProps.lp, pb = _chunk_newProps.pb;
114
+ if (!this.lzmaDecoder.setLcLpPb(lc, lp, pb)) {
115
+ throw new Error("Invalid LZMA properties: lc=".concat(lc, " lp=").concat(lp, " pb=").concat(pb));
116
+ }
117
+ this.propsSet = true;
118
+ }
119
+ if (!this.propsSet) {
120
+ throw new Error('LZMA chunk without properties');
121
+ }
122
+ // Reset probabilities if state reset
123
+ if (chunk.stateReset) {
124
+ this.lzmaDecoder.resetProbabilities();
125
+ }
126
+ // Determine solid mode
127
+ var useSolid = !chunk.stateReset || chunk.stateReset && !chunk.dictReset;
128
+ // Decode LZMA chunk directly to sink
129
+ totalBytes += this.lzmaDecoder.decodeWithSink(input, dataOffset, chunk.uncompSize, useSolid);
130
+ offset = dataOffset + chunk.compSize;
131
+ }
132
+ }
133
+ // Flush any remaining data in the OutWindow
134
+ this.lzmaDecoder.flushOutWindow();
135
+ return totalBytes;
136
+ };
137
+ /**
47
138
  * Decode LZMA2 data
48
139
  * @param input - LZMA2 compressed data
49
140
  * @param unpackSize - Expected output size (optional, for pre-allocation)
@@ -128,8 +219,13 @@ var Lzma2Decoder = /*#__PURE__*/ function() {
128
219
  };
129
220
  return Lzma2Decoder;
130
221
  }();
131
- function decodeLzma2(input, properties, unpackSize) {
132
- var decoder = new Lzma2Decoder(properties);
222
+ function decodeLzma2(input, properties, unpackSize, outputSink) {
223
+ var decoder = new Lzma2Decoder(properties, outputSink);
224
+ if (outputSink) {
225
+ // Zero-copy mode: write to sink during decode
226
+ return decoder.decodeWithSink(input);
227
+ }
228
+ // Buffering mode: returns Buffer (zero-copy)
133
229
  return decoder.decode(input, unpackSize);
134
230
  }
135
231
  /* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (var key in exports) { exports.default[key] = exports[key]; } } catch (_) {}; module.exports = exports.default; }