Skip to content

Commit 6b60f38

Browse files
joyeecheungtargos
authored andcommitted
src: use AliasedUint32Array for encodeInto results
Getting the buffer from a TypedArray created from the JS land incurs a copy. For encodeInto() results we can just use an AliasedArray and let the binding always own the store. PR-URL: #46658 Reviewed-By: Darshan Sen <raisinten@gmail.com>
1 parent dcba3a0 commit 6b60f38

File tree

3 files changed

+41
-26
lines changed

3 files changed

+41
-26
lines changed

lib/internal/encoding.js

+8-7
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ const {
1313
StringPrototypeSlice,
1414
Symbol,
1515
SymbolToStringTag,
16-
Uint32Array,
1716
Uint8Array,
1817
} = primordials;
1918

@@ -49,12 +48,12 @@ const {
4948
validateString,
5049
validateObject,
5150
} = require('internal/validators');
52-
51+
const binding = internalBinding('encoding_binding');
5352
const {
5453
encodeInto,
5554
encodeUtf8String,
5655
decodeUTF8,
57-
} = internalBinding('encoding_binding');
56+
} = binding;
5857

5958
const { Buffer } = require('buffer');
6059

@@ -318,8 +317,6 @@ function getEncodingFromLabel(label) {
318317
return encodings.get(trimAsciiWhitespace(label.toLowerCase()));
319318
}
320319

321-
const encodeIntoResults = new Uint32Array(2);
322-
323320
class TextEncoder {
324321
constructor() {
325322
this[kEncoder] = true;
@@ -340,8 +337,12 @@ class TextEncoder {
340337
validateString(src, 'src');
341338
if (!dest || !isUint8Array(dest))
342339
throw new ERR_INVALID_ARG_TYPE('dest', 'Uint8Array', dest);
343-
encodeInto(src, dest, encodeIntoResults);
344-
return { read: encodeIntoResults[0], written: encodeIntoResults[1] };
340+
341+
encodeInto(src, dest);
342+
// We need to read from the binding here since the buffer gets refreshed
343+
// from the snapshot.
344+
const { 0: read, 1: written } = binding.encodeIntoResults;
345+
return { read, written };
345346
}
346347

347348
[inspect](depth, opts) {

src/encoding_binding.cc

+27-17
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,30 @@ using v8::Local;
2020
using v8::MaybeLocal;
2121
using v8::Object;
2222
using v8::String;
23-
using v8::Uint8Array;
2423
using v8::Uint32Array;
24+
using v8::Uint8Array;
2525
using v8::Value;
2626

27-
BindingData::BindingData(Environment* env, Local<Object> object)
28-
: SnapshotableObject(env, object, type_int) {}
27+
void BindingData::MemoryInfo(MemoryTracker* tracker) const {
28+
tracker->TrackField("encode_into_results_buffer",
29+
encode_into_results_buffer_);
30+
}
31+
32+
BindingData::BindingData(Realm* realm, v8::Local<v8::Object> object)
33+
: SnapshotableObject(realm, object, type_int),
34+
encode_into_results_buffer_(realm->isolate(), kEncodeIntoResultsLength) {
35+
object
36+
->Set(realm->context(),
37+
FIXED_ONE_BYTE_STRING(realm->isolate(), "encodeIntoResults"),
38+
encode_into_results_buffer_.GetJSArray())
39+
.Check();
40+
}
2941

3042
bool BindingData::PrepareForSerialization(Local<Context> context,
3143
v8::SnapshotCreator* creator) {
44+
// We'll just re-initialize the buffers in the constructor since their
45+
// contents can be thrown away once consumed in the previous call.
46+
encode_into_results_buffer_.Release();
3247
// Return true because we need to maintain the reference to the binding from
3348
// JS land.
3449
return true;
@@ -47,19 +62,19 @@ void BindingData::Deserialize(Local<Context> context,
4762
InternalFieldInfoBase* info) {
4863
DCHECK_EQ(index, BaseObject::kEmbedderType);
4964
v8::HandleScope scope(context->GetIsolate());
50-
Environment* env = Environment::GetCurrent(context);
65+
Realm* realm = Realm::GetCurrent(context);
5166
// Recreate the buffer in the constructor.
52-
BindingData* binding = env->AddBindingData<BindingData>(context, holder);
67+
BindingData* binding = realm->AddBindingData<BindingData>(context, holder);
5368
CHECK_NOT_NULL(binding);
5469
}
5570

5671
void BindingData::EncodeInto(const FunctionCallbackInfo<Value>& args) {
5772
Environment* env = Environment::GetCurrent(args);
5873
Isolate* isolate = env->isolate();
59-
CHECK_GE(args.Length(), 3);
74+
CHECK_GE(args.Length(), 2);
6075
CHECK(args[0]->IsString());
6176
CHECK(args[1]->IsUint8Array());
62-
CHECK(args[2]->IsUint32Array());
77+
BindingData* binding_data = Realm::GetBindingData<BindingData>(args);
6378

6479
Local<String> source = args[0].As<String>();
6580

@@ -68,21 +83,16 @@ void BindingData::EncodeInto(const FunctionCallbackInfo<Value>& args) {
6883
char* write_result = static_cast<char*>(buf->Data()) + dest->ByteOffset();
6984
size_t dest_length = dest->ByteLength();
7085

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-
7786
int nchars;
7887
int written = source->WriteUtf8(
7988
isolate,
8089
write_result,
8190
dest_length,
8291
&nchars,
8392
String::NO_NULL_TERMINATION | String::REPLACE_INVALID_UTF8);
84-
results[0] = nchars;
85-
results[1] = written;
93+
94+
binding_data->encode_into_results_buffer_[0] = nchars;
95+
binding_data->encode_into_results_buffer_[1] = written;
8696
}
8797

8898
// Encode a single string to a UTF-8 Uint8Array (not Buffer).
@@ -175,9 +185,9 @@ void BindingData::Initialize(Local<Object> target,
175185
Local<Value> unused,
176186
Local<Context> context,
177187
void* priv) {
178-
Environment* env = Environment::GetCurrent(context);
188+
Realm* realm = Realm::GetCurrent(context);
179189
BindingData* const binding_data =
180-
env->AddBindingData<BindingData>(context, target);
190+
realm->AddBindingData<BindingData>(context, target);
181191
if (binding_data == nullptr) return;
182192

183193
SetMethod(context, target, "encodeInto", EncodeInto);

src/encoding_binding.h

+6-2
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@ class ExternalReferenceRegistry;
1414
namespace encoding_binding {
1515
class BindingData : public SnapshotableObject {
1616
public:
17-
BindingData(Environment* env, v8::Local<v8::Object> obj);
17+
BindingData(Realm* realm, v8::Local<v8::Object> obj);
1818

1919
using InternalFieldInfo = InternalFieldInfoBase;
2020

2121
SERIALIZABLE_OBJECT_METHODS()
2222
SET_BINDING_ID(encoding_binding_data)
2323

24-
SET_NO_MEMORY_INFO()
24+
void MemoryInfo(MemoryTracker* tracker) const override;
2525
SET_SELF_SIZE(BindingData)
2626
SET_MEMORY_INFO_NAME(BindingData)
2727

@@ -35,6 +35,10 @@ class BindingData : public SnapshotableObject {
3535
void* priv);
3636
static void RegisterTimerExternalReferences(
3737
ExternalReferenceRegistry* registry);
38+
39+
private:
40+
static constexpr size_t kEncodeIntoResultsLength = 2;
41+
AliasedUint32Array encode_into_results_buffer_;
3842
};
3943

4044
} // namespace encoding_binding

0 commit comments

Comments
 (0)