From 7aefcc83713615a63c704d38df8a6d944ccb482f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Sat, 13 Jul 2024 08:37:14 +0000 Subject: [PATCH] Bug 1907302 - Part 1: Add option to omit padding in Uint8Array.prototype.toBase64. r=spidermonkey-reviewers,dminor Changes from: https://github.com/tc39/proposal-arraybuffer-base64/pull/60 Differential Revision: https://phabricator.services.mozilla.com/D216279 --- js/src/tests/jstests.list | 3 -- js/src/vm/CommonPropertyNames.h | 1 + js/src/vm/TypedArrayObject.cpp | 51 +++++++++++++++++++++++++++------ 3 files changed, 43 insertions(+), 12 deletions(-) diff --git a/js/src/tests/jstests.list b/js/src/tests/jstests.list index 9dbe611596368..7d4c3092b746b 100644 --- a/js/src/tests/jstests.list +++ b/js/src/tests/jstests.list @@ -691,9 +691,6 @@ skip script test262/intl402/DateTimeFormat/offset-timezone-no-unicode-minus-sign skip script test262/built-ins/Uint8Array/prototype/setFromBase64/writes-up-to-error.js skip script test262/built-ins/Uint8Array/prototype/setFromHex/writes-up-to-error.js -# https://github.com/tc39/proposal-arraybuffer-base64/pull/60 -skip script test262/built-ins/Uint8Array/prototype/toBase64/omit-padding.js - ########################################################### # Tests disabled due to issues in test262 importer script # diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h index b2fd434de3667..912a27122b50e 100644 --- a/js/src/vm/CommonPropertyNames.h +++ b/js/src/vm/CommonPropertyNames.h @@ -445,6 +445,7 @@ MACRO2(of, "of") \ MACRO_(offset, "offset") \ MACRO2(ok, "ok") \ + MACRO_(omitPadding, "omitPadding") \ MACRO_(one, "one") \ MACRO_(optimizedOut, "optimizedOut") \ MACRO_(other, "other") \ diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp index 413ccc2679b3c..a75d7ebdee7dd 100644 --- a/js/src/vm/TypedArrayObject.cpp +++ b/js/src/vm/TypedArrayObject.cpp @@ -2618,6 +2618,24 @@ static bool GetLastChunkHandlingOption(JSContext* cx, Handle options, return false; } +enum class OmitPadding : bool { No, Yes }; + +/** + * Uint8Array.prototype.toBase64 ( [ options ] ) + * + * Helper to retrieve the "omitPadding" option. + */ +static bool GetOmitPaddingOption(JSContext* cx, Handle options, + OmitPadding* result) { + Rooted value(cx); + if (!GetProperty(cx, options, options, cx->names().omitPadding, &value)) { + return false; + } + + *result = static_cast(JS::ToBoolean(value)); + return true; +} + /** * Uint8Array.fromBase64 ( string [ , options ] ) * @@ -2945,6 +2963,7 @@ static bool uint8array_toBase64(JSContext* cx, const CallArgs& args) { // Steps 3-7. auto alphabet = Alphabet::Base64; + auto omitPadding = OmitPadding::No; if (args.hasDefined(0)) { // Step 3. (Inlined GetOptionsObject) Rooted options( @@ -2953,10 +2972,15 @@ static bool uint8array_toBase64(JSContext* cx, const CallArgs& args) { return false; } - // Steps 4-7. + // Steps 4-6. if (!GetAlphabetOption(cx, options, &alphabet)) { return false; } + + // Step 7. + if (!GetOmitPaddingOption(cx, options, &omitPadding)) { + return false; + } } // Step 8. (Partial) @@ -2967,11 +2991,16 @@ static bool uint8array_toBase64(JSContext* cx, const CallArgs& args) { } // Compute the output string length. Three input bytes are encoded as four - // characters, so the output length is ⌈length × 4/3⌉. + // characters, so the output length is ⌈length × 4/3⌉. When omitting padding, + // the output length is length + ⌈length / 3⌉. auto outLength = mozilla::CheckedInt{*length}; outLength += 2; outLength /= 3; - outLength *= 4; + if (omitPadding == OmitPadding::No) { + outLength *= 4; + } else { + outLength += *length; + } if (!outLength.isValid() || outLength.value() > JSString::MAX_LENGTH) { ReportAllocationOverflow(cx); return false; @@ -3009,28 +3038,32 @@ static bool uint8array_toBase64(JSContext* cx, const CallArgs& args) { sb.infallibleAppend(encode(u24 >> 0)); } - // Trailing two and one element bytes are padded with '='. + // Trailing two and one element bytes are optionally padded with '='. if (toRead == 2) { // Combine two input bytes into a single uint24 value. auto byte0 = jit::AtomicOperations::loadSafeWhenRacy(data++); auto byte1 = jit::AtomicOperations::loadSafeWhenRacy(data++); auto u24 = (uint32_t(byte0) << 16) | (uint32_t(byte1) << 8); - // Encode the uint24 value as base64, including padding. + // Encode the uint24 value as base64, optionally including padding. sb.infallibleAppend(encode(u24 >> 18)); sb.infallibleAppend(encode(u24 >> 12)); sb.infallibleAppend(encode(u24 >> 6)); - sb.infallibleAppend('='); + if (omitPadding == OmitPadding::No) { + sb.infallibleAppend('='); + } } else if (toRead == 1) { // Combine one input byte into a single uint24 value. auto byte0 = jit::AtomicOperations::loadSafeWhenRacy(data++); auto u24 = uint32_t(byte0) << 16; - // Encode the uint24 value as base64, including padding. + // Encode the uint24 value as base64, optionally including padding. sb.infallibleAppend(encode(u24 >> 18)); sb.infallibleAppend(encode(u24 >> 12)); - sb.infallibleAppend('='); - sb.infallibleAppend('='); + if (omitPadding == OmitPadding::No) { + sb.infallibleAppend('='); + sb.infallibleAppend('='); + } } else { MOZ_ASSERT(toRead == 0); }