@patrickdk77/archive-defuse 0.1.2 → 0.2.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.
@@ -1,20 +1,26 @@
1
1
  "use strict";
2
- // Copied from lzma1 (Apache-2.0, Copyright Filip Seman) and changed for lzma-defuse. NOTICE in this folder lists the changes.
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
3
  /**
4
4
  * Probability models and state machine shared by the LZMA encoder and decoder.
5
5
  *
6
- * Ported from XZ for Java (0BSD) by Lasse Collin and Igor Pavlov.
6
+ * Ported from XZ for Java (0BSD), `src/org/tukaani/xz/lzma/LZMACoder.java`
7
+ * and `State.java`, by Lasse Collin and Igor Pavlov.
8
+ *
9
+ * The nested coder classes of the Java code (`LiteralCoder.LiteralSubcoder`,
10
+ * `LengthCoder`) are top-level classes here, because TypeScript has no inner
11
+ * classes; subclasses in the encoder and decoder files pass their outer
12
+ * coder explicitly where Java captures it implicitly.
7
13
  */
8
14
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.LzmaCoder = exports.LITERAL = exports.REP_LEN = exports.MATCH_LEN = exports.DIST_ALIGN = exports.DIST_SPECIAL = exports.DIST_SLOTS = exports.IS_REP0_LONG = exports.IS_REP2 = exports.IS_REP1 = exports.IS_REP0 = exports.IS_REP = exports.IS_MATCH = exports.INITIAL_STATE = exports.STATES = exports.REPS = exports.ALIGN_MASK = exports.ALIGN_SIZE = exports.ALIGN_BITS = exports.FULL_DISTANCES = exports.DIST_MODEL_END = exports.DIST_MODEL_START = exports.DIST_SLOT_COUNT = exports.DIST_SLOT_BITS = exports.DIST_STATES = exports.LEN_SIZE = exports.LEN_HIGH = exports.LEN_MID = exports.LEN_LOW = exports.LEN_CHOICE2 = exports.LEN_CHOICE = exports.MATCH_LEN_MAX = exports.HIGH_SYMBOLS = exports.MID_SYMBOLS = exports.LOW_SYMBOLS = exports.MATCH_LEN_MIN = exports.POS_STATES_MAX = void 0;
10
- exports.isLiteralState = isLiteralState;
15
+ exports.LengthCoder = exports.LiteralSubcoder = exports.LiteralCoder = exports.LzmaCoder = exports.LITERAL = exports.REP_LEN = exports.MATCH_LEN = exports.DIST_ALIGN = exports.DIST_SPECIAL = exports.DIST_SLOT_OFFSET = exports.IS_REP0_LONG = exports.IS_REP2 = exports.IS_REP1 = exports.IS_REP0 = exports.IS_REP = exports.IS_MATCH = exports.STATES = exports.State = exports.REPS = exports.ALIGN_MASK = exports.ALIGN_SIZE = exports.ALIGN_BITS = exports.FULL_DISTANCES = exports.DIST_MODEL_END = exports.DIST_MODEL_START = exports.DIST_SLOT_BITS = exports.DIST_SLOTS = exports.DIST_STATES = exports.LEN_SIZE = exports.LEN_HIGH = exports.LEN_MID = exports.LEN_LOW = exports.LEN_CHOICE2 = exports.LEN_CHOICE = exports.MATCH_LEN_MAX = exports.HIGH_SYMBOLS = exports.MID_SYMBOLS = exports.LOW_SYMBOLS = exports.MATCH_LEN_MIN = exports.POS_STATES_MAX = void 0;
16
+ exports.getDistState = getDistState;
17
+ exports.probsSize = probsSize;
11
18
  exports.stateAfterLiteral = stateAfterLiteral;
12
19
  exports.stateAfterMatch = stateAfterMatch;
13
20
  exports.stateAfterLongRep = stateAfterLongRep;
14
21
  exports.stateAfterShortRep = stateAfterShortRep;
