Skip to content

Commit 1250410

Browse files
anonrigaddaleax
andcommitted
src: add encoding_methods with fast api
Co-authored-by: Anna Henningsen <anna@addaleax.net>
1 parent 50fb246 commit 1250410

10 files changed

+243
-82
lines changed

benchmark/util/text-encoder.js

+15-5
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

+1
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

+7-4
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,15 @@ class TextEncoder {
337340

338341
encode(input = '') {
339342
validateEncoder(this);
340-
return encodeUtf8String(`${input}`);
343+
return encodeUtf8(`${input}`);
341344
}
342345

343346
encodeInto(src, dest) {
344347
validateEncoder(this);
345348
validateString(src, 'src');
346349
if (!dest || !isUint8Array(dest))
347350
throw new ERR_INVALID_ARG_TYPE('dest', 'Uint8Array', dest);
348-
encodeInto(src, dest, encodeIntoResults);
351+
encodeIntoUtf8(src, dest, encodeIntoResults);
349352
return { read: encodeIntoResults[0], written: encodeIntoResults[1] };
350353
}
351354

node.gyp

+1
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

+1
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

-73
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

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

src/node_external_reference.h

+8
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,13 @@
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 =
16+
void (*)(v8::Local<v8::Value> receiver,
17+
const v8::FastOneByteString& source,
18+
const v8::FastApiTypedArray<uint8_t>& destination,
19+
const v8::FastApiTypedArray<uint32_t>& result);
1420

1521
// This class manages the external references from the V8 heap
1622
// to the C++ addresses in Node.js.
@@ -20,6 +26,7 @@ class ExternalReferenceRegistry {
2026

2127
#define ALLOWED_EXTERNAL_REFERENCE_TYPES(V) \
2228
V(CFunctionCallback) \
29+
V(CFunctionCallbackWithInput) \
2330
V(const v8::CFunctionInfo*) \
2431
V(v8::FunctionCallback) \
2532
V(v8::AccessorGetterCallback) \
@@ -67,6 +74,7 @@ class ExternalReferenceRegistry {
6774
V(credentials) \
6875
V(env_var) \
6976
V(errors) \
77+
V(encoding_methods) \
7078
V(fs) \
7179
V(fs_dir) \
7280
V(fs_event_wrap) \

test/parallel/test-bootstrap-modules.js

+1
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)