Skip to content

Commit c39f9db

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.
1 parent 455d753 commit c39f9db

File tree

9 files changed

+256
-129
lines changed

9 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');
5858

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

node.gyp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,7 @@
476476
'src/connect_wrap.cc',
477477
'src/connection_wrap.cc',
478478
'src/debug_utils.cc',
479+
'src/encoding.cc',
479480
'src/env.cc',
480481
'src/fs_event_wrap.cc',
481482
'src/handle_wrap.cc',
@@ -580,6 +581,7 @@
580581
'src/connection_wrap.h',
581582
'src/debug_utils.h',
582583
'src/debug_utils-inl.h',
584+
'src/encoding.h',
583585
'src/env_properties.h',
584586
'src/env.h',
585587
'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.cc

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

src/encoding.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#ifndef SRC_ENCODING_H_
2+
#define SRC_ENCODING_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_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) \
4546
V(errors) \
4647
V(fs) \
4748
V(fs_dir) \

0 commit comments

Comments
 (0)