Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions doc/api/zlib.md
Original file line number Diff line number Diff line change
Expand Up @@ -784,8 +784,9 @@ const stream = zlib.createZstdCompress({
#### Pledged Source Size

It's possible to specify the expected total size of the uncompressed input via
`opts.pledgedSrcSize`. If the size doesn't match at the end of the input,
compression will fail with the code `ZSTD_error_srcSize_wrong`.
`opts.pledgedSrcSize`, which must be a non-negative safe integer. If the size
doesn't match at the end of the input, compression will fail with the code
`ZSTD_error_srcSize_wrong`.

#### Decompressor options

Expand Down Expand Up @@ -1923,7 +1924,8 @@ added: v25.9.0
`ZSTD_btultra2`.
See the [Zstd compressor options][] in the zlib documentation for the
full list.
* `pledgedSrcSize` {number} Expected uncompressed size (optional hint).
* `pledgedSrcSize` {number} Expected uncompressed size as a non-negative safe
integer (optional hint).
* `dictionary` {Buffer|TypedArray|DataView}
* Returns: {Object} A stateful transform.

Expand Down
10 changes: 2 additions & 8 deletions lib/internal/streams/iter/transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const { kValidatedTransform } = require('internal/streams/iter/types');
const {
checkRangesOrGetDefault,
validateFiniteNumber,
validateInteger,
validateObject,
} = require('internal/validators');
const binding = internalBinding('zlib');
Expand Down Expand Up @@ -258,14 +259,7 @@ function createZstdHandle(mode, options, processCallback, onError) {

const pledgedSrcSize = options.pledgedSrcSize;
if (pledgedSrcSize !== undefined) {
if (typeof pledgedSrcSize !== 'number' || NumberIsNaN(pledgedSrcSize)) {
throw new ERR_INVALID_ARG_TYPE('options.pledgedSrcSize', 'number',
pledgedSrcSize);
}
if (pledgedSrcSize < 0) {
throw new ERR_OUT_OF_RANGE('options.pledgedSrcSize', '>= 0',
pledgedSrcSize);
}
validateInteger(pledgedSrcSize, 'options.pledgedSrcSize', 0);
}

const handle = isCompress ?
Expand Down
8 changes: 6 additions & 2 deletions lib/zlib.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ const {
checkRangesOrGetDefault,
validateBoolean,
validateFunction,
validateInteger,
validateUint32,
validateFiniteNumber,
} = require('internal/validators');
Expand Down Expand Up @@ -926,11 +927,14 @@ class Zstd extends ZlibBase {
});
}

const pledgedSrcSize = opts?.pledgedSrcSize;
if (pledgedSrcSize !== undefined) {
validateInteger(pledgedSrcSize, 'options.pledgedSrcSize', 0);
}

const handle = mode === ZSTD_COMPRESS ?
new binding.ZstdCompress() : new binding.ZstdDecompress();

const pledgedSrcSize = opts?.pledgedSrcSize ?? undefined;

const writeState = new Uint32Array(2);

handle.init(
Expand Down
24 changes: 13 additions & 11 deletions src/node_zlib.cc
Original file line number Diff line number Diff line change
Expand Up @@ -939,9 +939,6 @@ class ZstdStream final : public CompressionStream<CompressionContext> {
}

static void Init(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Local<Context> context = env->context();

CHECK((args.Length() == 4 || args.Length() == 5) &&
"init(params, pledgedSrcSize, writeResult, writeCallback[, "
"dictionary])");
Expand All @@ -958,19 +955,24 @@ class ZstdStream final : public CompressionStream<CompressionContext> {
wrap->InitStream(write_result, write_js_callback);

uint64_t pledged_src_size = ZSTD_CONTENTSIZE_UNKNOWN;
if (args[1]->IsNumber()) {
int64_t signed_pledged_src_size;
if (!args[1]->IntegerValue(context).To(&signed_pledged_src_size)) {
THROW_ERR_INVALID_ARG_VALUE(wrap->env(),
"pledgedSrcSize should be an integer");
if (!args[1]->IsUndefined()) {
if (!args[1]->IsNumber()) {
THROW_ERR_INVALID_ARG_TYPE(wrap->env(),
"pledgedSrcSize must be a number");
return;
}
if (!IsSafeJsInt(args[1])) {
THROW_ERR_OUT_OF_RANGE(wrap->env(),
"pledgedSrcSize must be a safe integer");
return;
}
const int64_t signed_pledged_src_size = args[1].As<Integer>()->Value();
if (signed_pledged_src_size < 0) {
THROW_ERR_INVALID_ARG_VALUE(wrap->env(),
"pledgedSrcSize may not be negative");
THROW_ERR_OUT_OF_RANGE(wrap->env(),
"pledgedSrcSize must be non-negative");
return;
}
pledged_src_size = signed_pledged_src_size;
pledged_src_size = static_cast<uint64_t>(signed_pledged_src_size);
}

AllocScope alloc_scope(wrap);
Expand Down
11 changes: 10 additions & 1 deletion test/parallel/test-stream-iter-validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,16 @@ async function testAsyncValidation() {

// Zstd pledgedSrcSize
await assert.rejects(consume(compressZstd({ pledgedSrcSize: 'bad' })), TYPE);
await assert.rejects(consume(compressZstd({ pledgedSrcSize: -1 })), RANGE);
for (const pledgedSrcSize of [
NaN,
Infinity,
-Infinity,
1.9,
-1,
Number.MAX_SAFE_INTEGER + 1,
]) {
await assert.rejects(consume(compressZstd({ pledgedSrcSize })), RANGE);
}
}

// =============================================================================
Expand Down
36 changes: 36 additions & 0 deletions test/parallel/test-zlib-zstd-pledged-src-size.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,39 @@ compressWithPledgedSrcSize({ pledgedSrcSize: 42, actualSrcSize: 0 });
compressWithPledgedSrcSize({ pledgedSrcSize: 42, actualSrcSize: 13 });

compressWithPledgedSrcSize({ pledgedSrcSize: 42, actualSrcSize: 42 });

function assertInvalidPledgedSrcSize(pledgedSrcSize, expected) {
assert.throws(
() => zlib.createZstdCompress({ pledgedSrcSize }),
expected,
);
assert.throws(
() => zlib.zstdCompressSync('', { pledgedSrcSize }),
expected,
);
}

for (const pledgedSrcSize of ['1', null]) {
assertInvalidPledgedSrcSize(pledgedSrcSize, {
name: 'TypeError',
code: 'ERR_INVALID_ARG_TYPE',
});
}

for (const pledgedSrcSize of [
NaN,
Infinity,
-Infinity,
1.9,
-1,
Number.MAX_SAFE_INTEGER + 1,
]) {
assertInvalidPledgedSrcSize(pledgedSrcSize, {
name: 'RangeError',
code: 'ERR_OUT_OF_RANGE',
});
}

zlib.createZstdCompress({
pledgedSrcSize: Number.MAX_SAFE_INTEGER,
}).destroy();
Loading