15
- exports.getDistState = getDistState;
22
+ exports.isLiteralState = isLiteralState;
16
23
  exports.distSpecialOffset = distSpecialOffset;
17
- exports.probsSize = probsSize;
18
24
  const range_coder_1 = require("./range-coder");
19
25
  exports.POS_STATES_MAX = 1 << 4;
20
26
  exports.MATCH_LEN_MIN = 2;
@@ -34,8 +40,9 @@ exports.LEN_MID = exports.LEN_LOW + exports.POS_STATES_MAX * exports.LOW_SYMBOLS
34
40
  exports.LEN_HIGH = exports.LEN_MID + exports.POS_STATES_MAX * exports.MID_SYMBOLS;
35
41
  exports.LEN_SIZE = exports.LEN_HIGH + exports.HIGH_SYMBOLS;
36
42
  exports.DIST_STATES = 4;
43
+ exports.DIST_SLOTS = 1 << 6;
44
+ /** Bits of a distance slot: the depth of the slot bit trees. */
37
45
  exports.DIST_SLOT_BITS = 6;
38
- exports.DIST_SLOT_COUNT = 1 << exports.DIST_SLOT_BITS;
39
46
  exports.DIST_MODEL_START = 4;
40
47
  exports.DIST_MODEL_END = 14;
41
48
  exports.FULL_DISTANCES = 1 << (exports.DIST_MODEL_END / 2);
@@ -44,13 +51,54 @@ exports.ALIGN_SIZE = 1 << exports.ALIGN_BITS;
44
51
  exports.ALIGN_MASK = exports.ALIGN_SIZE - 1;
45
52
  /** Number of recent match distances ("reps") that can be reused cheaply. */
46
53
  exports.REPS = 4;
54
+ /** Distances are coded with different probabilities for lengths 2, 3, 4 and 5+. */
55
+ function getDistState(len) {
56
+ return len < exports.DIST_STATES + exports.MATCH_LEN_MIN ? len - exports.MATCH_LEN_MIN : exports.DIST_STATES - 1;
57
+ }
58
+ /** Number of probabilities of a coder with `lc` and `lp` literal bits. */
59
+ function probsSize(lc, lp) {
60
+ return exports.LITERAL + (0x300 << (lc + lp));
61
+ }
47
62
  /**
48
63
  * The coder state remembers the kinds of the most recent symbols
49
64
  * (literal, match, long rep, short rep). It selects which probabilities
50
65
  * are used for the next symbol.
51
66
  */
52
- exports.STATES = 12;
53
- /** States below this value mean the previous symbol was a literal. */
67
+ class State {
68
+ static STATES = 12;
69
+ state = 0;
70
+ constructor(other) {
71
+ if (other !== undefined) {
72
+ this.state = other.state;
73
+ }
74
+ }
75
+ reset() {
76
+ this.state = LIT_LIT;
77
+ }
78
+ get() {
79
+ return this.state;
80
+ }
81
+ set(other) {
82
+ this.state = other.state;
83
+ }
84
+ updateLiteral() {
85
+ this.state = stateAfterLiteral(this.state);
86
+ }
87
+ updateMatch() {
88
+ this.state = stateAfterMatch(this.state);
89
+ }
90
+ updateLongRep() {
91
+ this.state = stateAfterLongRep(this.state);
92
+ }
93
+ updateShortRep() {
94
+ this.state = stateAfterShortRep(this.state);
95
+ }
96
+ isLiteral() {
97
+ return isLiteralState(this.state);
98
+ }
99
+ }
100
+ exports.State = State;
101
+ exports.STATES = State.STATES;
54
102
  const LIT_STATES = 7;
55
103
  const LIT_LIT = 0;
56
104
  const SHORTREP_LIT_LIT = 3;
@@ -59,16 +107,21 @@ const LIT_LONGREP = 8;
59
107
  const LIT_SHORTREP = 9;
