Skip to content

Commit a8fe2ca

Browse files
committed
buffer: treat detached ArrayBuffers as empty
Treat detached ArrayBuffers and Buffer or TypedArray views backed by them as zero-length inputs in buffer.isUtf8() and buffer.isAscii(). Both functions now return true for these inputs, consistent with other empty inputs. Signed-off-by: Archkon <180910180+Archkon@users.noreply.github.com>
1 parent 7a11a9b commit a8fe2ca

4 files changed

Lines changed: 51 additions & 65 deletions

File tree

doc/api/buffer.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5319,6 +5319,11 @@ npx codemod@latest @nodejs/buffer-atob-btoa
53195319
added:
53205320
- v19.6.0
53215321
- v18.15.0
5322+
changes:
5323+
- version: REPLACEME
5324+
pr-url: https://github.com/nodejs/node/pull/64504
5325+
description: Detached `ArrayBuffer`s and views backed by them are treated
5326+
as empty.
53225327
-->
53235328

53245329
* `input` {Buffer | ArrayBuffer | TypedArray} The input to validate.
@@ -5327,14 +5332,19 @@ added:
53275332
This function returns `true` if `input` contains only valid ASCII-encoded data,
53285333
including the case in which `input` is empty.
53295334

5330-
Throws if the `input` is a detached array buffer.
5335+
A detached `ArrayBuffer`, or a `TypedArray` backed by one, is treated as empty.
53315336

53325337
### `buffer.isUtf8(input)`
53335338

53345339
<!-- YAML
53355340
added:
53365341
- v19.4.0
53375342
- v18.14.0
5343+
changes:
5344+
- version: REPLACEME
5345+
pr-url: https://github.com/nodejs/node/pull/64504
5346+
description: Detached `ArrayBuffer`s and views backed by them are treated
5347+
as empty.
53385348
-->
53395349

53405350
* `input` {Buffer | ArrayBuffer | TypedArray} The input to validate.
@@ -5343,7 +5353,7 @@ added:
53435353
This function returns `true` if `input` contains only valid UTF-8-encoded data,
53445354
including the case in which `input` is empty.
53455355

5346-
Throws if the `input` is a detached array buffer.
5356+
A detached `ArrayBuffer`, or a `TypedArray` backed by one, is treated as empty.
53475357

53485358
### `buffer.INSPECT_MAX_BYTES`
53495359

src/node_buffer.cc

