Skip to content

Commit 8432a57

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

File tree

3 files changed

+107
-2
lines changed

3 files changed

+107
-2
lines changed

src/node_buffer.cc

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1175,6 +1175,28 @@ 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),
1184+
buffer.length());
1185+
}
1186+
1187+
static v8::CFunction fast_is_utf8(v8::CFunction::Make(FastIsUtf8));
1188+
1189+
bool FastIsAscii(v8::Local<v8::Value>,
1190+
const v8::FastApiTypedArray<uint8_t>& buffer) {
1191+
uint8_t* buffer_data;
1192+
CHECK(buffer.getStorageIfAligned(&buffer_data));
1193+
TRACK_V8_FAST_API_CALL("buffer.isAscii");
1194+
return simdutf::validate_ascii(reinterpret_cast<const char*>(buffer_data),
1195+
buffer.length());
1196+
}
1197+
1198+
static v8::CFunction fast_is_ascii(v8::CFunction::Make(FastIsAscii));
1199+
11781200
static void IsUtf8(const FunctionCallbackInfo<Value>& args) {
11791201
Environment* env = Environment::GetCurrent(args);
11801202
CHECK_EQ(args.Length(), 1);
@@ -1565,8 +1587,9 @@ void Initialize(Local<Object> target,
15651587
SetMethod(context, target, "swap32", Swap32);
15661588
SetMethod(context, target, "swap64", Swap64);
15671589

1568-
SetMethodNoSideEffect(context, target, "isUtf8", IsUtf8);
1569-
SetMethodNoSideEffect(context, target, "isAscii", IsAscii);
1590+
SetFastMethodNoSideEffect(context, target, "isUtf8", IsUtf8, &fast_is_utf8);
1591+
SetFastMethodNoSideEffect(
1592+
context, target, "isAscii", IsAscii, &fast_is_ascii);
15701593

15711594
target
15721595
->Set(context,
@@ -1673,6 +1696,11 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
16731696

16741697
registry->Register(Atob);
16751698
registry->Register(Btoa);
1699+
1700+
registry->Register(FastIsUtf8);
1701+
registry->Register(fast_is_utf8.GetTypeInfo());
1702+
registry->Register(FastIsAscii);
1703+
registry->Register(fast_is_ascii.GetTypeInfo());
16761704
}
16771705

16781706
} // 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 (*)(
46+
v8::Local<v8::Value>, 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)