buffer: use fast API for writing one-byte strings
PR-URL: https://github.com/nodejs/node/pull/54310 PR-URL: https://github.com/nodejs/node/pull/54311 Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Santiago Gimeno <santiago.gimeno@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
This commit is contained in:
parent
75d25bc6db
commit
ccf05ef751
20
benchmark/buffers/buffer-write-string-short.js
Normal file
20
benchmark/buffers/buffer-write-string-short.js
Normal file
@ -0,0 +1,20 @@
|
||||
'use strict';
|
||||
|
||||
const common = require('../common.js');
|
||||
const bench = common.createBenchmark(main, {
|
||||
encoding: [
|
||||
'utf8', 'ascii', 'latin1',
|
||||
],
|
||||
len: [1, 8, 16, 32],
|
||||
n: [1e6],
|
||||
});
|
||||
|
||||
function main({ len, n, encoding }) {
|
||||
const buf = Buffer.allocUnsafe(len);
|
||||
const string = Buffer.from('a'.repeat(len)).toString();
|
||||
bench.start();
|
||||
for (let i = 0; i < n; ++i) {
|
||||
buf.write(string, 0, encoding);
|
||||
}
|
||||
bench.end(n);
|
||||
}
|
@ -23,13 +23,13 @@ const {
|
||||
hexSlice,
|
||||
ucs2Slice,
|
||||
utf8Slice,
|
||||
asciiWrite,
|
||||
asciiWriteStatic,
|
||||
base64Write,
|
||||
base64urlWrite,
|
||||
latin1Write,
|
||||
latin1WriteStatic,
|
||||
hexWrite,
|
||||
ucs2Write,
|
||||
utf8Write,
|
||||
utf8WriteStatic,
|
||||
getZeroFillToggle,
|
||||
} = internalBinding('buffer');
|
||||
|
||||
@ -1036,13 +1036,37 @@ function addBufferPrototypeMethods(proto) {
|
||||
proto.hexSlice = hexSlice;
|
||||
proto.ucs2Slice = ucs2Slice;
|
||||
proto.utf8Slice = utf8Slice;
|
||||
proto.asciiWrite = asciiWrite;
|
||||
proto.asciiWrite = function(string, offset = 0, length = this.byteLength) {
|
||||
if (offset < 0 || offset > this.byteLength) {
|
||||
throw new ERR_BUFFER_OUT_OF_BOUNDS('offset');
|
||||
}
|
||||
if (length < 0 || length > this.byteLength - offset) {
|
||||
throw new ERR_BUFFER_OUT_OF_BOUNDS('length');
|
||||
}
|
||||
return asciiWriteStatic(this, string, offset, length);
|
||||
};
|
||||
proto.base64Write = base64Write;
|
||||
proto.base64urlWrite = base64urlWrite;
|
||||
proto.latin1Write = latin1Write;
|
||||
proto.latin1Write = function(string, offset = 0, length = this.byteLength) {
|
||||
if (offset < 0 || offset > this.byteLength) {
|
||||
throw new ERR_BUFFER_OUT_OF_BOUNDS('offset');
|
||||
}
|
||||
if (length < 0 || length > this.byteLength - offset) {
|
||||
throw new ERR_BUFFER_OUT_OF_BOUNDS('length');
|
||||
}
|
||||
return latin1WriteStatic(this, string, offset, length);
|
||||
};
|
||||
proto.hexWrite = hexWrite;
|
||||
proto.ucs2Write = ucs2Write;
|
||||
proto.utf8Write = utf8Write;
|
||||
proto.utf8Write = function(string, offset = 0, length = this.byteLength) {
|
||||
if (offset < 0 || offset > this.byteLength) {
|
||||
throw new ERR_BUFFER_OUT_OF_BOUNDS('offset');
|
||||
}
|
||||
if (length < 0 || length > this.byteLength - offset) {
|
||||
throw new ERR_BUFFER_OUT_OF_BOUNDS('length');
|
||||
}
|
||||
return utf8WriteStatic(this, string, offset, length);
|
||||
};
|
||||
}
|
||||
|
||||
// This would better be placed in internal/worker/io.js, but that doesn't work
|
||||
|
@ -1442,6 +1442,52 @@ void CopyArrayBuffer(const FunctionCallbackInfo<Value>& args) {
|
||||
memcpy(dest, src, bytes_to_copy);
|
||||
}
|
||||
|
||||
template <encoding encoding>
|
||||
void SlowWriteString(const FunctionCallbackInfo<Value>& args) {
|
||||
Environment* env = Environment::GetCurrent(args);
|
||||
|
||||
THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]);
|
||||
SPREAD_BUFFER_ARG(args[0], ts_obj);
|
||||
|
||||
THROW_AND_RETURN_IF_NOT_STRING(env, args[1], "argument");
|
||||
|
||||
Local<String> str = args[1]->ToString(env->context()).ToLocalChecked();
|
||||
|
||||
size_t offset = 0;
|
||||
size_t max_length = 0;
|
||||
|
||||
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[2], 0, &offset));
|
||||
THROW_AND_RETURN_IF_OOB(
|
||||
ParseArrayIndex(env, args[3], ts_obj_length - offset, &max_length));
|
||||
|
||||
max_length = std::min(ts_obj_length - offset, max_length);
|
||||
|
||||
if (max_length == 0) return args.GetReturnValue().Set(0);
|
||||
|
||||
uint32_t written = StringBytes::Write(
|
||||
env->isolate(), ts_obj_data + offset, max_length, str, encoding);
|
||||
args.GetReturnValue().Set(written);
|
||||
}
|
||||
|
||||
uint32_t FastWriteString(Local<Value> receiver,
|
||||
const v8::FastApiTypedArray<uint8_t>& dst,
|
||||
const v8::FastOneByteString& src,
|
||||
uint32_t offset,
|
||||
uint32_t max_length) {
|
||||
uint8_t* dst_data;
|
||||
CHECK(dst.getStorageIfAligned(&dst_data));
|
||||
CHECK(offset <= dst.length());
|
||||
CHECK(dst.length() - offset <= std::numeric_limits<uint32_t>::max());
|
||||
|
||||
max_length = std::min<uint32_t>(dst.length() - offset, max_length);
|
||||
|
||||
memcpy(dst_data, src.data, max_length);
|
||||
|
||||
return max_length;
|
||||
}
|
||||
|
||||
static v8::CFunction fast_write_string(v8::CFunction::Make(FastWriteString));
|
||||
|
||||
void Initialize(Local<Object> target,
|
||||
Local<Value> unused,
|
||||
Local<Context> context,
|
||||
@ -1502,13 +1548,26 @@ void Initialize(Local<Object> target,
|
||||
SetMethodNoSideEffect(context, target, "ucs2Slice", StringSlice<UCS2>);
|
||||
SetMethodNoSideEffect(context, target, "utf8Slice", StringSlice<UTF8>);
|
||||
|
||||
SetMethod(context, target, "asciiWrite", StringWrite<ASCII>);
|
||||
SetMethod(context, target, "base64Write", StringWrite<BASE64>);
|
||||
SetMethod(context, target, "base64urlWrite", StringWrite<BASE64URL>);
|
||||
SetMethod(context, target, "latin1Write", StringWrite<LATIN1>);
|
||||
SetMethod(context, target, "hexWrite", StringWrite<HEX>);
|
||||
SetMethod(context, target, "ucs2Write", StringWrite<UCS2>);
|
||||
SetMethod(context, target, "utf8Write", StringWrite<UTF8>);
|
||||
|
||||
SetFastMethod(context,
|
||||
target,
|
||||
"asciiWriteStatic",
|
||||
SlowWriteString<ASCII>,
|
||||
&fast_write_string);
|
||||
SetFastMethod(context,
|
||||
target,
|
||||
"latin1WriteStatic",
|
||||
SlowWriteString<LATIN1>,
|
||||
&fast_write_string);
|
||||
SetFastMethod(context,
|
||||
target,
|
||||
"utf8WriteStatic",
|
||||
SlowWriteString<UTF8>,
|
||||
&fast_write_string);
|
||||
|
||||
SetMethod(context, target, "getZeroFillToggle", GetZeroFillToggle);
|
||||
}
|
||||
@ -1550,6 +1609,11 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
|
||||
registry->Register(StringSlice<UCS2>);
|
||||
registry->Register(StringSlice<UTF8>);
|
||||
|
||||
registry->Register(SlowWriteString<ASCII>);
|
||||
registry->Register(SlowWriteString<LATIN1>);
|
||||
registry->Register(SlowWriteString<UTF8>);
|
||||
registry->Register(fast_write_string.GetTypeInfo());
|
||||
registry->Register(FastWriteString);
|
||||
registry->Register(StringWrite<ASCII>);
|
||||
registry->Register(StringWrite<BASE64>);
|
||||
registry->Register(StringWrite<BASE64URL>);
|
||||
|
@ -56,6 +56,13 @@ using CFunctionWithInt64Fallback = void (*)(v8::Local<v8::Value>,
|
||||
v8::FastApiCallbackOptions&);
|
||||
using CFunctionWithBool = void (*)(v8::Local<v8::Value>, bool);
|
||||
|
||||
using CFunctionWriteString =
|
||||
uint32_t (*)(v8::Local<v8::Value> receiver,
|
||||
const v8::FastApiTypedArray<uint8_t>& dst,
|
||||
const v8::FastOneByteString& src,
|
||||
uint32_t offset,
|
||||
uint32_t max_length);
|
||||
|
||||
using CFunctionBufferCopy =
|
||||
uint32_t (*)(v8::Local<v8::Value> receiver,
|
||||
const v8::FastApiTypedArray<uint8_t>& source,
|
||||
@ -88,6 +95,7 @@ class ExternalReferenceRegistry {
|
||||
V(CFunctionWithInt64Fallback) \
|
||||
V(CFunctionWithBool) \
|
||||
V(CFunctionBufferCopy) \
|
||||
V(CFunctionWriteString) \
|
||||
V(const v8::CFunctionInfo*) \
|
||||
V(v8::FunctionCallback) \
|
||||
V(v8::AccessorNameGetterCallback) \
|
||||
|
Loading…
x
Reference in New Issue
Block a user