Skip to content

Commit 6bb4020

Browse files
committed
buffer: add fast api for isAscii & isUtf8
1 parent c1b15a4 commit 6bb4020

File tree

3 files changed

+104
-2
lines changed

3 files changed

+104
-2
lines changed

src/node_buffer.cc

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1175,6 +1175,26 @@ void Swap64(const FunctionCallbackInfo<Value>& args) {
11751175
args.GetReturnValue().Set(args[0]);
11761176
}
11771177

1178+
bool FastIsUtf8(v8::Local<v8::Value>,
1179+
const v8::FastApiTypedArray<uint8_t>& buffer) {
1180+
uint8_t* buffer_data;
1181+
CHECK(buffer.getStorageIfAligned(&buffer_data));
1182+
TRACK_V8_FAST_API_CALL("buffer.isUtf8");
1183+
return simdutf::validate_utf8(reinterpret_cast<const char*>(buffer_data), buffer.length());
1184+
}
1185+
1186+
static v8::CFunction fast_is_utf8(v8::CFunction::Make(FastIsUtf8));
1187+
1188+
bool FastIsAscii(v8::Local<v8::Value>,
1189+
const v8::FastApiTypedArray<uint8_t>& buffer) {
1190+
uint8_t* buffer_data;
1191+
CHECK(buffer.getStorageIfAligned(&buffer_data));
1192+
TRACK_V8_FAST_API_CALL("buffer.isAscii");
1193+
return simdutf::validate_ascii(reinterpret_cast<const char*>(buffer_data), buffer.length());
1194+
}
1195+
1196+
static v8::CFunction fast_is_ascii(v8::CFunction::Make(FastIsAscii));
1197+
11781198
static void IsUtf8(const FunctionCallbackInfo<Value>& args) {
11791199
Environment* env = Environment::GetCurrent(args);
11801200
CHECK_EQ(args.Length(), 1);
@@ -1565,8 +1585,8 @@ void Initialize(Local<Object> target,
15651585
SetMethod(context, target, "swap32", Swap32);
15661586
SetMethod(context, target, "swap64", Swap64);
15671587

1568-
SetMethodNoSideEffect(context, target, "isUtf8", IsUtf8);
1569-
SetMethodNoSideEffect(context, target, "isAscii", IsAscii);
1588+
SetFastMethodNoSideEffect(context, target, "isUtf8", IsUtf8, &fast_is_utf8);
1589+
SetFastMethodNoSideEffect(context, target, "isAscii", IsAscii, &fast_is_ascii);
15701590

15711591
target
15721592
->Set(context,
@@ -1673,6 +1693,11 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
16731693

16741694
registry->Register(Atob);
16751695
registry->Register(Btoa);
1696+
1697+
registry->Register(FastIsUtf8);
1698+
registry->Register(fast_is_utf8.GetTypeInfo());
1699+
registry->Register(FastIsAscii);
1700+
registry->Register(fast_is_ascii.GetTypeInfo());
16761701
}
16771702

16781703
} // namespace Buffer

src/node_external_reference.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ using CFunctionCallbackWithInt64 = void (*)(v8::Local<v8::Object> unused,
4242
using CFunctionCallbackWithBool = void (*)(v8::Local<v8::Object> unused,
4343
v8::Local<v8::Object> receiver,
4444
bool);
45+
using CFunctionFastIsUtf8 = bool (*)(v8::Local<v8::Value>,
46+
const v8::FastApiTypedArray<uint8_t>& buffer);
4547
using CFunctionCallbackWithString =
4648
bool (*)(v8::Local<v8::Value>, const v8::FastOneByteString& input);
4749
using CFunctionCallbackWithStrings =
@@ -109,6 +111,7 @@ class ExternalReferenceRegistry {
109111
V(CFunctionCallbackValueReturnDoubleUnusedReceiver) \
110112
V(CFunctionCallbackWithInt64) \
111113
V(CFunctionCallbackWithBool) \
114+
V(CFunctionFastIsUtf8) \
112115
V(CFunctionCallbackWithString) \
113116
V(CFunctionCallbackWithStrings) \
114117
V(CFunctionCallbackWithTwoUint8Arrays) \
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
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 { internalBinding } = require('internal/test/binding');
7+
8+
// Get direct access to the buffer validation methods
9+
const buffer = require('buffer');
10+
11+
// Create test buffers
12+
const utf8Buffer = Buffer.from('Hello, 世界!'); // Valid UTF-8 with actual Unicode characters
13+
const asciiBuffer = Buffer.from('Hello, World!'); // Valid ASCII
14+
const nonUtf8Buffer = Buffer.from([0xFF, 0xFF, 0xFF]); // Invalid UTF-8
15+
const nonAsciiBuffer = Buffer.from([0x80, 0x90, 0xA0]); // Invalid ASCII
16+
17+
// Test basic functionality for isUtf8
18+
assert.strictEqual(buffer.isUtf8(utf8Buffer), true);
19+
assert.strictEqual(buffer.isUtf8(nonUtf8Buffer), false);
20+
21+
// Test basic functionality for isAscii
22+
assert.strictEqual(buffer.isAscii(asciiBuffer), true);
23+
assert.strictEqual(buffer.isAscii(nonAsciiBuffer), false);
24+
25+
// Test detached buffers
26+
const detachedBuffer = new ArrayBuffer(10);
27+
try {
28+
detachedBuffer.detach();
29+
} catch (e) {
30+
console.log('Skipping detached buffer tests - detach not supported');
31+
}
32+
33+
if (detachedBuffer.detached) {
34+
const typedArray = new Uint8Array(detachedBuffer);
35+
36+
assert.throws(() => {
37+
buffer.isUtf8(typedArray);
38+
}, {
39+
name: 'Error',
40+
code: 'ERR_INVALID_STATE'
41+
});
42+
43+
assert.throws(() => {
44+
buffer.isAscii(typedArray);
45+
}, {
46+
name: 'Error',
47+
code: 'ERR_INVALID_STATE'
48+
});
49+
}
50+
51+
// Test optimization and fast API paths
52+
function testFastPaths() {
53+
// Test both valid and invalid cases to ensure both paths are optimized
54+
buffer.isUtf8(utf8Buffer);
55+
buffer.isUtf8(nonUtf8Buffer);
56+
buffer.isAscii(asciiBuffer);
57+
buffer.isAscii(nonAsciiBuffer);
58+
}
59+
60+
// Since we want to optimize the C++ methods, we need to prepare them
61+
// through their JS wrappers
62+
eval('%PrepareFunctionForOptimization(buffer.isUtf8)');
63+
eval('%PrepareFunctionForOptimization(buffer.isAscii)');
64+
testFastPaths();
65+
eval('%OptimizeFunctionOnNextCall(buffer.isUtf8)');
66+
eval('%OptimizeFunctionOnNextCall(buffer.isAscii)');
67+
testFastPaths();
68+
69+
// Verify fast API calls were made if running in debug mode
70+
if (common.isDebug) {
71+
const { getV8FastApiCallCount } = internalBinding('debug');
72+
assert.strictEqual(getV8FastApiCallCount('buffer.isUtf8'), 2); // Called twice in testFastPaths
73+
assert.strictEqual(getV8FastApiCallCount('buffer.isAscii'), 2); // Called twice in testFastPaths
74+
}

0 commit comments

Comments
 (0)