node-pkware 0.6.0 → 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 MAX_REP_LENGTH = 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,121 +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))
80
- }
81
-
82
- export const parseFirstChunk = (chunk, debug = false) => {
83
- return new Promise((resolve, reject) => {
84
- if (chunk.length <= 4) {
85
- reject(new Error(ERROR_INVALID_DATA))
86
- return
87
- }
103
+ tables.asciiTable2D34 = mergeSparseArrays(populateAsciiTable(value, index, 4, 0x100), tables.asciiTable2D34)
104
+ return value - 4
105
+ })
88
106
 
89
- let state = {
90
- compressionType: chunk.readUInt8(0),
91
- dictionarySizeBits: chunk.readUInt8(1),
92
- bitBuffer: chunk.readUInt8(2)
93
- }
107
+ return tables
108
+ }
94
109
 
95
- if (!isBetween(4, 6, state.dictionarySizeBits)) {
96
- reject(new Error(ERROR_INVALID_DICTIONARY_SIZE))
97
- return
98
- }
110
+ const parseInitialData = (state, debug = false) => {
111
+ if (state.inputBuffer.size() < 4) {
112
+ return false
113
+ }
99
114
 
100
- state.dictionarySizeMask = nBitsOfOnes(state.dictionarySizeBits)
115
+ const { compressionType, dictionarySizeBits } = readHeader(state.inputBuffer.read())
101
116
 
102
- if (state.compressionType !== BINARY_COMPRESSION) {
103
- if (state.compressionType !== ASCII_COMPRESSION) {
104
- reject(new Error(ERROR_INVALID_COMPRESSION_TYPE))
105
- return
106
- }
107
- state = mergeRight(state, generateAsciiTables())
108
- }
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)
109
122
 
110
- if (debug) {
111
- console.log(`compression type: ${state.compressionType === BINARY_COMPRESSION ? 'binary' : 'ascii'}`)
112
- console.log(`compression level: ${state.dictionarySizeBits === 4 ? 1 : state.dictionarySizeBits === 5 ? 2 : 3}`)
113
- }
123
+ if (compressionType === COMPRESSION_ASCII) {
124
+ const tables = generateAsciiTables()
125
+ Object.entries(tables).forEach(([key, value]) => {
126
+ state[key] = value
127
+ })
128
+ }
114
129
 
115
- state.chunk = chunk.slice(3)
130
+ if (debug) {
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
+ )
137
+ }
116
138
 
117
- resolve(state)
118
- })
139
+ return true
119
140
  }
120
141
 
