Created
April 2, 2024 18:37
-
-
Save lemire/237b40db76087d332d73f0c702538dab to your computer and use it in GitHub Desktop.
base64 runtime functions (simdutf)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// on success: returns a non-negative integer indicating the size of the | |
// binary produced, it most be no larger than 2147483647 bytes. | |
// In case of error, a negativ value is returned: | |
// * -2 indicates an invalid character, | |
// * -1 indicates a single character remained, | |
// * -3 indicates a possible overflow (i.e., more than 2 GB output). | |
void Base64ToBinary(const FunctionCallbackInfo<Value>& args) { | |
Environment* env = Environment::GetCurrent(args); | |
THROW_AND_RETURN_UNLESS_BUFFER(env, args.This()); | |
SPREAD_BUFFER_ARG(args.This(), ts_obj); | |
THROW_AND_RETURN_IF_NOT_STRING(env, args[0], "argument"); | |
Local<String> str = args[0]->ToString(env->context()).ToLocalChecked(); | |
size_t offset = 0; | |
size_t max_length = 0; | |
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[1], 0, &offset)); | |
if (offset > ts_obj_length) { | |
return node::THROW_ERR_BUFFER_OUT_OF_BOUNDS( | |
env, "\"offset\" is outside of buffer bounds"); | |
} | |
THROW_AND_RETURN_IF_OOB(ParseArrayIndex(env, args[2], ts_obj_length - offset, | |
&max_length)); | |
if (max_length == 0) | |
return args.GetReturnValue().Set(0); | |
char* buf = ts_obj_data + offset; | |
size_t buflen = max_length; | |
if(buflen > INT32_MAX) { | |
return args.GetReturnValue().Set(-3); | |
} | |
int32_t written{0}; | |
if (str->IsExternalOneByte()) { // 8-bit case | |
auto ext = str->GetExternalOneByteStringResource(); | |
simdutf::result r = simdutf::base64_to_binary_safe(ext->data(), ext->length(), buf, buflen, simdutf::base64_default); | |
if(r.error == simdutf::error_code::SUCCESS) { | |
written = buflen; | |
} else if(r.error == simdutf::error_code::INVALID_BASE64_CHARACTER) { | |
written = -2; | |
} else if(r.error == simdutf::error_code::BASE64_INPUT_REMAINDER) { | |
written = -1; | |
} else { | |
written = -3; | |
} | |
} else { // 16-bit case | |
String::Value value(env->isolate(), str); | |
simdutf::result r = simdutf::base64_to_binary_safe(reinterpret_cast<const char16_t*>(*value), value.length(), buf, buflen, simdutf::base64_default); | |
if(r.error == simdutf::error_code::SUCCESS) { | |
written = buflen; | |
} else if(r.error == simdutf::error_code::INVALID_BASE64_CHARACTER) { | |
written = -2; | |
} else if(r.error == simdutf::error_code::BASE64_INPUT_REMAINDER) { | |
written = -1; | |
} else { | |
written = -3; | |
} | |
} | |
args.GetReturnValue().Set(written); | |
} | |
// rest is made of code samples, not actual working code... | |
///.... | |
case BASE64URL: { | |
// When in URL mode, base64_encode uses a non-accelerated routine, so we | |
// adopt simdutf. | |
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 = simdutf::binary_to_base64(buf, buflen, dst, simdutf::base64_url); | |
CHECK_EQ(written, dlen); | |
return ExternOneByteString::New(isolate, dst, dlen, error); | |
} | |
///.... | |
case BASE64URL: | |
if (str->IsExternalOneByte()) { // 8-bit case | |
auto ext = str->GetExternalOneByteStringResource(); | |
// Try with WHATWG base64 standard first, adapted for base64url | |
simdutf::result r = simdutf::base64_to_binary_safe(ext->data(), ext->length(), buf, buflen, simdutf::base64_url); | |
if(r.error == simdutf::error_code::SUCCESS) { | |
nbytes = buflen; | |
} 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 { // 16-bit case | |
String::Value value(isolate, str); | |
// Try with WHATWG base64 standard first | |
simdutf::result r = simdutf::base64_to_binary_safe(reinterpret_cast<const char16_t*>(*value), value.length(), buf, buflen, simdutf::base64_url); | |
if(r.error == simdutf::error_code::SUCCESS) { | |
nbytes = buflen; | |
} 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()); | |
} | |
} | |
break; | |
case BASE64: | |
if (str->IsExternalOneByte()) { // 8-bit case | |
auto ext = str->GetExternalOneByteStringResource(); | |
// Try with WHATWG base64 standard first | |
simdutf::result r = simdutf::base64_to_binary_safe(ext->data(), ext->length(), buf, buflen, simdutf::base64_default); | |
if(r.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 { // 16-bit case | |
String::Value value(isolate, str); | |
// Try with WHATWG base64 standard first | |
simdutf::result r = simdutf::base64_to_binary_safe(reinterpret_cast<const char16_t*>(*value), value.length(), buf, buflen, simdutf::base64_default); | |
if(r.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; | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment