From 535e1bf4545f2fddbf558e9bc96be304cc4dc55f Mon Sep 17 00:00:00 2001 From: Joyee Cheung Date: Sat, 11 Feb 2023 19:25:00 +0100 Subject: [PATCH] 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. --- lib/internal/encoding.js | 13 ++++++------- src/encoding.cc | 32 ++++++++++++++++++++------------ src/encoding.h | 6 +++++- 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/lib/internal/encoding.js b/lib/internal/encoding.js index 174ebeb58de92b..4d1468aa1434fd 100644 --- a/lib/internal/encoding.js +++ b/lib/internal/encoding.js @@ -13,7 +13,6 @@ const { StringPrototypeSlice, Symbol, SymbolToStringTag, - Uint32Array, Uint8Array, } = primordials; @@ -49,12 +48,12 @@ const { validateString, validateObject, } = require('internal/validators'); - +const binding = internalBinding('encoding'); const { encodeInto, encodeUtf8String, decodeUTF8, -} = internalBinding('encoding'); +} = binding; const { Buffer } = require('buffer'); @@ -318,8 +317,6 @@ function getEncodingFromLabel(label) { return encodings.get(trimAsciiWhitespace(label.toLowerCase())); } -const encodeIntoResults = new Uint32Array(2); - class TextEncoder { constructor() { this[kEncoder] = true; @@ -340,8 +337,10 @@ class TextEncoder { validateString(src, 'src'); if (!dest || !isUint8Array(dest)) throw new ERR_INVALID_ARG_TYPE('dest', 'Uint8Array', dest); - encodeInto(src, dest, encodeIntoResults); - return { read: encodeIntoResults[0], written: encodeIntoResults[1] }; + + encodeInto(src, dest); + const { 0: read, 1: written } = binding.encodeIntoResults; + return { read, written }; } [inspect](depth, opts) { diff --git a/src/encoding.cc b/src/encoding.cc index 0854ff677c9c60..c29951768e100e 100644 --- a/src/encoding.cc +++ b/src/encoding.cc @@ -25,11 +25,24 @@ using v8::Uint8Array; using v8::Uint32Array; using v8::Value; -BindingData::BindingData(Environment* env, Local object) - : SnapshotableObject(env, object, type_int) {} +void BindingData::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("encode_into_results_buffer", encode_into_results_buffer_); +} + +BindingData::BindingData(Environment* env, v8::Local object) + : SnapshotableObject(env, object, type_int), + encode_into_results_buffer_(env->isolate(), kEncodeIntoResultsLength) { + object->Set(env->context(), + FIXED_ONE_BYTE_STRING(env->isolate(), "encodeIntoResults"), + encode_into_results_buffer_.GetJSArray()) + .Check(); +} bool BindingData::PrepareForSerialization(Local context, v8::SnapshotCreator* creator) { + // We'll just re-initialize the buffers in the constructor since their + // contents can be thrown away once consumed in the previous call. + encode_into_results_buffer_.Release(); // Return true because we need to maintain the reference to the binding from // JS land. return true; @@ -57,10 +70,10 @@ void BindingData::Deserialize(Local context, void BindingData::EncodeInto(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); Isolate* isolate = env->isolate(); - CHECK_GE(args.Length(), 3); + CHECK_GE(args.Length(), 2); CHECK(args[0]->IsString()); CHECK(args[1]->IsUint8Array()); - CHECK(args[2]->IsUint32Array()); + BindingData* binding_data = Environment::GetBindingData(args); Local source = args[0].As(); @@ -69,12 +82,6 @@ void BindingData::EncodeInto(const FunctionCallbackInfo& args) { char* write_result = static_cast(buf->Data()) + dest->ByteOffset(); size_t dest_length = dest->ByteLength(); - // results = [ read, written ] - Local result_arr = args[2].As(); - uint32_t* results = reinterpret_cast( - static_cast(result_arr->Buffer()->Data()) + - result_arr->ByteOffset()); - int nchars; int written = source->WriteUtf8( isolate, @@ -82,8 +89,9 @@ void BindingData::EncodeInto(const FunctionCallbackInfo& args) { dest_length, &nchars, String::NO_NULL_TERMINATION | String::REPLACE_INVALID_UTF8); - results[0] = nchars; - results[1] = written; + + binding_data->encode_into_results_buffer_[0] = nchars; + binding_data->encode_into_results_buffer_[1] = written; } // Encode a single string to a UTF-8 Uint8Array (not Buffer). diff --git a/src/encoding.h b/src/encoding.h index 87aa851fc61b5d..17ff2986f66ffe 100644 --- a/src/encoding.h +++ b/src/encoding.h @@ -21,7 +21,7 @@ class BindingData : public SnapshotableObject { SERIALIZABLE_OBJECT_METHODS() SET_BINDING_ID(encoding_binding_data) - SET_NO_MEMORY_INFO() + void MemoryInfo(MemoryTracker* tracker) const override; SET_SELF_SIZE(BindingData) SET_MEMORY_INFO_NAME(BindingData) @@ -35,6 +35,10 @@ class BindingData : public SnapshotableObject { void* priv); static void RegisterTimerExternalReferences( ExternalReferenceRegistry* registry); + +private: + static constexpr size_t kEncodeIntoResultsLength = 2; + AliasedUint32Array encode_into_results_buffer_; }; } // namespace encoding_binding