zstdify 1.1.2 → 1.3.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 (67) hide show
  1. package/README.md +51 -2
  2. package/dist/bitstream/bitReaderReverse.d.ts +6 -0
  3. package/dist/bitstream/bitReaderReverse.js +51 -1
  4. package/dist/bitstream/bitReaderReverse.js.map +1 -1
  5. package/dist/bitstream/index.d.ts +1 -0
  6. package/dist/bitstream/index.js +1 -0
  7. package/dist/bitstream/index.js.map +1 -1
  8. package/dist/bitstream/reverseBitWriter.d.ts +1 -0
  9. package/dist/bitstream/reverseBitWriter.js +66 -0
  10. package/dist/bitstream/reverseBitWriter.js.map +1 -0
  11. package/dist/compress.js +47 -7
  12. package/dist/compress.js.map +1 -1
  13. package/dist/decode/debugTrace.d.ts +31 -0
  14. package/dist/decode/debugTrace.js +2 -0
  15. package/dist/decode/debugTrace.js.map +1 -0
  16. package/dist/decode/decompressFrame.d.ts +3 -1
  17. package/dist/decode/decompressFrame.js +153 -59
  18. package/dist/decode/decompressFrame.js.map +1 -1
  19. package/dist/decode/fusedSequences.d.ts +9 -0
  20. package/dist/decode/fusedSequences.js +26 -0
  21. package/dist/decode/fusedSequences.js.map +1 -0
  22. package/dist/decode/literals.js +164 -111
  23. package/dist/decode/literals.js.map +1 -1
  24. package/dist/decode/reconstruct.d.ts +33 -1
  25. package/dist/decode/reconstruct.js +591 -24
  26. package/dist/decode/reconstruct.js.map +1 -1
  27. package/dist/decode/sequences.d.ts +19 -7
  28. package/dist/decode/sequences.js +225 -133
  29. package/dist/decode/sequences.js.map +1 -1
  30. package/dist/decompress.d.ts +10 -0
  31. package/dist/decompress.js +5 -3
  32. package/dist/decompress.js.map +1 -1
  33. package/dist/encode/blockWriter.js +8 -2
  34. package/dist/encode/blockWriter.js.map +1 -1
  35. package/dist/encode/compressedBlock.d.ts +27 -1
  36. package/dist/encode/compressedBlock.js +594 -339
  37. package/dist/encode/compressedBlock.js.map +1 -1
  38. package/dist/encode/fastMatcher.d.ts +7 -0
  39. package/dist/encode/fastMatcher.js +13 -0
  40. package/dist/encode/fastMatcher.js.map +1 -0
  41. package/dist/encode/greedySequences.d.ts +9 -6
  42. package/dist/encode/greedySequences.js +21 -77
  43. package/dist/encode/greedySequences.js.map +1 -1
  44. package/dist/encode/lazyMatcher.d.ts +7 -0
  45. package/dist/encode/lazyMatcher.js +13 -0
  46. package/dist/encode/lazyMatcher.js.map +1 -0
  47. package/dist/encode/literalsEncoder.d.ts +14 -0
  48. package/dist/encode/literalsEncoder.js +343 -0
  49. package/dist/encode/literalsEncoder.js.map +1 -0
  50. package/dist/encode/optimalParser.d.ts +7 -0
  51. package/dist/encode/optimalParser.js +13 -0
  52. package/dist/encode/optimalParser.js.map +1 -0
  53. package/dist/encode/sequencePlanner.d.ts +23 -0
  54. package/dist/encode/sequencePlanner.js +280 -0
  55. package/dist/encode/sequencePlanner.js.map +1 -0
  56. package/dist/entropy/fse.d.ts +13 -6
  57. package/dist/entropy/fse.js +176 -13
  58. package/dist/entropy/fse.js.map +1 -1
  59. package/dist/entropy/huffman.d.ts +7 -5
  60. package/dist/entropy/huffman.js +18 -7
  61. package/dist/entropy/huffman.js.map +1 -1
  62. package/dist/entropy/index.d.ts +2 -2
  63. package/dist/entropy/weights.js +20 -14
  64. package/dist/entropy/weights.js.map +1 -1
  65. package/dist/index.d.ts +1 -1
  66. package/dist/index.js.map +1 -1
  67. package/package.json +1 -1
@@ -2,26 +2,208 @@
2
2
  * Sequence execution: copy literals + match copies with window.
3
3
  */
4
4
  import { ZstdError } from '../errors.js';
