Skip to content

Commit 9b54d80

Browse files
committed
encoding: 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.
1 parent 57bb217 commit 9b54d80

File tree

3 files changed

+41
-26
lines changed

3 files changed

+41
-26
lines changed

lib/internal/encoding.js

Lines changed: 8 additions & 7 deletions
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

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,30 @@ using v8::Local;
2121
using v8::MaybeLocal;
2222
using v8::Object;
2323
using v8::String;
24-
using v8::Uint8Array;
2524
using v8::Uint32Array;
25+
using v8::Uint8Array;
2626
using v8::Value;
2727

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

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

5772
void BindingData::EncodeInto(const FunctionCallbackInfo<Value>& args) {
5873
Environment* env = Environment::GetCurrent(args);
5974
Isolate* isolate = env->isolate();
60-
CHECK_GE(args.Length(), 3);
75+
CHECK_GE(args.Length(), 2);
6176
CHECK(args[0]->IsString());
6277
CHECK(args[1]->IsUint8Array());
63-
CHECK(args[2]->IsUint32Array());
78+
BindingData* binding_data = Realm::GetBindingData<BindingData>(args);
6479

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

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

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

8999
// Encode a single string to a UTF-8 Uint8Array (not Buffer).
@@ -176,9 +186,9 @@ void BindingData::Initialize(Local<Object> target,
176186
Local<Value> unused,
177187
Local<Context> context,
178188
void* priv) {
179-
Environment* env = Environment::GetCurrent(context);
189+
Realm* realm = Realm::GetCurrent(context);
180190
BindingData* const binding_data =
181-
env->AddBindingData<BindingData>(context, target);
191+
realm->AddBindingData<BindingData>(context, target);
182192
if (binding_data == nullptr) return;
183193

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

src/encoding_binding.h

Lines changed: 6 additions & 2 deletions
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)