Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
buffer: improve base64 and base64url performance
Co-authored-by: Daniel Lemire <daniel@lemire.me>
  • Loading branch information
anonrig and lemire committed Apr 8, 2024
commit a1670bc60377d0d5eb349b0dad2ec5d0ea56282d
85 changes: 71 additions & 14 deletions src/string_bytes.cc
Original file line number Diff line number Diff line change
Expand Up @@ -346,17 +346,65 @@ size_t StringBytes::Write(Isolate* isolate,
}

case BASE64URL:
// Fall through
case BASE64:
if (str->IsExternalOneByte()) {
if (str->IsExternalOneByte()) { // 8-bit case
auto ext = str->GetExternalOneByteStringResource();
nbytes = base64_decode(buf, buflen, ext->data(), ext->length());
auto result = simdutf::base64_to_binary_safe(
ext->data(), ext->length(), buf, buflen, simdutf::base64_url);
if (result.error == simdutf::error_code::SUCCESS) {
nbytes = result.count;
} else {
// The input does not follow the WHATWG forgiving-base64 specification
// adapted for base64url
// https://infra.spec.whatwg.org/#forgiving-base64-decode
nbytes = base64_decode(buf, buflen, ext->data(), ext->length());
}
} else {
String::Value value(isolate, str);
nbytes = base64_decode(buf, buflen, *value, value.length());
auto result =
simdutf::base64_to_binary(reinterpret_cast<const char16_t*>(*value),
value.length(),
buf,
simdutf::base64_url);
if (result.error == simdutf::error_code::SUCCESS) {
nbytes = result.count;
} else {
// The input does not follow the WHATWG forgiving-base64 specification
// (adapted for base64url with + and / replaced by - and _).
// https://infra.spec.whatwg.org/#forgiving-base64-decode
nbytes = base64_decode(buf, buflen, *value, value.length());
}
}
Comment thread
anonrig marked this conversation as resolved.
break;

case BASE64: {
if (str->IsExternalOneByte()) { // 8-bit case
auto ext = str->GetExternalOneByteStringResource();
auto result = simdutf::base64_to_binary_safe(
ext->data(), ext->length(), buf, buflen);
if (result.error == simdutf::error_code::SUCCESS) {
nbytes = buflen;
} else {
// The input does not follow the WHATWG forgiving-base64 specification
// https://infra.spec.whatwg.org/#forgiving-base64-decode
nbytes = base64_decode(buf, buflen, ext->data(), ext->length());
}
} else {
String::Value value(isolate, str);
auto result = simdutf::base64_to_binary_safe(
reinterpret_cast<const char16_t*>(*value),
value.length(),
buf,
buflen);
if (result.error == simdutf::error_code::SUCCESS) {
nbytes = buflen;
} else {
// The input does not follow the WHATWG base64 specification
// https://infra.spec.whatwg.org/#forgiving-base64-decode
nbytes = base64_decode(buf, buflen, *value, value.length());
}
}
break;
}
case HEX:
if (str->IsExternalOneByte()) {
auto ext = str->GetExternalOneByteStringResource();
Expand Down Expand Up @@ -411,9 +459,12 @@ Maybe<size_t> StringBytes::StorageSize(Isolate* isolate,
break;

case BASE64URL:
// Fall through
data_size = simdutf::base64_length_from_binary(str->Length(),
simdutf::base64_url);
break;

case BASE64:
data_size = base64_decoded_size_fast(str->Length());
data_size = simdutf::base64_length_from_binary(str->Length());
break;

case HEX:
Expand Down Expand Up @@ -452,11 +503,15 @@ Maybe<size_t> StringBytes::Size(Isolate* isolate,
case UCS2:
return Just(str->Length() * sizeof(uint16_t));

case BASE64URL:
// Fall through
case BASE64URL: {
String::Value value(isolate, str);
return Just(simdutf::base64_length_from_binary(value.length(),
simdutf::base64_url));
}

case BASE64: {
String::Value value(isolate, str);
return Just(base64_decoded_size(*value, value.length()));
return Just(simdutf::base64_length_from_binary(value.length()));
}

case HEX:
Expand Down Expand Up @@ -609,28 +664,30 @@ MaybeLocal<Value> StringBytes::Encode(Isolate* isolate,
return ExternOneByteString::NewFromCopy(isolate, buf, buflen, error);

case BASE64: {
size_t dlen = base64_encoded_size(buflen);
size_t dlen = simdutf::base64_length_from_binary(buflen);
char* dst = node::UncheckedMalloc(dlen);
if (dst == nullptr) {
*error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate);
return MaybeLocal<Value>();
}

size_t written = base64_encode(buf, buflen, dst, dlen);
size_t written = simdutf::binary_to_base64(buf, buflen, dst);
CHECK_EQ(written, dlen);

return ExternOneByteString::New(isolate, dst, dlen, error);
}

case BASE64URL: {
size_t dlen = base64_encoded_size(buflen, Base64Mode::URL);
size_t dlen =
simdutf::base64_length_from_binary(buflen, simdutf::base64_url);
char* dst = node::UncheckedMalloc(dlen);
if (dst == nullptr) {
*error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate);
return MaybeLocal<Value>();
}

size_t written = base64_encode(buf, buflen, dst, dlen, Base64Mode::URL);
size_t written =
simdutf::binary_to_base64(buf, buflen, dst, simdutf::base64_url);
CHECK_EQ(written, dlen);

return ExternOneByteString::New(isolate, dst, dlen, error);
Expand Down