Lines changed: 9 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1360,31 +1360,17 @@ void FastSwap64(Local<Value> receiver,
13601360

13611361
static CFunction fast_swap64(CFunction::Make(FastSwap64));
13621362

1363-
struct ValidationResult {
1364-
bool is_valid;
1365-
bool was_detached;
1366-
};
1367-
1368-
static ValidationResult ValidateUtf8(Local<Value> value) {
1363+
static bool ValidateUtf8(Local<Value> value) {
13691364
ArrayBufferViewContents<char> abv(value);
1370-
bool was_detached = abv.WasDetached();
1371-
return {!was_detached && simdutf::validate_utf8(abv.data(), abv.length()),
1372-
was_detached};
1365+
return abv.length() == 0 || simdutf::validate_utf8(abv.data(), abv.length());
13731366
}
13741367

13751368
static void IsUtf8(const FunctionCallbackInfo<Value>& args) {
1376-
Environment* env = Environment::GetCurrent(args);
13771369
CHECK_EQ(args.Length(), 1);
13781370
CHECK(args[0]->IsTypedArray() || args[0]->IsArrayBuffer() ||
13791371
args[0]->IsSharedArrayBuffer());
13801372

1381-
const ValidationResult result = ValidateUtf8(args[0]);
1382-
if (result.was_detached) {
1383-
return node::THROW_ERR_INVALID_STATE(
1384-
env, "Cannot validate on a detached buffer");
1385-
}
1386-
1387-
args.GetReturnValue().Set(result.is_valid);
1373+
args.GetReturnValue().Set(ValidateUtf8(args[0]));
13881374
}
13891375

13901376
static bool FastIsUtf8(Local<Value> receiver,
@@ -1393,40 +1379,23 @@ static bool FastIsUtf8(Local<Value> receiver,
13931379
FastApiCallbackOptions& options) {
13941380
TRACK_V8_FAST_API_CALL("buffer.isUtf8");
13951381
HandleScope scope(options.isolate);
1396-
1397-
const ValidationResult result = ValidateUtf8(value);
1398-
if (result.was_detached) {
1399-
node::THROW_ERR_INVALID_STATE(options.isolate,
1400-
"Cannot validate on a detached buffer");
1401-
return false;
1402-
}
1403-
return result.is_valid;
1382+
return ValidateUtf8(value);
14041383
}
14051384

14061385
static CFunction fast_is_utf8(CFunction::Make(FastIsUtf8));
14071386

1408-
static ValidationResult ValidateAscii(Local<Value> value) {
1387+
static bool ValidateAscii(Local<Value> value) {
14091388
ArrayBufferViewContents<char> abv(value);
1410-
bool was_detached = abv.WasDetached();
1411-
return {
1412-
!was_detached &&
1413-
!simdutf::validate_ascii_with_errors(abv.data(), abv.length()).error,
1414-
was_detached};
1389+
return abv.length() == 0 ||
1390+
!simdutf::validate_ascii_with_errors(abv.data(), abv.length()).error;
14151391
}
14161392

14171393
static void IsAscii(const FunctionCallbackInfo<Value>& args) {
1418-
Environment* env = Environment::GetCurrent(args);
14191394
CHECK_EQ(args.Length(), 1);
14201395
CHECK(args[0]->IsTypedArray() || args[0]->IsArrayBuffer() ||
14211396
args[0]->IsSharedArrayBuffer());
14221397

1423-
const ValidationResult result = ValidateAscii(args[0]);
1424-
if (result.was_detached) {
1425-
return node::THROW_ERR_INVALID_STATE(
1426-
env, "Cannot validate on a detached buffer");
1427-
}
1428-
1429-
args.GetReturnValue().Set(result.is_valid);
1398+
args.GetReturnValue().Set(ValidateAscii(args[0]));
14301399
}
14311400

14321401
static bool FastIsAscii(Local<Value> receiver,
@@ -1435,14 +1404,7 @@ static bool FastIsAscii(Local<Value> receiver,
14351404
FastApiCallbackOptions& options) {
14361405
TRACK_V8_FAST_API_CALL("buffer.isAscii");
14371406
HandleScope scope(options.isolate);
1438-
1439-
const ValidationResult result = ValidateAscii(value);
1440-
if (result.was_detached) {
1441-
node::THROW_ERR_INVALID_STATE(options.isolate,
1442-
"Cannot validate on a detached buffer");
1443-
return false;
1444-
}
1445-
return result.is_valid;
1407+
return ValidateAscii(value);
14461408
}
14471409

14481410
static CFunction fast_is_ascii(CFunction::Make(FastIsAscii));

test/parallel/test-buffer-isascii.js

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,20 @@ assert.strictEqual(isAscii(Buffer.from([])), true);
3030
});
3131

3232
{
33-
// Test with detached array buffers
34-
const arrayBuffer = new ArrayBuffer(1024);
33+
// Detached array buffers and views are treated as empty.
34+
const arrayBuffer = new ArrayBuffer(1);
35+
const typedArray = new Uint8Array(arrayBuffer);
36+
typedArray[0] = 0xff;
37+
const inputs = [
38+
arrayBuffer,
39+
typedArray,
40+
Buffer.from(arrayBuffer),
41+
];
42+
for (const input of inputs) {
43+
assert.strictEqual(isAscii(input), false);
44+
}
3545
structuredClone(arrayBuffer, { transfer: [arrayBuffer] });
36-
assert.throws(
37-
() => { isAscii(arrayBuffer); },
38-
{
39-
code: 'ERR_INVALID_STATE'
40-
}
41-
);
46+
for (const input of inputs) {
47+
assert.strictEqual(isAscii(input), true);
48+
}
4249
}

test/parallel/test-buffer-isutf8.js

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,20 @@ assert.strictEqual(isUtf8(Buffer.from([])), true);
7474
});
7575

7676
{
77-
// Test with detached array buffers
78-
const arrayBuffer = new ArrayBuffer(1024);
77+
// Detached array buffers and views are treated as empty.
78+
const arrayBuffer = new ArrayBuffer(1);
79+
const typedArray = new Uint8Array(arrayBuffer);
80+
typedArray[0] = 0xff;
81+
const inputs = [
82+
arrayBuffer,
83+
typedArray,
84+
Buffer.from(arrayBuffer),
85+
];
86+
for (const input of inputs) {
87+
assert.strictEqual(isUtf8(input), false);
88+
}
7989
structuredClone(arrayBuffer, { transfer: [arrayBuffer] });
80-
assert.throws(
81-
() => { isUtf8(arrayBuffer); },
82-
{
83-
code: 'ERR_INVALID_STATE'
84-
}
85-
);
90+
for (const input of inputs) {
91+
assert.strictEqual(isUtf8(input), true);
92+
}
8693
}

0 commit comments

Comments
 (0)