60
108
  const NONLIT_MATCH = 10;
61
109
  const NONLIT_REP = 11;
62
- exports.INITIAL_STATE = LIT_LIT;
63
- function isLiteralState(state) {
64
- return state < LIT_STATES;
65
- }
110
+ /*
111
+ * The transitions of `State` on a plain state value, so that the optimum
112
+ * search can keep its per-position states as numbers. `State`'s methods
113
+ * call these, so the state machine is written once.
114
+ */
66
115
  function stateAfterLiteral(state) {
67
- if (state <= SHORTREP_LIT_LIT)
116
+ if (state <= SHORTREP_LIT_LIT) {
68
117
  return LIT_LIT;
69
- if (state <= LIT_SHORTREP)
118
+ }
119
+ else if (state <= LIT_SHORTREP) {
70
120
  return state - 3;
71
- return state - 6;
121
+ }
122
+ else {
123
+ return state - 6;
124
+ }
72
125
  }
73
126
  function stateAfterMatch(state) {
74
127
  return state < LIT_STATES ? LIT_MATCH : NONLIT_MATCH;
@@ -79,14 +132,14 @@ function stateAfterLongRep(state) {
79
132
  function stateAfterShortRep(state) {
80
133
  return state < LIT_STATES ? LIT_SHORTREP : NONLIT_REP;
81
134
  }
82
- /** Distances are coded with different probabilities for lengths 2, 3, 4 and 5+. */
83
- function getDistState(len) {
84
- return len < exports.DIST_STATES + exports.MATCH_LEN_MIN ? len - exports.MATCH_LEN_MIN : exports.DIST_STATES - 1;
135
+ function isLiteralState(state) {
136
+ return state < LIT_STATES;
85
137
  }
86
138
  /**
87
139
  * Layout of the probability array. All probabilities of a coder live in one
88
- * `Uint16Array`, like in the LZMA SDK and liblzma: one allocation, one reset,
89
- * and the hot loops index a single array.
140
+ * `Uint16Array`, like in the LZMA SDK and liblzma, instead of the
141
+ * Java code's per-model arrays: one allocation, and the hot loops index a
142
+ * single array.
90
143
  *
91
144
  * Bit trees of `2^n` leaves occupy `2^n` entries starting at their offset;
92
145
  * index 0 of a tree is unused.
@@ -98,9 +151,9 @@ exports.IS_REP1 = exports.IS_REP0 + exports.STATES; // [state]
98
151
  exports.IS_REP2 = exports.IS_REP1 + exports.STATES; // [state]
99
152
  exports.IS_REP0_LONG = exports.IS_REP2 + exports.STATES; // [state][posState]
100
153
  /** Bit trees of 6 bits per distance state. */
101
- exports.DIST_SLOTS = exports.IS_REP0_LONG + exports.STATES * exports.POS_STATES_MAX;
154
+ exports.DIST_SLOT_OFFSET = exports.IS_REP0_LONG + exports.STATES * exports.POS_STATES_MAX;
102
155
  /** Reverse bit trees for the low bits of distances in slots 4-13. */
103
- exports.DIST_SPECIAL = exports.DIST_SLOTS + exports.DIST_STATES * exports.DIST_SLOT_COUNT;
156
+ exports.DIST_SPECIAL = exports.DIST_SLOT_OFFSET + exports.DIST_STATES * exports.DIST_SLOTS;
104
157
  /** Reverse bit tree for the lowest 4 bits of distances in slots 14+. */
105
158
  exports.DIST_ALIGN = exports.DIST_SPECIAL + exports.FULL_DISTANCES - exports.DIST_MODEL_END;
106
159
  exports.MATCH_LEN = exports.DIST_ALIGN + exports.ALIGN_SIZE;
@@ -116,19 +169,10 @@ exports.LITERAL = exports.REP_LEN + exports.LEN_SIZE;
116
169
  function distSpecialOffset(distSlot, base) {
117
170
  return exports.DIST_SPECIAL + base - distSlot - 1;
118
171
  }
119
- /** Number of probabilities of a coder with `lc` and `lp` literal bits. */
120
- function probsSize(lc, lp) {
121
- return exports.LITERAL + (0x300 << (lc + lp));
122
- }
123
- /**
124
- * Probability models and coder state shared by the encoder and decoder.
125
- */
126
172
  class LzmaCoder {
127
173
  posMask;
128
- lc;
129
- literalPosMask;
130
174
  reps = new Int32Array(exports.REPS);
131
- state = exports.INITIAL_STATE;
175
+ state = new State();
132
176
  probs;
133
177
  /**
134
178
  * @param probs Probability array to reuse, of `probsSize(lc, lp)` entries.
@@ -136,27 +180,49 @@ class LzmaCoder {
136
180
  */
137
181
  constructor(lc, lp, pb, probs) {
138
182
  this.posMask = (1 << pb) - 1;
139
- this.lc = lc;
140
- this.literalPosMask = (1 << lp) - 1;
141
183
  this.probs = probs ?? new Uint16Array(probsSize(lc, lp));
142
184
  }
143
185
  reset() {
144
- this.reps.fill(0);
145
- this.state = exports.INITIAL_STATE;
186
+ this.reps[0] = 0;
187
+ this.reps[1] = 0;
188
+ this.reps[2] = 0;
189
+ this.reps[3] = 0;
190
+ this.state.reset();
146
191
  (0, range_coder_1.initProbs)(this.probs);
147
192
  }
148
- /**
149
- * Offset of the literal coder for a literal at `pos` after `prevByte`:
150
- * selected by the high `lc` bits of the previous byte and the low `lp`
151
- * bits of the position.
152
- *
153
- * Each literal coder has 0x300 probabilities: 0x100 for a plain bit
154
- * tree, and 2 * 0x100 used while the bits still agree with the "match byte".
155
- */
156
- literalOffset(prevByte, pos) {
193
+ }
194
+ exports.LzmaCoder = LzmaCoder;
195
+ class LiteralCoder {
196
+ lc;
197
+ literalPosMask;
198
+ constructor(lc, lp) {
199
+ this.lc = lc;
200
+ this.literalPosMask = (1 << lp) - 1;
201
+ }
202
+ getSubcoderIndex(prevByte, pos) {
157
203
  const low = prevByte >> (8 - this.lc);
158
204
  const high = (pos & this.literalPosMask) << this.lc;
159
- return exports.LITERAL + 0x300 * (low + high);
205
+ return low + high;
160
206
  }
161
207
  }
162
- exports.LzmaCoder = LzmaCoder;
208
+ exports.LiteralCoder = LiteralCoder;
209
+ class LiteralSubcoder {
210
+ probs;
211
+ /** Offset of this subcoder's 0x300 probabilities in `probs`. */
212
+ literalOffset;
213
+ constructor(probs, literalOffset) {
214
+ this.probs = probs;
215
+ this.literalOffset = literalOffset;
216
+ }
217
+ }
218
+ exports.LiteralSubcoder = LiteralSubcoder;
219
+ class LengthCoder {
220
+ probs;
221
+ /** Offset of this length coder in `probs`. */
222
+ coder;
223
+ constructor(probs, coder) {
224
+ this.probs = probs;
225
+ this.coder = coder;
226
+ }
227
+ }
228
+ exports.LengthCoder = LengthCoder;
@@ -1,36 +1,58 @@
1
1
  /**
2
2
  * LZMA decoder: turns range-coded symbols back into literals and matches.
3
3
  *
4
- * Ported from XZ for Java (0BSD) by Lasse Collin and Igor Pavlov. The main
5
- * loop is structured like `LzmaDec_DecodeReal` of the LZMA SDK.
4
+ * Ported from XZ for Java (0BSD), `src/org/tukaani/xz/lzma/LZMADecoder.java`,
5
+ * by Lasse Collin and Igor Pavlov.
6
+ *
7
+ * Contract adaptations, pinned by the recorded malformed-stream
8
+ * expectations in `fixtures/malformed.json` and
9
+ * `fixtures/raw-malformed.json`:
10
+ * - `decode(outLimit, inLimit)` drives the loop by the number of bytes
11
+ * produced (`outPos`) and the input position, instead of the Java code's
12
+ * `LZDecoder` window limit, because the input arrives in chunks and the
13
+ * output goes to a sink. `outPos` also provides the position bits
14
+ * (`posState` and the literal subcoder index). The Java code reads them
15
+ * from `LZDecoder.getPos()`, its position in a circular dictionary
16
+ * buffer that `LZMAInputStream.getDictSize` and
17
+ * `LZMA2InputStream.getDictSize` round up to a multiple of 16 for exactly
18
+ * this purpose, so that position equals `outPos` modulo 16. LZMA2 sets
19
+ * `outPos` to the bytes since the last dictionary reset, where
20
+ * `LZDecoder.reset` puts the Java position back to 0. The position masks
21
+ * keep at most the low 4 bits (`pb`, `lp` <= 4), so the two agree for
22
+ * every dictionary size.
23
+ * - The end marker returns `true` instead of surfacing as the error the
24
+ * Java code's `lz.repeat` raises for it; a match that would cross the
25
+ * output limit, the declared size or the end of an LZMA2 chunk, is an
26
+ * error.
6
27
  */
7
28
  import type { LzDecoder } from './lz-decoder';
8
29
  import { LzmaCoder } from './lzma-coder';
9
30
  import type { RangeDecoder } from './range-decoder';
10
31
  export declare class LzmaDecoder extends LzmaCoder {
11
- private readonly lz;
12
- private readonly rc;
13
- /** Number of bytes decoded so far. */
32
+ readonly lz: LzDecoder;
33
+ readonly rc: RangeDecoder;
34
+ private readonly literalDecoder;
35
+ private readonly matchLenDecoder;
36
+ private readonly repLenDecoder;
37
+ /** Bytes decoded so far; for LZMA2, since the last dictionary reset. */
14
38
  outPos: number;
39
+ /**
40
+ * @param probs Probability array of an earlier decoder to reuse, of
41
+ * `probsSize(lc, lp)` entries; a new one is allocated without it.
42
+ */
15
43
  constructor(lz: LzDecoder, rc: RangeDecoder, lc: number, lp: number, pb: number, probs?: Uint16Array);
16
44
  /**
17
- * Decodes symbols until `outLimit` bytes have been produced or the input
45
+ * Returns true if LZMA end marker was detected. It is encoded as
46
+ * the maximum match distance which with int32 values becomes -1.
47
+ */
48
+ private endMarkerDetected;
49
+ /**
50
+ * Decodes symbols until `outPos` reaches `outLimit` or the input
18
51
  * position passes `inLimit`.
19
52
  *
20
- * Performance: this is the hot loop of decompression. The range decoder
21
- * state, the coder state and the reps live in local variables, and the
22
- * bit decoding step (`decodeBit` in `range-coder_test.ts`) is repeated inline:
23
- *
24
- * prob = probs[i];
25
- * bound = (range >>> 11) * prob;
26
- * if (code < bound) { range = bound; probs[i] = prob + ((2048 - prob) >>> 5); bit 0 }
27
- * else { range -= bound; code -= bound; probs[i] = prob - (prob >>> 5); bit 1 }
28
- * if (range < 2^24) { shift in the next input byte }
29
- *
30
- * With the state in object fields, engines store and reload it around
31
- * every probability update, which costs 10-15% on V8.
32
- *
33
53
  * @returns `true` if the end marker was decoded
34
54
  */
35
55
  decode(outLimit: number, inLimit: number): boolean;
56
+ private decodeMatch;
57
+ private decodeRepMatch;
36
58
  }