Skip to content

Commit 9929ec2

Browse files
committed
buffer: add fast api for isUtf8 and isAscii
1 parent 47d2d62 commit 9929ec2

2 files changed

Lines changed: 98 additions & 9 deletions

File tree

src/node_buffer.cc

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

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

1363+
static bool ValidateUtf8(Local<Value> value, bool* was_detached) {
1364+
ArrayBufferViewContents<char> abv(value);
1365+
*was_detached = abv.WasDetached();
1366+
return !*was_detached && simdutf::validate_utf8(abv.data(), abv.length());
1367+
}
1368+
13631369
static void IsUtf8(const FunctionCallbackInfo<Value>& args) {
13641370
Environment* env = Environment::GetCurrent(args);
13651371
CHECK_EQ(args.Length(), 1);
13661372
CHECK(args[0]->IsTypedArray() || args[0]->IsArrayBuffer() ||
13671373
args[0]->IsSharedArrayBuffer());
1368-
ArrayBufferViewContents<char> abv(args[0]);
13691374

1370-
if (abv.WasDetached()) {
1375+
bool was_detached;
1376+
const bool result = ValidateUtf8(args[0], &was_detached);
1377+
if (was_detached) {
13711378
return node::THROW_ERR_INVALID_STATE(
13721379
env, "Cannot validate on a detached buffer");
13731380
}
13741381

1375-
args.GetReturnValue().Set(simdutf::validate_utf8(abv.data(), abv.length()));
1382+
args.GetReturnValue().Set(result);
1383+
}
1384+
1385+
static bool FastIsUtf8(Local<Value> receiver,
1386+
Local<Value> value,
1387+
// NOLINTNEXTLINE(runtime/references)
1388+
FastApiCallbackOptions& options) {
1389+
TRACK_V8_FAST_API_CALL("buffer.isUtf8");
1390+
HandleScope scope(options.isolate);
1391+
1392+
bool was_detached;
1393+
const bool result = ValidateUtf8(value, &was_detached);
1394+
if (was_detached) {
1395+
node::THROW_ERR_INVALID_STATE(options.isolate,
1396+
"Cannot validate on a detached buffer");
1397+
return false;
1398+
}
1399+
return result;
1400+
}
1401+
1402+
static CFunction fast_is_utf8(CFunction::Make(FastIsUtf8));
1403+
1404+
static bool ValidateAscii(Local<Value> value, bool* was_detached) {
1405+
ArrayBufferViewContents<char> abv(value);
1406+
*was_detached = abv.WasDetached();
1407+
return !*was_detached &&
1408+
!simdutf::validate_ascii_with_errors(abv.data(), abv.length()).error;
13761409
}
13771410

13781411
static void IsAscii(const FunctionCallbackInfo<Value>& args) {
13791412
Environment* env = Environment::GetCurrent(args);
13801413
CHECK_EQ(args.Length(), 1);
13811414
CHECK(args[0]->IsTypedArray() || args[0]->IsArrayBuffer() ||
13821415
args[0]->IsSharedArrayBuffer());
1383-
ArrayBufferViewContents<char> abv(args[0]);
13841416

1385-
if (abv.WasDetached()) {
1417+
bool was_detached;
1418+
const bool result = ValidateAscii(args[0], &was_detached);
1419+
if (was_detached) {
13861420
return node::THROW_ERR_INVALID_STATE(
13871421
env, "Cannot validate on a detached buffer");
13881422
}
13891423

1390-
args.GetReturnValue().Set(
1391-
!simdutf::validate_ascii_with_errors(abv.data(), abv.length()).error);
1424+
args.GetReturnValue().Set(result);
1425+
}
1426+
1427+
static bool FastIsAscii(Local<Value> receiver,
1428+
Local<Value> value,
1429+
// NOLINTNEXTLINE(runtime/references)
1430+
FastApiCallbackOptions& options) {
1431+
TRACK_V8_FAST_API_CALL("buffer.isAscii");
1432+
HandleScope scope(options.isolate);
1433+
1434+
bool was_detached;
1435+
const bool result = ValidateAscii(value, &was_detached);
1436+
if (was_detached) {
1437+
node::THROW_ERR_INVALID_STATE(options.isolate,
1438+
"Cannot validate on a detached buffer");
1439+
return false;
1440+
}
1441+
return result;
13921442
}
13931443

1444+
static CFunction fast_is_ascii(CFunction::Make(FastIsAscii));
1445+
13941446
void SetBufferPrototype(const FunctionCallbackInfo<Value>& args) {
13951447
Realm* realm = Realm::GetCurrent(args);
13961448

@@ -1762,8 +1814,9 @@ void Initialize(Local<Object> target,
17621814
SetFastMethod(context, target, "swap32", Swap32, &fast_swap32);
17631815
SetFastMethod(context, target, "swap64", Swap64, &fast_swap64);
17641816

1765-
SetMethodNoSideEffect(context, target, "isUtf8", IsUtf8);
1766-
SetMethodNoSideEffect(context, target, "isAscii", IsAscii);
1817+
SetFastMethodNoSideEffect(context, target, "isUtf8", IsUtf8, &fast_is_utf8);
1818+
SetFastMethodNoSideEffect(
1819+
context, target, "isAscii", IsAscii, &fast_is_ascii);
17671820

17681821
target
17691822
->Set(context,
@@ -1836,7 +1889,9 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
18361889
registry->Register(fast_swap64);
18371890

18381891
registry->Register(IsUtf8);
1892+
registry->Register(fast_is_utf8);
18391893
registry->Register(IsAscii);
1894+
registry->Register(fast_is_ascii);
18401895

18411896
registry->Register(StringSlice<ASCII>);
18421897
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)