node-pkware 0.7.0 → 1.0.0-alpha.1

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,28 +1,23 @@
1
- export const BINARY_COMPRESSION = 0
2
- export const ASCII_COMPRESSION = 1
1
+ const COMPRESSION_BINARY = 0
2
+ const COMPRESSION_ASCII = 1
3
3
 
4
- export const ERROR_INVALID_DICTIONARY_SIZE = 'Invalid dictionary size'
5
- export const ERROR_INVALID_COMPRESSION_TYPE = 'Invalid compression type'
6
- export const ERROR_INVALID_DATA = 'Invalid data'
7
- export const ERROR_ABORTED = 'Aborted'
4
+ const DICTIONARY_SIZE_SMALL = 4
5
+ const DICTIONARY_SIZE_MEDIUM = 5
6
+ const DICTIONARY_SIZE_LARGE = 6
8
7
 
9
- export const DICTIONARY_SIZE1 = 0x0400
10
- export const DICTIONARY_SIZE2 = 0x0800
11
- export const DICTIONARY_SIZE3 = 0x1000
8
+ const LONGEST_ALLOWED_REPETITION = 0x204
12
9
 
13
- export const LONGEST_ALLOWED_REPETITION = 0x204
10
+ const PKDCL_OK = 'OK'
11
+ const PKDCL_STREAM_END = 'All data from the input stream is read'
12
+ const PKDCL_NEED_DICT = 'Need more data (dictionary)'
13
+ const PKDCL_CONTINUE = 'Continue (internal flag)'
14
+ const PKDCL_GET_INPUT = 'Get input (internal flag)'
14
15
 
15
- export const PKDCL_OK = 'OK'
16
- export const PKDCL_STREAM_END = 'All data from the input stream is read'
17
- export const PKDCL_NEED_DICT = 'Need more data (dictionary)'
18
- export const PKDCL_CONTINUE = 'Continue (internal flag)'
19
- export const PKDCL_GET_INPUT = 'Get input (internal flag)'
20
-
21
- export const LITERAL_END_STREAM = 0x305
22
- export const LITERAL_STREAM_ABORTED = 0x306
16
+ const LITERAL_END_STREAM = 0x305
17
+ const LITERAL_STREAM_ABORTED = 0x306
23
18
 
24
19
  // prettier-ignore