5
+ const FAST_LITERAL_COPY_LOOP_THRESHOLD = 8;
6
+ const FAST_SMALL_OFFSET_LOOP_THRESHOLD = 16;
7
+ const FAST_HISTORY_COPY_LOOP_THRESHOLD = 16;
8
+ function isHistoryWindow(value) {
9
+ return (typeof value.length === 'number' &&
10
+ typeof value.writePos === 'number' &&
11
+ value.buffer instanceof Uint8Array);
12
+ }
13
+ export function createHistoryWindow(windowSize, initial) {
14
+ const initialLength = initial?.length ?? 0;
15
+ const capacity = Math.max(windowSize, initialLength);
16
+ if (capacity <= 0) {
17
+ return { buffer: new Uint8Array(0), length: 0, writePos: 0 };
18
+ }
19
+ const buffer = new Uint8Array(capacity);
20
+ const history = { buffer, length: 0, writePos: 0 };
21
+ if (initialLength > 0 && initial) {
22
+ appendToHistoryWindow(history, initial);
23
+ }
24
+ return history;
25
+ }
26
+ function createPackedSequences(capacity) {
27
+ return {
28
+ literalsLength: new Uint32Array(capacity),
29
+ offset: new Uint32Array(capacity),
30
+ matchLength: new Uint32Array(capacity),
31
+ length: 0,
32
+ };
33
+ }
34
+ export function ensurePackedSequencesCapacity(existing, minLength) {
35
+ if (existing && existing.literalsLength.length >= minLength) {
36
+ existing.length = minLength;
37
+ return existing;
38
+ }
39
+ let capacity = existing?.literalsLength.length ?? 0;
40
+ if (capacity === 0) {
41
+ capacity = 16;
42
+ }
43
+ while (capacity < minLength) {
44
+ capacity *= 2;
45
+ }
46
+ return createPackedSequences(capacity);
47
+ }
48
+ export function packSequences(sequences, reuse) {
49
+ const packed = ensurePackedSequencesCapacity(reuse, sequences.length);
50
+ for (let i = 0; i < sequences.length; i++) {
51
+ const seq = sequences[i];
52
+ if (!seq) {
53
+ throw new ZstdError('Invalid sequence object', 'corruption_detected');
54
+ }
55
+ packed.literalsLength[i] = seq.literalsLength;
56
+ packed.offset[i] = seq.offset;
57
+ packed.matchLength[i] = seq.matchLength;
58
+ }
59
+ packed.length = sequences.length;
60
+ return packed;
61
+ }
62
+ export function packedSequencesToArray(sequences) {
63
+ const out = new Array(sequences.length);
64
+ for (let i = 0; i < sequences.length; i++) {
65
+ out[i] = {
66
+ literalsLength: sequences.literalsLength[i] ?? 0,
67
+ offset: sequences.offset[i] ?? 0,
68
+ matchLength: sequences.matchLength[i] ?? 0,
69
+ };
70
+ }
71
+ return out;
72
+ }
5
73
  /**
6
- * Execute sequences to produce decompressed output.
7
- * repOffsets: [Repeated_Offset1, Repeated_Offset2, Repeated_Offset3], updated in place.
74
+ * Get or create a history window, reusing from bag when buffer is large enough.
75
+ * Caller may pass a mutable bag; it will be updated with the history used.
8
76
  */
