diff --git a/doc/api/zlib.md b/doc/api/zlib.md index f32715ba6cc6b2..a94bc7569d1c70 100644 --- a/doc/api/zlib.md +++ b/doc/api/zlib.md @@ -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 @@ -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. diff --git a/lib/internal/streams/iter/transform.js b/lib/internal/streams/iter/transform.js index 9782f5f50ebf2c..be286560ff238f 100644 --- a/lib/internal/streams/iter/transform.js +++ b/lib/internal/streams/iter/transform.js @@ -41,6 +41,7 @@ const { kValidatedTransform } = require('internal/streams/iter/types'); const { checkRangesOrGetDefault, validateFiniteNumber, + validateInteger, validateObject, } = require('internal/validators'); const binding = internalBinding('zlib'); @@ -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 ? diff --git a/lib/zlib.js b/lib/zlib.js index 47726337cf720d..42b983717df993 100644 --- a/lib/zlib.js +++ b/lib/zlib.js @@ -68,6 +68,7 @@ const { checkRangesOrGetDefault, validateBoolean, validateFunction, + validateInteger, validateUint32, validateFiniteNumber, } = require('internal/validators'); @@ -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( diff --git a/src/node_zlib.cc b/src/node_zlib.cc index 95201624cfa2fa..cecaf0543855f8 100644 --- a/src/node_zlib.cc +++ b/src/node_zlib.cc @@ -939,9 +939,6 @@ class ZstdStream final : public CompressionStream { } static void Init(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); - Local context = env->context(); - CHECK((args.Length() == 4 || args.Length() == 5) && "init(params, pledgedSrcSize, writeResult, writeCallback[, " "dictionary])"); @@ -958,19 +955,24 @@ class ZstdStream final : public CompressionStream { 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()->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(signed_pledged_src_size); } AllocScope alloc_scope(wrap); diff --git a/test/parallel/test-stream-iter-validation.js b/test/parallel/test-stream-iter-validation.js index 08eeaeee685656..c2917bcd42b8fe 100644 --- a/test/parallel/test-stream-iter-validation.js +++ b/test/parallel/test-stream-iter-validation.js @@ -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); + } } // ============================================================================= diff --git a/test/parallel/test-zlib-zstd-pledged-src-size.js b/test/parallel/test-zlib-zstd-pledged-src-size.js index b1e32e14ae732a..4a5f27394c6609 100644 --- a/test/parallel/test-zlib-zstd-pledged-src-size.js +++ b/test/parallel/test-zlib-zstd-pledged-src-size.js @@ -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();