Skip to content

Commit 6344ddb

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

File tree

4 files changed

+114
-6
lines changed

4 files changed

+114
-6
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-6
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,13 @@ const {
2323
hexSlice,
2424
ucs2Slice,
2525
utf8Slice,
26-
asciiWrite,
2726
base64Write,
2827
base64urlWrite,
29-
latin1Write,
3028
hexWrite,
3129
ucs2Write,
32-
utf8Write,
3330
getZeroFillToggle,
3431
} = internalBinding('buffer');
32+
const bufferBinding = internalBinding('buffer');
3533

3634
const {
3735
privateSymbols: {
@@ -1036,13 +1034,28 @@ function addBufferPrototypeMethods(proto) {
10361034
proto.hexSlice = hexSlice;
10371035
proto.ucs2Slice = ucs2Slice;
10381036
proto.utf8Slice = utf8Slice;
1039-
proto.asciiWrite = asciiWrite;
1037+
proto.asciiWrite = function(string, offset = 0, length = this.byteLength) {
1038+
if (offset > this.byteLength) {
1039+
throw new ERR_BUFFER_OUT_OF_BOUNDS('offset');
1040+
}
1041+
return bufferBinding.asciiWriteStatic(this, string, offset, length);
1042+
};
10401043
proto.base64Write = base64Write;
10411044
proto.base64urlWrite = base64urlWrite;
1042-
proto.latin1Write = latin1Write;
1045+
proto.latin1Write = function(string, offset = 0, length = this.byteLength) {
1046+
if (offset > this.byteLength) {
1047+
throw new ERR_BUFFER_OUT_OF_BOUNDS('offset');
1048+
}
1049+
return bufferBinding.latin1WriteStatic(this, string, offset, length);
1050+
};
10431051
proto.hexWrite = hexWrite;
10441052
proto.ucs2Write = ucs2Write;
1045-
proto.utf8Write = utf8Write;
1053+
proto.utf8Write = function(string, offset = 0, length = this.byteLength) {
1054+
if (offset > this.byteLength) {
1055+
throw new ERR_BUFFER_OUT_OF_BOUNDS('offset');
1056+
}
1057+
return bufferBinding.utf8WriteStatic(this, string, offset, length);
1058+
};
10461059
}
10471060

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

src/node_buffer.cc

+67
Original file line numberDiff line numberDiff line change
@@ -1425,6 +1425,54 @@ 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+
CHECK(dst.length() <= std::numeric_limits<uint32_t>::max());
1465+
1466+
max_length = std::min(static_cast<uint32_t>(dst.length()) - offset, max_length);
1467+
1468+
memcpy(dst_data, src.data, max_length);
1469+
1470+
return max_length;
1471+
}
1472+
1473+
static v8::CFunction fast_write_string(
1474+
v8::CFunction::Make(FastWriteString));
1475+
14281476
void Initialize(Local<Object> target,
14291477
Local<Value> unused,
14301478
Local<Context> context,
@@ -1494,6 +1542,22 @@ void Initialize(Local<Object> target,
14941542
SetMethod(context, target, "ucs2Write", StringWrite<UCS2>);
14951543
SetMethod(context, target, "utf8Write", StringWrite<UTF8>);
14961544

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

@@ -1535,6 +1599,9 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
15351599
registry->Register(StringSlice<UCS2>);
15361600
registry->Register(StringSlice<UTF8>);
15371601

1602+
registry->Register(SlowWriteString<ASCII>);
1603+
registry->Register(fast_write_string.GetTypeInfo());
1604+
registry->Register(FastWriteString);
15381605
registry->Register(StringWrite<ASCII>);
15391606
registry->Register(StringWrite<BASE64>);
15401607
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)