25
- export const DistCode = [
20
+ const DistCode = [
26
21
  0x03, 0x0D, 0x05, 0x19, 0x09, 0x11, 0x01, 0x3E, 0x1E, 0x2E, 0x0E, 0x36, 0x16, 0x26, 0x06, 0x3A,
27
22
  0x1A, 0x2A, 0x0A, 0x32, 0x12, 0x22, 0x42, 0x02, 0x7C, 0x3C, 0x5C, 0x1C, 0x6C, 0x2C, 0x4C, 0x0C,
28
23
  0x74, 0x34, 0x54, 0x14, 0x64, 0x24, 0x44, 0x04, 0x78, 0x38, 0x58, 0x18, 0x68, 0x28, 0x48, 0x08,
@@ -30,7 +25,7 @@ export const DistCode = [
30
25
  ]
31
26
 
32
27
  // prettier-ignore
33
- export const DistBits = [
28
+ const DistBits = [
34
29
  0x02, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06,
35
30
  0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
36
31
  0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07,
@@ -38,28 +33,28 @@ export const DistBits = [
38
33
  ]
39
34
 
40
35
  // prettier-ignore
41
- export const LenBits = [
36
+ const LenBits = [
42
37
  0x03, 0x02, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x07, 0x07
43
38
  ]
44
39
 
45
40
  // prettier-ignore
46
- export const LenCode = [
41
+ const LenCode = [
47
42
  0x05, 0x03, 0x01, 0x06, 0x0a, 0x02, 0x0c, 0x14, 0x04, 0x18, 0x08, 0x30, 0x10, 0x20, 0x40, 0x00
48
43
  ]
49
44
 
50
45
  // prettier-ignore
51
- export const ExLenBits = [
46
+ const ExLenBits = [
52
47
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08
53
48
  ]
54
49
 
55
50
  // prettier-ignore
56
- export const LenBase = [
51
+ const LenBase = [
57
52
  0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
58
53
  0x0008, 0x000A, 0x000E, 0x0016, 0x0026, 0x0046, 0x0086, 0x0106
59
54
  ]
60
55
 
61
56
  // prettier-ignore
62
- export const ChBitsAsc = [
57
+ const ChBitsAsc = [
63
58
  0x0B, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x08, 0x07, 0x0C, 0x0C, 0x07, 0x0C, 0x0C,
64
59
  0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
65
60
  0x04, 0x0A, 0x08, 0x0C, 0x0A, 0x0C, 0x0A, 0x08, 0x07, 0x07, 0x08, 0x09, 0x07, 0x06, 0x07, 0x08,
@@ -79,7 +74,7 @@ export const ChBitsAsc = [
79
74
  ]
80
75
 
81
76
  // prettier-ignore
82
- export const ChCodeAsc = [
77
+ const ChCodeAsc = [
83
78
  0x0490, 0x0FE0, 0x07E0, 0x0BE0, 0x03E0, 0x0DE0, 0x05E0, 0x09E0,
84
79
  0x01E0, 0x00B8, 0x0062, 0x0EE0, 0x06E0, 0x0022, 0x0AE0, 0x02E0,
85
80
  0x0CE0, 0x04E0, 0x08E0, 0x00E0, 0x0F60, 0x0760, 0x0B60, 0x0360,
@@ -113,3 +108,32 @@ export const ChCodeAsc = [
113
108
  0x0600, 0x1A00, 0x0E40, 0x0640, 0x0A40, 0x0A00, 0x1200, 0x0200,
114
109
  0x1C00, 0x0C00, 0x1400, 0x0400, 0x1800, 0x0800, 0x1000, 0x0000
115
110
  ]
111
+
112
+ module.exports = {
113
+ COMPRESSION_BINARY,
114
+ COMPRESSION_ASCII,
115
+
116
+ DICTIONARY_SIZE_SMALL,
117
+ DICTIONARY_SIZE_MEDIUM,
118
+ DICTIONARY_SIZE_LARGE,
119
+
120
+ LONGEST_ALLOWED_REPETITION,
121
+
122
+ PKDCL_OK,
123
+ PKDCL_STREAM_END,
124
+ PKDCL_NEED_DICT,
125
+ PKDCL_CONTINUE,
126
+ PKDCL_GET_INPUT,
127
+
128
+ LITERAL_END_STREAM,
129
+ LITERAL_STREAM_ABORTED,
130
+
131
+ DistCode,
132
+ DistBits,
133
+ LenBits,
134
+ LenCode,
135
+ ExLenBits,
136
+ LenBase,
137
+ ChBitsAsc,
138
+ ChCodeAsc
139
+ }
package/src/errors.js ADDED
@@ -0,0 +1,50 @@
1
+ class InvalidDictionarySizeError extends Error {
2
+ constructor() {
3
+ super('Invalid dictionary size')
4
+ this.name = 'InvalidDictionarySizeError'
5
+ }
6
+ }
7
+
8
+ class InvalidCompressionTypeError extends Error {
9
+ constructor() {
10
+ super('Invalid compression type')
11
+ this.name = 'InvalidCompressionTypeError'
12
+ }
13
+ }
14
+
15
+ class InvalidDataError extends Error {
16
+ constructor() {
17
+ super('Invalid data')
18
+ this.name = 'InvalidDataError'
19
+ }
20
+ }
21
+
22
+ class AbortedError extends Error {
23
+ constructor() {
24
+ super('Aborted')
25
+ this.name = 'AbortedError'
26
+ }
27
+ }
28
+
29
+ class ExpectedBufferError extends TypeError {
30
+ constructor() {
31
+ super('Expected variable to be of type Buffer')
32
+ this.name = 'ExpectedBufferError'
33
+ }
34
+ }
35
+
36
+ class ExpectedFunctionError extends TypeError {
37
+ constructor() {
38
+ super('Expected variable to be a Function')
39
+ this.name = 'ExpectedFunctionError'
40
+ }
41
+ }
42
+
43
+ module.exports = {
44
+ InvalidDictionarySizeError,
45
+ InvalidCompressionTypeError,
46
+ InvalidDataError,
47
+ AbortedError,
48
+ ExpectedBufferError,
49
+ ExpectedFunctionError
50
+ }
package/src/explode.js ADDED
@@ -0,0 +1,414 @@
1
+ const { repeat, unfold, reduce, has, includes } = require('ramda')
2
+ const { isFunction } = require('ramda-adjunct')
3
+ const {
4
+ InvalidDataError,
5
+ InvalidCompressionTypeError,
6
+ InvalidDictionarySizeError,
7
+ ExpectedBufferError,
8
+ ExpectedFunctionError,
9
+ AbortedError
10
+ } = require('./errors.js')
11
+ const { mergeSparseArrays, getLowestNBits, nBitsOfOnes, toHex } = require('./helpers/functions.js')
12
+ const {
13
+ ChBitsAsc,
14
+ ChCodeAsc,
15
+ COMPRESSION_BINARY,
16
+ COMPRESSION_ASCII,
17
+ DICTIONARY_SIZE_SMALL,
18
+ DICTIONARY_SIZE_MEDIUM,
19
+ DICTIONARY_SIZE_LARGE,
20
+ PKDCL_OK,
21
+ PKDCL_STREAM_END,
22
+ LITERAL_STREAM_ABORTED,
23
+ LITERAL_END_STREAM,
24
+ LenBits,
25
+ LenBase,
26
+ ExLenBits,
27
+ DistBits,
28
+ LenCode,
29
+ DistCode
30
+ } = require('./constants.js')
31
+ const ExpandingBuffer = require('./helpers/ExpandingBuffer.js')
32
+
33
+ const readHeader = buffer => {
34
+ if (!Buffer.isBuffer(buffer)) {
35
+ throw new ExpectedBufferError()
36
+ }
37
+ if (buffer.length < 4) {
38
+ throw new InvalidDataError()
39
+ }
40
+
41
+ const compressionType = buffer.readUInt8(0)
42
+ const dictionarySizeBits = buffer.readUInt8(1)
43
+ if (compressionType !== COMPRESSION_BINARY && compressionType !== COMPRESSION_ASCII) {
44
+ throw new InvalidCompressionTypeError()
45
+ }
46
+ if (!includes(dictionarySizeBits, [DICTIONARY_SIZE_SMALL, DICTIONARY_SIZE_MEDIUM, DICTIONARY_SIZE_LARGE])) {
47
+ throw new InvalidDictionarySizeError()
48
+ }
49
+
50
+ return {
51
+ compressionType,
52
+ dictionarySizeBits
53
+ }
54
+ }
55
+
56
+ // PAT = populate ascii table
57
+ const createPATIterator = (limit, stepper) => n => {
58
+ return n >= limit ? false : [n, n + (1 << stepper)]
59
+ }
60
+
61
+ const populateAsciiTable = (value, index, bits, limit) => {
62
+ const iterator = createPATIterator(limit, value - bits)
63
+ const seed = ChCodeAsc[index] >> bits
64
+ const idxs = unfold(iterator, seed)
65
+
66
+ return reduce(
67
+ (acc, idx) => {
68
+ acc[idx] = index
69
+ return acc
70
+ },
71
+ [],
72
+ idxs
73
+ )
74
+ }
75
+
76
+ const generateAsciiTables = () => {
77
+ const tables = {
78
+ asciiTable2C34: repeat(0, 0x100),
79
+ asciiTable2D34: repeat(0, 0x100),
80
+ asciiTable2E34: repeat(0, 0x80),
81
+ asciiTable2EB4: repeat(0, 0x100)
82
+ }
83
+
84
+ tables.chBitsAsc = ChBitsAsc.map((value, index) => {
85
+ if (value <= 8) {
86
+ tables.asciiTable2C34 = mergeSparseArrays(populateAsciiTable(value, index, 0, 0x100), tables.asciiTable2C34)
87
+ return value - 0
88
+ }
89
+
90
+ const acc = getLowestNBits(8, ChCodeAsc[index])
91
+ if (acc === 0) {
92
+ tables.asciiTable2EB4 = mergeSparseArrays(populateAsciiTable(value, index, 8, 0x100), tables.asciiTable2EB4)
93
+ return value - 8
94
+ }
95
+
96
+ tables.asciiTable2C34[acc] = 0xff
97
+
98
+ if (getLowestNBits(6, ChCodeAsc[index]) === 0) {
99
+ tables.asciiTable2E34 = mergeSparseArrays(populateAsciiTable(value, index, 6, 0x80), tables.asciiTable2E34)
100
+ return value - 6
101
+ }
102
+
103
+ tables.asciiTable2D34 = mergeSparseArrays(populateAsciiTable(value, index, 4, 0x100), tables.asciiTable2D34)
104
+ return value - 4
105
+ })
106
+
107
+ return tables
108
+ }
109
+
110
+ const parseInitialData = (state, debug = false) => {
111
+ if (state.inputBuffer.size() < 4) {
112
+ return false
113
+ }
114
+
115
+ const { compressionType, dictionarySizeBits } = readHeader(state.inputBuffer.read())
116
+
117
+ state.compressionType = compressionType
118
+ state.dictionarySizeBits = dictionarySizeBits
119
+ state.bitBuffer = state.inputBuffer.read(2, 1)
120
+ state.inputBuffer.dropStart(3)
121
+ state.dictionarySizeMask = nBitsOfOnes(dictionarySizeBits)
122
+
123
+ if (compressionType === COMPRESSION_ASCII) {
124
+ const tables = generateAsciiTables()
125
+ Object.entries(tables).forEach(([key, value]) => {
126
+ state[key] = value
127
+ })
128
+ }
129
+
130
+ if (debug) {
131
+ console.log(`explode: compression type: ${state.compressionType === COMPRESSION_BINARY ? 'binary' : 'ascii'}`)
132
+ console.log(
133
+ `explode: compression level: ${
134
+ state.dictionarySizeBits === 4 ? 'small' : state.dictionarySizeBits === 5 ? 'medium' : 'large'
135
+ }`
136
+ )
137
+ }
138
+
139
+ return true
140
+ }
141
+
142
+ const wasteBits = (state, numberOfBits) => {
143
+ if (numberOfBits > state.extraBits && state.inputBuffer.isEmpty()) {
144
+ return PKDCL_STREAM_END
145
+ }
146
+
147
+ if (numberOfBits <= state.extraBits) {
148
+ state.bitBuffer = state.bitBuffer >> numberOfBits
149
+ state.extraBits = state.extraBits - numberOfBits
150
+ } else {
151
+ const nextByte = state.inputBuffer.read(0, 1)
152
+ state.inputBuffer.dropStart(1)
153
+
154
+ state.bitBuffer = ((state.bitBuffer >> state.extraBits) | (nextByte << 8)) >> (numberOfBits - state.extraBits)
155
+ state.extraBits = state.extraBits + 8 - numberOfBits
156
+ }
157
+
158
+ return PKDCL_OK
159
+ }
160
+
161
+ const decodeNextLiteral = state => {
162
+ const lastBit = state.bitBuffer & 1
163
+
164
+ if (wasteBits(state, 1) === PKDCL_STREAM_END) {
165
+ return LITERAL_STREAM_ABORTED
166
+ }
167
+
168
+ if (lastBit) {
169
+ let lengthCode = state.lengthCodes[getLowestNBits(8, state.bitBuffer)]
170
+
171
+ if (wasteBits(state, LenBits[lengthCode]) === PKDCL_STREAM_END) {
172
+ return LITERAL_STREAM_ABORTED
173
+ }
174
+
175
+ const extraLenghtBits = ExLenBits[lengthCode]
176
+ if (extraLenghtBits !== 0) {
177
+ const extraLength = getLowestNBits(extraLenghtBits, state.bitBuffer)
178
+
179
+ if (wasteBits(state, extraLenghtBits) === PKDCL_STREAM_END && lengthCode + extraLength !== 0x10e) {
180
+ return LITERAL_STREAM_ABORTED
181
+ }
182
+
183
+ lengthCode = LenBase[lengthCode] + extraLength
184
+ }
185
+
186
+ return lengthCode + 0x100
187
+ } else {
188
+ const lastByte = getLowestNBits(8, state.bitBuffer)
189
+
190
+ if (state.compressionType === COMPRESSION_BINARY) {
191
+ return wasteBits(state, 8) === PKDCL_STREAM_END ? LITERAL_STREAM_ABORTED : lastByte
192
+ } else {
193
+ let value
194
+ if (lastByte > 0) {
195
+ value = state.asciiTable2C34[lastByte]
196
+
197
+ if (value === 0xff) {
198
+ if (getLowestNBits(6, state.bitBuffer)) {
199
+ if (wasteBits(state, 4) === PKDCL_STREAM_END) {
200
+ return LITERAL_STREAM_ABORTED
201
+ }
202
+
203
+ value = state.asciiTable2D34[getLowestNBits(8, state.bitBuffer)]
204
+ } else {
205
+ if (wasteBits(state, 6) === PKDCL_STREAM_END) {
206
+ return LITERAL_STREAM_ABORTED
207
+ }
208
+
209
+ value = state.asciiTable2E34[getLowestNBits(7, state.bitBuffer)]
210
+ }
211
+ }
212
+ } else {
213
+ if (wasteBits(state, 8) === PKDCL_STREAM_END) {
214
+ return LITERAL_STREAM_ABORTED
215
+ }
216
+
217
+ value = state.asciiTable2EB4[getLowestNBits(8, state.bitBuffer)]
218
+ }
219
+
220
+ return wasteBits(state, state.chBitsAsc[value]) === PKDCL_STREAM_END ? LITERAL_STREAM_ABORTED : value
221
+ }
222
+ }
223
+ }
224
+
225
+ const decodeDistance = (state, repeatLength) => {
226
+ const distPosCode = state.distPosCodes[getLowestNBits(8, state.bitBuffer)]
227
+ const distPosBits = DistBits[distPosCode]
228
+ if (wasteBits(state, distPosBits) === PKDCL_STREAM_END) {
229
+ return 0
230
+ }
231
+
232
+ let distance
233
+ let bitsToWaste
234
+
235
+ if (repeatLength === 2) {
236
+ distance = (distPosCode << 2) | getLowestNBits(2, state.bitBuffer)
237
+ bitsToWaste = 2
238
+ } else {
239
+ distance = (distPosCode << state.dictionarySizeBits) | (state.bitBuffer & state.dictionarySizeMask)
240
+ bitsToWaste = state.dictionarySizeBits
241
+ }
242
+
243
+ if (wasteBits(state, bitsToWaste) === PKDCL_STREAM_END) {
244
+ return 0
245
+ }
246
+
247
+ return distance + 1
248
+ }
249
+
250
+ const processChunkData = (state, debug = false) => {
251
+ if (state.inputBuffer.isEmpty()) {
252
+ return
253
+ }
254
+
255
+ if (!has('compressionType', state)) {
256
+ const parsedHeader = parseInitialData(state, debug)
257
+ if (!parsedHeader || state.inputBuffer.isEmpty()) {
258
+ return
259
+ }
260
+ }
261
+
262
+ state.needMoreInput = false
263
+
264
+ state.backup()
265
+ let nextLiteral = decodeNextLiteral(state)
266
+
267
+ while (nextLiteral !== LITERAL_END_STREAM && nextLiteral !== LITERAL_STREAM_ABORTED) {
268
+ let addition
269
+ if (nextLiteral >= 0x100) {
270
+ const repeatLength = nextLiteral - 0xfe
271
+ const minusDistance = decodeDistance(state, repeatLength)
272
+ if (minusDistance === 0) {
273
+ state.needMoreInput = true
274
+ break
275
+ }
276
+
277
+ const availableData = state.outputBuffer.read(state.outputBuffer.size() - minusDistance, repeatLength)
278
+
279
+ if (repeatLength > minusDistance) {
280
+ const multipliedData = repeat(availableData, Math.ceil(repeatLength / availableData.length))
281
+ addition = Buffer.concat(multipliedData).slice(0, repeatLength)
282
+ } else {
283
+ addition = availableData
284
+ }
285
+ } else {
286
+ addition = Buffer.from([nextLiteral])
287
+ }
288
+
289
+ state.outputBuffer.append(addition)
290
+
291
+ state.backup()
292
+ nextLiteral = decodeNextLiteral(state)
293
+ }
294
+
295
+ if (nextLiteral === LITERAL_STREAM_ABORTED) {
296
+ state.needMoreInput = true
297
+ }
298
+
299
+ if (state.needMoreInput) {
300
+ state.restore()
301
+ }
302
+ }
303
+
304
+ const generateDecodeTables = (startIndexes, lengthBits) => {
305
+ return lengthBits.reduce((acc, lengthBit, i) => {
306
+ for (let index = startIndexes[i]; index < 0x100; index += 1 << lengthBit) {
307
+ acc[index] = i
308
+ }
309
+
310
+ return acc
311
+ }, repeat(0, 0x100))
312
+ }
313
+
314
+ const explode = (config = {}) => {
315
+ const { debug = false, inputBufferSize = 0x0, outputBufferSize = 0x0 } = config
316
+
317
+ const handler = function (chunk, encoding, callback) {
318
+ if (!isFunction(callback)) {
319
+ // can't call callback to pass in data or errors, so we throw up
320
+ throw new ExpectedFunctionError()
321
+ }
322
+
323
+ const state = handler._state
324
+ state.needMoreInput = true
325
+
326
+ try {
327
+ state.inputBuffer.append(chunk)
328
+ if (state.isFirstChunk) {
329
+ state.isFirstChunk = false
330
+ this._flush = state.onInputFinished
331
+ }
332
+
333
+ if (debug) {
334
+ console.log(`explode: reading ${toHex(chunk.length)} bytes from chunk #${state.stats.chunkCounter++}`)
335
+ }
336
+
337
+ processChunkData(state, debug)
338
+
339
+ const blockSize = 0x1000
340
+ if (state.outputBuffer.size() > blockSize) {
341
+ const numberOfBytes = (Math.floor(state.outputBuffer.size() / blockSize) - 1) * blockSize
342
+ const output = Buffer.from(state.outputBuffer.read(0, numberOfBytes))
343
+ state.outputBuffer.flushStart(numberOfBytes)
344
+
345
+ callback(null, output)
346
+ } else {
347
+ callback(null, Buffer.from([]))
348
+ }
349
+ } catch (e) {
350
+ callback(e)
351
+ }
352
+ }
353
+
354
+ handler._state = {
355
+ _backup: {
356
+ extraBits: null,
357
+ bitBuffer: null
358
+ },
359
+ needMoreInput: true,
360
+ isFirstChunk: true,
361
+ extraBits: 0,
362
+ chBitsAsc: repeat(0, 0x100), // DecodeLit and GenAscTabs uses this
363
+ lengthCodes: generateDecodeTables(LenCode, LenBits),
364
+ distPosCodes: generateDecodeTables(DistCode, DistBits),
365
+ inputBuffer: new ExpandingBuffer(inputBufferSize),
366
+ outputBuffer: new ExpandingBuffer(outputBufferSize),
367
+ onInputFinished: callback => {
368
+ const state = handler._state
369
+
370
+ if (debug) {
371
+ console.log('---------------')
372
+ console.log('explode: total number of chunks read:', state.stats.chunkCounter)
373
+ console.log('explode: inputBuffer heap size', toHex(state.inputBuffer.heapSize()))
374
+ console.log('explode: outputBuffer heap size', toHex(state.outputBuffer.heapSize()))
375
+ }
376
+
377
+ if (state.needMoreInput) {
378
+ callback(new AbortedError())
379
+ } else {
380
+ callback(null, state.outputBuffer.read())
381
+ }
382
+ },
383
+ backup: () => {
384
+ const state = handler._state
385
+ state._backup.extraBits = state.extraBits
386
+ state._backup.bitBuffer = state.bitBuffer
387
+ state.inputBuffer._saveIndices()
388
+ },
389
+ restore: () => {
390
+ const state = handler._state
391
+ state.extraBits = state._backup.extraBits
392
+ state.bitBuffer = state._backup.bitBuffer
393
+ state.inputBuffer._restoreIndices()
394
+ },
395
+ stats: {
396
+ chunkCounter: 0
397
+ }
398
+ }
399
+
400
+ return handler
401
+ }
402
+
403
+ module.exports = {
404
+ readHeader,
405
+ explode,
406
+ createPATIterator,
407
+ populateAsciiTable,
408
+ generateAsciiTables,
409
+ processChunkData,
410
+ wasteBits,
411
+ decodeNextLiteral,
412
+ decodeDistance,
413
+ generateDecodeTables
414
+ }
@@ -1,6 +1,7 @@
1
- import { clamp } from '../node_modules/ramda/src/index.mjs'
1
+ const { clamp } = require('ramda')
2
+ const { ExpectedBufferError } = require('../errors')
2
3
 
3
- export default class QuasiImmutableBuffer {
4
+ class ExpandingBuffer {
4
5
  constructor(numberOfBytes = 0) {
5
6
  this._heap = Buffer.allocUnsafe(numberOfBytes)
6
7
  this._startIndex = 0
@@ -29,6 +30,10 @@ export default class QuasiImmutableBuffer {
29
30
  }
30
31
 
31
32
  append(buffer) {
33
+ if (!Buffer.isBuffer(buffer)) {
34
+ throw new ExpectedBufferError()
35
+ }
36
+
32
37
  if (this._endIndex + buffer.length < this.heapSize()) {
33
38
  buffer.copy(this._heap, this._endIndex)
34
39
  this._endIndex += buffer.length
@@ -39,6 +44,7 @@ export default class QuasiImmutableBuffer {
39
44
  }
40
45
  }
41
46
 
47
+ // watch out! the buffer returned by Buffer.slice() will point to the same memory!
42
48
  read(offset, limit) {
43
49
  if (offset < 0 || limit < 1) {
44
50
  return Buffer.from([])
@@ -54,6 +60,8 @@ export default class QuasiImmutableBuffer {
54
60
  return this._getActualData(offset)
55
61
  }
56
62
 
63
+ // hard delete
64
+ // removes data from the buffer by copying bytes to lower indices
57
65
  flushStart(numberOfBytes) {
58
66
  numberOfBytes = clamp(0, this.heapSize(), numberOfBytes)
59
67
  if (numberOfBytes > 0) {
@@ -72,6 +80,8 @@ export default class QuasiImmutableBuffer {
72
80
  }
73
81
  }
74
82
 
83
+ // soft delete
84
+ // removes data from the buffer by moving the startIndex forward
75
85
  dropStart(numberOfBytes) {
76
86
  if (numberOfBytes > 0) {
77
87
  this._startIndex += numberOfBytes
@@ -109,3 +119,5 @@ export default class QuasiImmutableBuffer {
109
119
  this._endIndex = this._backup._endIndex
110
120
  }
111
121
  }
122
+
123
+ module.exports = ExpandingBuffer