Skip to content

Commit 1f41306

Browse files
committed
src: add encoding_methods with fast api
1 parent 50fb246 commit 1f41306

File tree

10 files changed

+227
-82
lines changed

10 files changed

+227
-82
lines changed

benchmark/util/text-encoder.js

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,27 @@
22

33
const common = require('../common.js');
44

5-
const BASE = 'string\ud801';
6-
75
const bench = common.createBenchmark(main, {
8-
len: [256, 1024, 1024 * 32],
6+
len: [0, 256, 1024, 1024 * 32],
97
n: [1e4],
8+
type: ['v8-one-byte-string', 'v8-two-byte-string'],
109
op: ['encode', 'encodeInto']
1110
});
1211

13-
function main({ n, op, len }) {
12+
function main({ n, op, len, type }) {
1413
const encoder = new TextEncoder();
15-
const input = BASE.repeat(len);
14+
let base = '';
15+
16+
switch (type) {
17+
case 'v8-one-byte-string':
18+
base = 'a';
19+
break;
20+
case 'v8-two-byte-string':
21+
base = 'ğ';
22+
break;
23+
}
24+
25+
const input = base.repeat(len);
1626
const subarray = new Uint8Array(len);
1727

1828
bench.start();

lib/internal/bootstrap/loaders.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ const internalBindingAllowlist = new SafeSet([
8585
'constants',
8686
'contextify',
8787
'crypto',
88+
'encoding_methods',
8889
'fs',
8990
'fs_event_wrap',
9091
'http_parser',

lib/internal/encoding.js

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,14 @@ const {
5151
} = require('internal/validators');
5252

5353
const {
54-
encodeInto,
55-
encodeUtf8String,
5654
decodeUTF8,
5755
} = internalBinding('buffer');
5856

57+
const {
58+
encodeUtf8,
59+
encodeIntoUtf8,
60+
} = internalBinding('encoding_methods');
61+
5962
let Buffer;
6063
function lazyBuffer() {
6164
if (Buffer === undefined)
@@ -337,15 +340,22 @@ class TextEncoder {
337340

338341
encode(input = '') {
339342
validateEncoder(this);
340-
return encodeUtf8String(`${input}`);
343+
input = `${input}`;
344+
if (input.length > 0) {
345+
return encodeUtf8(input);
346+
}
347+
return new Uint8Array([]);
341348
}
342349

343350
encodeInto(src, dest) {
344351
validateEncoder(this);
345352
validateString(src, 'src');
346353
if (!dest || !isUint8Array(dest))
347354
throw new ERR_INVALID_ARG_TYPE('dest', 'Uint8Array', dest);
348-
encodeInto(src, dest, encodeIntoResults);
355+
if (src.length === 0) {
356+
return { read: 0, written: 0 };
357+
}
358+
encodeIntoUtf8(src, dest, encodeIntoResults);
349359
return { read: encodeIntoResults[0], written: encodeIntoResults[1] };
350360
}
351361

node.gyp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,7 @@
498498
'src/node_contextify.cc',
499499
'src/node_credentials.cc',
500500
'src/node_dir.cc',
501+
'src/node_encoding.cc',
501502
'src/node_env_var.cc',
502503
'src/node_errors.cc',
503504
'src/node_external_reference.cc',

src/node_binding.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
V(contextify) \
4444
V(credentials) \
4545
V(errors) \
46+
V(encoding_methods) \
4647
V(fs) \
4748
V(fs_dir) \
4849
V(fs_event_wrap) \

src/node_buffer.cc

Lines changed: 0 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1146,73 +1146,6 @@ void Swap64(const FunctionCallbackInfo<Value>& args) {
11461146
args.GetReturnValue().Set(args[0]);
11471147
}
11481148

1149-
1150-
// Encode a single string to a UTF-8 Uint8Array (not Buffer).
1151-
// Used in TextEncoder.prototype.encode.
1152-
static void EncodeUtf8String(const FunctionCallbackInfo<Value>& args) {
1153-
Environment* env = Environment::GetCurrent(args);
1154-
Isolate* isolate = env->isolate();
1155-
CHECK_GE(args.Length(), 1);
1156-
CHECK(args[0]->IsString());
1157-
1158-
Local<String> str = args[0].As<String>();
1159-
size_t length = str->Utf8Length(isolate);
1160-
1161-
Local<ArrayBuffer> ab;
1162-
{
1163-
NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
1164-
std::unique_ptr<BackingStore> bs =
1165-
ArrayBuffer::NewBackingStore(isolate, length);
1166-
1167-
CHECK(bs);
1168-
1169-
str->WriteUtf8(isolate,
1170-
static_cast<char*>(bs->Data()),
1171-
-1, // We are certain that `data` is sufficiently large
1172-
nullptr,
1173-
String::NO_NULL_TERMINATION | String::REPLACE_INVALID_UTF8);
1174-
1175-
ab = ArrayBuffer::New(isolate, std::move(bs));
1176-
}
1177-
1178-
auto array = Uint8Array::New(ab, 0, length);
1179-
args.GetReturnValue().Set(array);
1180-
}
1181-
1182-
1183-
static void EncodeInto(const FunctionCallbackInfo<Value>& args) {
1184-
Environment* env = Environment::GetCurrent(args);
1185-
Isolate* isolate = env->isolate();
1186-
CHECK_GE(args.Length(), 3);
1187-
CHECK(args[0]->IsString());
1188-
CHECK(args[1]->IsUint8Array());
1189-
CHECK(args[2]->IsUint32Array());
1190-
1191-
Local<String> source = args[0].As<String>();
1192-
1193-
Local<Uint8Array> dest = args[1].As<Uint8Array>();
1194-
Local<ArrayBuffer> buf = dest->Buffer();
1195-
char* write_result = static_cast<char*>(buf->Data()) + dest->ByteOffset();
1196-
size_t dest_length = dest->ByteLength();
1197-
1198-
// results = [ read, written ]
1199-
Local<Uint32Array> result_arr = args[2].As<Uint32Array>();
1200-
uint32_t* results = reinterpret_cast<uint32_t*>(
1201-
static_cast<char*>(result_arr->Buffer()->Data()) +
1202-
result_arr->ByteOffset());
1203-
1204-
int nchars;
1205-
int written = source->WriteUtf8(
1206-
isolate,
1207-
write_result,
1208-
dest_length,
1209-
&nchars,
1210-
String::NO_NULL_TERMINATION | String::REPLACE_INVALID_UTF8);
1211-
results[0] = nchars;
1212-
results[1] = written;
1213-
}
1214-
1215-
12161149
void SetBufferPrototype(const FunctionCallbackInfo<Value>& args) {
12171150
Environment* env = Environment::GetCurrent(args);
12181151

@@ -1344,9 +1277,6 @@ void Initialize(Local<Object> target,
13441277
SetMethod(context, target, "swap32", Swap32);
13451278
SetMethod(context, target, "swap64", Swap64);
13461279

1347-
SetMethod(context, target, "encodeInto", EncodeInto);
1348-
SetMethodNoSideEffect(context, target, "encodeUtf8String", EncodeUtf8String);
1349-
13501280
target
13511281
->Set(context,
13521282
FIXED_ONE_BYTE_STRING(isolate, "kMaxLength"),
@@ -1399,9 +1329,6 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
13991329
registry->Register(Swap32);
14001330
registry->Register(Swap64);
14011331

1402-
registry->Register(EncodeInto);
1403-
registry->Register(EncodeUtf8String);
1404-
14051332
registry->Register(StringSlice<ASCII>);
14061333
registry->Register(StringSlice<BASE64>);
14071334
registry->Register(StringSlice<BASE64URL>);

src/node_encoding.cc

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
#include "env-inl.h"
2+
#include "node.h"
3+
#include "node_external_reference.h"
4+
#include "node_internals.h"
5+
#include "util-inl.h"
6+
#include "v8-fast-api-calls.h"
7+
#include "v8.h"
8+
9+
#if defined(NODE_HAVE_I18N_SUPPORT)
10+
#include <unicode/utf8.h>
11+
#endif // NODE_HAVE_I18N_SUPPORT
12+
13+
namespace node {
14+
15+
using v8::ArrayBuffer;
16+
using v8::BackingStore;
17+
using v8::CFunction;
18+
using v8::Context;
19+
using v8::FastApiCallbackOptions;
20+
using v8::FastApiTypedArray;
21+
using v8::FastOneByteString;
22+
using v8::FunctionCallbackInfo;
23+
using v8::Isolate;
24+
using v8::Local;
25+
using v8::Object;
26+
using v8::String;
27+
using v8::Uint32Array;
28+
using v8::Uint8Array;
29+
using v8::Value;
30+
31+
namespace encoding_methods {
32+
33+
static void EncodeUtf8(const FunctionCallbackInfo<Value>& args) {
34+
Environment* env = Environment::GetCurrent(args);
35+
Isolate* isolate = env->isolate();
36+
CHECK_GE(args.Length(), 1);
37+
CHECK(args[0]->IsString());
38+
39+
Local<String> str = args[0].As<String>();
40+
size_t length = str->Utf8Length(isolate);
41+
42+
Local<ArrayBuffer> ab;
43+
{
44+
NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
45+
std::unique_ptr<BackingStore> bs =
46+
ArrayBuffer::NewBackingStore(isolate, length);
47+
48+
CHECK(bs);
49+
50+
str->WriteUtf8(isolate,
51+
static_cast<char*>(bs->Data()),
52+
-1, // We are certain that `data` is sufficiently large
53+
nullptr,
54+
String::NO_NULL_TERMINATION | String::REPLACE_INVALID_UTF8);
55+
56+
ab = ArrayBuffer::New(isolate, std::move(bs));
57+
}
58+
59+
args.GetReturnValue().Set(Uint8Array::New(ab, 0, length));
60+
}
61+
62+
static void EncodeIntoUtf8(const FunctionCallbackInfo<Value>& args) {
63+
Environment* env = Environment::GetCurrent(args);
64+
Isolate* isolate = env->isolate();
65+
CHECK_GE(args.Length(), 3);
66+
CHECK(args[0]->IsString());
67+
CHECK(args[1]->IsUint8Array());
68+
CHECK(args[2]->IsUint32Array());
69+
70+
Local<String> source = args[0].As<String>();
71+
72+
Local<Uint8Array> dest = args[1].As<Uint8Array>();
73+
Local<ArrayBuffer> buf = dest->Buffer();
74+
char* write_result = static_cast<char*>(buf->Data()) + dest->ByteOffset();
75+
size_t dest_length = dest->ByteLength();
76+
77+
// results = [ read, written ]
78+
Local<Uint32Array> result_arr = args[2].As<Uint32Array>();
79+
uint32_t* results = reinterpret_cast<uint32_t*>(
80+
static_cast<char*>(result_arr->Buffer()->Data()) +
81+
result_arr->ByteOffset());
82+
83+
int nchars;
84+
int written = source->WriteUtf8(
85+
isolate,
86+
write_result,
87+
dest_length,
88+
&nchars,
89+
String::NO_NULL_TERMINATION | String::REPLACE_INVALID_UTF8);
90+
results[0] = nchars;
91+
results[1] = written;
92+
}
93+
94+
static void FastEncodeIntoUtf8(
95+
Local<Value> receiver,
96+
const FastOneByteString& source,
97+
const FastApiTypedArray<uint8_t>& destination,
98+
const FastApiTypedArray<uint32_t>& result,
99+
FastApiCallbackOptions& options) { // NOLINT(runtime/references)
100+
#if defined(NODE_HAVE_I18N_SUPPORT)
101+
uint8_t* destination_data;
102+
CHECK(destination.getStorageIfAligned(&destination_data));
103+
104+
uint32_t* results;
105+
CHECK(result.getStorageIfAligned(&results));
106+
107+
size_t destination_length = destination.length();
108+
size_t source_length = source.length;
109+
size_t written = std::min(source_length, destination_length);
110+
111+
U8_GET_UNSAFE(source.data, 0, *results);
112+
results[0] = source_length;
113+
results[1] = written;
114+
#else
115+
options.fallback = true;
116+
#endif // NODE_HAVE_I18N_SUPPORT
117+
}
118+
119+
CFunction fast_encode_into_utf8_(CFunction::Make(FastEncodeIntoUtf8));
120+
121+
static void Initialize(Local<Object> target,
122+
Local<Value> unused,
123+
Local<Context> context,
124+
void* priv) {
125+
SetMethodNoSideEffect(context, target, "encodeUtf8", EncodeUtf8);
126+
SetFastMethod(context,
127+
target,
128+
"encodeIntoUtf8",
129+
EncodeIntoUtf8,
130+
&fast_encode_into_utf8_);
131+
}
132+
133+
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
134+
registry->Register(EncodeUtf8);
135+
136+
registry->Register(EncodeIntoUtf8);
137+
registry->Register(FastEncodeIntoUtf8);
138+
registry->Register(fast_encode_into_utf8_.GetTypeInfo());
139+
}
140+
141+
} // namespace encoding_methods
142+
} // namespace node
143+
144+
NODE_BINDING_CONTEXT_AWARE_INTERNAL(encoding_methods,
145+
node::encoding_methods::Initialize)
146+
NODE_BINDING_EXTERNAL_REFERENCE(
147+
encoding_methods, node::encoding_methods::RegisterExternalReferences)

src/node_external_reference.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,14 @@
1010

1111
namespace node {
1212

13+
// TODO(anonrig): Find a good way of reusing existing types for fast api usages.
1314
using CFunctionCallback = void (*)(v8::Local<v8::Value> receiver);
15+
using CFunctionCallbackWithInput = void (*)(
16+
v8::Local<v8::Value> receiver,
17+
const v8::FastOneByteString& source,
18+
const v8::FastApiTypedArray<uint8_t>& destination,
19+
const v8::FastApiTypedArray<uint32_t>& result,
20+
v8::FastApiCallbackOptions& options); // NOLINT(runtime/references)
1421

1522
// This class manages the external references from the V8 heap
1623
// to the C++ addresses in Node.js.
@@ -20,6 +27,7 @@ class ExternalReferenceRegistry {
2027

2128
#define ALLOWED_EXTERNAL_REFERENCE_TYPES(V) \
2229
V(CFunctionCallback) \
30+
V(CFunctionCallbackWithInput) \
2331
V(const v8::CFunctionInfo*) \
2432
V(v8::FunctionCallback) \
2533
V(v8::AccessorGetterCallback) \
@@ -67,6 +75,7 @@ class ExternalReferenceRegistry {
6775
V(credentials) \
6876
V(env_var) \
6977
V(errors) \
78+
V(encoding_methods) \
7079
V(fs) \
7180
V(fs_dir) \
7281
V(fs_event_wrap) \

test/parallel/test-bootstrap-modules.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ const expectedModules = new Set([
1818
'Internal Binding contextify',
1919
'Internal Binding credentials',
2020
'Internal Binding errors',
21+
'Internal Binding encoding_methods',
2122
'Internal Binding fs_dir',
2223
'Internal Binding fs_event_wrap',
2324
'Internal Binding fs',

0 commit comments

Comments
 (0)