Skip to content

Commit 4221631

Browse files
anonriglemire
andauthored
buffer: improve base64 and base64url performance
Co-authored-by: Daniel Lemire <daniel@lemire.me> PR-URL: #52428 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent abb46cf commit 4221631

File tree

1 file changed

+119
-14
lines changed

1 file changed

+119
-14
lines changed

src/string_bytes.cc

+119-14
Original file line numberDiff line numberDiff line change
@@ -346,17 +346,113 @@ size_t StringBytes::Write(Isolate* isolate,
346346
}
347347

348348
case BASE64URL:
349-
// Fall through
350-
case BASE64:
351-
if (str->IsExternalOneByte()) {
349+
if (str->IsExternalOneByte()) { // 8-bit case
352350
auto ext = str->GetExternalOneByteStringResource();
353-
nbytes = base64_decode(buf, buflen, ext->data(), ext->length());
351+
size_t written_len = buflen;
352+
auto result = simdutf::base64_to_binary_safe(
353+
ext->data(), ext->length(), buf, written_len, simdutf::base64_url);
354+
if (result.error == simdutf::error_code::SUCCESS) {
355+
nbytes = written_len;
356+
} else {
357+
// The input does not follow the WHATWG forgiving-base64 specification
358+
// adapted for base64url
359+
// https://infra.spec.whatwg.org/#forgiving-base64-decode
360+
nbytes = base64_decode(buf, buflen, ext->data(), ext->length());
361+
}
362+
} else if (str->IsOneByte()) {
363+
MaybeStackBuffer<uint8_t> stack_buf(str->Length());
364+
str->WriteOneByte(isolate,
365+
stack_buf.out(),
366+
0,
367+
str->Length(),
368+
String::NO_NULL_TERMINATION);
369+
size_t written_len = buflen;
370+
auto result = simdutf::base64_to_binary_safe(
371+
reinterpret_cast<const char*>(*stack_buf),
372+
stack_buf.length(),
373+
buf,
374+
written_len,
375+
simdutf::base64_url);
376+
if (result.error == simdutf::error_code::SUCCESS) {
377+
nbytes = written_len;
378+
} else {
379+
// The input does not follow the WHATWG forgiving-base64 specification
380+
// (adapted for base64url with + and / replaced by - and _).
381+
// https://infra.spec.whatwg.org/#forgiving-base64-decode
382+
nbytes = base64_decode(buf, buflen, *stack_buf, stack_buf.length());
383+
}
354384
} else {
355385
String::Value value(isolate, str);
356-
nbytes = base64_decode(buf, buflen, *value, value.length());
386+
size_t written_len = buflen;
387+
auto result = simdutf::base64_to_binary_safe(
388+
reinterpret_cast<const char16_t*>(*value),
389+
value.length(),
390+
buf,
391+
written_len,
392+
simdutf::base64_url);
393+
if (result.error == simdutf::error_code::SUCCESS) {
394+
nbytes = written_len;
395+
} else {
396+
// The input does not follow the WHATWG forgiving-base64 specification
397+
// (adapted for base64url with + and / replaced by - and _).
398+
// https://infra.spec.whatwg.org/#forgiving-base64-decode
399+
nbytes = base64_decode(buf, buflen, *value, value.length());
400+
}
357401
}
358402
break;
359403

404+
case BASE64: {
405+
if (str->IsExternalOneByte()) { // 8-bit case
406+
auto ext = str->GetExternalOneByteStringResource();
407+
size_t written_len = buflen;
408+
auto result = simdutf::base64_to_binary_safe(
409+
ext->data(), ext->length(), buf, written_len);
410+
if (result.error == simdutf::error_code::SUCCESS) {
411+
nbytes = written_len;
412+
} else {
413+
// The input does not follow the WHATWG forgiving-base64 specification
414+
// https://infra.spec.whatwg.org/#forgiving-base64-decode
415+
nbytes = base64_decode(buf, buflen, ext->data(), ext->length());
416+
}
417+
} else if (str->IsOneByte()) {
418+
MaybeStackBuffer<uint8_t> stack_buf(str->Length());
419+
str->WriteOneByte(isolate,
420+
stack_buf.out(),
421+
0,
422+
str->Length(),
423+
String::NO_NULL_TERMINATION);
424+
size_t written_len = buflen;
425+
auto result = simdutf::base64_to_binary_safe(
426+
reinterpret_cast<const char*>(*stack_buf),
427+
stack_buf.length(),
428+
buf,
429+
written_len);
430+
if (result.error == simdutf::error_code::SUCCESS) {
431+
nbytes = written_len;
432+
} else {
433+
// The input does not follow the WHATWG forgiving-base64 specification
434+
// (adapted for base64url with + and / replaced by - and _).
435+
// https://infra.spec.whatwg.org/#forgiving-base64-decode
436+
nbytes = base64_decode(buf, buflen, *stack_buf, stack_buf.length());
437+
}
438+
} else {
439+
String::Value value(isolate, str);
440+
size_t written_len = buflen;
441+
auto result = simdutf::base64_to_binary_safe(
442+
reinterpret_cast<const char16_t*>(*value),
443+
value.length(),
444+
buf,
445+
written_len);
446+
if (result.error == simdutf::error_code::SUCCESS) {
447+
nbytes = written_len;
448+
} else {
449+
// The input does not follow the WHATWG base64 specification
450+
// https://infra.spec.whatwg.org/#forgiving-base64-decode
451+
nbytes = base64_decode(buf, buflen, *value, value.length());
452+
}
453+
}
454+
break;
455+
}
360456
case HEX:
361457
if (str->IsExternalOneByte()) {
362458
auto ext = str->GetExternalOneByteStringResource();
@@ -411,9 +507,12 @@ Maybe<size_t> StringBytes::StorageSize(Isolate* isolate,
411507
break;
412508

413509
case BASE64URL:
414-
// Fall through
510+
data_size = simdutf::base64_length_from_binary(str->Length(),
511+
simdutf::base64_url);
512+
break;
513+
415514
case BASE64:
416-
data_size = base64_decoded_size_fast(str->Length());
515+
data_size = simdutf::base64_length_from_binary(str->Length());
417516
break;
418517

419518
case HEX:
@@ -452,11 +551,15 @@ Maybe<size_t> StringBytes::Size(Isolate* isolate,
452551
case UCS2:
453552
return Just(str->Length() * sizeof(uint16_t));
454553

455-
case BASE64URL:
456-
// Fall through
554+
case BASE64URL: {
555+
String::Value value(isolate, str);
556+
return Just(simdutf::base64_length_from_binary(value.length(),
557+
simdutf::base64_url));
558+
}
559+
457560
case BASE64: {
458561
String::Value value(isolate, str);
459-
return Just(base64_decoded_size(*value, value.length()));
562+
return Just(simdutf::base64_length_from_binary(value.length()));
460563
}
461564

462565
case HEX:
@@ -609,28 +712,30 @@ MaybeLocal<Value> StringBytes::Encode(Isolate* isolate,
609712
return ExternOneByteString::NewFromCopy(isolate, buf, buflen, error);
610713

611714
case BASE64: {
612-
size_t dlen = base64_encoded_size(buflen);
715+
size_t dlen = simdutf::base64_length_from_binary(buflen);
613716
char* dst = node::UncheckedMalloc(dlen);
614717
if (dst == nullptr) {
615718
*error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate);
616719
return MaybeLocal<Value>();
617720
}
618721

619-
size_t written = base64_encode(buf, buflen, dst, dlen);
722+
size_t written = simdutf::binary_to_base64(buf, buflen, dst);
620723
CHECK_EQ(written, dlen);
621724

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

625728
case BASE64URL: {
626-
size_t dlen = base64_encoded_size(buflen, Base64Mode::URL);
729+
size_t dlen =
730+
simdutf::base64_length_from_binary(buflen, simdutf::base64_url);
627731
char* dst = node::UncheckedMalloc(dlen);
628732
if (dst == nullptr) {
629733
*error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate);
630734
return MaybeLocal<Value>();
631735
}
632736

633-
size_t written = base64_encode(buf, buflen, dst, dlen, Base64Mode::URL);
737+
size_t written =
738+
simdutf::binary_to_base64(buf, buflen, dst, simdutf::base64_url);
634739
CHECK_EQ(written, dlen);
635740

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

0 commit comments

Comments
 (0)