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.
- package/README.md +228 -72
- package/bin/explode.js +99 -0
- package/bin/implode.js +113 -0
- package/package.json +32 -29
- package/src/{constants.mjs → constants.js} +50 -26
- package/src/errors.js +50 -0
- package/src/{explode.mjs → explode.js} +196 -122
- 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 -97
- package/bin/helpers.mjs +0 -36
- package/bin/implode.mjs +0 -110
- package/notes-on-implode.txt +0 -130
- package/src/helpers.mjs +0 -126
- package/src/implode.mjs +0 -285
- 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,114 +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
|
-
|
|
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
|
-
|
|
83
|
-
if (
|
|
84
|
-
|
|
110
|
+
const parseInitialData = (state, debug = false) => {
|
|
111
|
+
if (state.inputBuffer.size() < 4) {
|
|
112
|
+
return false
|
|
85
113
|
}
|
|
86
114
|
|
|
87
|
-
|
|
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
|
-
|
|
94
|
-
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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 ===
|
|
108
|
-
console.log(
|
|
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
|
|
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 ===
|
|
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
|
-
|
|
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
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
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
|
-
|
|
354
|
+
handler._state = {
|
|
355
|
+
_backup: {
|
|
356
|
+
extraBits: null,
|
|
357
|
+
bitBuffer: null
|
|
358
|
+
},
|
|
359
|
+
needMoreInput: true,
|
|
273
360
|
isFirstChunk: true,
|
|
274
|
-
|
|
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
|
-
|
|
279
|
-
|
|
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
|
|
378
|
+
callback(new AbortedError())
|
|
291
379
|
} else {
|
|
292
380
|
callback(null, state.outputBuffer.read())
|
|
293
381
|
}
|
|
294
382
|
},
|
|
295
383
|
backup: () => {
|
|
296
|
-
|
|
297
|
-
|
|
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
|
|
302
|
-
state.
|
|
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
|
|
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
|
-
|
|
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
|