Skip to content

Commit a5a60e6

Browse files
ronagRafaelGSS
authored andcommitted
buffer: use fast API for writing one-byte strings
PR-URL: #54310 PR-URL: #54311 Reviewed-By: Michaël Zasso <[email protected]> Reviewed-By: Yagiz Nizipli <[email protected]> Reviewed-By: Santiago Gimeno <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]>
1 parent 7b641bc commit a5a60e6

File tree

4 files changed

+125
-9
lines changed

4 files changed

+125
-9
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

+30-6
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,13 @@ const {
2323
hexSlice,
2424
ucs2Slice,
2525
utf8Slice,
26-
asciiWrite,
26+
asciiWriteStatic,
2727
base64Write,
2828
base64urlWrite,
29-
latin1Write,
29+
latin1WriteStatic,
3030
hexWrite,
3131
ucs2Write,
32-
utf8Write,
32+
utf8WriteStatic,
3333
getZeroFillToggle,
3434
} = internalBinding('buffer');
3535

@@ -1036,13 +1036,37 @@ function addBufferPrototypeMethods(proto) {
10361036
proto.hexSlice = hexSlice;
10371037
proto.ucs2Slice = ucs2Slice;
10381038
proto.utf8Slice = utf8Slice;
1039-
proto.asciiWrite = asciiWrite;
1039+
proto.asciiWrite = function(string, offset = 0, length = this.byteLength) {
1040+
if (offset < 0 || offset > this.byteLength) {
1041+
throw new ERR_BUFFER_OUT_OF_BOUNDS('offset');
1042+
}
1043+
if (length < 0 || length > this.byteLength - offset) {
1044+
throw new ERR_BUFFER_OUT_OF_BOUNDS('length');
1045+
}
1046+
return asciiWriteStatic(this, string, offset, length);
1047+
};
10401048
proto.base64Write = base64Write;
10411049
proto.base64urlWrite = base64urlWrite;
1042-
proto.latin1Write = latin1Write;
1050+
proto.latin1Write = function(string, offset = 0, length = this.byteLength) {
1051+
if (offset < 0 || offset > this.byteLength) {
1052+
throw new ERR_BUFFER_OUT_OF_BOUNDS('offset');
1053+
}
1054+
if (length < 0 || length > this.byteLength - offset) {
1055+
throw new ERR_BUFFER_OUT_OF_BOUNDS('length');
1056+
}
1057+
return latin1WriteStatic(this, string, offset, length);
1058+
};
10431059
proto.hexWrite = hexWrite;
10441060
proto.ucs2Write = ucs2Write;
1045-
proto.utf8Write = utf8Write;
1061+
proto.utf8Write = function(string, offset = 0, length = this.byteLength) {
1062+
if (offset < 0 || offset > this.byteLength) {
1063+
throw new ERR_BUFFER_OUT_OF_BOUNDS('offset');
1064+
}
1065+
if (length < 0 || length > this.byteLength - offset) {
1066+
throw new ERR_BUFFER_OUT_OF_BOUNDS('length');
1067+
}
1068+
return utf8WriteStatic(this, string, offset, length);
1069+
};
10461070
}
10471071

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

src/node_buffer.cc

+67-3
Original file line numberDiff line numberDiff line change
@@ -1442,6 +1442,52 @@ void CopyArrayBuffer(const FunctionCallbackInfo<Value>& args) {
14421442
memcpy(dest, src, bytes_to_copy);
14431443
}
14441444

