Skip to content

Commit ea40342

Browse files
author
Andy Weiss
committed
Fix lint, convert to r-value ref instead of string_view, add tests
1 parent 2af550d commit ea40342

File tree

3 files changed

+84
-24
lines changed

3 files changed

+84
-24
lines changed

lib/zlib.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,19 @@ function Brotli(opts, mode) {
830830
});
831831
}
832832

833+
let dictionary = opts?.dictionary;
834+
if (dictionary !== undefined && !isArrayBufferView(dictionary)) {
835+
if (isAnyArrayBuffer(dictionary)) {
836+
dictionary = Buffer.from(dictionary);
837+
} else {
838+
throw new ERR_INVALID_ARG_TYPE(
839+
'options.dictionary',
840+
['Buffer', 'TypedArray', 'DataView', 'ArrayBuffer'],
841+
dictionary,
842+
);
843+
}
844+
}
845+
833846
const handle = mode === BROTLI_DECODE ?
834847
new binding.BrotliDecoder(mode) : new binding.BrotliEncoder(mode);
835848

@@ -838,7 +851,7 @@ function Brotli(opts, mode) {
838851
brotliInitParamsArray,
839852
this._writeState,
840853
processCallback,
841-
opts?.dictionary && isArrayBufferView(opts.dictionary) ? opts.dictionary : undefined,
854+
dictionary,
842855
);
843856

844857
ZlibBase.call(this, opts, mode, handle, brotliDefaultOpts);

