Skip to content

Commit

Permalink
src: add encoding_methods with fast api
Browse files Browse the repository at this point in the history
  • Loading branch information
anonrig committed Dec 2, 2022
1 parent 50fb246 commit af9270a
Show file tree
Hide file tree
Showing 9 changed files with 119 additions and 38 deletions.
1 change: 1 addition & 0 deletions lib/internal/bootstrap/loaders.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ const internalBindingAllowlist = new SafeSet([
'constants',
'contextify',
'crypto',
'encoding_methods',
'fs',
'fs_event_wrap',
'http_parser',
Expand Down
13 changes: 11 additions & 2 deletions lib/internal/encoding.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const {
customInspectSymbol: inspect,
kEmptyObject,
kEnumerableProperty,
getUtf8Length,
} = require('internal/util');

const {
Expand All @@ -52,10 +53,13 @@ const {

const {
encodeInto,
encodeUtf8String,
decodeUTF8,
} = internalBinding('buffer');

const {
encodeUtf8,
} = internalBinding('encoding_methods');

let Buffer;
function lazyBuffer() {
if (Buffer === undefined)
Expand Down Expand Up @@ -337,7 +341,12 @@ class TextEncoder {

encode(input = '') {
validateEncoder(this);
return encodeUtf8String(`${input}`);
const utf8Length = getUtf8Length(input);
const array = new Uint8Array(utf8Length);
if (utf8Length > 0) {
encodeUtf8(`${input}`, array);
}
return array;
}

encodeInto(src, dest) {
Expand Down
8 changes: 8 additions & 0 deletions lib/internal/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,13 @@ function isArrayBufferDetached(value) {
return false;
}

function getUtf8Length(str) {
let length = 0;
for (let i = 0; i < str.length; i++)
length += str.charCodeAt(i) > 127 ? 2 : 1;
return length;
}

module.exports = {
assertCrypto,
cachedResult,
Expand All @@ -585,6 +592,7 @@ module.exports = {
filterOwnProperties,
getConstructorOf,
getInternalGlobal,
getUtf8Length,
getSystemErrorMap,
getSystemErrorName,
isArrayBufferDetached,
Expand Down
1 change: 1 addition & 0 deletions node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,7 @@
'src/node_contextify.cc',
'src/node_credentials.cc',
'src/node_dir.cc',
'src/node_encoding.cc',
'src/node_env_var.cc',
'src/node_errors.cc',
'src/node_external_reference.cc',
Expand Down
1 change: 1 addition & 0 deletions src/node_binding.cc
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
V(contextify) \
V(credentials) \
V(errors) \
V(encoding_methods) \
V(fs) \
V(fs_dir) \
V(fs_event_wrap) \
Expand Down
36 changes: 0 additions & 36 deletions src/node_buffer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1146,40 +1146,6 @@ void Swap64(const FunctionCallbackInfo<Value>& args) {
args.GetReturnValue().Set(args[0]);
}


// Encode a single string to a UTF-8 Uint8Array (not Buffer).
// Used in TextEncoder.prototype.encode.
static void EncodeUtf8String(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Isolate* isolate = env->isolate();
CHECK_GE(args.Length(), 1);
CHECK(args[0]->IsString());

Local<String> str = args[0].As<String>();
size_t length = str->Utf8Length(isolate);

Local<ArrayBuffer> ab;
{
NoArrayBufferZeroFillScope no_zero_fill_scope(env->isolate_data());
std::unique_ptr<BackingStore> bs =
ArrayBuffer::NewBackingStore(isolate, length);

CHECK(bs);

str->WriteUtf8(isolate,
static_cast<char*>(bs->Data()),
-1, // We are certain that `data` is sufficiently large
nullptr,
String::NO_NULL_TERMINATION | String::REPLACE_INVALID_UTF8);

ab = ArrayBuffer::New(isolate, std::move(bs));
}

auto array = Uint8Array::New(ab, 0, length);
args.GetReturnValue().Set(array);
}


static void EncodeInto(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Isolate* isolate = env->isolate();
Expand Down Expand Up @@ -1345,7 +1311,6 @@ void Initialize(Local<Object> target,
SetMethod(context, target, "swap64", Swap64);

SetMethod(context, target, "encodeInto", EncodeInto);
SetMethodNoSideEffect(context, target, "encodeUtf8String", EncodeUtf8String);

target
->Set(context,
Expand Down Expand Up @@ -1400,7 +1365,6 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
registry->Register(Swap64);

registry->Register(EncodeInto);
registry->Register(EncodeUtf8String);

registry->Register(StringSlice<ASCII>);
registry->Register(StringSlice<BASE64>);
Expand Down
88 changes: 88 additions & 0 deletions src/node_encoding.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#include "env-inl.h"
#include "node.h"
#include "node_external_reference.h"
#include "node_internals.h"
#include "util-inl.h"
#include "v8-fast-api-calls.h"
#include "v8.h"

namespace node {

using v8::ArrayBuffer;
using v8::BackingStore;
using v8::CFunction;
using v8::Context;
using v8::FastApiTypedArray;
using v8::FastOneByteString;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::String;
using v8::Uint8Array;
using v8::Value;

namespace encoding_methods {

void EncodeUtf8(const v8::FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Isolate* isolate = env->isolate();
CHECK_GE(args.Length(), 2);
CHECK(args[0]->IsString());
CHECK(args[1]->IsUint8Array());

Local<String> str = args[0].As<String>();
Local<Uint8Array> out = args[1].As<Uint8Array>();
Local<ArrayBuffer> buf = out->Buffer();
size_t out_length = out->ByteLength();
char* write_result = static_cast<char*>(buf->Data()) + out->ByteOffset();

str->WriteUtf8(isolate,
write_result,
out_length,
nullptr,
String::NO_NULL_TERMINATION | String::REPLACE_INVALID_UTF8);
}

void latin1_to_utf8(const char* str, char* c) {
for (; *str; ++str) {
if (*str & 0x80) {
*c++ = *str;
} else {
*c++ = (char)(0xc0 | (unsigned)*str >> 6);
*c++ = (char)(0x80 | (*str & 0x3f));
}
}
*c++ = '\0';
}

void FastEncodeUtf8(Local<Value> receiver,
const FastOneByteString& source,
const FastApiTypedArray<uint8_t>& output) {
uint8_t* storage;
auto is_available = output.getStorageIfAligned(&storage);
CHECK(is_available);
latin1_to_utf8(source.data, reinterpret_cast<char*>(storage));
}

CFunction fast_encode_utf8_(CFunction::Make(FastEncodeUtf8));

static void Initialize(Local<Object> target,
Local<Value> unused,
Local<Context> context,
void* priv) {
SetFastMethod(context, target, "encodeUtf8", EncodeUtf8, &fast_encode_utf8_);
}

void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
registry->Register(EncodeUtf8);
registry->Register(FastEncodeUtf8);
registry->Register(fast_encode_utf8_.GetTypeInfo());
}

} // namespace encoding_methods
} // namespace node

NODE_BINDING_CONTEXT_AWARE_INTERNAL(encoding_methods,
node::encoding_methods::Initialize)
NODE_BINDING_EXTERNAL_REFERENCE(
encoding_methods, node::encoding_methods::RegisterExternalReferences)
8 changes: 8 additions & 0 deletions src/node_external_reference.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,14 @@

namespace node {

// TODO(anonrig): Find a good way of reusing existing types for fast api usages.
using CFunctionCallback = void (*)(v8::Local<v8::Value> receiver);

using CFunctionCallbackWithInput =
void (*)(v8::Local<v8::Value> receiver,
const v8::FastOneByteString& input,
const v8::FastApiTypedArray<uint8_t>& output);

// This class manages the external references from the V8 heap
// to the C++ addresses in Node.js.
class ExternalReferenceRegistry {
Expand All @@ -20,6 +26,7 @@ class ExternalReferenceRegistry {

#define ALLOWED_EXTERNAL_REFERENCE_TYPES(V) \
V(CFunctionCallback) \
V(CFunctionCallbackWithInput) \
V(const v8::CFunctionInfo*) \
V(v8::FunctionCallback) \
V(v8::AccessorGetterCallback) \
Expand Down Expand Up @@ -67,6 +74,7 @@ class ExternalReferenceRegistry {
V(credentials) \
V(env_var) \
V(errors) \
V(encoding_methods) \
V(fs) \
V(fs_dir) \
V(fs_event_wrap) \
Expand Down
1 change: 1 addition & 0 deletions test/parallel/test-bootstrap-modules.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const expectedModules = new Set([
'Internal Binding contextify',
'Internal Binding credentials',
'Internal Binding errors',
'Internal Binding encoding_methods',
'Internal Binding fs_dir',
'Internal Binding fs_event_wrap',
'Internal Binding fs',
Expand Down

0 comments on commit af9270a

Please sign in to comment.