Skip to content

Commit 9e4ba62

Browse files
committed
Restored back dictionary option and removed onNeedDict hook
1 parent abf8db9 commit 9e4ba62

7 files changed

Lines changed: 75 additions & 146 deletions

File tree

examples/advanced-hooks.mjs

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,12 @@
11
import {
22
Deflate,
33
GZheader,
4-
Inflate,
5-
zlibDeflateSetDictionary,
64
zlibDeflateSetHeader,
7-
zlibInflateSetDictionary,
85
Z_OK
96
} from '../src/index.ts';
107

118
const data = new Uint8Array([ 1, 2, 3, 1, 2, 3 ]);
12-
const dict = new Uint8Array([ 1, 2, 3 ]);
139

14-
// Deflate needs the dictionary before the first byte is compressed.
15-
const deflator = new Deflate();
16-
deflator.onStart = function (strm) {
17-
if (zlibDeflateSetDictionary(strm, dict) !== Z_OK) throw new Error('dictionary rejected');
18-
};
19-
deflator.push(data, true);
20-
21-
// Zlib-wrapped inflate asks for the dictionary after reading its dictionary id.
22-
const inflator = new Inflate();
23-
inflator.onNeedDict = function () { return dict; };
24-
inflator.push(deflator.result, true);
25-
26-
// Raw deflate has no wrapper header, so both sides set the dictionary up front.
27-
const rawDeflator = new Deflate({ raw: true });
28-
rawDeflator.onStart = function (strm) {
29-
if (zlibDeflateSetDictionary(strm, dict) !== Z_OK) throw new Error('dictionary rejected');
30-
};
31-
rawDeflator.push(data, true);
32-
33-
const rawInflator = new Inflate({ raw: true });
34-
rawInflator.onStart = function (strm) {
35-
if (zlibInflateSetDictionary(strm, dict) !== Z_OK) throw new Error('dictionary rejected');
36-
};
37-
rawInflator.push(rawDeflator.result, true);
38-
39-
// Gzip header is written before compression starts.
4010
const gzipper = new Deflate({ gzip: true });
4111
gzipper.onStart = function (strm) {
4212
const header = new GZheader();

src/deflate.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {
22
messages,
33
ZStream,
44
zlibDeflateInit2,
5+
zlibDeflateSetDictionary,
56
zlibDeflate,
67
zlibDeflateEnd
78
} from './zlib.mjs';
@@ -36,6 +37,7 @@ interface DeflateOptions {
3637
raw?: boolean;
3738
gzip?: boolean;
3839
legacyHash?: boolean;
40+
dictionary?: Uint8Array | ArrayBuffer;
3941
}
4042

4143
const defaultOptions: Required<DeflateOptions> = {
@@ -47,7 +49,8 @@ const defaultOptions: Required<DeflateOptions> = {
4749
strategy: Z_DEFAULT_STRATEGY,
4850
raw: false,
4951
gzip: false,
50-
legacyHash: false
52+
legacyHash: false,
53+
dictionary: new Uint8Array(0)
5154
};
5255

5356
/**
@@ -99,6 +102,7 @@ const defaultOptions: Required<DeflateOptions> = {
99102
* - `windowBits`
100103
* - `memLevel`
101104
* - `strategy`
105+
* - `dictionary`
102106
*
103107
* [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
104108
* for more information on these.
@@ -175,6 +179,24 @@ class Deflate {
175179
if (status !== Z_OK) {
176180
throw new Error(messages[status]);
177181
}
182+
183+
if (toString.call(opt.dictionary) === '[object ArrayBuffer]') {
184+
opt.dictionary = new Uint8Array(opt.dictionary as ArrayBuffer);
185+
}
186+
187+
const dictionary = opt.dictionary as Uint8Array;
188+
189+
if (dictionary.length) {
190+
if (opt.gzip) {
191+
throw new Error('dictionary is not supported with gzip');
192+
}
193+
194+
status = zlibDeflateSetDictionary(this.strm, dictionary);
195+
196+
if (status !== Z_OK) {
197+
throw new Error(messages[status]);
198+
}
199+
}
178200
}
179201

180202
/**
@@ -330,6 +352,7 @@ class Deflate {
330352
* - windowBits
331353
* - memLevel
332354
* - strategy
355+
* - dictionary
333356
*
334357
* [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
335358
* for more information on these.

src/inflate.ts

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,15 @@ interface InflateOptions {
3333
windowBits?: number;
3434
raw?: boolean;
3535
to?: string;
36+
dictionary?: Uint8Array | ArrayBuffer;
3637
}
3738

3839
const defaultOptions: Required<InflateOptions> = {
3940
chunkSize: 1024 * 64,
4041
windowBits: 15,
4142
raw: false,
42-
to: ''
43+
to: '',
44+
dictionary: new Uint8Array(0)
4345
};
4446

4547
/**
@@ -86,6 +88,7 @@ const defaultOptions: Required<InflateOptions> = {
8688
* on bad params. Supported options:
8789
*
8890
* - `windowBits`
91+
* - `dictionary`
8992
* [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
9093
* for more information on these.
9194
*
@@ -179,6 +182,19 @@ class Inflate {
179182
this.header = new GZheader();
180183

181184
zlibInflateGetHeader(this.strm, this.header);
185+
186+
if (toString.call(opt.dictionary) === '[object ArrayBuffer]') {
187+
opt.dictionary = new Uint8Array(opt.dictionary as ArrayBuffer);
188+
}
189+
190+
const dictionary = opt.dictionary as Uint8Array;
191+
192+
if (opt.raw && dictionary.length) {
193+
status = zlibInflateSetDictionary(this.strm, dictionary);
194+
if (status !== Z_OK) {
195+
throw new Error(messages[status]);
196+
}
197+
}
182198
}
183199

184200
/**
@@ -243,9 +259,9 @@ class Inflate {
243259
status = zlibInflate(strm, _flush_mode);
244260

245261
if (status === Z_NEED_DICT) {
246-
const dictionary = this.onNeedDict(strm);
262+
const dictionary = this.options.dictionary as Uint8Array;
247263

248-
if (dictionary?.length) {
264+
if (dictionary.length) {
249265
status = zlibInflateSetDictionary(strm, dictionary);
250266

251267
if (status === Z_OK) {
@@ -365,15 +381,6 @@ class Inflate {
365381
onStart(strm: ZStream): void {}
366382

367383

368-
/**
369-
* Inflate#onNeedDict(strm) -> Uint8Array
370-
* - strm (ZStream): low-level zlib stream.
371-
*
372-
* Called when inflate needs a preset dictionary. Return a Uint8Array dictionary.
373-
**/
374-
onNeedDict(strm: ZStream): Uint8Array { return new Uint8Array(0); }
375-
376-
377384
/**
378385
* Inflate#onData(chunk) -> Void
379386
* - chunk (Uint8Array|String): output data. When string output requested,
@@ -424,6 +431,7 @@ class Inflate {
424431
* Supported options are:
425432
*
426433
* - windowBits
434+
* - dictionary
427435
*
428436
* [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
429437
* for more information.

test/deflate.test.mjs

Lines changed: 11 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@ import {
44
Inflate,
55
deflate,
66
deflateRaw,
7+
gzip,
78
inflate,
89
inflateRaw,
9-
zlibDeflateSetDictionary,
1010
Z_FULL_FLUSH,
11-
Z_OK,
1211
Z_SYNC_FLUSH
1312
} from '../src/index.ts';
1413
import assert from 'assert';
@@ -23,19 +22,15 @@ describe('deflate misc', () => {
2322

2423
it('handles a dictionary across multiple pushes', () => {
2524
const dict = Buffer.from('abcd');
26-
const deflate = new Deflate();
27-
deflate.onStart = function (strm) {
28-
assert.strictEqual(zlibDeflateSetDictionary(strm, dict), Z_OK);
29-
};
25+
const deflate = new Deflate({ dictionary: dict });
3026

3127
deflate.push(Buffer.from('hello'), false);
3228
deflate.push(Buffer.from('hello'), false);
3329
deflate.push(Buffer.from(' world'), true);
3430

3531
if (deflate.err) { throw new Error(deflate.err); }
3632

37-
const inflate = new Inflate();
38-
inflate.onNeedDict = function () { return dict; };
33+
const inflate = new Inflate({ dictionary: dict });
3934
inflate.push(Buffer.from(deflate.result), true);
4035
assert.ok(!inflate.err, 'inflate error: ' + inflate.err);
4136

@@ -51,42 +46,24 @@ describe('deflate misc', () => {
5146
assert.deepStrictEqual(deflate(sample.buffer), deflate(sample));
5247
});
5348

54-
it('sets a dictionary from onStart', () => {
55-
const dict = new Uint8Array([ 0x61, 0x62, 0x63, 0x64 ]); // 'abcd'
56-
57-
const withDictionary = new Deflate();
58-
withDictionary.onStart = function (strm) {
59-
assert.strictEqual(zlibDeflateSetDictionary(strm, dict), Z_OK);
60-
};
61-
withDictionary.push(Buffer.from('hellohello world'), true);
62-
assert.ok(!withDictionary.err, 'deflate error: ' + withDictionary.err);
63-
64-
const withoutDictionary = new Deflate();
65-
withoutDictionary.push(Buffer.from('hellohello world'), true);
66-
67-
assert.notDeepStrictEqual(withDictionary.result, withoutDictionary.result);
49+
it('accepts a dictionary passed as ArrayBuffer', () => {
50+
const dict = Uint8Array.from('abcd', c => c.charCodeAt(0));
6851

69-
const inflate = new Inflate();
70-
inflate.onNeedDict = function () { return dict; };
71-
inflate.push(Buffer.from(withDictionary.result), true);
72-
assert.ok(!inflate.err, 'inflate error: ' + inflate.err);
52+
const compressed = deflate(Buffer.from('hellohello world'), { dictionary: dict.buffer });
53+
const uncompressed = inflate(compressed, { dictionary: dict });
7354
assert.deepStrictEqual(
7455
new Uint8Array(Buffer.from('hellohello world')),
75-
inflate.result
56+
uncompressed
7657
);
7758
});
7859

7960
it('throws on invalid init options', () => {
8061
assert.throws(() => new Deflate({ level: 42 }));
8162
});
8263

83-
it('reports gzip dictionary errors from onStart', () => {
84-
const deflate = new Deflate({ gzip: true });
85-
deflate.onStart = function (strm) {
86-
assert.notStrictEqual(zlibDeflateSetDictionary(strm, Buffer.from('abcd')), Z_OK);
87-
};
88-
deflate.push(Buffer.from('hello'), true);
89-
assert.ok(!deflate.err, 'deflate error: ' + deflate.err);
64+
it('throws when a dictionary is used with gzip compression', () => {
65+
assert.throws(() => new Deflate({ gzip: true, dictionary: Buffer.from('abcd') }), /dictionary is not supported with gzip/);
66+
assert.throws(() => gzip(Buffer.from('hello'), { dictionary: Buffer.from('abcd') }), /dictionary is not supported with gzip/);
9067
});
9168

9269
it('returns false when pushing after the stream has ended', () => {

test/inflate.test.mjs

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@ import {
1212
inflate,
1313
inflateRaw,
1414
ungzip,
15-
zlibDeflateSetDictionary,
16-
zlibInflateSetDictionary,
17-
Z_OK,
1815
Z_SYNC_FLUSH
1916
} from '../src/index.ts';
2017
import { fileURLToPath } from 'node:url';
@@ -41,17 +38,11 @@ describe('inflate misc', () => {
4138
it('applies a dictionary early in raw mode', () => {
4239
const dict = Buffer.from('abcd');
4340

44-
const deflate = new Deflate({ raw: true });
45-
deflate.onStart = function (strm) {
46-
assert.strictEqual(zlibDeflateSetDictionary(strm, dict), Z_OK);
47-
};
41+
const deflate = new Deflate({ raw: true, dictionary: dict });
4842
deflate.push(Buffer.from('hellohello world'), true);
4943
assert.ok(!deflate.err, 'deflate error: ' + deflate.err);
5044

51-
const inflate = new Inflate({ raw: true });
52-
inflate.onStart = function (strm) {
53-
assert.strictEqual(zlibInflateSetDictionary(strm, dict), Z_OK);
54-
};
45+
const inflate = new Inflate({ raw: true, dictionary: dict });
5546
inflate.push(Buffer.from(deflate.result), true);
5647
assert.ok(!inflate.err, 'inflate error: ' + inflate.err);
5748

test/zlib/binary_compare.test.mjs

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,9 @@ import path from 'path';
55
import zlib from 'zlib';
66

77
import {
8-
Deflate,
98
deflate,
109
deflateRaw,
11-
gzip,
12-
zlibDeflateSetDictionary,
13-
Z_OK
10+
gzip
1411
} from '../../src/index.ts';
1512
import { loadSamples } from '../helpers.mjs';
1613
import { fileURLToPath } from 'node:url';
@@ -20,16 +17,6 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
2017
const sample = loadSamples().lorem_en_100k;
2118
const buf = Buffer.from(sample);
2219

23-
function deflateWithDictionary(data, options, dictionary) {
24-
const deflator = new Deflate(options);
25-
deflator.onStart = function (strm) {
26-
assert.strictEqual(zlibDeflateSetDictionary(strm, dictionary), Z_OK);
27-
};
28-
deflator.push(data, true);
29-
if (deflator.err) throw deflator.msg;
30-
return deflator.result;
31-
}
32-
3320

3421
// pako `legacyHash` output must match canonical zlib (pre-chromium node.js)
3522
// fixtures byte-for-byte.
@@ -139,9 +126,9 @@ describe('Deflate vs canonical zlib snapshots (legacyHash)', () => {
139126
it('trivial dictionary', () => {
140127
const dict = Buffer.from('abcdefghijklmnoprstuvwxyz');
141128
testSample(
142-
(data, options) => deflateWithDictionary(data, options, dict),
129+
deflate,
143130
sample,
144-
{},
131+
{ dictionary: dict },
145132
'deflate_dictionary=trivial.bin'
146133
);
147134
});
@@ -150,9 +137,9 @@ describe('Deflate vs canonical zlib snapshots (legacyHash)', () => {
150137
const spdyDict = fs.readFileSync(path.join(__dirname, '..', 'fixtures', 'spdy_dict.txt'));
151138

152139
testSample(
153-
(data, options) => deflateWithDictionary(data, options, spdyDict),
140+
deflate,
154141
sample,
155-
{},
142+
{ dictionary: spdyDict },
156143
'deflate_dictionary=spdy.bin'
157144
);
158145
});
@@ -260,13 +247,13 @@ describe('Deflate vs node.js zlib (ANZAC++ hash)', () => {
260247

261248
it('trivial dictionary', () => {
262249
const dict = Buffer.from('abcdefghijklmnoprstuvwxyz');
263-
testNode(deflateWithDictionary(sample, { legacyHash: false }, dict), zlib.deflateSync(buf, { dictionary: dict }));
250+
testNode(deflate(sample, { legacyHash: false, dictionary: dict }), zlib.deflateSync(buf, { dictionary: dict }));
264251
});
265252

266253
it('spdy dictionary', () => {
267254
const spdyDict = fs.readFileSync(path.join(__dirname, '..', 'fixtures', 'spdy_dict.txt'));
268255

269-
testNode(deflateWithDictionary(sample, { legacyHash: false }, spdyDict), zlib.deflateSync(buf, { dictionary: spdyDict }));
256+
testNode(deflate(sample, { legacyHash: false, dictionary: spdyDict }), zlib.deflateSync(buf, { dictionary: spdyDict }));
270257
});
271258
});
272259
});

0 commit comments

Comments
 (0)