src/node_zlib.cc

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ class BrotliEncoderContext final : public BrotliContext {
257257
public:
258258
void Close();
259259
void DoThreadPoolWork();
260-
CompressionError Init(std::string_view dictionary = {});
260+
CompressionError Init(std::vector<uint8_t>&& dictionary = {});
261261
CompressionError ResetStream();
262262
CompressionError SetParams(int key, uint32_t value);
263263
CompressionError GetErrorInfo() const;
@@ -279,7 +279,7 @@ class BrotliDecoderContext final : public BrotliContext {
279279
public:
280280
void Close();
281281
void DoThreadPoolWork();
282-
CompressionError Init(std::string_view dictionary = {});
282+
CompressionError Init(std::vector<uint8_t>&& dictionary = {});
283283
CompressionError ResetStream();
284284
CompressionError SetParams(int key, uint32_t value);
285285
CompressionError GetErrorInfo() const;
@@ -849,19 +849,18 @@ class BrotliCompressionStream final :
849849
wrap->InitStream(write_result, write_js_callback);
850850

851851
AllocScope alloc_scope(wrap);
852-
std::string_view dictionary;
853-
ArrayBufferViewContents<char> contents;
852+
std::vector<uint8_t> dictionary;
854853
if (args.Length() == 4 && !args[3]->IsUndefined()) {
855854
if (!args[3]->IsArrayBufferView()) {
856855
THROW_ERR_INVALID_ARG_TYPE(
857856
wrap->env(), "dictionary must be an ArrayBufferView if provided");
858857
return;
859858
}
860-
contents.ReadValue(args[3]);
861-
dictionary = std::string_view(contents.data(), contents.length());
859+
ArrayBufferViewContents<uint8_t> contents(args[3]);
860+
dictionary.assign(contents.data(), contents.data() + contents.length());
862861
}
863862

864-
CompressionError err = wrap->context()->Init(dictionary);
863+
CompressionError err = wrap->context()->Init(std::move(dictionary));
865864
if (err.IsError()) {
866865
wrap->EmitError(err);
867866
// TODO(addaleax): Sometimes we generate better error codes in C++ land,
@@ -1412,7 +1411,7 @@ void BrotliEncoderContext::Close() {
14121411
mode_ = NONE;
14131412
}
14141413

1415-
CompressionError BrotliEncoderContext::Init(std::string_view dictionary) {
1414+
CompressionError BrotliEncoderContext::Init(std::vector<uint8_t>&& dictionary) {
14161415
brotli_alloc_func alloc = CompressionStreamMemoryOwner::AllocForBrotli;
14171416
brotli_free_func free = CompressionStreamMemoryOwner::FreeForZlib;
14181417
void* opaque =
@@ -1432,11 +1431,8 @@ CompressionError BrotliEncoderContext::Init(std::string_view dictionary) {
14321431

14331432
if (!dictionary.empty()) {
14341433
// The dictionary data must remain valid for the lifetime of the prepared
1435-
// dictionary, so copy it into a member vector.
1436-
dictionary_.assign(
1437-
reinterpret_cast<const uint8_t*>(dictionary.data()),
1438-
reinterpret_cast<const uint8_t*>(dictionary.data()) +
1439-
dictionary.size());
1434+
// dictionary, so take ownership via move.
1435+
dictionary_ = std::move(dictionary);
14401436

14411437
prepared_dictionary_.reset(BrotliEncoderPrepareDictionary(
14421438
BROTLI_SHARED_DICTIONARY_RAW,
@@ -1513,7 +1509,7 @@ void BrotliDecoderContext::DoThreadPoolWork() {
15131509
}
15141510
}
15151511

1516-
CompressionError BrotliDecoderContext::Init(std::string_view dictionary) {
1512+
CompressionError BrotliDecoderContext::Init(std::vector<uint8_t>&& dictionary) {
15171513
brotli_alloc_func alloc = CompressionStreamMemoryOwner::AllocForBrotli;
15181514
brotli_free_func free = CompressionStreamMemoryOwner::FreeForZlib;
15191515
void* opaque =
@@ -1532,11 +1528,8 @@ CompressionError BrotliDecoderContext::Init(std::string_view dictionary) {
15321528

15331529
if (!dictionary.empty()) {
15341530
// The dictionary data must remain valid for the lifetime of the decoder,
1535-
// so copy it into a member vector.
1536-
dictionary_.assign(
1537-
reinterpret_cast<const uint8_t*>(dictionary.data()),
1538-
reinterpret_cast<const uint8_t*>(dictionary.data()) +
1539-
dictionary.size());
1531+
// so take ownership via move.
1532+
dictionary_ = std::move(dictionary);
15401533

15411534
if (!BrotliDecoderAttachDictionary(state_.get(),
15421535
BROTLI_SHARED_DICTIONARY_RAW,

test/parallel/test-zlib-brotli-dictionary.js

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,63 @@ zlib.brotliCompress(input, { dictionary }, common.mustSucceed((compressed) => {
6464
assert.throws(() => {
6565
zlib.brotliDecompressSync(compressed);
6666
}, (err) => {
67-
// The exact error may vary, but decoding should fail without the
68-
// matching dictionary.
69-
return err.code === 'ERR_BROTLI_COMPRESSION_FAILED' ||
70-
err instanceof Error;
67+
assert.match(err.code, /ERR_/);
68+
return true;
7169
});
7270
}
71+
72+
// Test that decompression with wrong dictionary fails.
73+
{
74+
const compressed = zlib.brotliCompressSync(input, { dictionary });
75+
const wrongDictionary = Buffer.from('this is the wrong dictionary');
76+
assert.throws(() => {
77+
zlib.brotliDecompressSync(compressed, { dictionary: wrongDictionary });
78+
}, (err) => {
79+
assert.match(err.code, /ERR_/);
80+
return true;
81+
});
82+
}
83+
84+
// Test that dictionary works with ArrayBuffer (converted to Buffer).
85+
{
86+
const arrayBufferDict = dictionary.buffer.slice(
87+
dictionary.byteOffset,
88+
dictionary.byteOffset + dictionary.byteLength,
89+
);
90+
const compressed = zlib.brotliCompressSync(input, { dictionary: arrayBufferDict });
91+
const decompressed = zlib.brotliDecompressSync(compressed, { dictionary: arrayBufferDict });
92+
assert.strictEqual(decompressed.toString(), input.toString());
93+
}
94+
95+
// Test that dictionary works with TypedArray (Uint8Array).
96+
{
97+
const uint8Dict = new Uint8Array(dictionary);
98+
const compressed = zlib.brotliCompressSync(input, { dictionary: uint8Dict });
99+
const decompressed = zlib.brotliDecompressSync(compressed, { dictionary: uint8Dict });
100+
assert.strictEqual(decompressed.toString(), input.toString());
101+
}
102+
103+
// Test that invalid dictionary type throws ERR_INVALID_ARG_TYPE.
104+
for (const invalidDict of ['string', 123, true, { object: true }, [1, 2, 3]]) {
105+
assert.throws(() => {
106+
zlib.createBrotliCompress({ dictionary: invalidDict });
107+
}, { code: 'ERR_INVALID_ARG_TYPE' });
108+
109+
assert.throws(() => {
110+
zlib.createBrotliDecompress({ dictionary: invalidDict });
111+
}, { code: 'ERR_INVALID_ARG_TYPE' });
112+
}
113+
114+
// Test with streaming API and wrong dictionary emits error event.
115+
{
116+
const compressed = zlib.brotliCompressSync(input, { dictionary });
117+
const wrongDict = Buffer.from('wrong dictionary data');
118+
const decoder = zlib.createBrotliDecompress({ dictionary: wrongDict });
119+
120+
decoder.on('error', common.mustCall((err) => {
121+
assert.match(err.code, /ERR_/);
122+
}));
123+
124+
decoder.write(compressed);
125+
decoder.end();
126+
}

0 commit comments

Comments
 (0)