121
142
  const wasteBits = (state, numberOfBits) => {
@@ -166,7 +187,7 @@ const decodeNextLiteral = state => {
166
187
  } else {
167
188
  const lastByte = getLowestNBits(8, state.bitBuffer)
168
189
 
169
- if (state.compressionType === BINARY_COMPRESSION) {
190
+ if (state.compressionType === COMPRESSION_BINARY) {
170
191
  return wasteBits(state, 8) === PKDCL_STREAM_END ? LITERAL_STREAM_ABORTED : lastByte
171
192
  } else {
172
193
  let value
@@ -226,12 +247,22 @@ const decodeDistance = (state, repeatLength) => {
226
247
  return distance + 1
227
248
  }
228
249
 
229
- const processChunkData = state => {
230
- 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
+
231
262
  state.needMoreInput = false
232
263
 
233
264
  state.backup()
234
- nextLiteral = decodeNextLiteral(state)
265
+ let nextLiteral = decodeNextLiteral(state)
235
266
 
236
267
  while (nextLiteral !== LITERAL_END_STREAM && nextLiteral !== LITERAL_STREAM_ABORTED) {
237
268
  let addition
@@ -270,22 +301,72 @@ const processChunkData = state => {
270
301
  }
271
302
  }
272
303
 
273
- const explode = ({ debug = false, inputBufferSize = 0x0, outputBufferSize = 0x0 } = {}) => {
274
- const stateBackup = {
275
- extraBits: null,
276
- 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
+ }
277
352
  }
278
353
 
279
- let state = {
354
+ handler._state = {
355
+ _backup: {
356
+ extraBits: null,
357
+ bitBuffer: null
358
+ },
359
+ needMoreInput: true,
280
360
  isFirstChunk: true,
281
- needMoreInput: false,
361
+ extraBits: 0,
282
362
  chBitsAsc: repeat(0, 0x100), // DecodeLit and GenAscTabs uses this
283
363
  lengthCodes: generateDecodeTables(LenCode, LenBits),
284
364
  distPosCodes: generateDecodeTables(DistCode, DistBits),
285
- extraBits: 0,
286
- inputBuffer: new QuasiImmutableBuffer(inputBufferSize),
287
- outputBuffer: new QuasiImmutableBuffer(outputBufferSize),
365
+ inputBuffer: new ExpandingBuffer(inputBufferSize),
366
+ outputBuffer: new ExpandingBuffer(outputBufferSize),
288
367
  onInputFinished: callback => {
368
+ const state = handler._state
369
+
289
370
  if (debug) {
290
371
  console.log('---------------')
291
372
  console.log('total number of chunks read:', state.stats.chunkCounter)
@@ -294,19 +375,21 @@ const explode = ({ debug = false, inputBufferSize = 0x0, outputBufferSize = 0x0
294
375
  }
295
376
 
296
377
  if (state.needMoreInput) {
297
- callback(new Error(ERROR_ABORTED))
378
+ callback(new AbortedError())
298
379
  } else {
299
380
  callback(null, state.outputBuffer.read())
300
381
  }
301
382
  },
302
383
  backup: () => {
303
- stateBackup.extraBits = state.extraBits
304
- stateBackup.bitBuffer = state.bitBuffer
384
+ const state = handler._state
385
+ state._backup.extraBits = state.extraBits
386
+ state._backup.bitBuffer = state.bitBuffer
305
387
  state.inputBuffer._saveIndices()
306
388
  },
307
389
  restore: () => {
308
- state.extraBits = stateBackup.extraBits
309
- state.bitBuffer = stateBackup.bitBuffer
390
+ const state = handler._state
391
+ state.extraBits = state._backup.extraBits
392
+ state.bitBuffer = state._backup.bitBuffer
310
393
  state.inputBuffer._restoreIndices()
311
394
  },
312
395
  stats: {
@@ -314,40 +397,18 @@ const explode = ({ debug = false, inputBufferSize = 0x0, outputBufferSize = 0x0
314
397
  }
315
398
  }
316
399
 
317
- return function (chunk, encoding, callback) {
318
- state.needMoreInput = false
319
-
320
- let work
321
- if (state.isFirstChunk) {
322
- state.isFirstChunk = false
323
- this._flush = state.onInputFinished
324
- work = parseFirstChunk(chunk, debug).then(({ chunk, ...newState }) => {
325
- state = mergeRight(state, newState)
326
- state.inputBuffer.append(chunk)
327
- return state
328
- })
329
- } else {
330
- state.inputBuffer.append(chunk)
331
- work = Promise.resolve(state)
332
- }
333
-
334
- if (debug) {
335
- console.log(`reading ${toHex(chunk.length)} bytes from chunk #${state.stats.chunkCounter++}`)
336
- }
337
-
338
- work
339
- .then(processChunkData)
340
- .then(() => {
341
- const blockSize = 0x1000
342
- const numberOfBytes = Math.floor(state.outputBuffer.size() / blockSize) * blockSize
343
- const output = Buffer.from(state.outputBuffer.read(0, numberOfBytes))
344
- state.outputBuffer.flushStart(numberOfBytes)
345
- callback(null, output)
346
- })
347
- .catch(e => {
348
- callback(e)
349
- })
350
- }
400
+ return handler
351
401
  }
352
402
 
353
- 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