Skip to content

Commit

Permalink
buffer: make indexOf(byte) faster
Browse files Browse the repository at this point in the history
Add a V8 fast API implementation for indexOfNumber, which significantly
improves the performance of finding a single byte within a buffer.

Refs: nodejs#52993
PR-URL: nodejs#53455
Reviewed-By: Yagiz Nizipli <[email protected]>
Reviewed-By: Robert Nagy <[email protected]>
Reviewed-By: Mohammed Keyvanzadeh <[email protected]>
  • Loading branch information
tniessen authored and sophonieb committed Jun 20, 2024
1 parent ce8388d commit 953c7e9
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 20 deletions.
66 changes: 46 additions & 20 deletions src/node_buffer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1089,37 +1089,57 @@ void IndexOfBuffer(const FunctionCallbackInfo<Value>& args) {
result == haystack_length ? -1 : static_cast<int>(result));
}

void IndexOfNumber(const FunctionCallbackInfo<Value>& args) {
int32_t IndexOfNumber(const uint8_t* buffer_data,
size_t buffer_length,
uint32_t needle,
int64_t offset_i64,
bool is_forward) {
int64_t opt_offset = IndexOfOffset(buffer_length, offset_i64, 1, is_forward);
if (opt_offset <= -1 || buffer_length == 0) {
return -1;
}
size_t offset = static_cast<size_t>(opt_offset);
CHECK_LT(offset, buffer_length);

const void* ptr;
if (is_forward) {
ptr = memchr(buffer_data + offset, needle, buffer_length - offset);
} else {
ptr = node::stringsearch::MemrchrFill(buffer_data, needle, offset + 1);
}
const uint8_t* ptr_uint8 = static_cast<const uint8_t*>(ptr);
return ptr != nullptr ? static_cast<int32_t>(ptr_uint8 - buffer_data) : -1;
}

void SlowIndexOfNumber(const FunctionCallbackInfo<Value>& args) {
CHECK(args[1]->IsUint32());
CHECK(args[2]->IsNumber());
CHECK(args[3]->IsBoolean());

THROW_AND_RETURN_UNLESS_BUFFER(Environment::GetCurrent(args), args[0]);
ArrayBufferViewContents<char> buffer(args[0]);
ArrayBufferViewContents<uint8_t> buffer(args[0]);

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

int64_t opt_offset =
IndexOfOffset(buffer.length(), offset_i64, 1, is_forward);
if (opt_offset <= -1 || buffer.length() == 0) {
return args.GetReturnValue().Set(-1);
}
size_t offset = static_cast<size_t>(opt_offset);
CHECK_LT(offset, buffer.length());
args.GetReturnValue().Set(IndexOfNumber(
buffer.data(), buffer.length(), needle, offset_i64, is_forward));
}

const void* ptr;
if (is_forward) {
ptr = memchr(buffer.data() + offset, needle, buffer.length() - offset);
} else {
ptr = node::stringsearch::MemrchrFill(buffer.data(), needle, offset + 1);
}
const char* ptr_char = static_cast<const char*>(ptr);
args.GetReturnValue().Set(ptr ? static_cast<int>(ptr_char - buffer.data())
: -1);
int32_t FastIndexOfNumber(v8::Local<v8::Value>,
const FastApiTypedArray<uint8_t>& buffer,
uint32_t needle,
int64_t offset_i64,
bool is_forward) {
uint8_t* buffer_data;
CHECK(buffer.getStorageIfAligned(&buffer_data));
return IndexOfNumber(
buffer_data, buffer.length(), needle, offset_i64, is_forward);
}

static v8::CFunction fast_index_of_number(
v8::CFunction::Make(FastIndexOfNumber));

void Swap16(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Expand Down Expand Up @@ -1431,7 +1451,11 @@ void Initialize(Local<Object> target,
SetMethodNoSideEffect(context, target, "compareOffset", CompareOffset);
SetMethod(context, target, "fill", Fill);
SetMethodNoSideEffect(context, target, "indexOfBuffer", IndexOfBuffer);
SetMethodNoSideEffect(context, target, "indexOfNumber", IndexOfNumber);
SetFastMethodNoSideEffect(context,
target,
"indexOfNumber",
SlowIndexOfNumber,
&fast_index_of_number);
SetMethodNoSideEffect(context, target, "indexOfString", IndexOfString);

SetMethod(context, target, "detachArrayBuffer", DetachArrayBuffer);
Expand Down Expand Up @@ -1492,7 +1516,9 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
registry->Register(CompareOffset);
registry->Register(Fill);
registry->Register(IndexOfBuffer);
registry->Register(IndexOfNumber);
registry->Register(SlowIndexOfNumber);
registry->Register(FastIndexOfNumber);
registry->Register(fast_index_of_number.GetTypeInfo());
registry->Register(IndexOfString);

registry->Register(Swap16);
Expand Down
7 changes: 7 additions & 0 deletions src/node_external_reference.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ using CFunctionCallbackWithTwoUint8ArraysFallback =
const v8::FastApiTypedArray<uint8_t>&,
const v8::FastApiTypedArray<uint8_t>&,
v8::FastApiCallbackOptions&);
using CFunctionCallbackWithUint8ArrayUint32Int64Bool =
int32_t (*)(v8::Local<v8::Value>,
const v8::FastApiTypedArray<uint8_t>&,
uint32_t,
int64_t,
bool);
using CFunctionWithUint32 = uint32_t (*)(v8::Local<v8::Value>,
const uint32_t input);
using CFunctionWithDoubleReturnDouble = double (*)(v8::Local<v8::Value>,
Expand Down Expand Up @@ -68,6 +74,7 @@ class ExternalReferenceRegistry {
V(CFunctionCallbackWithStrings) \
V(CFunctionCallbackWithTwoUint8Arrays) \
V(CFunctionCallbackWithTwoUint8ArraysFallback) \
V(CFunctionCallbackWithUint8ArrayUint32Int64Bool) \
V(CFunctionWithUint32) \
V(CFunctionWithDoubleReturnDouble) \
V(CFunctionWithInt64Fallback) \
Expand Down

0 comments on commit 953c7e9

Please sign in to comment.