@patrickdk77/archive-defuse 0.1.1 → 0.1.3

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.
@@ -1,41 +1,68 @@
1
1
  "use strict";
2
+ // Copied from lzma1 0.6.1 (Apache-2.0, Copyright Filip Seman) and changed for archive-defuse. NOTICE in this folder lists the changes.
3
+ /**
4
+ * Range encoder writing to its own growable buffer.
5
+ *
6
+ * Ported from XZ for Java (0BSD), `src/org/tukaani/xz/rangecoder/RangeEncoder.java`
7
+ * and `RangeEncoderToBuffer.java`, by Lasse Collin and Igor Pavlov.
8
+ *
9
+ * Integer adaptations (see the hazard checklist in `docs/verification.md`):
10
+ * - `low` needs 33 bits (32 bits plus a carry), so it is a regular number;
11
+ * doubles represent integers up to 2^53 exactly. The carry test uses
12
+ * division instead of `low >>> 32`.
13
+ * - `range` is an unsigned 32-bit value in a regular number: `<<` is
14
+ * followed by `>>> 0`, and `(range & TOP_MASK) == 0` becomes an
15
+ * unsigned `range < TOP_VALUE`.
16
+ * - Bitwise ops on values that may exceed 2^31 are kept off the signed
17
+ * int32 path (`low`'s 24-bit wrap multiplies by 256 instead of
18
+ * shifting).
19
+ */
2
20
  Object.defineProperty(exports, "__esModule", { value: true });
3
21
  exports.RangeEncoder = void 0;
22
+ exports.createPriceTable = createPriceTable;
4
23
  exports.getBitPrice = getBitPrice;
5
24
  exports.getBitTreePrice = getBitTreePrice;
6
25
  exports.getReverseBitTreePrice = getReverseBitTreePrice;
7
26
  exports.getDirectBitsPrice = getDirectBitsPrice;
8
- // Copied from lzma1 (Apache-2.0, Copyright Filip Seman) and changed for lzma-defuse. NOTICE in this folder lists the changes.
9
27
  const range_coder_1 = require("./range-coder");
10
28
  const MOVE_REDUCING_BITS = 4;
11
29
  const BIT_PRICE_SHIFT_BITS = 4;
