Skip to content

Commit 6d14279

Browse files
gurgundayrichardlau
authored andcommitted
buffer: add fast api for isUtf8 and isAscii
Signed-off-by: Gürgün Dayıoğlu <hey@gurgun.day> PR-URL: #64169 Reviewed-By: Daniel Lemire <daniel@lemire.me> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net>
1 parent 05eae28 commit 6d14279

2 files changed

Lines changed: 102 additions & 9 deletions

File tree

src/node_buffer.cc

Lines changed: 68 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1360,37 +1360,93 @@ 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) {
1369+
ArrayBufferViewContents<char> abv(value);
1370+
bool was_detached = abv.WasDetached();
1371+
return {!was_detached && simdutf::validate_utf8(abv.data(), abv.length()),
1372+
was_detached};
1373+
}
1374+
13631375
static void IsUtf8(const FunctionCallbackInfo<Value>& args) {
13641376
Environment* env = Environment::GetCurrent(args);
13651377
CHECK_EQ(args.Length(), 1);
13661378
CHECK(args[0]->IsTypedArray() || args[0]->IsArrayBuffer() ||
13671379
args[0]->IsSharedArrayBuffer());
1368-
ArrayBufferViewContents<char> abv(args[0]);
13691380

1370-
if (abv.WasDetached()) {
1381+
const ValidationResult result = ValidateUtf8(args[0]);
1382+
if (result.was_detached) {
13711383
return node::THROW_ERR_INVALID_STATE(
13721384
env, "Cannot validate on a detached buffer");
13731385
}
13741386

1375-
args.GetReturnValue().Set(simdutf::validate_utf8(abv.data(), abv.length()));
1387+
args.GetReturnValue().Set(result.is_valid);
1388+
}
1389+
1390+
static bool FastIsUtf8(Local<Value> receiver,
1391+
Local<Value> value,
1392+
// NOLINTNEXTLINE(runtime/references)
1393+
FastApiCallbackOptions& options) {
1394+
TRACK_V8_FAST_API_CALL("buffer.isUtf8");
1395+
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;
1404+
}
1405+
1406+
static CFunction fast_is_utf8(CFunction::Make(FastIsUtf8));
1407+
1408+
static ValidationResult ValidateAscii(Local<Value> value) {
1409+
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};
13761415
}
13771416

13781417
static void IsAscii(const FunctionCallbackInfo<Value>& args) {
13791418
Environment* env = Environment::GetCurrent(args);
13801419
CHECK_EQ(args.Length(), 1);
13811420
CHECK(args[0]->IsTypedArray() || args[0]->IsArrayBuffer() ||
13821421
args[0]->IsSharedArrayBuffer());
1383-
ArrayBufferViewContents<char> abv(args[0]);
13841422

1385-
if (abv.WasDetached()) {
1423+
const ValidationResult result = ValidateAscii(args[0]);
1424+
if (result.was_detached) {
13861425
return node::THROW_ERR_INVALID_STATE(
13871426
env, "Cannot validate on a detached buffer");
13881427
}
13891428

1390-
args.GetReturnValue().Set(
1391-
!simdutf::validate_ascii_with_errors(abv.data(), abv.length()).error);
1429+
args.GetReturnValue().Set(result.is_valid);
13921430
}
13931431

1432+
static bool FastIsAscii(Local<Value> receiver,
1433+
Local<Value> value,
1434+
// NOLINTNEXTLINE(runtime/references)
1435+
FastApiCallbackOptions& options) {
1436+
TRACK_V8_FAST_API_CALL("buffer.isAscii");
1437+
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;
1446+
}
1447+
1448+
static CFunction fast_is_ascii(CFunction::Make(FastIsAscii));
1449+
13941450
void SetBufferPrototype(const FunctionCallbackInfo<Value>& args) {
13951451
Realm* realm = Realm::GetCurrent(args);
13961452

@@ -1762,8 +1818,9 @@ void Initialize(Local<Object> target,
17621818
SetFastMethod(context, target, "swap32", Swap32, &fast_swap32);
17631819
SetFastMethod(context, target, "swap64", Swap64, &fast_swap64);
17641820

1765-
SetMethodNoSideEffect(context, target, "isUtf8", IsUtf8);
1766-
SetMethodNoSideEffect(context, target, "isAscii", IsAscii);
1821+
SetFastMethodNoSideEffect(context, target, "isUtf8", IsUtf8, &fast_is_utf8);
1822+
SetFastMethodNoSideEffect(
1823+
context, target, "isAscii", IsAscii, &fast_is_ascii);
17671824

17681825
target
17691826
->Set(context,
@@ -1836,7 +1893,9 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
18361893
registry->Register(fast_swap64);
18371894

18381895
registry->Register(IsUtf8);
1896+
registry->Register(fast_is_utf8);
18391897
registry->Register(IsAscii);
1898+
registry->Register(fast_is_ascii);
18401899

18411900
registry->Register(StringSlice<ASCII>);
18421901
registry->Register(StringSlice<BASE64>);
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Flags: --expose-internals --no-warnings --allow-natives-syntax
2+
'use strict';
3+
4+
const common = require('../common');
5+
const assert = require('assert');
6+
const { Buffer, isAscii, isUtf8 } = require('buffer');
7+
8+
const ascii = Buffer.from('hello');
9+
const utf8 = Buffer.from('hello \xc4\x9f');
10+
11+
function testFastIsAscii() {
12+
assert.strictEqual(isAscii(ascii), true);
13+
}
14+
15+
function testFastIsUtf8() {
16+
assert.strictEqual(isUtf8(utf8), true);
17+
}
18+
19+
eval('%PrepareFunctionForOptimization(isAscii)');
20+
testFastIsAscii();
21+
eval('%OptimizeFunctionOnNextCall(isAscii)');
22+
testFastIsAscii();
23+
24+
eval('%PrepareFunctionForOptimization(isUtf8)');
25+
testFastIsUtf8();
26+
eval('%OptimizeFunctionOnNextCall(isUtf8)');
27+
testFastIsUtf8();
28+
29+
if (common.isDebug) {
30+
const { internalBinding } = require('internal/test/binding');
31+
const { getV8FastApiCallCount } = internalBinding('debug');
32+
assert.strictEqual(getV8FastApiCallCount('buffer.isAscii'), 1);
33+
assert.strictEqual(getV8FastApiCallCount('buffer.isUtf8'), 1);
34+
}

0 commit comments

Comments
 (0)