node-pkware 0.8.1 → 1.0.0--beta.8

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
+ }
@@ -1,114 +1,142 @@
1
- import { repeat, mergeRight, unfold, forEach } from '../node_modules/ramda/src/index.mjs'
2
- import {
3
- ERROR_INVALID_DATA,
4
- ERROR_INVALID_DICTIONARY_SIZE,
5
- BINARY_COMPRESSION,
6
- ASCII_COMPRESSION,
7
- ERROR_INVALID_COMPRESSION_TYPE,
8
- ERROR_ABORTED,
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,
9
20
  PKDCL_OK,
10
21
  PKDCL_STREAM_END,
11
- ChCodeAsc,
12
- ChBitsAsc,
22
+ LITERAL_STREAM_ABORTED,
23
+ LITERAL_END_STREAM,
13
24
  LenBits,
14
- LenCode,
15
- ExLenBits,
16
25
  LenBase,
26
+ ExLenBits,
17
27
  DistBits,
18
- DistCode,
19
- LITERAL_STREAM_ABORTED,
20
- LITERAL_END_STREAM
21
- } from './constants.mjs'
22
- import { isBetween, getLowestNBits, nBitsOfOnes, toHex } from './helpers.mjs'
23
- import QuasiImmutableBuffer from './QuasiImmutableBuffer.mjs'
24
-
25
- const populateAsciiTable = (value, index, bits, target, limit = 0x100) => {
26
- const seed = n => {
27
- if (n >= limit) {
28
- return false
29
- } else {
30
- return [n, n + (1 << (value - bits))]
31
- }
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()
32
36
  }
33
- const idxs = unfold(seed, ChCodeAsc[index] >> bits)
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
+ }
34
60
 
35
- forEach(idx => {
36
- target[idx] = index
37
- }, idxs)
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)
38
65
 
39
- return value - bits
66
+ return reduce(
67
+ (acc, idx) => {
68
+ acc[idx] = index
69
+ return acc
70
+ },
71
+ [],
72
+ idxs
73
+ )
40
74
  }
41
75
 
