Skip to content

Commit 25c2186

Browse files
committed
src: move encoding bindings to a new binding
Move the bindings used by TextEncoder to a new binding for more self-contained code. PR-URL: #46658 Reviewed-By: Darshan Sen <[email protected]>
1 parent 044021d commit 25c2186

10 files changed

+256
-129
lines changed

lib/internal/encoding.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ const {
5454
encodeInto,
5555
encodeUtf8String,
5656
decodeUTF8,
57-
} = internalBinding('buffer');
57+
} = internalBinding('encoding_binding');
5858

5959
const { Buffer } = require('buffer');
6060

node.gyp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,7 @@
479479
'src/connection_wrap.cc',
480480
'src/dataqueue/queue.cc',
481481
'src/debug_utils.cc',
482+
'src/encoding_binding.cc',
482483
'src/env.cc',
483484
'src/fs_event_wrap.cc',
484485
'src/handle_wrap.cc',
@@ -590,6 +591,7 @@
590591
'src/dataqueue/queue.h',
591592
'src/debug_utils.h',
592593
'src/debug_utils-inl.h',
594+
'src/encoding_binding.h',
593595
'src/env_properties.h',
594596
'src/env.h',
595597
'src/env-inl.h',

src/base_object_types.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ namespace node {
1010
// what the class passes to SET_BINDING_ID(), the second argument should match
1111
// the C++ class name.
1212
#define SERIALIZABLE_BINDING_TYPES(V) \
13+
V(encoding_binding_data, encoding_binding::BindingData) \
1314
V(fs_binding_data, fs::BindingData) \
1415
V(v8_binding_data, v8_utils::BindingData) \
1516
V(blob_binding_data, BlobBindingData) \

src/encoding_binding.cc

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
#include "encoding_binding.h"
2+
#include "env-inl.h"
3+
#include "node_errors.h"
4+
#include "node_external_reference.h"
5+
#include "simdutf.h"
6+
#include "string_bytes.h"
7+
#include "v8.h"
8+
9+
#include <cstdint>
10+
11+
namespace node {
12+
namespace encoding_binding {
13+
14+
using v8::ArrayBuffer;
15+
using v8::BackingStore;
16+
using v8::Context;
17+
using v8::FunctionCallbackInfo;
18+
using v8::Isolate;
19+
using v8::Local;
20+
using v8::MaybeLocal;
21+
using v8::Object;
22+
using v8::String;
23+
using v8::Uint8Array;
24+
using v8::Uint32Array;
25+
using v8::Value;
26+
27+
BindingData::BindingData(Environment* env, Local<Object> object)
28+
: SnapshotableObject(env, object, type_int) {}
29+
30+
bool BindingData::PrepareForSerialization(Local<Context> context,
31+
v8::SnapshotCreator* creator) {
32+
// Return true because we need to maintain the reference to the binding from
33+
// JS land.
34+
return true;
35+
}
36+
37+
InternalFieldInfoBase* BindingData::Serialize(int index) {
38+
DCHECK_EQ(index, BaseObject::kEmbedderType);
39+
InternalFieldInfo* info =
40+
InternalFieldInfoBase::New<InternalFieldInfo>(type());
41+
return info;
42+
}
43+
44+
void BindingData::Deserialize(Local<Context> context,
45+
Local<Object> holder,
46+
int index,
47+
InternalFieldInfoBase* info) {
48+
DCHECK_EQ(index, BaseObject::kEmbedderType);
49+
v8::HandleScope scope(context->GetIsolate());
50+
Environment* env = Environment::GetCurrent(context);
51+
// Recreate the buffer in the constructor.
52+
BindingData* binding = env->AddBindingData<BindingData>(context, holder);
53+
CHECK_NOT_NULL(binding);
54+
}
55+
56+
void BindingData::EncodeInto(const FunctionCallbackInfo<Value>& args) {
57+
Environment* env = Environment::GetCurrent(args);
58+
Isolate* isolate = env->isolate();
59+
CHECK_GE(args.Length(), 3);
60+
CHECK(args[0]->IsString());
61+
CHECK(args[1]->IsUint8Array());
62+
CHECK(args[2]->IsUint32Array());
63+
64+
Local<String> source = args[0].As<String>();
65+
66+
Local<Uint8Array> dest = args[1].As<Uint8Array>();
67+
Local<ArrayBuffer> buf = dest->Buffer();
68+
char* write_result = static_cast<char*>(buf->Data()) + dest->ByteOffset();
69+
size_t dest_length = dest->ByteLength();
70+
71+
// results = [ read, written ]
72+
Local<Uint32Array> result_arr = args[2].As<Uint32Array>();
73+
uint32_t* results = reinterpret_cast<uint32_t*>(
74+
static_cast<char*>(result_arr->Buffer()->Data()) +
75+
result_arr->ByteOffset());
76+
77+
int nchars;
78+
int written = source->WriteUtf8(
79+
isolate,
80+
write_result,
81+
dest_length,
82+
&nchars,
83+
String::NO_NULL_TERMINATION | String::REPLACE_INVALID_UTF8);
84+
results[0] = nchars;
85+
results[1] = written;
86+
}
87+
88+
// Encode a single string to a UTF-8 Uint8Array (not Buffer).
89+
// Used in TextEncoder.prototype.encode.
90+
void BindingData::EncodeUtf8String(const FunctionCallbackInfo<Value>& args) {
91+
Environment* env = Environment::GetCurrent(args);
92+
Isolate* isolate = env->isolate();
93+
CHECK_GE(args.Length(), 1);
94+
CHECK(args[0]->IsString());
95+
96+
Local<String> str = args[0].As<String>();
97+
size_t length = str->Utf8Length(isolate);
98+
99+
Local<ArrayBuffer> ab;
100+
{
101+
NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
102+
std::unique_ptr<BackingStore> bs =
103+
ArrayBuffer::NewBackingStore(isolate, length);
104+
105+
CHECK(bs);
106+
107+
str->WriteUtf8(isolate,
108+
static_cast<char*>(bs->Data()),
109+
-1, // We are certain that `data` is sufficiently large
110+
nullptr,
111+
String::NO_NULL_TERMINATION | String::REPLACE_INVALID_UTF8);
112+
113+
ab = ArrayBuffer::New(isolate, std::move(bs));
114+
}
115+
116+
auto array = Uint8Array::New(ab, 0, length);
117+
args.GetReturnValue().Set(array);
118+
}
119+
120+
// Convert the input into an encoded string
121+
void BindingData::DecodeUTF8(const FunctionCallbackInfo<Value>& args) {
122+
Environment* env = Environment::GetCurrent(args); // list, flags
123+
124+
CHECK_GE(args.Length(), 1);
125+
126+
if (!(args[0]->IsArrayBuffer() || args[0]->IsSharedArrayBuffer() ||
127+
args[0]->IsArrayBufferView())) {
128+
return node::THROW_ERR_INVALID_ARG_TYPE(
129+
env->isolate(),
130+
"The \"list\" argument must be an instance of SharedArrayBuffer, "
131+
"ArrayBuffer or ArrayBufferView.");
132+
}
133+
134+
ArrayBufferViewContents<char> buffer(args[0]);
135+
136+
bool ignore_bom = args[1]->IsTrue();
137+
bool has_fatal = args[2]->IsTrue();
138+
139+
const char* data = buffer.data();
140+
size_t length = buffer.length();
141+
142+
if (has_fatal) {
143+
auto result = simdutf::validate_utf8_with_errors(data, length);
144+
145+
if (result.error) {
146+
return node::THROW_ERR_ENCODING_INVALID_ENCODED_DATA(
147+
env->isolate(), "The encoded data was not valid for encoding utf-8");
148+
}
149+
}
150+
151+
if (!ignore_bom && length >= 3) {
152+
if (memcmp(data, "\xEF\xBB\xBF", 3) == 0) {
153+
data += 3;
154+
length -= 3;
155+
}
156+
}
157+
158+
if (length == 0) return args.GetReturnValue().SetEmptyString();
159+
160+
Local<Value> error;
161+
MaybeLocal<Value> maybe_ret =
162+
StringBytes::Encode(env->isolate(), data, length, UTF8, &error);
163+
Local<Value> ret;
164+
165+
if (!maybe_ret.ToLocal(&ret)) {
166+
CHECK(!error.IsEmpty());
167+
env->isolate()->ThrowException(error);
168+
return;
169+
}
170+
171+
args.GetReturnValue().Set(ret);
172+
}
173+
174+
void BindingData::Initialize(Local<Object> target,
175+
Local<Value> unused,
176+
Local<Context> context,
177+
void* priv) {
178+
Environment* env = Environment::GetCurrent(context);
179+
BindingData* const binding_data =
180+
env->AddBindingData<BindingData>(context, target);
181+
if (binding_data == nullptr) return;
182+
183+
SetMethod(context, target, "encodeInto", EncodeInto);
184+
SetMethodNoSideEffect(context, target, "encodeUtf8String", EncodeUtf8String);
185+
SetMethodNoSideEffect(context, target, "decodeUTF8", DecodeUTF8);
186+
}
187+
188+
void BindingData::RegisterTimerExternalReferences(
189+
ExternalReferenceRegistry* registry) {
190+
registry->Register(EncodeInto);
191+
registry->Register(EncodeUtf8String);
192+
registry->Register(DecodeUTF8);
193+
}
194+
195+
} // namespace encoding_binding
196+
} // namespace node
197+
198+
NODE_BINDING_CONTEXT_AWARE_INTERNAL(
199+
encoding_binding, node::encoding_binding::BindingData::Initialize)
200+
NODE_BINDING_EXTERNAL_REFERENCE(
201+
encoding_binding,
202+
node::encoding_binding::BindingData::RegisterTimerExternalReferences)

src/encoding_binding.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#ifndef SRC_ENCODING_BINDING_H_
2+
#define SRC_ENCODING_BINDING_H_
3+
4+
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
5+
6+
#include <cinttypes>
7+
#include "aliased_buffer.h"
8+
#include "node_snapshotable.h"
9+
#include "v8-fast-api-calls.h"
10+
11+
namespace node {
12+
class ExternalReferenceRegistry;
13+
14+
namespace encoding_binding {
15+
class BindingData : public SnapshotableObject {
16+
public:
17+
BindingData(Environment* env, v8::Local<v8::Object> obj);
18+
19+
using InternalFieldInfo = InternalFieldInfoBase;
20+
21+
SERIALIZABLE_OBJECT_METHODS()
22+
SET_BINDING_ID(encoding_binding_data)
23+
24+
SET_NO_MEMORY_INFO()
25+
SET_SELF_SIZE(BindingData)
26+
SET_MEMORY_INFO_NAME(BindingData)
27+
28+
static void EncodeInto(const v8::FunctionCallbackInfo<v8::Value>& args);
29+
static void EncodeUtf8String(const v8::FunctionCallbackInfo<v8::Value>& args);
30+
static void DecodeUTF8(const v8::FunctionCallbackInfo<v8::Value>& args);
31+
32+
static void Initialize(v8::Local<v8::Object> target,
33+
v8::Local<v8::Value> unused,
34+
v8::Local<v8::Context> context,
35+
void* priv);
36+
static void RegisterTimerExternalReferences(
37+
ExternalReferenceRegistry* registry);
38+
};
39+
40+
} // namespace encoding_binding
41+
42+
} // namespace node
43+
44+
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
45+
46+
#endif // SRC_ENCODING_BINDING_H_

src/node_binding.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
V(config) \
4343
V(contextify) \
4444
V(credentials) \
45+
V(encoding_binding) \
4546
V(errors) \
4647
V(fs) \
4748
V(fs_dir) \

0 commit comments

Comments
 (0)