diff --git a/doc/api/buffer.md b/doc/api/buffer.md index 7e380f7425fbd5..2a100a07c8c9b0 100644 --- a/doc/api/buffer.md +++ b/doc/api/buffer.md @@ -5319,6 +5319,11 @@ npx codemod@latest @nodejs/buffer-atob-btoa added: - v19.6.0 - v18.15.0 +changes: + - version: REPLACEME + pr-url: https://github.com/nodejs/node/pull/64504 + description: Detached `ArrayBuffer`s and views backed by them are treated + as empty. --> * `input` {Buffer | ArrayBuffer | TypedArray} The input to validate. @@ -5327,7 +5332,7 @@ added: This function returns `true` if `input` contains only valid ASCII-encoded data, including the case in which `input` is empty. -Throws if the `input` is a detached array buffer. +A detached `ArrayBuffer`, or a `TypedArray` backed by one, is treated as empty. ### `buffer.isUtf8(input)` @@ -5335,6 +5340,11 @@ Throws if the `input` is a detached array buffer. added: - v19.4.0 - v18.14.0 +changes: + - version: REPLACEME + pr-url: https://github.com/nodejs/node/pull/64504 + description: Detached `ArrayBuffer`s and views backed by them are treated + as empty. --> * `input` {Buffer | ArrayBuffer | TypedArray} The input to validate. @@ -5343,7 +5353,7 @@ added: This function returns `true` if `input` contains only valid UTF-8-encoded data, including the case in which `input` is empty. -Throws if the `input` is a detached array buffer. +A detached `ArrayBuffer`, or a `TypedArray` backed by one, is treated as empty. ### `buffer.INSPECT_MAX_BYTES` diff --git a/src/node_buffer.cc b/src/node_buffer.cc index ea6b3bc9ab7dd7..a227c739751243 100644 --- a/src/node_buffer.cc +++ b/src/node_buffer.cc @@ -1360,31 +1360,17 @@ void FastSwap64(Local receiver, static CFunction fast_swap64(CFunction::Make(FastSwap64)); -struct ValidationResult { - bool is_valid; - bool was_detached; -}; - -static ValidationResult ValidateUtf8(Local value) { +static bool ValidateUtf8(Local value) { ArrayBufferViewContents abv(value); - bool was_detached = abv.WasDetached(); - return {!was_detached && simdutf::validate_utf8(abv.data(), abv.length()), - was_detached}; + return abv.length() == 0 || simdutf::validate_utf8(abv.data(), abv.length()); } static void IsUtf8(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); CHECK_EQ(args.Length(), 1); CHECK(args[0]->IsTypedArray() || args[0]->IsArrayBuffer() || args[0]->IsSharedArrayBuffer()); - const ValidationResult result = ValidateUtf8(args[0]); - if (result.was_detached) { - return node::THROW_ERR_INVALID_STATE( - env, "Cannot validate on a detached buffer"); - } - - args.GetReturnValue().Set(result.is_valid); + args.GetReturnValue().Set(ValidateUtf8(args[0])); } static bool FastIsUtf8(Local receiver, @@ -1393,40 +1379,23 @@ static bool FastIsUtf8(Local receiver, FastApiCallbackOptions& options) { TRACK_V8_FAST_API_CALL("buffer.isUtf8"); HandleScope scope(options.isolate); - - const ValidationResult result = ValidateUtf8(value); - if (result.was_detached) { - node::THROW_ERR_INVALID_STATE(options.isolate, - "Cannot validate on a detached buffer"); - return false; - } - return result.is_valid; + return ValidateUtf8(value); } static CFunction fast_is_utf8(CFunction::Make(FastIsUtf8)); -static ValidationResult ValidateAscii(Local value) { +static bool ValidateAscii(Local value) { ArrayBufferViewContents abv(value); - bool was_detached = abv.WasDetached(); - return { - !was_detached && - !simdutf::validate_ascii_with_errors(abv.data(), abv.length()).error, - was_detached}; + return abv.length() == 0 || + !simdutf::validate_ascii_with_errors(abv.data(), abv.length()).error; } static void IsAscii(const FunctionCallbackInfo& args) { - Environment* env = Environment::GetCurrent(args); CHECK_EQ(args.Length(), 1); CHECK(args[0]->IsTypedArray() || args[0]->IsArrayBuffer() || args[0]->IsSharedArrayBuffer()); - const ValidationResult result = ValidateAscii(args[0]); - if (result.was_detached) { - return node::THROW_ERR_INVALID_STATE( - env, "Cannot validate on a detached buffer"); - } - - args.GetReturnValue().Set(result.is_valid); + args.GetReturnValue().Set(ValidateAscii(args[0])); } static bool FastIsAscii(Local receiver, @@ -1435,14 +1404,7 @@ static bool FastIsAscii(Local receiver, FastApiCallbackOptions& options) { TRACK_V8_FAST_API_CALL("buffer.isAscii"); HandleScope scope(options.isolate); - - const ValidationResult result = ValidateAscii(value); - if (result.was_detached) { - node::THROW_ERR_INVALID_STATE(options.isolate, - "Cannot validate on a detached buffer"); - return false; - } - return result.is_valid; + return ValidateAscii(value); } static CFunction fast_is_ascii(CFunction::Make(FastIsAscii)); diff --git a/test/parallel/test-buffer-isascii.js b/test/parallel/test-buffer-isascii.js index b9468ca13359e6..48cc96a17d61d7 100644 --- a/test/parallel/test-buffer-isascii.js +++ b/test/parallel/test-buffer-isascii.js @@ -30,13 +30,20 @@ assert.strictEqual(isAscii(Buffer.from([])), true); }); { - // Test with detached array buffers - const arrayBuffer = new ArrayBuffer(1024); + // Detached array buffers and views are treated as empty. + const arrayBuffer = new ArrayBuffer(1); + const typedArray = new Uint8Array(arrayBuffer); + typedArray[0] = 0xff; + const inputs = [ + arrayBuffer, + typedArray, + Buffer.from(arrayBuffer), + ]; + for (const input of inputs) { + assert.strictEqual(isAscii(input), false); + } structuredClone(arrayBuffer, { transfer: [arrayBuffer] }); - assert.throws( - () => { isAscii(arrayBuffer); }, - { - code: 'ERR_INVALID_STATE' - } - ); + for (const input of inputs) { + assert.strictEqual(isAscii(input), true); + } } diff --git a/test/parallel/test-buffer-isutf8.js b/test/parallel/test-buffer-isutf8.js index 204db3e6a5fe25..151fc0baf4aefc 100644 --- a/test/parallel/test-buffer-isutf8.js +++ b/test/parallel/test-buffer-isutf8.js @@ -74,13 +74,20 @@ assert.strictEqual(isUtf8(Buffer.from([])), true); }); { - // Test with detached array buffers - const arrayBuffer = new ArrayBuffer(1024); + // Detached array buffers and views are treated as empty. + const arrayBuffer = new ArrayBuffer(1); + const typedArray = new Uint8Array(arrayBuffer); + typedArray[0] = 0xff; + const inputs = [ + arrayBuffer, + typedArray, + Buffer.from(arrayBuffer), + ]; + for (const input of inputs) { + assert.strictEqual(isUtf8(input), false); + } structuredClone(arrayBuffer, { transfer: [arrayBuffer] }); - assert.throws( - () => { isUtf8(arrayBuffer); }, - { - code: 'ERR_INVALID_STATE' - } - ); + for (const input of inputs) { + assert.strictEqual(isUtf8(input), true); + } }