Skip to content

Commit 3505782

Browse files
tniessentargos
authored andcommitted
buffer: make indexOf(byte) faster
Add a V8 fast API implementation for indexOfNumber, which significantly improves the performance of finding a single byte within a buffer. Refs: #52993 PR-URL: #53455 Reviewed-By: Yagiz Nizipli <[email protected]> Reviewed-By: Robert Nagy <[email protected]> Reviewed-By: Mohammed Keyvanzadeh <[email protected]>
1 parent b05c56e commit 3505782

File tree

2 files changed

+54
-20
lines changed

2 files changed

+54
-20
lines changed

src/node_buffer.cc

Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ using v8::ArrayBufferView;
5757
using v8::BackingStore;
5858
using v8::Context;
5959
using v8::EscapableHandleScope;
60+
using v8::FastApiTypedArray;
6061
using v8::FunctionCallbackInfo;
6162
using v8::Global;
6263
using v8::HandleScope;
@@ -1071,37 +1072,57 @@ void IndexOfBuffer(const FunctionCallbackInfo<Value>& args) {
10711072
result == haystack_length ? -1 : static_cast<int>(result));
10721073
}
10731074

1074-
void IndexOfNumber(const FunctionCallbackInfo<Value>& args) {
1075+
int32_t IndexOfNumber(const uint8_t* buffer_data,
1076+
size_t buffer_length,
1077+
uint32_t needle,
1078+
int64_t offset_i64,
1079+
bool is_forward) {
1080+
int64_t opt_offset = IndexOfOffset(buffer_length, offset_i64, 1, is_forward);
1081+
if (opt_offset <= -1 || buffer_length == 0) {
1082+
return -1;
1083+
}
1084+
size_t offset = static_cast<size_t>(opt_offset);
1085+
CHECK_LT(offset, buffer_length);
1086+
1087+
const void* ptr;
1088+
if (is_forward) {
1089+
ptr = memchr(buffer_data + offset, needle, buffer_length - offset);
1090+
} else {
1091+
ptr = node::stringsearch::MemrchrFill(buffer_data, needle, offset + 1);
1092+
}
1093+
const uint8_t* ptr_uint8 = static_cast<const uint8_t*>(ptr);
1094+
return ptr != nullptr ? static_cast<int32_t>(ptr_uint8 - buffer_data) : -1;
1095+
}
1096+
1097+
void SlowIndexOfNumber(const FunctionCallbackInfo<Value>& args) {
10751098
CHECK(args[1]->IsUint32());
10761099
CHECK(args[2]->IsNumber());
10771100
CHECK(args[3]->IsBoolean());
10781101

10791102
THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]);
1080-
ArrayBufferViewContents<char> buffer(args[0]);
1103+
ArrayBufferViewContents<uint8_t> buffer(args[0]);
10811104

10821105
uint32_t needle = args[1].As<Uint32>()->Value();
10831106
int64_t offset_i64 = args[2].As<Integer>()->Value();
10841107
bool is_forward = args[3]->IsTrue();
10851108

1086-
int64_t opt_offset =
1087-
IndexOfOffset(buffer.length(), offset_i64, 1, is_forward);
1088-
if (opt_offset <= -1 || buffer.length() == 0) {
1089-
return args.GetReturnValue().Set(-1);
1090-
}
1091-
size_t offset = static_cast<size_t>(opt_offset);
1092-
CHECK_LT(offset, buffer.length());
1109+
args.GetReturnValue().Set(IndexOfNumber(
1110+
buffer.data(), buffer.length(), needle, offset_i64, is_forward));
1111+
}
10931112

1094-
const void* ptr;
1095-
if (is_forward) {
1096-
ptr = memchr(buffer.data() + offset, needle, buffer.length() - offset);
1097-
} else {
1098-
ptr = node::stringsearch::MemrchrFill(buffer.data(), needle, offset + 1);
1099-
}
1100-
const char* ptr_char = static_cast<const char*>(ptr);
1101-
args.GetReturnValue().Set(ptr ? static_cast<int>(ptr_char - buffer.data())
1102-
: -1);
1113+
int32_t FastIndexOfNumber(v8::Local<v8::Value>,
1114+
const FastApiTypedArray<uint8_t>& buffer,
1115+
uint32_t needle,
1116+
int64_t offset_i64,
1117+
bool is_forward) {
1118+
uint8_t* buffer_data;
1119+
CHECK(buffer.getStorageIfAligned(&buffer_data));
1120+
return IndexOfNumber(
1121+
buffer_data, buffer.length(), needle, offset_i64, is_forward);
11031122
}
11041123

1124+
static v8::CFunction fast_index_of_number(
1125+
v8::CFunction::Make(FastIndexOfNumber));
11051126

11061127
void Swap16(const FunctionCallbackInfo<Value>& args) {
11071128
Environment* env = Environment::GetCurrent(args);
@@ -1413,7 +1434,11 @@ void Initialize(Local<Object> target,
14131434
SetMethodNoSideEffect(context, target, "compareOffset", CompareOffset);
14141435
SetMethod(context, target, "fill", Fill);
14151436
SetMethodNoSideEffect(context, target, "indexOfBuffer", IndexOfBuffer);
1416-
SetMethodNoSideEffect(context, target, "indexOfNumber", IndexOfNumber);
1437+
SetFastMethodNoSideEffect(context,
1438+
target,
1439+
"indexOfNumber",
1440+
SlowIndexOfNumber,
1441+
&fast_index_of_number);
14171442
SetMethodNoSideEffect(context, target, "indexOfString", IndexOfString);
14181443

14191444
SetMethod(context, target, "detachArrayBuffer", DetachArrayBuffer);
@@ -1472,7 +1497,9 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
14721497
registry->Register(CompareOffset);
14731498
registry->Register(Fill);
14741499
registry->Register(IndexOfBuffer);
1475-
registry->Register(IndexOfNumber);
1500+
registry->Register(SlowIndexOfNumber);
1501+
registry->Register(FastIndexOfNumber);
1502+
registry->Register(fast_index_of_number.GetTypeInfo());
14761503
registry->Register(IndexOfString);
14771504

14781505
registry->Register(Swap16);

src/node_external_reference.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ using CFunctionCallbackWithStrings =
2727
bool (*)(v8::Local<v8::Value>,
2828
const v8::FastOneByteString& input,
2929
const v8::FastOneByteString& base);
30+
using CFunctionCallbackWithUint8ArrayUint32Int64Bool =
31+
int32_t (*)(v8::Local<v8::Value>,
32+
const v8::FastApiTypedArray<uint8_t>&,
33+
uint32_t,
34+
int64_t,
35+
bool);
3036
using CFunctionWithUint32 = uint32_t (*)(v8::Local<v8::Value>,
3137
const uint32_t input);
3238
using CFunctionWithDoubleReturnDouble = double (*)(v8::Local<v8::Value>,
@@ -51,6 +57,7 @@ class ExternalReferenceRegistry {
5157
V(CFunctionCallbackWithBool) \
5258
V(CFunctionCallbackWithString) \
5359
V(CFunctionCallbackWithStrings) \
60+
V(CFunctionCallbackWithUint8ArrayUint32Int64Bool) \
5461
V(CFunctionWithUint32) \
5562
V(CFunctionWithDoubleReturnDouble) \
5663
V(CFunctionWithInt64Fallback) \

0 commit comments

Comments
 (0)