Skip to content

Commit d47c4e4

Browse files
committed
buffer: use fast API for writing one-byte strings
PR-URL: nodejs#54310
1 parent 298ff4f commit d47c4e4

File tree

4 files changed

+113
-3
lines changed

4 files changed

+113
-3
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
'use strict';
2+
3+
const common = require('../common.js');
4+
const bench = common.createBenchmark(main, {
5+
encoding: [
6+
'utf8', 'ascii', 'latin1',
7+
],
8+
len: [1, 8, 16, 32],
9+
n: [1e6],
10+
});
11+
12+
function main({ len, n, encoding }) {
13+
const buf = Buffer.allocUnsafe(len);
14+
const string = Buffer.from('a'.repeat(len)).toString();
15+
bench.start();
16+
for (let i = 0; i < n; ++i) {
17+
buf.write(string, 0, encoding);
18+
}
19+
bench.end(n);
20+
}

lib/internal/buffer.js

+19-3
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ const {
3232
utf8Write,
3333
getZeroFillToggle,
3434
} = internalBinding('buffer');
35+
const bufferBinding = internalBinding('buffer');
3536

3637
const {
3738
privateSymbols: {
@@ -1036,13 +1037,28 @@ function addBufferPrototypeMethods(proto) {
10361037
proto.hexSlice = hexSlice;
10371038
proto.ucs2Slice = ucs2Slice;
10381039
proto.utf8Slice = utf8Slice;
1039-
proto.asciiWrite = asciiWrite;
1040+
proto.asciiWrite = function (string, offset = 0, length = this.byteLength) {
1041+
if (offset > this.byteLength) {
1042+
throw new ERR_BUFFER_OUT_OF_BOUNDS('offset');
1043+
}
1044+
return bufferBinding.asciiWriteStatic(this, string, offset, length);
1045+
}
10401046
proto.base64Write = base64Write;
10411047
proto.base64urlWrite = base64urlWrite;
1042-
proto.latin1Write = latin1Write;
1048+
proto.latin1Write = function (string, offset = 0, length = this.byteLength) {
1049+
if (offset > this.byteLength) {
1050+
throw new ERR_BUFFER_OUT_OF_BOUNDS('offset');
1051+
}
1052+
return bufferBinding.latin1WriteStatic(this, string, offset, length);
1053+
}
10431054
proto.hexWrite = hexWrite;
10441055
proto.ucs2Write = ucs2Write;
1045-
proto.utf8Write = utf8Write;
1056+
proto.utf8Write = function (string, offset = 0, length = this.byteLength) {
1057+
if (offset > this.byteLength) {
1058+
throw new ERR_BUFFER_OUT_OF_BOUNDS('offset');
1059+
}
1060+
return bufferBinding.utf8WriteStatic(this, string, offset, length);
1061+
}
10461062
}
10471063

10481064
// This would better be placed in internal/worker/io.js, but that doesn't work

src/node_buffer.cc

+66
Original file line numberDiff line numberDiff line change
@@ -1425,6 +1425,53 @@ void CopyArrayBuffer(const FunctionCallbackInfo<Value>& args) {
14251425
memcpy(dest, src, bytes_to_copy);
14261426
}
14271427

1428+
template <encoding encoding>
1429+
void SlowWriteString(const FunctionCallbackInfo<Value>& args) {
1430+
Environment* env = Environment::GetCurrent(args);
1431+
1432+
THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]);
1433+
SPREAD_BUFFER_ARG(args[0], ts_obj);
1434+
1435+
THROW_AND_RETURN_IF_NOT_STRING(env, args[1], "argument");
1436+
1437+
Local<String> str = args[1]->ToString(env->context()).ToLocalChecked();
1438+
1439+
size_t offset = 0;
1440+
size_t max_length = 0;
1441+
1442+
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[2], 0, &offset));
1443+
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[3], ts_obj_length - offset,
1444+
&max_length));
1445+
1446+
max_length = std::min(ts_obj_length - offset, max_length);
1447+
1448+
if (max_length == 0)
1449+
return args.GetReturnValue().Set(0);
1450+
1451+
uint32_t written = StringBytes::Write(
1452+
env->isolate(), ts_obj_data + offset, max_length, str, encoding);
1453+
args.GetReturnValue().Set(written);
1454+
}
1455+
1456+
uint32_t FastWriteString(Local<Value> receiver,
1457+
const v8::FastApiTypedArray<uint8_t>& dst,
1458+
const v8::FastOneByteString& src,
1459+
uint32_t offset,
1460+
uint32_t max_length) {
1461+
uint8_t* dst_data;
1462+
CHECK(dst.getStorageIfAligned(&dst_data));
1463+
CHECK(offset <= dst.length());
1464+
1465+
max_length = std::min(static_cast<uint32_t>(dst.length()) - offset, max_length);
1466+
1467+
memcpy(dst_data, src.data, max_length);
1468+
1469+
return max_length;
1470+
}
1471+
1472+
static v8::CFunction fast_write_string(
1473+
v8::CFunction::Make(FastWriteString));
1474+
14281475
void Initialize(Local<Object> target,
14291476
Local<Value> unused,
14301477
Local<Context> context,
@@ -1494,6 +1541,22 @@ void Initialize(Local<Object> target,
14941541
SetMethod(context, target, "ucs2Write", StringWrite<UCS2>);
14951542
SetMethod(context, target, "utf8Write", StringWrite<UTF8>);
14961543

1544+
SetFastMethod(context,
1545+
target,
1546+
"asciiWriteStatic",
1547+
SlowWriteString<ASCII>,
1548+
&fast_write_string);
1549+
SetFastMethod(context,
1550+
target,
1551+
"latin1WriteStatic",
1552+
SlowWriteString<LATIN1>,
1553+
&fast_write_string);
1554+
SetFastMethod(context,
1555+
target,
1556+
"utf8WriteStatic",
1557+
SlowWriteString<UTF8>,
1558+
&fast_write_string);
1559+
14971560
SetMethod(context, target, "getZeroFillToggle", GetZeroFillToggle);
14981561
}
14991562

@@ -1535,6 +1598,9 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
15351598
registry->Register(StringSlice<UCS2>);
15361599
registry->Register(StringSlice<UTF8>);
15371600

1601+
registry->Register(SlowWriteString<ASCII>);
1602+
registry->Register(fast_write_string.GetTypeInfo());
1603+
registry->Register(FastWriteString);
15381604
registry->Register(StringWrite<ASCII>);
15391605
registry->Register(StringWrite<BASE64>);
15401606
registry->Register(StringWrite<BASE64URL>);

src/node_external_reference.h

+8
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,13 @@ using CFunctionWithInt64Fallback = void (*)(v8::Local<v8::Value>,
5656
v8::FastApiCallbackOptions&);
5757
using CFunctionWithBool = void (*)(v8::Local<v8::Value>, bool);
5858

59+
using CFunctionWriteString =
60+
uint32_t (*)(v8::Local<v8::Value> receiver,
61+
const v8::FastApiTypedArray<uint8_t>& dst,
62+
const v8::FastOneByteString& src,
63+
uint32_t offset,
64+
uint32_t max_length);
65+
5966
using CFunctionBufferCopy =
6067
uint32_t (*)(v8::Local<v8::Value> receiver,
6168
const v8::FastApiTypedArray<uint8_t>& source,
@@ -88,6 +95,7 @@ class ExternalReferenceRegistry {
8895
V(CFunctionWithInt64Fallback) \
8996
V(CFunctionWithBool) \
9097
V(CFunctionBufferCopy) \
98+
V(CFunctionWriteString) \
9199
V(const v8::CFunctionInfo*) \
92100
V(v8::FunctionCallback) \
93101
V(v8::AccessorNameGetterCallback) \

0 commit comments

Comments
 (0)