Skip to content

Commit 8a6f12e

Browse files
committed
util: Detect whether the string is one byte representation or not.
References: #56090
1 parent c4aa34a commit 8a6f12e

File tree

5 files changed

+105
-0
lines changed

5 files changed

+105
-0
lines changed

doc/api/v8.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1304,6 +1304,27 @@ setTimeout(() => {
13041304
}, 1000);
13051305
```
13061306

1307+
1308+
## `v8.isOneByteRepresentation(content)`
1309+
1310+
<!-- YAML
1311+
added: REPLACEME
1312+
-->
1313+
1314+
* `content` {string}
1315+
* Returns: {boolean}
1316+
1317+
Returns `true` if `content` uses one byte as the underlying representation Otherwise, returns `false`.
1318+
1319+
```js
1320+
const { isOneByteRepresentation } = require('node:v8');
1321+
1322+
isOneByteRepresentation('hello world');
1323+
// Returns: true
1324+
isOneByteRepresentation('你好');
1325+
// Returns: false
1326+
```
1327+
13071328
[HTML structured clone algorithm]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm
13081329
[Hook Callbacks]: #hook-callbacks
13091330
[V8]: https://developers.google.com/v8/

lib/v8.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ const binding = internalBinding('v8');
104104
const {
105105
cachedDataVersionTag,
106106
setFlagsFromString: _setFlagsFromString,
107+
isStringOneByteRepresentation: _isStringOneByteRepresentation,
107108
updateHeapStatisticsBuffer,
108109
updateHeapSpaceStatisticsBuffer,
109110
updateHeapCodeStatisticsBuffer,
@@ -155,6 +156,17 @@ function setFlagsFromString(flags) {
155156
_setFlagsFromString(flags);
156157
}
157158

159+
/**
160+
* Return whether this string uses one byte as underlying representation or not.
161+
* @param {string} content
162+
* @returns {boolean}
163+
*/
164+
function isStringOneByteRepresentation(content) {
165+
validateString(content, 'content');
166+
return _isStringOneByteRepresentation(content);
167+
}
168+
169+
158170
/**
159171
* Gets the current V8 heap statistics.
160172
* @returns {{
@@ -439,4 +451,5 @@ module.exports = {
439451
startupSnapshot,
440452
setHeapSnapshotNearHeapLimit,
441453
GCProfiler,
454+
isStringOneByteRepresentation,
442455
};

src/node_external_reference.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ namespace node {
1212

1313
using CFunctionCallbackWithOneByteString =
1414
uint32_t (*)(v8::Local<v8::Value>, const v8::FastOneByteString&);
15+
16+
using CFunctionCallbackReturnBool = bool (*)(v8::Local<v8::Value> unused,
17+
v8::Local<v8::Value> receiver);
1518
using CFunctionCallback = void (*)(v8::Local<v8::Value> unused,
1619
v8::Local<v8::Value> receiver);
1720
using CFunctionCallbackReturnDouble =
@@ -90,6 +93,7 @@ class ExternalReferenceRegistry {
9093
#define ALLOWED_EXTERNAL_REFERENCE_TYPES(V) \
9194
V(CFunctionCallback) \
9295
V(CFunctionCallbackWithOneByteString) \
96+
V(CFunctionCallbackReturnBool) \
9397
V(CFunctionCallbackReturnDouble) \
9498
V(CFunctionCallbackReturnInt32) \
9599
V(CFunctionCallbackValueReturnDouble) \

src/node_v8.cc

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
namespace node {
3333
namespace v8_utils {
3434
using v8::Array;
35+
using v8::CFunction;
3536
using v8::Context;
3637
using v8::FunctionCallbackInfo;
3738
using v8::FunctionTemplate;
@@ -238,6 +239,25 @@ void SetFlagsFromString(const FunctionCallbackInfo<Value>& args) {
238239
V8::SetFlagsFromString(*flags, static_cast<size_t>(flags.length()));
239240
}
240241

242+
243+
static void IsStringOneByteRepresentation(
244+
const FunctionCallbackInfo<Value>& args) {
245+
CHECK_EQ(args.Length(), 1);
246+
CHECK(args[0]->IsString());
247+
bool is_one_byte = args[0].As<String>()->IsOneByte();
248+
args.GetReturnValue().Set(is_one_byte);
249+
}
250+
251+
static bool FastIsStringOneByteRepresentation(Local<Value> receiver,
252+
const Local<Value> target) {
253+
CHECK(target->IsString());
254+
return target.As<String>()->IsOneByte();
255+
}
256+
257+
CFunction fast_is_string_one_byte_representation_(
258+
CFunction::Make(FastIsStringOneByteRepresentation));
259+
260+
241261
static const char* GetGCTypeName(v8::GCType gc_type) {
242262
switch (gc_type) {
243263
case v8::GCType::kGCTypeScavenge:
@@ -479,6 +499,13 @@ void Initialize(Local<Object> target,
479499
// Export symbols used by v8.setFlagsFromString()
480500
SetMethod(context, target, "setFlagsFromString", SetFlagsFromString);
481501

502+
// Export symbols used by v8.isStringOneByteRepresentation()
503+
SetFastMethodNoSideEffect(context,
504+
target,
505+
"isStringOneByteRepresentation",
506+
IsStringOneByteRepresentation,
507+
&fast_is_string_one_byte_representation_);
508+
482509
// GCProfiler
483510
Local<FunctionTemplate> t =
484511
NewFunctionTemplate(env->isolate(), GCProfiler::New);
@@ -498,6 +525,9 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
498525
registry->Register(GCProfiler::New);
499526
registry->Register(GCProfiler::Start);
500527
registry->Register(GCProfiler::Stop);
528+
registry->Register(IsStringOneByteRepresentation);
529+
registry->Register(FastIsStringOneByteRepresentation);
530+
registry->Register(fast_is_string_one_byte_representation_.GetTypeInfo());
501531
}
502532

503533
} // namespace v8_utils
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Flags: --expose-internals
2+
'use strict';
3+
require('../common');
4+
const assert = require('assert');
5+
const { isStringOneByteRepresentation } = require('v8');
6+
7+
[
8+
undefined,
9+
null,
10+
false,
11+
5n,
12+
5,
13+
Symbol(),
14+
() => {},
15+
{},
16+
].forEach((value) => {
17+
assert.throws(
18+
() => { isStringOneByteRepresentation(value); },
19+
/The "content" argument must be of type string/
20+
);
21+
});
22+
23+
{
24+
const latin1String = 'hello world!';
25+
// Run this inside a for loop to trigger the fast API
26+
for (let i = 0; i < 10_000; i++) {
27+
assert.strictEqual(isStringOneByteRepresentation(latin1String), true);
28+
}
29+
}
30+
31+
{
32+
const utf16String = '你好😀😃';
33+
// Run this inside a for loop to trigger the fast API
34+
for (let i = 0; i < 10_000; i++) {
35+
assert.strictEqual(isStringOneByteRepresentation(utf16String), false);
36+
}
37+
}

0 commit comments

Comments
 (0)