12
- /**
13
- * Price (approximate cost in 1/16 bits) of encoding a bit with a given
14
- * probability, indexed by `probability >>> MOVE_REDUCING_BITS`.
15
- */
16
- const PRICES = createPriceTable();
30
+ // Bit prices in 1/16 bits: the price of a 0 bit, then the same prices
31
+ // reversed, which are the prices of a 1 bit, at
32
+ // (bit << 7) | (prob >>> MOVE_REDUCING_BITS). One lookup without the Java
33
+ // code's bit flip was measured faster.
34
+ const bitPrices = createBitPriceTable();
35
+ function createBitPriceTable() {
36
+ const prices = createPriceTable();
37
+ const half = prices.length;
38
+ const table = new Uint32Array(2 * half);
39
+ for (let i = 0; i < half; ++i) {
40
+ table[i] = prices[i];
41
+ table[half + i] = prices[half - 1 - i];
42
+ }
43
+ return table;
44
+ }
17
45
  function createPriceTable() {
18
- const prices = new Uint32Array(range_coder_1.BIT_MODEL_TOTAL >>> MOVE_REDUCING_BITS);
46
+ const table = new Uint32Array(range_coder_1.BIT_MODEL_TOTAL >>> MOVE_REDUCING_BITS);
19
47
  for (let i = (1 << MOVE_REDUCING_BITS) / 2; i < range_coder_1.BIT_MODEL_TOTAL; i += 1 << MOVE_REDUCING_BITS) {
20
- // Approximate -log2(i / BIT_MODEL_TOTAL) using repeated squaring.
21
48
  let w = i;
22
49
  let bitCount = 0;
23
50
  for (let j = 0; j < BIT_PRICE_SHIFT_BITS; ++j) {
24
51
  w *= w;
25
52
  bitCount <<= 1;
26
- while (w >= 0x10000) {
53
+ while ((w & 0xffff0000) !== 0) {
27
54
  w >>>= 1;
28
55
  ++bitCount;
29
56
  }
30
57
  }
31
- prices[i >> MOVE_REDUCING_BITS] = (range_coder_1.BIT_MODEL_TOTAL_BITS << BIT_PRICE_SHIFT_BITS) - 15 - bitCount;
58
+ table[i >> MOVE_REDUCING_BITS] = (range_coder_1.BIT_MODEL_TOTAL_BITS << BIT_PRICE_SHIFT_BITS) - 15 - bitCount;
32
59
  }
33
- return prices;
60
+ return table;
34
61
  }
35
62
  function getBitPrice(prob, bit) {
36
- return PRICES[(prob ^ (-bit & (range_coder_1.BIT_MODEL_TOTAL - 1))) >>> MOVE_REDUCING_BITS];
63
+ // NOTE: Unlike in encodeBit(), here bit must be 0 or 1.
64
+ return bitPrices[(bit << 7) | (prob >>> MOVE_REDUCING_BITS)];
37
65
  }
38
- /** Price of a `bits`-bit symbol coded MSB-first with the bit tree at `offset`. */
39
66
  function getBitTreePrice(probs, offset, bits, symbolIn) {
40
67
  let symbol = symbolIn;
41
68
  let price = 0;
@@ -47,42 +74,47 @@ function getBitTreePrice(probs, offset, bits, symbolIn) {
47
74
  } while (symbol !== 1);
48
75
  return price;
49
76
  }
50
- /** Price of a `bits`-bit symbol coded LSB-first with the bit tree at `offset`. */
51
77
  function getReverseBitTreePrice(probs, offset, bits, symbolIn) {
52
78
  let symbol = symbolIn;
53
79
  let price = 0;
54
80
  let index = 1;
55
- for (let i = 0; i < bits; ++i) {
81
+ symbol |= 1 << bits;
82
+ do {
56
83
  const bit = symbol & 1;
57
84
  symbol >>>= 1;
58
85
  price += getBitPrice(probs[offset + index], bit);
59
86
  index = (index << 1) | bit;
60
- }
87
+ } while (symbol !== 1);
61
88
  return price;
62
89
  }
63
- /** Price of bits coded with a fixed 50% probability. */
64
90
  function getDirectBitsPrice(count) {
65
91
  return count << BIT_PRICE_SHIFT_BITS;
66
92
  }
67
- /**
68
- * Range encoder writing to its own growable buffer.
69
- *
70
- * `low` needs 33 bits (32 bits plus a carry), so it is kept as a regular
71
- * number instead of an int32. Doubles represent integers up to 2^53 exactly.
72
- *
73
- * Performance: like `RangeDecoder`, the methods that encode several bits
74
- * keep `range` and `low` in local variables for the whole symbol.
75
- */
76
93
  class RangeEncoder {
77
94
  low = 0;
78
95
  range = 0xffffffff;
79
96
  cache = 0;
97
+ /**
98
+ * NOTE: int is OK for LZMA2 because a compressed chunk is not more
99
+ * than 64 KiB, but with LZMA1 there is no chunking so in theory
100
+ * cacheSize can grow very big. A JS number counts well past 2^32.
101
+ */
80
102
  cacheSize = 1;
103
+ // Instead of the fixed output array of RangeEncoderToBuffer, the
104
+ // buffer grows on demand and the written bytes are handed out with
105
+ // take()/finish(), because the compressed size isn't known in advance
106
+ // here (contract adaptation).
81
107
  buf;
82
108
  size = 0;
83
109
  constructor(initialCapacity) {
84
110
  this.buf = new Uint8Array(Math.max(64, initialCapacity));
85
111
  }
112
+ reset() {
113
+ this.low = 0;
114
+ this.range = 0xffffffff;
115
+ this.cache = 0x00;
116
+ this.cacheSize = 1;
117
+ }
86
118
  /** Appends bytes that are not range coded, e.g. a header. */
87
119
  writeBytes(bytes) {
88
120
  this.ensureCapacity(bytes.length);
@@ -97,16 +129,34 @@ class RangeEncoder {
97
129
  }
98
130
  /** Flushes all pending bytes and returns the rest of the output. */
99
131
  finish() {
100
- let low = this.low;
101
132
  for (let i = 0; i < 5; ++i) {
102
- low = this.shiftLow(low);
133
+ this.shiftLow();
103
134
  }
104
- this.low = low;
105
135
  return this.size === this.buf.length ? this.buf : this.buf.slice(0, this.size);
106
136
  }
137
+ writeByte(b) {
138
+ this.ensureCapacity(1);
139
+ this.buf[this.size++] = b;
140
+ }
141
+ shiftLow() {
142
+ // (int)(low >>> 32), the pending carry: 0 or 1.
143
+ const lowHi = Math.floor(this.low / 0x100000000);
144
+ if (lowHi !== 0 || this.low < 0xff000000) {
145
+ let temp = this.cache;
146
+ do {
147
+ this.writeByte(temp + lowHi);
148
+ temp = 0xff;
149
+ } while (--this.cacheSize !== 0);
150
+ this.cache = (this.low >>> 24) & 0xff;
151
+ }
152
+ ++this.cacheSize;
153
+ // (low & 0x00FFFFFF) << 8, kept exact above 2^31 with * 256.
154
+ this.low = (this.low & 0x00ffffff) * 256;
155
+ }
107
156
  encodeBit(probs, index, bit) {
108
157
  const prob = probs[index];
109
158
  const bound = (this.range >>> range_coder_1.BIT_MODEL_TOTAL_BITS) * prob;
159
+ // NOTE: Any non-zero value for bit is taken as 1.
110
160
  if (bit === 0) {
111
161
  this.range = bound;
112
162
  probs[index] = prob + ((range_coder_1.BIT_MODEL_TOTAL - prob) >>> range_coder_1.MOVE_BITS);
@@ -118,145 +168,43 @@ class RangeEncoder {
118
168
  }
119
169
  if (this.range < range_coder_1.TOP_VALUE) {
120
170
  this.range = (this.range << 8) >>> 0;
121
- this.low = this.shiftLow(this.low);
171
+ this.shiftLow();
122
172
  }
123
173
  }
124
- /** Encodes a `bits`-bit symbol MSB-first with the bit tree at `offset`. */
125
174
  encodeBitTree(probs, offset, bits, symbol) {
126
- let range = this.range;
127
- let low = this.low;
128
175
  let index = 1;
129
- for (let i = bits - 1; i >= 0; --i) {
130
- const bit = (symbol >>> i) & 1;
131
- const prob = probs[offset + index];
132
- const bound = (range >>> range_coder_1.BIT_MODEL_TOTAL_BITS) * prob;
133
- if (bit === 0) {
134
- range = bound;
135
- probs[offset + index] = prob + ((range_coder_1.BIT_MODEL_TOTAL - prob) >>> range_coder_1.MOVE_BITS);
136
- }
137
- else {
138
- low += bound;
139
- range -= bound;
140
- probs[offset + index] = prob - (prob >>> range_coder_1.MOVE_BITS);
141
- }
142
- if (range < range_coder_1.TOP_VALUE) {
143
- range = (range << 8) >>> 0;
144
- low = this.shiftLow(low);
176
+ let mask = 1 << bits;
177
+ do {
178
+ mask >>>= 1;
179
+ const bit = symbol & mask;
180
+ this.encodeBit(probs, offset + index, bit);
181
+ index <<= 1;
182
+ if (bit !== 0) {
183
+ index |= 1;
145
184
  }
146
- index = (index << 1) | bit;
147
- }
148
- this.range = range;
149
- this.low = low;
185
+ } while (mask !== 1);
150
186
  }
151
- /** Encodes a `bits`-bit symbol LSB-first with the bit tree at `offset`. */
152
187
  encodeReverseBitTree(probs, offset, bits, symbolIn) {
153
188
  let symbol = symbolIn;
154
- let range = this.range;
155
- let low = this.low;
156
189
  let index = 1;
157
- for (let i = 0; i < bits; ++i) {
190
+ symbol |= 1 << bits;
191
+ do {
158
192
  const bit = symbol & 1;
159
193
  symbol >>>= 1;
160
- const prob = probs[offset + index];
161
- const bound = (range >>> range_coder_1.BIT_MODEL_TOTAL_BITS) * prob;
162
- if (bit === 0) {
163
- range = bound;
164
- probs[offset + index] = prob + ((range_coder_1.BIT_MODEL_TOTAL - prob) >>> range_coder_1.MOVE_BITS);
165
- }
166
- else {
167
- low += bound;
168
- range -= bound;
169
- probs[offset + index] = prob - (prob >>> range_coder_1.MOVE_BITS);
170
- }
171
- if (range < range_coder_1.TOP_VALUE) {
172
- range = (range << 8) >>> 0;
173
- low = this.shiftLow(low);
174
- }
194
+ this.encodeBit(probs, offset + index, bit);
175
195
  index = (index << 1) | bit;
176
- }
177
- this.range = range;
178
- this.low = low;
179
- }
180
- /** Encodes a literal byte with the 0x100-leaf bit tree at `offset`. */
181
- encodeLiteral(probs, offset, symbol) {
182
- this.encodeBitTree(probs, offset, 8, symbol);
196
+ } while (symbol !== 1);
183
197
  }
184
- /**
185
- * Encodes a literal after a match. The bits of `matchByte` (the byte at
186
- * rep0) select separate probabilities until the first bit that differs.
187
- */
188
- encodeMatchedLiteral(probs, offset, symbolIn, matchByteIn) {
189
- let symbol = symbolIn;
190
- let matchByte = matchByteIn;
191
- let range = this.range;
192
- let low = this.low;
193
- let matchOffset = 0x100;
194
- symbol |= 0x100;
195
- do {
196
- matchByte <<= 1;
197
- const matchBit = matchByte & matchOffset;
198
- const index = offset + matchOffset + matchBit + (symbol >>> 8);
199
- const bit = (symbol >>> 7) & 1;
200
- const prob = probs[index];
201
- const bound = (range >>> range_coder_1.BIT_MODEL_TOTAL_BITS) * prob;
202
- if (bit === 0) {
203
- range = bound;
204
- probs[index] = prob + ((range_coder_1.BIT_MODEL_TOTAL - prob) >>> range_coder_1.MOVE_BITS);
205
- }
206
- else {
207
- low += bound;
208
- range -= bound;
209
- probs[index] = prob - (prob >>> range_coder_1.MOVE_BITS);
210
- }
211
- if (range < range_coder_1.TOP_VALUE) {
212
- range = (range << 8) >>> 0;
213
- low = this.shiftLow(low);
214
- }
215
- symbol <<= 1;
216
- matchOffset &= ~(matchByte ^ symbol);
217
- } while (symbol < 0x10000);
218
- this.range = range;
219
- this.low = low;
220
- }
221
- /** Encodes the `count` low bits of `value` with a fixed 50% probability. */
222
198
  encodeDirectBits(value, countIn) {
223
199
  let count = countIn;
224
- let range = this.range;
225
- let low = this.low;
226
200
  do {
227
- range >>>= 1;
228
- if (((value >>> --count) & 1) !== 0) {
229
- low += range;
230
- }
231
- if (range < range_coder_1.TOP_VALUE) {
232
- range = (range << 8) >>> 0;
233
- low = this.shiftLow(low);
201
+ this.range >>>= 1;
202
+ this.low += this.range & (0 - ((value >>> --count) & 1));
203
+ if (this.range < range_coder_1.TOP_VALUE) {
204
+ this.range = (this.range << 8) >>> 0;
205
+ this.shiftLow();
234
206
  }
235
207
  } while (count !== 0);
236
- this.range = range;
237
- this.low = low;
238
- }
239
- /**
240
- * Moves the top byte of `low` to the output and returns the new `low`.
241
- * A run of 0xFF bytes is held back (`cache` + `cacheSize`) until it is
242
- * known whether a carry will propagate into it.
243
- */
244
- shiftLow(low) {
245
- if (low < 0xff000000 || low >= 0x100000000) {
246
- const carry = low >= 0x100000000 ? 1 : 0;
247
- this.ensureCapacity(this.cacheSize);
248
- const buf = this.buf;
249
- let size = this.size;
250
- buf[size++] = (this.cache + carry) & 0xff;
251
- for (let i = 1; i < this.cacheSize; ++i) {
252
- buf[size++] = (0xff + carry) & 0xff;
253
- }
254
- this.size = size;
255
- this.cacheSize = 0;
256
- this.cache = (low >>> 24) & 0xff;
257
- }
258
- ++this.cacheSize;
259
- return (low & 0x00ffffff) * 256;
260
208
  }
261
209
  ensureCapacity(extra) {
262
210
  if (this.size + extra > this.buf.length) {
package/dist/pack.d.ts CHANGED
@@ -13,6 +13,13 @@ export interface PackEntry {
13
13
  * a caller open a stream only when its turn comes.
14
14
  */
15
15
  data?: PackData | (() => PackData | Promise<PackData>);
16
+ /**
17
+ * A file's size in bytes, for content given as a stream whose length the caller knows, such as a file in a database
18
+ * that records it. tar writes such a file as its data arrives instead of reading it into memory or a temporary file
19
+ * first, and streaming ZIP gives it ZIP64 fields only when its size needs them. Content that comes to another size
20
+ * fails the archive.
21
+ */
22
+ size?: number;
16
23
  /** The target of a symbolic link. */
17
24
  linkTarget?: string;
18
25
  /** Permission bits. Defaults to 0644 for a file and 0755 for a folder. */
@@ -44,8 +51,9 @@ export interface PackOptions {
44
51
  /** Folder for temporary files. Defaults to `os.tmpdir()`. */
45
52
  tempDir?: string;
46
53
  /**
47
- * Bytes of one entry held in memory before a temporary file takes over, default 8 MiB. ZIP, tar and 7z need an
48
- * entry's size before its data, so an entry given as a stream is read into memory or a temporary file first.
54
+ * Bytes of one entry held in memory before a temporary file takes over, default 8 MiB. An entry given as a stream
55
+ * is read into memory or a temporary file before it is written when the format needs something of it first: its
56
+ * CRC and sizes for ZIP without `streaming` and for 7z, and its size for tar when the entry gives no `size`.
49
57
  */
50
58
  memoryThreshold?: number;
51
59
  /** Stops the work when aborted: the sink is aborted and the call throws the signal's reason. */
@@ -54,7 +62,10 @@ export interface PackOptions {
54
62
  * ZIP only: write each file as its data arrives, deflated on the way, with its CRC and sizes in a data descriptor
55
63
  * after the data. Nothing goes to a temporary file. An entry with `store: true` is held in memory, since a stored
56
64
  * entry keeps its sizes in its header. `ratioLimit` does not apply, since an entry is written before its ratio is
57
- * known.
65
+ * known. A file whose size is known, from its `size` or because its content is bytes or text, gets ZIP64 fields in
66
+ * its local header only when deflating it could pass 4 GiB. A stream without a size gets none, since some readers
67
+ * refuse ZIP64 fields an entry does not need; should it reach 4 GiB, its data descriptor holds eight-byte sizes, as
68
+ * archiver writes one. tar writes a file of known size as it arrives without this option.
58
69
  */
59
70
  streaming?: boolean;
60
71
  }
@@ -71,6 +82,31 @@ export declare function createArchive(format: PackFormat, entries: Iterable<Pack
71
82
  export declare function createArchiveStream(format: PackFormat, entries: Iterable<PackEntry> | AsyncIterable<PackEntry>, options?: PackOptions): Readable;
72
83
  /** `createArchive` into memory. */
73
84
  export declare function createArchiveBytes(format: PackFormat, entries: Iterable<PackEntry> | AsyncIterable<PackEntry>, options?: PackOptions): Promise<Uint8Array>;
85
+ /** An archive that entries are added to one at a time, as archiver's `append` and `finalize` work. */
86
+ export interface ArchiveWriter {
87
+ /**
88
+ * The archive as it is written, to read or pipe while entries are added. Entries wait in memory until it is read.
89
+ * An error destroys it, so a reader never takes a partial archive for a whole one.
90
+ */
91
+ readonly stream: Readable;
92
+ /**
93
+ * Adds an entry after the ones added before it. Its data is read when its turn comes, and a function given as its
94
+ * data is called then. Throws a PackError once `finish` or `abort` has been called or the archive has failed.
95
+ */
96
+ add(entry: PackEntry): void;
97
+ /**
98
+ * Says no more entries come. Resolves once the end of the archive is written to `stream`, and rejects with the error
99
+ * the archive failed on. The end is written only as `stream` is read, so keep reading it while this waits.
100
+ */
101
+ finish(): Promise<void>;
102
+ /** Stops the archive: `stream` is destroyed with `reason`, and `finish` rejects with it. Does nothing once it has finished. */
103
+ abort(reason?: unknown): void;
104
+ }
105
+ /**
106
+ * `createArchiveStream` for a caller that finds its entries as it goes. An entry `createArchive` refuses, or data that
107
+ * fails, fails the archive: `finish` rejects with the error and `stream` is destroyed with it.
108
+ */
109
+ export declare function createArchiveWriter(format: PackFormat, options?: PackOptions): ArchiveWriter;
74
110
  /** Compresses `data` as one stream of `format` to `sink`, then closes it. */
75
111
  export declare function compressTo(format: CompressFormat, data: PackData, sink: ByteSink, options?: PackOptions): Promise<void>;
76
112
  /** `compressTo` as a Node Readable. */