42
- export const generateAsciiTables = () => {
43
- const state = {
76
+ const generateAsciiTables = () => {
77
+ const tables = {
44
78
  asciiTable2C34: repeat(0, 0x100),
45
79
  asciiTable2D34: repeat(0, 0x100),
46
80
  asciiTable2E34: repeat(0, 0x80),
47
81
  asciiTable2EB4: repeat(0, 0x100)
48
82
  }
49
83
 
50
- state.chBitsAsc = ChBitsAsc.map((value, index) => {
84
+ tables.chBitsAsc = ChBitsAsc.map((value, index) => {
51
85
  if (value <= 8) {
52
- return populateAsciiTable(value, index, 0, state.asciiTable2C34)
86
+ tables.asciiTable2C34 = mergeSparseArrays(populateAsciiTable(value, index, 0, 0x100), tables.asciiTable2C34)
87
+ return value - 0
53
88
  }
54
89
 
55
90
  const acc = getLowestNBits(8, ChCodeAsc[index])
56
- if (acc !== 0) {
57
- state.asciiTable2C34[acc] = 0xff
58
-
59
- if (getLowestNBits(6, ChCodeAsc[index]) === 0) {
60
- return populateAsciiTable(value, index, 6, state.asciiTable2E34, 0x80)
61
- } else {
62
- return populateAsciiTable(value, index, 4, state.asciiTable2D34)
63
- }
91
+ if (acc === 0) {
92
+ tables.asciiTable2EB4 = mergeSparseArrays(populateAsciiTable(value, index, 8, 0x100), tables.asciiTable2EB4)
93
+ return value - 8
64
94
  }
65
95
 
66
- return populateAsciiTable(value, index, 8, state.asciiTable2EB4)
67
- })
68
-
69
- return state
70
- }
96
+ tables.asciiTable2C34[acc] = 0xff
71
97
 
72
- export const generateDecodeTables = (startIndexes, lengthBits) => {
73
- return lengthBits.reduce((acc, lengthBit, i) => {
74
- for (let index = startIndexes[i]; index < 0x100; index += 1 << lengthBit) {
75
- acc[index] = i
98
+ if (getLowestNBits(6, ChCodeAsc[index]) === 0) {
99
+ tables.asciiTable2E34 = mergeSparseArrays(populateAsciiTable(value, index, 6, 0x80), tables.asciiTable2E34)
100
+ return value - 6
76
101
  }
77
102
 
78
- return acc
79
- }, repeat(0, 0x100))
103
+ tables.asciiTable2D34 = mergeSparseArrays(populateAsciiTable(value, index, 4, 0x100), tables.asciiTable2D34)
104
+ return value - 4
105
+ })
106
+
107
+ return tables
80
108
  }
81
109
 
82
- export const parseFirstChunk = (chunk, debug = false) => {
83
- if (chunk.length <= 4) {
84
- throw new Error(ERROR_INVALID_DATA)
110
+ const parseInitialData = (state, debug = false) => {
111
+ if (state.inputBuffer.size() < 4) {
112
+ return false
85
113
  }
86
114
 
87
- let state = {
88
- compressionType: chunk.readUInt8(0),
89
- dictionarySizeBits: chunk.readUInt8(1),
90
- bitBuffer: chunk.readUInt8(2)
91
- }
115
+ const { compressionType, dictionarySizeBits } = readHeader(state.inputBuffer.read())
92
116
 
93
- if (!isBetween(4, 6, state.dictionarySizeBits)) {
94
- throw new Error(ERROR_INVALID_DICTIONARY_SIZE)
95
- }
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)
96
122
 
97
- state.dictionarySizeMask = nBitsOfOnes(state.dictionarySizeBits)
98
-
99
- if (state.compressionType !== BINARY_COMPRESSION) {
100
- if (state.compressionType !== ASCII_COMPRESSION) {
101
- throw new Error(ERROR_INVALID_COMPRESSION_TYPE)
102
- }
103
- state = mergeRight(state, generateAsciiTables())
123
+ if (compressionType === COMPRESSION_ASCII) {
124
+ const tables = generateAsciiTables()
125
+ Object.entries(tables).forEach(([key, value]) => {
126
+ state[key] = value
127
+ })
104
128
  }
105
129
 
106
130
  if (debug) {
107
- console.log(`compression type: ${state.compressionType === BINARY_COMPRESSION ? 'binary' : 'ascii'}`)
108
- console.log(`compression level: ${state.dictionarySizeBits === 4 ? 1 : state.dictionarySizeBits === 5 ? 2 : 3}`)
131
+ console.log(`compression type: ${state.compressionType === COMPRESSION_BINARY ? 'binary' : 'ascii'}`)
132
+ console.log(
133
+ `compression level: ${
134
+ state.dictionarySizeBits === 4 ? 'small' : state.dictionarySizeBits === 5 ? 'medium' : 'large'
135
+ }`
136
+ )
109
137
  }
110
138
 
111
- return state
139
+ return true
112
140
  }
113
141
 
114
142
  const wasteBits = (state, numberOfBits) => {
@@ -159,7 +187,7 @@ const decodeNextLiteral = state => {
159
187
  } else {
160
188
  const lastByte = getLowestNBits(8, state.bitBuffer)
161
189
 
162
- if (state.compressionType === BINARY_COMPRESSION) {
190
+ if (state.compressionType === COMPRESSION_BINARY) {
163
191
  return wasteBits(state, 8) === PKDCL_STREAM_END ? LITERAL_STREAM_ABORTED : lastByte
164
192
  } else {
165
193
  let value
@@ -219,12 +247,22 @@ const decodeDistance = (state, repeatLength) => {
219
247
  return distance + 1
220
248
  }
221
249
 
222
- const processChunkData = state => {
223
- let nextLiteral
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
+
224
262
  state.needMoreInput = false
225
263
 
226
264
  state.backup()
227
- nextLiteral = decodeNextLiteral(state)
265
+ let nextLiteral = decodeNextLiteral(state)
228
266
 
229
267
  while (nextLiteral !== LITERAL_END_STREAM && nextLiteral !== LITERAL_STREAM_ABORTED) {
230
268
  let addition
@@ -263,22 +301,72 @@ const processChunkData = state => {
263
301
  }
264
302
  }
265
303
 
266
- const explode = ({ debug = false, inputBufferSize = 0x0, outputBufferSize = 0x0 } = {}) => {
267
- const stateBackup = {
268
- extraBits: null,
269
- bitBuffer: null
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(`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
+ }
270
352
  }
271
353
 
272
- let state = {
354
+ handler._state = {
355
+ _backup: {
356
+ extraBits: null,
357
+ bitBuffer: null
358
+ },
359
+ needMoreInput: true,
273
360
  isFirstChunk: true,
274
- needMoreInput: false,
361
+ extraBits: 0,
275
362
  chBitsAsc: repeat(0, 0x100), // DecodeLit and GenAscTabs uses this
276
363
  lengthCodes: generateDecodeTables(LenCode, LenBits),
277
364
  distPosCodes: generateDecodeTables(DistCode, DistBits),
278
- extraBits: 0,
279
- inputBuffer: new QuasiImmutableBuffer(inputBufferSize),
280
- outputBuffer: new QuasiImmutableBuffer(outputBufferSize),
365
+ inputBuffer: new ExpandingBuffer(inputBufferSize),
366
+ outputBuffer: new ExpandingBuffer(outputBufferSize),
281
367
  onInputFinished: callback => {
368
+ const state = handler._state
369
+
282
370
  if (debug) {
283
371
  console.log('---------------')
284
372
  console.log('total number of chunks read:', state.stats.chunkCounter)
@@ -287,19 +375,21 @@ const explode = ({ debug = false, inputBufferSize = 0x0, outputBufferSize = 0x0
287
375
  }
288
376
 
289
377
  if (state.needMoreInput) {
290
- callback(new Error(ERROR_ABORTED))
378
+ callback(new AbortedError())
291
379
  } else {
292
380
  callback(null, state.outputBuffer.read())
293
381
  }
294
382
  },
295
383
  backup: () => {
296
- stateBackup.extraBits = state.extraBits
297
- stateBackup.bitBuffer = state.bitBuffer
384
+ const state = handler._state
385
+ state._backup.extraBits = state.extraBits
386
+ state._backup.bitBuffer = state.bitBuffer
298
387
  state.inputBuffer._saveIndices()
299
388
  },
300
389
  restore: () => {
301
- state.extraBits = stateBackup.extraBits
302
- state.bitBuffer = stateBackup.bitBuffer
390
+ const state = handler._state
391
+ state.extraBits = state._backup.extraBits
392
+ state.bitBuffer = state._backup.bitBuffer
303
393
  state.inputBuffer._restoreIndices()
304
394
  },
305
395
  stats: {
@@ -307,34 +397,18 @@ const explode = ({ debug = false, inputBufferSize = 0x0, outputBufferSize = 0x0
307
397
  }
308
398
  }
309
399
 
310
- return function (chunk, encoding, callback) {
311
- state.needMoreInput = false
312
- state.inputBuffer.append(chunk)
313
-
314
- try {
315
- if (state.isFirstChunk) {
316
- state.isFirstChunk = false
317
- this._flush = state.onInputFinished
318
- state = mergeRight(state, parseFirstChunk(chunk, debug))
319
- state.inputBuffer.dropStart(3)
320
- }
321
-
322
- if (debug) {
323
- console.log(`reading ${toHex(chunk.length)} bytes from chunk #${state.stats.chunkCounter++}`)
324
- }
325
-
326
- processChunkData(state)
327
-
328
- const blockSize = 0x1000
329
- const numberOfBytes = Math.floor(state.outputBuffer.size() / blockSize) * blockSize
330
- const output = Buffer.from(state.outputBuffer.read(0, numberOfBytes))
331
- state.outputBuffer.flushStart(numberOfBytes)
332
-
333
- callback(null, output)
334
- } catch (e) {
335
- callback(e)
336
- }
337
- }
400
+ return handler
338
401
  }
339
402
 
340
- export default explode
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