1445+
template <encoding encoding>
1446+
void SlowWriteString(const FunctionCallbackInfo<Value>& args) {
1447+
Environment* env = Environment::GetCurrent(args);
1448+
1449+
THROW_AND_RETURN_UNLESS_BUFFER(env, args[0]);
1450+
SPREAD_BUFFER_ARG(args[0], ts_obj);
1451+
1452+
THROW_AND_RETURN_IF_NOT_STRING(env, args[1], "argument");
1453+
1454+
Local<String> str = args[1]->ToString(env->context()).ToLocalChecked();
1455+
1456+
size_t offset = 0;
1457+
size_t max_length = 0;
1458+
1459+
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[2], 0, &offset));
1460+
THROW_AND_RETURN_IF_OOB(
1461+
ParseArrayIndex(env, args[3], ts_obj_length - offset, &max_length));
1462+
1463+
max_length = std::min(ts_obj_length - offset, max_length);
1464+
1465+
if (max_length == 0) return args.GetReturnValue().Set(0);
1466+
1467+
uint32_t written = StringBytes::Write(
1468+
env->isolate(), ts_obj_data + offset, max_length, str, encoding);
1469+
args.GetReturnValue().Set(written);
1470+
}
1471+
1472+
uint32_t FastWriteString(Local<Value> receiver,
1473+
const v8::FastApiTypedArray<uint8_t>& dst,
1474+
const v8::FastOneByteString& src,
1475+
uint32_t offset,
1476+
uint32_t max_length) {
1477+
uint8_t* dst_data;
1478+
CHECK(dst.getStorageIfAligned(&dst_data));
1479+
CHECK(offset <= dst.length());
1480+
CHECK(dst.length() - offset <= std::numeric_limits<uint32_t>::max());
1481+
1482+
max_length = std::min<uint32_t>(dst.length() - offset, max_length);
1483+
1484+
memcpy(dst_data, src.data, max_length);
1485+
1486+
return max_length;
1487+
}
1488+
1489+
static v8::CFunction fast_write_string(v8::CFunction::Make(FastWriteString));
1490+
14451491
void Initialize(Local<Object> target,
14461492
Local<Value> unused,
14471493
Local<Context> context,
@@ -1502,13 +1548,26 @@ void Initialize(Local<Object> target,
15021548
SetMethodNoSideEffect(context, target, "ucs2Slice", StringSlice<UCS2>);
15031549
SetMethodNoSideEffect(context, target, "utf8Slice", StringSlice<UTF8>);
15041550

1505-
SetMethod(context, target, "asciiWrite", StringWrite<ASCII>);
15061551
SetMethod(context, target, "base64Write", StringWrite<BASE64>);
15071552
SetMethod(context, target, "base64urlWrite", StringWrite<BASE64URL>);
1508-
SetMethod(context, target, "latin1Write", StringWrite<LATIN1>);
15091553
SetMethod(context, target, "hexWrite", StringWrite<HEX>);
15101554
SetMethod(context, target, "ucs2Write", StringWrite<UCS2>);
1511-
SetMethod(context, target, "utf8Write", StringWrite<UTF8>);
1555+
1556+
SetFastMethod(context,
1557+
target,
1558+
"asciiWriteStatic",
1559+
SlowWriteString<ASCII>,
1560+
&fast_write_string);
1561+
SetFastMethod(context,
1562+
target,
1563+
"latin1WriteStatic",
1564+
SlowWriteString<LATIN1>,
1565+
&fast_write_string);
1566+
SetFastMethod(context,
1567+
target,
1568+
"utf8WriteStatic",
1569+
SlowWriteString<UTF8>,
1570+
&fast_write_string);
15121571

15131572
SetMethod(context, target, "getZeroFillToggle", GetZeroFillToggle);
15141573
}
@@ -1550,6 +1609,11 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
15501609
registry->Register(StringSlice<UCS2>);
15511610
registry->Register(StringSlice<UTF8>);
15521611

1612+
registry->Register(SlowWriteString<ASCII>);
1613+
registry->Register(SlowWriteString<LATIN1>);
1614+
registry->Register(SlowWriteString<UTF8>);
1615+
registry->Register(fast_write_string.GetTypeInfo());
1616+
registry->Register(FastWriteString);
15531617
registry->Register(StringWrite<ASCII>);
15541618
registry->Register(StringWrite<BASE64>);
15551619
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)