Skip to content

Commit dfdc062

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 <yagiz.nizipli@sentry.io> Reviewed-By: Robert Nagy <ronagy@icloud.com> Reviewed-By: Mohammed Keyvanzadeh <mohammadkeyvanzade94@gmail.com>
1 parent fad1793 commit dfdc062

File tree

2 files changed

+53
-20
lines changed

2 files changed

+53
-20
lines changed

src/node_buffer.cc

+46-20
Original file line numberDiff line numberDiff line change
@@ -1089,37 +1089,57 @@ void IndexOfBuffer(const FunctionCallbackInfo<Value>& args) {
10891089
result == haystack_length ? -1 : static_cast<int>(result));
10901090
}
10911091

1092-
void IndexOfNumber(const FunctionCallbackInfo<Value>& args) {
1092+
int32_t IndexOfNumber(const uint8_t* buffer_data,
1093+
size_t buffer_length,
1094+
uint32_t needle,
1095+
int64_t offset_i64,
1096+
bool is_forward) {
1097+
int64_t opt_offset = IndexOfOffset(buffer_length, offset_i64, 1, is_forward);
1098+
if (opt_offset <= -1 || buffer_length == 0) {
1099+
return -1;
1100+
}
1101+
size_t offset = static_cast<size_t>(opt_offset);
1102+
CHECK_LT(offset, buffer_length);
1103+
1104+
const void* ptr;
1105+
if (is_forward) {
1106+
ptr = memchr(buffer_data + offset, needle, buffer_length - offset);
1107+
} else {
1108+
ptr = node::stringsearch::MemrchrFill(buffer_data, needle, offset + 1);
1109+
}
1110+
const uint8_t* ptr_uint8 = static_cast<const uint8_t*>(ptr);
1111+
return ptr != nullptr ? static_cast<int32_t>(ptr_uint8 - buffer_data) : -1;
1112+
}
1113+
1114+
void SlowIndexOfNumber(const FunctionCallbackInfo<Value>& args) {
10931115
CHECK(args[1]->IsUint32());
10941116
CHECK(args[2]->IsNumber());
10951117
CHECK(args[3]->IsBoolean());
10961118

10971119
THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]);
1098-
ArrayBufferViewContents<char> buffer(args[0]);
1120+
ArrayBufferViewContents<uint8_t> buffer(args[0]);
10991121

11001122
uint32_t needle = args[1].As<Uint32>()->Value();
11011123
int64_t offset_i64 = args[2].As<Integer>()->Value();
11021124
bool is_forward = args[3]->IsTrue();
11031125

1104-
int64_t opt_offset =
1105-
IndexOfOffset(buffer.length(), offset_i64, 1, is_forward);
1106-
if (opt_offset <= -1 || buffer.length() == 0) {
1107-
return args.GetReturnValue().Set(-1);
1108-
}
1109-
size_t offset = static_cast<size_t>(opt_offset);
1110-
CHECK_LT(offset, buffer.length());
1126+
args.GetReturnValue().Set(IndexOfNumber(
1127+
buffer.data(), buffer.length(), needle, offset_i64, is_forward));
1128+
}
11111129

1112-
const void* ptr;
1113-
if (is_forward) {
1114-
ptr = memchr(buffer.data() + offset, needle, buffer.length() - offset);
1115-
} else {
1116-
ptr = node::stringsearch::MemrchrFill(buffer.data(), needle, offset + 1);
1117-
}
1118-
const char* ptr_char = static_cast<const char*>(ptr);
1119-
args.GetReturnValue().Set(ptr ? static_cast<int>(ptr_char - buffer.data())
1120-
: -1);
1130+
int32_t FastIndexOfNumber(v8::Local<v8::Value>,
1131+
const FastApiTypedArray<uint8_t>& buffer,
1132+
uint32_t needle,
1133+
int64_t offset_i64,
1134+
bool is_forward) {
1135+
uint8_t* buffer_data;
1136+
CHECK(buffer.getStorageIfAligned(&buffer_data));
1137+
return IndexOfNumber(
1138+
buffer_data, buffer.length(), needle, offset_i64, is_forward);
11211139
}
11221140

1141+
static v8::CFunction fast_index_of_number(
1142+
v8::CFunction::Make(FastIndexOfNumber));
11231143

11241144
void Swap16(const FunctionCallbackInfo<Value>& args) {
11251145
Environment* env = Environment::GetCurrent(args);
@@ -1431,7 +1451,11 @@ void Initialize(Local<Object> target,
14311451
SetMethodNoSideEffect(context, target, "compareOffset", CompareOffset);
14321452
SetMethod(context, target, "fill", Fill);
14331453
SetMethodNoSideEffect(context, target, "indexOfBuffer", IndexOfBuffer);
1434-
SetMethodNoSideEffect(context, target, "indexOfNumber", IndexOfNumber);
1454+
SetFastMethodNoSideEffect(context,
1455+
target,
1456+
"indexOfNumber",
1457+
SlowIndexOfNumber,
1458+
&fast_index_of_number);
14351459
SetMethodNoSideEffect(context, target, "indexOfString", IndexOfString);
14361460

14371461
SetMethod(context, target, "detachArrayBuffer", DetachArrayBuffer);
@@ -1492,7 +1516,9 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
14921516
registry->Register(CompareOffset);
14931517
registry->Register(Fill);
14941518
registry->Register(IndexOfBuffer);
1495-
registry->Register(IndexOfNumber);
1519+
registry->Register(SlowIndexOfNumber);
1520+
registry->Register(FastIndexOfNumber);
1521+
registry->Register(fast_index_of_number.GetTypeInfo());
14961522
registry->Register(IndexOfString);
14971523

14981524
registry->Register(Swap16);

src/node_external_reference.h

+7
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ using CFunctionCallbackWithTwoUint8ArraysFallback =
4141
const v8::FastApiTypedArray<uint8_t>&,
4242
const v8::FastApiTypedArray<uint8_t>&,
4343
v8::FastApiCallbackOptions&);
44+
using CFunctionCallbackWithUint8ArrayUint32Int64Bool =
45+
int32_t (*)(v8::Local<v8::Value>,
46+
const v8::FastApiTypedArray<uint8_t>&,
47+
uint32_t,
48+
int64_t,
49+
bool);
4450
using CFunctionWithUint32 = uint32_t (*)(v8::Local<v8::Value>,
4551
const uint32_t input);
4652
using CFunctionWithDoubleReturnDouble = double (*)(v8::Local<v8::Value>,
@@ -68,6 +74,7 @@ class ExternalReferenceRegistry {
6874
V(CFunctionCallbackWithStrings) \
6975
V(CFunctionCallbackWithTwoUint8Arrays) \
7076
V(CFunctionCallbackWithTwoUint8ArraysFallback) \
77+
V(CFunctionCallbackWithUint8ArrayUint32Int64Bool) \
7178
V(CFunctionWithUint32) \
7279
V(CFunctionWithDoubleReturnDouble) \
7380
V(CFunctionWithInt64Fallback) \

0 commit comments

Comments
 (0)