Skip to content

Commit 535e1bf

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 c39f9db commit 535e1bf

File tree

3 files changed

+31
-20
lines changed

3 files changed

+31
-20
lines changed

lib/internal/encoding.js

Lines changed: 6 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');
5352
const {
5453
encodeInto,
5554
encodeUtf8String,
5655
decodeUTF8,
57-
} = internalBinding('encoding');
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,10 @@ 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+
const { 0: read, 1: written } = binding.encodeIntoResults;
343+
return { read, written };
345344
}
346345

347346
[inspect](depth, opts) {

src/encoding.cc

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,24 @@ using v8::Uint8Array;
2525
using v8::Uint32Array;
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", encode_into_results_buffer_);
30+
}
31+
32+
BindingData::BindingData(Environment* env, v8::Local<v8::Object> object)
33+
: SnapshotableObject(env, object, type_int),
34+
encode_into_results_buffer_(env->isolate(), kEncodeIntoResultsLength) {
35+
object->Set(env->context(),
36+
FIXED_ONE_BYTE_STRING(env->isolate(), "encodeIntoResults"),
37+
encode_into_results_buffer_.GetJSArray())
38+
.Check();
39+
}
3040

3141
bool BindingData::PrepareForSerialization(Local<Context> context,
3242
v8::SnapshotCreator* creator) {
43+
// We'll just re-initialize the buffers in the constructor since their
44+
// contents can be thrown away once consumed in the previous call.
45+
encode_into_results_buffer_.Release();
3346
// Return true because we need to maintain the reference to the binding from
3447
// JS land.
3548
return true;
@@ -57,10 +70,10 @@ void BindingData::Deserialize(Local<Context> context,
5770
void BindingData::EncodeInto(const FunctionCallbackInfo<Value>& args) {
5871
Environment* env = Environment::GetCurrent(args);
5972
Isolate* isolate = env->isolate();
60-
CHECK_GE(args.Length(), 3);
73+
CHECK_GE(args.Length(), 2);
6174
CHECK(args[0]->IsString());
6275
CHECK(args[1]->IsUint8Array());
63-
CHECK(args[2]->IsUint32Array());
76+
BindingData* binding_data = Environment::GetBindingData<BindingData>(args);
6477

6578
Local<String> source = args[0].As<String>();
6679

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

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-
7885
int nchars;
7986
int written = source->WriteUtf8(
8087
isolate,
8188
write_result,
8289
dest_length,
8390
&nchars,
8491
String::NO_NULL_TERMINATION | String::REPLACE_INVALID_UTF8);
85-
results[0] = nchars;
86-
results[1] = written;
92+
93+
binding_data->encode_into_results_buffer_[0] = nchars;
94+
binding_data->encode_into_results_buffer_[1] = written;
8795
}
8896

8997
// Encode a single string to a UTF-8 Uint8Array (not Buffer).

src/encoding.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class BindingData : public SnapshotableObject {
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)