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.
- package/README.md +228 -61
- package/bin/explode.js +99 -0
- package/bin/implode.js +113 -0
- package/package.json +32 -28
- package/src/{constants.mjs → constants.js} +50 -26
- package/src/errors.js +50 -0
- package/src/{explode.mjs → explode.js} +200 -139
- package/src/{QuasiImmutableBuffer.mjs → helpers/ExpandingBuffer.js} +14 -2
- package/src/helpers/functions.js +125 -0
- package/src/helpers/stream.js +225 -0
- package/src/helpers/testing.js +73 -0
- package/src/implode.js +361 -0
- package/src/index.js +18 -0
- package/types/constants.d.ts +41 -0
- package/types/errors.d.ts +30 -0
- package/types/explode.d.ts +56 -0
- package/types/helpers/ExpandingBuffer.d.ts +25 -0
- package/types/helpers/Shared.d.ts +46 -0
- package/types/helpers/functions.d.ts +10 -0
- package/types/helpers/stream.d.ts +61 -0
- package/types/helpers/testing.d.ts +6 -0
- package/types/implode.d.ts +63 -0
- package/types/index.d.ts +8 -0
- package/bin/explode.mjs +0 -74
- package/bin/helpers.mjs +0 -36
- package/bin/implode.mjs +0 -103
- package/src/helpers.mjs +0 -65
- package/src/implode.mjs +0 -239
- package/src/index.mjs +0 -25
|
@@ -1,28 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
const COMPRESSION_BINARY = 0
|
|
2
|
+
const COMPRESSION_ASCII = 1
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
10
|
-
export const DICTIONARY_SIZE2 = 0x0800
|
|
11
|
-
export const DICTIONARY_SIZE3 = 0x1000
|
|
8
|
+
const LONGEST_ALLOWED_REPETITION = 0x204
|
|
12
9
|
|
|
13
|
-
|
|
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
|
-
|
|
16
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
12
|
-
|
|
22
|
+
LITERAL_STREAM_ABORTED,
|
|
23
|
+
LITERAL_END_STREAM,
|
|
13
24
|
LenBits,
|
|
14
|
-
LenCode,
|
|
15
|
-
ExLenBits,
|
|
16
25
|
LenBase,
|
|
26
|
+
ExLenBits,
|
|
17
27
|
DistBits,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
|
66
|
+
return reduce(
|
|
67
|
+
(acc, idx) => {
|
|
68
|
+
acc[idx] = index
|
|
69
|
+
return acc
|
|
70
|
+
},
|
|
71
|
+
[],
|
|
72
|
+
idxs
|
|
73
|
+
)
|
|
40
74
|
}
|
|
41
75
|
|
|
42
|
-
|
|
43
|
-
const
|
|
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
|
-
|
|
84
|
+
tables.chBitsAsc = ChBitsAsc.map((value, index) => {
|
|
51
85
|
if (value <= 8) {
|
|
52
|
-
|
|
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
|
|
57
|
-
|
|
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
|
-
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
return state
|
|
70
|
-
}
|
|
96
|
+
tables.asciiTable2C34[acc] = 0xff
|
|
71
97
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
79
|
-
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
dictionarySizeBits: chunk.readUInt8(1),
|
|
92
|
-
bitBuffer: chunk.readUInt8(2)
|
|
93
|
-
}
|
|
107
|
+
return tables
|
|
108
|
+
}
|
|
94
109
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
110
|
+
const parseInitialData = (state, debug = false) => {
|
|
111
|
+
if (state.inputBuffer.size() < 4) {
|
|
112
|
+
return false
|
|
113
|
+
}
|
|
99
114
|
|
|
100
|
-
|
|
115
|
+
const { compressionType, dictionarySizeBits } = readHeader(state.inputBuffer.read())
|
|
101
116
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 ===
|
|
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
|
-
|
|
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
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
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
|
-
|
|
354
|
+
handler._state = {
|
|
355
|
+
_backup: {
|
|
356
|
+
extraBits: null,
|
|
357
|
+
bitBuffer: null
|
|
358
|
+
},
|
|
359
|
+
needMoreInput: true,
|
|
280
360
|
isFirstChunk: true,
|
|
281
|
-
|
|
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
|
-
|
|
286
|
-
|
|
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
|
|
378
|
+
callback(new AbortedError())
|
|
298
379
|
} else {
|
|
299
380
|
callback(null, state.outputBuffer.read())
|
|
300
381
|
}
|
|
301
382
|
},
|
|
302
383
|
backup: () => {
|
|
303
|
-
|
|
304
|
-
|
|
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
|
|
309
|
-
state.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
1
|
+
const { clamp } = require('ramda')
|
|
2
|
+
const { ExpectedBufferError } = require('../errors')
|
|
2
3
|
|
|
3
|
-
|
|
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
|