9
- export function executeSequences(literals, sequences, windowSize, repOffsets = [1, 4, 8], history = new Uint8Array(0)) {
10
- // Sequence literals are slices of `literals`, so only matches expand output size.
11
- const totalSize = literals.length + sequences.reduce((s, seq) => s + seq.matchLength, 0);
12
- const historyLength = history.length;
13
- const buffer = new Uint8Array(historyLength + totalSize);
14
- if (historyLength > 0) {
15
- buffer.set(history, 0);
77
+ export function getOrCreateHistoryWindow(windowSize, initial, reuse) {
78
+ const existing = reuse?._history;
79
+ if (existing && existing.buffer.length >= windowSize) {
80
+ existing.length = 0;
81
+ existing.writePos = 0;
82
+ if (initial && initial.length > 0) {
83
+ appendToHistoryWindow(existing, initial);
84
+ }
85
+ return existing;
86
+ }
87
+ const history = createHistoryWindow(windowSize, initial);
88
+ if (reuse) {
89
+ reuse._history = history;
90
+ }
91
+ return history;
92
+ }
93
+ export function appendToHistoryWindow(history, chunk) {
94
+ const cap = history.buffer.length;
95
+ if (cap === 0 || chunk.length === 0) {
96
+ return;
97
+ }
98
+ if (chunk.length >= cap) {
99
+ const tail = chunk.subarray(chunk.length - cap);
100
+ history.buffer.set(tail, 0);
101
+ history.length = cap;
102
+ history.writePos = 0;
103
+ return;
104
+ }
105
+ const firstLen = Math.min(chunk.length, cap - history.writePos);
106
+ history.buffer.set(chunk.subarray(0, firstLen), history.writePos);
107
+ const remaining = chunk.length - firstLen;
108
+ if (remaining > 0) {
109
+ history.buffer.set(chunk.subarray(firstLen), 0);
110
+ }
111
+ history.writePos = (history.writePos + chunk.length) % cap;
112
+ history.length = Math.min(cap, history.length + chunk.length);
113
+ }
114
+ const APPEND_RANGE_LOOP_THRESHOLD = 16;
115
+ export function appendRangeToHistoryWindow(history, source, start, length) {
116
+ const cap = history.buffer.length;
117
+ if (cap === 0 || length <= 0) {
118
+ return;
16
119
  }
17
- let outPos = historyLength;
120
+ if (start < 0 || length < 0 || start + length > source.length) {
121
+ throw new RangeError('Invalid source range for history append');
122
+ }
123
+ if (length >= cap) {
124
+ const tailStart = start + length - cap;
125
+ history.buffer.set(source.subarray(tailStart, start + length), 0);
126
+ history.length = cap;
127
+ history.writePos = 0;
128
+ return;
129
+ }
130
+ const firstLen = Math.min(length, cap - history.writePos);
131
+ const remaining = length - firstLen;
132
+ if (length <= APPEND_RANGE_LOOP_THRESHOLD) {
133
+ let wp = history.writePos;
134
+ for (let i = 0; i < length; i++) {
135
+ history.buffer[wp] = source[start + i];
136
+ wp = wp + 1 === cap ? 0 : wp + 1;
137
+ }
138
+ }
139
+ else {
140
+ history.buffer.set(source.subarray(start, start + firstLen), history.writePos);
141
+ if (remaining > 0) {
142
+ history.buffer.set(source.subarray(start + firstLen, start + firstLen + remaining), 0);
143
+ }
144
+ }
145
+ history.writePos = (history.writePos + length) % cap;
146
+ history.length = Math.min(cap, history.length + length);
147
+ }
148
+ export function appendRLEToHistoryWindow(history, byte, length) {
149
+ const cap = history.buffer.length;
150
+ if (cap === 0 || length <= 0) {
151
+ return;
152
+ }
153
+ const fillByte = byte & 0xff;
154
+ if (length >= cap) {
155
+ history.buffer.fill(fillByte, 0, cap);
156
+ history.length = cap;
157
+ history.writePos = 0;
158
+ return;
159
+ }
160
+ const firstLen = Math.min(length, cap - history.writePos);
161
+ history.buffer.fill(fillByte, history.writePos, history.writePos + firstLen);
162
+ const remaining = length - firstLen;
163
+ if (remaining > 0) {
164
+ history.buffer.fill(fillByte, 0, remaining);
165
+ }
166
+ history.writePos = (history.writePos + length) % cap;
167
+ history.length = Math.min(cap, history.length + length);
168
+ }
169
+ export function executeSequencesInto(literals, sequences, windowSize, target, targetOffset, repOffsets = [1, 4, 8], historyInput = { buffer: new Uint8Array(0), length: 0, writePos: 0 }, updateHistory = false) {
170
+ const history = isHistoryWindow(historyInput) ? historyInput : createHistoryWindow(windowSize, historyInput);
171
+ const historyLength = history.length;
172
+ const historyCap = history.buffer.length;
173
+ const historyOldestPos = historyCap > 0 ? (history.writePos - historyLength + historyCap) % historyCap : 0;
174
+ const historyBuffer = history.buffer;
175
+ let outPos = targetOffset;
18
176
  let litPos = 0;
19
- for (const seq of sequences) {
20
- for (let i = 0; i < seq.literalsLength; i++) {
21
- buffer[outPos++] = literals[litPos++] ?? 0;
177
+ const LIT_COPY_LOOP_THRESHOLD = 16;
178
+ const HISTORY_COPY_LOOP_THRESHOLD = 16;
179
+ const seqCount = sequences.length;
180
+ const literalsLengthBySeq = sequences.literalsLength;
181
+ const offsetBySeq = sequences.offset;
182
+ const matchLengthBySeq = sequences.matchLength;
183
+ for (let seqIndex = 0; seqIndex < seqCount; seqIndex++) {
184
+ const seqLiteralsLength = literalsLengthBySeq[seqIndex];
185
+ if (seqLiteralsLength > 0) {
186
+ const litOutStart = outPos;
187
+ const litEnd = litPos + seqLiteralsLength;
188
+ if (litEnd > literals.length) {
189
+ throw new ZstdError('Literals overrun while executing sequence', 'corruption_detected');
190
+ }
191
+ if (seqLiteralsLength <= LIT_COPY_LOOP_THRESHOLD) {
192
+ for (let i = 0; i < seqLiteralsLength; i++) {
193
+ target[outPos + i] = literals[litPos + i];
194
+ }
195
+ }
196
+ else {
197
+ target.set(literals.subarray(litPos, litEnd), outPos);
198
+ }
199
+ outPos += seqLiteralsLength;
200
+ litPos = litEnd;
201
+ if (updateHistory) {
202
+ appendRangeToHistoryWindow(history, target, litOutStart, seqLiteralsLength);
203
+ }
22
204
  }
23
- const ov = seq.offset; // Offset_Value from sequence decode.
24
- const ll0 = seq.literalsLength === 0;
205
+ const ov = offsetBySeq[seqIndex]; // Offset_Value from sequence decode.
206
+ const ll0 = seqLiteralsLength === 0;
25
207
  let offset;
26
208
  let repeatIndex = null;
27
209
  const isNonRepeat = ov > 3 || (ov === 3 && ll0);
@@ -43,16 +225,74 @@ export function executeSequences(literals, sequences, windowSize, repOffsets = [
43
225
  else {
44
226
  repeatIndex = (ov - 1);
45
227
  }
46
- offset = repOffsets[repeatIndex] ?? 0;
228
+ offset = repOffsets[repeatIndex];
47
229
  }
48
- const produced = outPos - historyLength;
49
- const maxReachBack = produced <= windowSize ? produced + historyLength : windowSize;
230
+ const produced = outPos - targetOffset;
231
+ const maxReachBack = Math.min(windowSize, produced + historyLength);
50
232
  if (offset <= 0 || offset > maxReachBack) {
51
233
  throw new ZstdError(`Invalid match offset: offset=${offset} maxReachBack=${maxReachBack} produced=${produced} history=${historyLength} window=${windowSize}`, 'corruption_detected');
52
234
  }
53
- for (let i = 0; i < seq.matchLength; i++) {
54
- buffer[outPos] = buffer[outPos - offset] ?? 0;
55
- outPos++;
235
+ let remainingMatch = matchLengthBySeq[seqIndex];
236
+ const historyBytesNeeded = Math.max(0, offset - produced);
237
+ if (historyBytesNeeded > 0) {
238
+ if (historyCap === 0) {
239
+ throw new ZstdError('Invalid history read', 'corruption_detected');
240
+ }
241
+ const historyCopyLen = Math.min(historyBytesNeeded, remainingMatch);
242
+ const historyStart = historyLength - historyBytesNeeded;
243
+ if (historyStart < 0 || historyStart + historyCopyLen > historyLength) {
244
+ throw new ZstdError('Invalid history read', 'corruption_detected');
245
+ }
246
+ let physicalStart = historyOldestPos + historyStart;
247
+ if (physicalStart >= historyCap) {
248
+ physicalStart -= historyCap;
249
+ }
250
+ const firstHistoryChunk = Math.min(historyCopyLen, historyCap - physicalStart);
251
+ const remainingHistoryChunk = historyCopyLen - firstHistoryChunk;
252
+ const historyOutStart = outPos;
253
+ if (historyCopyLen <= HISTORY_COPY_LOOP_THRESHOLD) {
254
+ let phys = physicalStart;
255
+ for (let i = 0; i < historyCopyLen; i++) {
256
+ target[outPos + i] = historyBuffer[phys];
257
+ phys = phys + 1 === historyCap ? 0 : phys + 1;
258
+ }
259
+ outPos += historyCopyLen;
260
+ }
261
+ else {
262
+ target.set(historyBuffer.subarray(physicalStart, physicalStart + firstHistoryChunk), outPos);
263
+ outPos += firstHistoryChunk;
264
+ if (remainingHistoryChunk > 0) {
265
+ target.set(historyBuffer.subarray(0, remainingHistoryChunk), outPos);
266
+ outPos += remainingHistoryChunk;
267
+ }
268
+ }
269
+ if (updateHistory) {
270
+ appendRangeToHistoryWindow(history, target, historyOutStart, historyCopyLen);
271
+ }
272
+ remainingMatch -= historyCopyLen;
273
+ }
274
+ if (remainingMatch > 0) {
275
+ const matchOutStart = outPos;
276
+ const copyStart = outPos - offset;
277
+ if (offset >= remainingMatch) {
278
+ target.copyWithin(outPos, copyStart, copyStart + remainingMatch);
279
+ outPos += remainingMatch;
280
+ }
281
+ else {
282
+ // Handle overlapping copies with exponentially growing chunks.
283
+ let copied = offset;
284
+ target.copyWithin(outPos, copyStart, copyStart + copied);
285
+ outPos += copied;
286
+ while (copied < remainingMatch) {
287
+ const toCopy = Math.min(copied, remainingMatch - copied);
288
+ target.copyWithin(outPos, outPos - copied, outPos - copied + toCopy);
289
+ outPos += toCopy;
290
+ copied += toCopy;
291
+ }
292
+ }
293
+ if (updateHistory) {
294
+ appendRangeToHistoryWindow(history, target, matchOutStart, remainingMatch);
295
+ }
56
296
  }
57
297
  if (isNonRepeat) {
58
298
  repOffsets[2] = repOffsets[1];
@@ -72,9 +312,336 @@ export function executeSequences(literals, sequences, windowSize, repOffsets = [
72
312
  }
73
313
  }
74
314
  }
75
- while (litPos < literals.length) {
76
- buffer[outPos++] = literals[litPos++] ?? 0;
315
+ if (litPos < literals.length) {
316
+ const tailOutStart = outPos;
317
+ const remaining = literals.length - litPos;
318
+ if (remaining <= LIT_COPY_LOOP_THRESHOLD) {
319
+ for (let i = 0; i < remaining; i++) {
320
+ target[outPos + i] = literals[litPos + i];
321
+ }
322
+ }
323
+ else {
324
+ target.set(literals.subarray(litPos), outPos);
325
+ }
326
+ outPos += remaining;
327
+ if (updateHistory) {
328
+ appendRangeToHistoryWindow(history, target, tailOutStart, remaining);
329
+ }
330
+ }
331
+ return outPos - targetOffset;
332
+ }
333
+ export function executeSequencesIntoFast(literals, sequences, windowSize, target, targetOffset, repOffsets = [1, 4, 8], historyInput = { buffer: new Uint8Array(0), length: 0, writePos: 0 }, updateHistory = false) {
334
+ const history = isHistoryWindow(historyInput) ? historyInput : createHistoryWindow(windowSize, historyInput);
335
+ if (history.length === 0) {
336
+ return executeSequencesIntoFastNoHistory(literals, sequences, windowSize, target, targetOffset, repOffsets, history, updateHistory);
337
+ }
338
+ const historyLength = history.length;
339
+ const historyCap = history.buffer.length;
340
+ const historyOldestPos = historyCap > 0 ? (history.writePos - historyLength + historyCap) % historyCap : 0;
341
+ const historyBuffer = history.buffer;
342
+ let outPos = targetOffset;
343
+ let litPos = 0;
344
+ const seqCount = sequences.length;
345
+ const literalsLengthBySeq = sequences.literalsLength;
346
+ const offsetBySeq = sequences.offset;
347
+ const matchLengthBySeq = sequences.matchLength;
348
+ for (let seqIndex = 0; seqIndex < seqCount; seqIndex++) {
349
+ const seqLiteralsLength = literalsLengthBySeq[seqIndex];
350
+ if (seqLiteralsLength > 0) {
351
+ const litEnd = litPos + seqLiteralsLength;
352
+ if (litEnd > literals.length) {
353
+ throw new ZstdError('Literals overrun while executing sequence', 'corruption_detected');
354
+ }
355
+ if (seqLiteralsLength <= FAST_LITERAL_COPY_LOOP_THRESHOLD) {
356
+ for (let i = 0; i < seqLiteralsLength; i++) {
357
+ target[outPos + i] = literals[litPos + i];
358
+ }
359
+ }
360
+ else {
361
+ target.set(literals.subarray(litPos, litEnd), outPos);
362
+ }
363
+ outPos += seqLiteralsLength;
364
+ litPos = litEnd;
365
+ }
366
+ const ov = offsetBySeq[seqIndex];
367
+ const ll0 = seqLiteralsLength === 0;
368
+ let offset;
369
+ let repeatIndex = null;
370
+ const isNonRepeat = ov > 3 || (ov === 3 && ll0);
371
+ if (isNonRepeat) {
372
+ if (ov === 3) {
373
+ offset = repOffsets[0] - 1;
374
+ if (offset === 0) {
375
+ throw new ZstdError('Invalid match offset: repeat1-1 is 0', 'corruption_detected');
376
+ }
377
+ }
378
+ else {
379
+ offset = ov - 3;
380
+ }
381
+ }
382
+ else {
383
+ if (ll0) {
384
+ repeatIndex = ov === 1 ? 1 : 2;
385
+ }
386
+ else {
387
+ repeatIndex = (ov - 1);
388
+ }
389
+ offset = repOffsets[repeatIndex];
390
+ }
391
+ const produced = outPos - targetOffset;
392
+ const producedPlusHistory = produced + historyLength;
393
+ const maxReachBack = producedPlusHistory < windowSize ? producedPlusHistory : windowSize;
394
+ if (offset <= 0 || offset > maxReachBack) {
395
+ throw new ZstdError(`Invalid match offset: offset=${offset} maxReachBack=${maxReachBack} produced=${produced} history=${historyLength} window=${windowSize}`, 'corruption_detected');
396
+ }
397
+ const remainingMatch = matchLengthBySeq[seqIndex];
398
+ const historyBytesNeeded = offset > produced ? offset - produced : 0;
399
+ if (remainingMatch > 0) {
400
+ if (historyBytesNeeded === 0) {
401
+ const copyStart = outPos - offset;
402
+ if (offset >= remainingMatch) {
403
+ target.copyWithin(outPos, copyStart, copyStart + remainingMatch);
404
+ outPos += remainingMatch;
405
+ }
406
+ else if (offset <= FAST_SMALL_OFFSET_LOOP_THRESHOLD) {
407
+ for (let i = 0; i < remainingMatch; i++) {
408
+ target[outPos + i] = target[outPos - offset + i];
409
+ }
410
+ outPos += remainingMatch;
411
+ }
412
+ else {
413
+ let copied = offset;
414
+ target.copyWithin(outPos, copyStart, copyStart + copied);
415
+ outPos += copied;
416
+ while (copied < remainingMatch) {
417
+ const toCopy = Math.min(copied, remainingMatch - copied);
418
+ target.copyWithin(outPos, outPos - copied, outPos - copied + toCopy);
419
+ outPos += toCopy;
420
+ copied += toCopy;
421
+ }
422
+ }
423
+ }
424
+ else {
425
+ outPos = copyMatchWithHistorySlowPath(target, outPos, offset, remainingMatch, historyBytesNeeded, historyLength, historyCap, historyOldestPos, historyBuffer);
426
+ }
427
+ }
428
+ if (isNonRepeat) {
429
+ repOffsets[2] = repOffsets[1];
430
+ repOffsets[1] = repOffsets[0];
431
+ repOffsets[0] = offset;
432
+ }
433
+ else if (repeatIndex === 1) {
434
+ repOffsets[1] = repOffsets[0];
435
+ repOffsets[0] = offset;
436
+ }
437
+ else if (repeatIndex === 2) {
438
+ repOffsets[2] = repOffsets[1];
439
+ repOffsets[1] = repOffsets[0];
440
+ repOffsets[0] = offset;
441
+ }
442
+ }
443
+ if (litPos < literals.length) {
444
+ const remaining = literals.length - litPos;
445
+ if (remaining <= FAST_LITERAL_COPY_LOOP_THRESHOLD) {
446
+ for (let i = 0; i < remaining; i++) {
447
+ target[outPos + i] = literals[litPos + i];
448
+ }
449
+ outPos += remaining;
450
+ }
451
+ else {
452
+ target.set(literals.subarray(litPos), outPos);
453
+ outPos += remaining;
454
+ }
455
+ }
456
+ if (updateHistory && outPos > targetOffset) {
457
+ appendRangeToHistoryWindow(history, target, targetOffset, outPos - targetOffset);
458
+ }
459
+ return outPos - targetOffset;
460
+ }
461
+ function executeSequencesIntoFastNoHistory(literals, sequences, windowSize, target, targetOffset, repOffsets, history, updateHistory) {
462
+ let outPos = targetOffset;
463
+ let litPos = 0;
464
+ const seqCount = sequences.length;
465
+ const literalsLengthBySeq = sequences.literalsLength;
466
+ const offsetBySeq = sequences.offset;
467
+ const matchLengthBySeq = sequences.matchLength;
468
+ for (let seqIndex = 0; seqIndex < seqCount; seqIndex++) {
469
+ const seqLiteralsLength = literalsLengthBySeq[seqIndex];
470
+ if (seqLiteralsLength > 0) {
471
+ const litEnd = litPos + seqLiteralsLength;
472
+ if (litEnd > literals.length) {
473
+ throw new ZstdError('Literals overrun while executing sequence', 'corruption_detected');
474
+ }
475
+ if (seqLiteralsLength <= FAST_LITERAL_COPY_LOOP_THRESHOLD) {
476
+ for (let i = 0; i < seqLiteralsLength; i++) {
477
+ target[outPos + i] = literals[litPos + i];
478
+ }
479
+ }
480
+ else {
481
+ target.set(literals.subarray(litPos, litEnd), outPos);
482
+ }
483
+ outPos += seqLiteralsLength;
484
+ litPos = litEnd;
485
+ }
486
+ const ov = offsetBySeq[seqIndex];
487
+ const ll0 = seqLiteralsLength === 0;
488
+ let offset;
489
+ let repeatIndex = null;
490
+ const isNonRepeat = ov > 3 || (ov === 3 && ll0);
491
+ if (isNonRepeat) {
492
+ if (ov === 3) {
493
+ offset = repOffsets[0] - 1;
494
+ if (offset === 0) {
495
+ throw new ZstdError('Invalid match offset: repeat1-1 is 0', 'corruption_detected');
496
+ }
497
+ }
498
+ else {
499
+ offset = ov - 3;
500
+ }
501
+ }
502
+ else {
503
+ if (ll0) {
504
+ repeatIndex = ov === 1 ? 1 : 2;
505
+ }
506
+ else {
507
+ repeatIndex = (ov - 1);
508
+ }
509
+ offset = repOffsets[repeatIndex];
510
+ }
511
+ const produced = outPos - targetOffset;
512
+ const maxReachBack = produced < windowSize ? produced : windowSize;
513
+ if (offset <= 0 || offset > maxReachBack) {
514
+ throw new ZstdError(`Invalid match offset: offset=${offset} maxReachBack=${maxReachBack} produced=${produced} history=0 window=${windowSize}`, 'corruption_detected');
515
+ }
516
+ const remainingMatch = matchLengthBySeq[seqIndex];
517
+ if (remainingMatch > 0) {
518
+ const copyStart = outPos - offset;
519
+ if (offset >= remainingMatch) {
520
+ target.copyWithin(outPos, copyStart, copyStart + remainingMatch);
521
+ outPos += remainingMatch;
522
+ }
523
+ else if (offset <= FAST_SMALL_OFFSET_LOOP_THRESHOLD) {
524
+ for (let i = 0; i < remainingMatch; i++) {
525
+ target[outPos + i] = target[outPos - offset + i];
526
+ }
527
+ outPos += remainingMatch;
528
+ }
529
+ else {
530
+ let copied = offset;
531
+ target.copyWithin(outPos, copyStart, copyStart + copied);
532
+ outPos += copied;
533
+ while (copied < remainingMatch) {
534
+ const toCopy = Math.min(copied, remainingMatch - copied);
535
+ target.copyWithin(outPos, outPos - copied, outPos - copied + toCopy);
536
+ outPos += toCopy;
537
+ copied += toCopy;
538
+ }
539
+ }
540
+ }
541
+ if (isNonRepeat) {
542
+ repOffsets[2] = repOffsets[1];
543
+ repOffsets[1] = repOffsets[0];
544
+ repOffsets[0] = offset;
545
+ }
546
+ else if (repeatIndex === 1) {
547
+ repOffsets[1] = repOffsets[0];
548
+ repOffsets[0] = offset;
549
+ }
550
+ else if (repeatIndex === 2) {
551
+ repOffsets[2] = repOffsets[1];
552
+ repOffsets[1] = repOffsets[0];
553
+ repOffsets[0] = offset;
554
+ }
555
+ }
556
+ if (litPos < literals.length) {
557
+ const remaining = literals.length - litPos;
558
+ if (remaining <= FAST_LITERAL_COPY_LOOP_THRESHOLD) {
559
+ for (let i = 0; i < remaining; i++) {
560
+ target[outPos + i] = literals[litPos + i];
561
+ }
562
+ outPos += remaining;
563
+ }
564
+ else {
565
+ target.set(literals.subarray(litPos), outPos);
566
+ outPos += remaining;
567
+ }
568
+ }
569
+ if (updateHistory && outPos > targetOffset) {
570
+ appendRangeToHistoryWindow(history, target, targetOffset, outPos - targetOffset);
571
+ }
572
+ return outPos - targetOffset;
573
+ }
574
+ function copyMatchWithHistorySlowPath(target, outPos, offset, remainingMatch, historyBytesNeeded, historyLength, historyCap, historyOldestPos, historyBuffer) {
575
+ if (historyCap === 0) {
576
+ throw new ZstdError('Invalid history read', 'corruption_detected');
577
+ }
578
+ const historyCopyLen = Math.min(historyBytesNeeded, remainingMatch);
579
+ const historyStart = historyLength - historyBytesNeeded;
580
+ if (historyStart < 0 || historyStart + historyCopyLen > historyLength) {
581
+ throw new ZstdError('Invalid history read', 'corruption_detected');
582
+ }
583
+ let physicalStart = historyOldestPos + historyStart;
584
+ if (physicalStart >= historyCap) {
585
+ physicalStart -= historyCap;
586
+ }
587
+ const firstHistoryChunk = Math.min(historyCopyLen, historyCap - physicalStart);
588
+ const remainingHistoryChunk = historyCopyLen - firstHistoryChunk;
589
+ if (historyCopyLen <= FAST_HISTORY_COPY_LOOP_THRESHOLD) {
590
+ let phys = physicalStart;
591
+ for (let i = 0; i < historyCopyLen; i++) {
592
+ target[outPos + i] = historyBuffer[phys];
593
+ phys = phys + 1 === historyCap ? 0 : phys + 1;
594
+ }
595
+ outPos += historyCopyLen;
596
+ }
597
+ else {
598
+ target.set(historyBuffer.subarray(physicalStart, physicalStart + firstHistoryChunk), outPos);
599
+ outPos += firstHistoryChunk;
600
+ if (remainingHistoryChunk > 0) {
601
+ target.set(historyBuffer.subarray(0, remainingHistoryChunk), outPos);
602
+ outPos += remainingHistoryChunk;
603
+ }
604
+ }
605
+ const matchRemaining = remainingMatch - historyCopyLen;
606
+ if (matchRemaining <= 0) {
607
+ return outPos;
608
+ }
609
+ const copyStart = outPos - offset;
610
+ if (offset >= matchRemaining) {
611
+ target.copyWithin(outPos, copyStart, copyStart + matchRemaining);
612
+ return outPos + matchRemaining;
613
+ }
614
+ if (offset <= FAST_SMALL_OFFSET_LOOP_THRESHOLD) {
615
+ for (let i = 0; i < matchRemaining; i++) {
616
+ target[outPos + i] = target[outPos - offset + i];
617
+ }
618
+ return outPos + matchRemaining;
619
+ }
620
+ let copied = offset;
621
+ target.copyWithin(outPos, copyStart, copyStart + copied);
622
+ outPos += copied;
623
+ while (copied < matchRemaining) {
624
+ const toCopy = Math.min(copied, matchRemaining - copied);
625
+ target.copyWithin(outPos, outPos - copied, outPos - copied + toCopy);
626
+ outPos += toCopy;
627
+ copied += toCopy;
628
+ }
629
+ return outPos;
630
+ }
631
+ /**
632
+ * Execute sequences to produce decompressed output.
633
+ * repOffsets: [Repeated_Offset1, Repeated_Offset2, Repeated_Offset3], updated in place.
634
+ */
635
+ export function executeSequences(literals, sequences, windowSize, repOffsets = [1, 4, 8], historyInput = { buffer: new Uint8Array(0), length: 0, writePos: 0 }) {
636
+ const packed = packSequences(sequences);
637
+ // Sequence literals are slices of `literals`, so only matches expand output size.
638
+ let totalSize = literals.length;
639
+ for (let i = 0; i < packed.length; i++) {
640
+ totalSize += packed.matchLength[i];
77
641
  }
78
- return buffer.subarray(historyLength, outPos).slice();
642
+ const buffer = new Uint8Array(totalSize);
643
+ const outSize = executeSequencesInto(literals, packed, windowSize, buffer, 0, repOffsets, historyInput);
644
+ const outPos = outSize;
645
+ return outPos === buffer.length ? buffer : buffer.subarray(0, outPos);
79
646
  }
80
647
  //# sourceMappingURL=reconstruct.js.map