tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit f1ac1edc87e033d6227f27441acdb5824e1a43e1
parent d915ceffa193657cb035ed140c6f3de567cc47af
Author: André Bargull <andre.bargull@gmail.com>
Date:   Thu, 30 Oct 2025 18:21:57 +0000

Bug 1997029: Simplify condition when to take base64 fast path. r=iain

Differential Revision: https://phabricator.services.mozilla.com/D270539

Diffstat:
Ajs/src/tests/non262/TypedArray/base64-whitespace.js | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mjs/src/vm/TypedArrayObject.cpp | 111++++++++++++++++++++++++++++++++++++++++---------------------------------------
2 files changed, 117 insertions(+), 55 deletions(-)

diff --git a/js/src/tests/non262/TypedArray/base64-whitespace.js b/js/src/tests/non262/TypedArray/base64-whitespace.js @@ -0,0 +1,61 @@ +for (let string of [ + // No trailing padding + "ab", + "abc", + + // With trailing padding + "ab==", + "abc=", + + // Full chunk + "abcd", +]) { + for (let i = 0; i <= 4; ++i) { + let str = "abcd".repeat(i) + string; + + let expected = Uint8Array.fromBase64(str); + + for (let j = 1; j < 8; ++j) { + let space = " ".repeat(j); + + // Leading whitespace + assertEqArray(Uint8Array.fromBase64(space + str), expected); + + // Trailing whitespace + assertEqArray(Uint8Array.fromBase64(str + space), expected); + + // Interspersed whitespace + assertEqArray(Uint8Array.fromBase64(str.split("").join(space)), expected); + } + } +} + +// Invalid trailing chunk +for (let string of [ + "a", + "a=", + "a==", + "a===", +]) { + for (let i = 0; i <= 4; ++i) { + let str = "abcd".repeat(i) + string; + + assertThrowsInstanceOf(() => Uint8Array.fromBase64(str), SyntaxError); + + for (let j = 1; j < 8; ++j) { + let space = " ".repeat(j); + + // Leading whitespace + assertThrowsInstanceOf(() => Uint8Array.fromBase64(space + str), SyntaxError); + + // Trailing whitespace + assertThrowsInstanceOf(() => Uint8Array.fromBase64(str + space), SyntaxError); + + // Interspersed whitespace + assertThrowsInstanceOf(() => Uint8Array.fromBase64(str.split("").join(space)), SyntaxError); + } + } +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp @@ -4773,74 +4773,81 @@ static auto FromBase64(const CharT* chars, size_t length, Alphabet alphabet, // Initial loop to process only full chunks. Doesn't perform any error // reporting and expects that at least four characters can be read per loop // iteration and that the output has enough space for a decoded chunk. + if (length >= 4) { + size_t lastValidIndex = length - 4; + while (canAppend(3) && index <= lastValidIndex) { + // Fast path: Read four consecutive characters. - size_t alignedLength = length & ~0x3; - while (canAppend(3) && index < alignedLength) { - // Fast path: Read four consecutive characters. + // Step 10.a. (Performed in slow path.) - // Step 10.a. (Performed in slow path.) + // Step 10.b. (Moved out of loop.) - // Step 10.b. (Moved out of loop.) + // Steps 10.c and 10.e-g. + uint32_t chunk = decode4Chars(chars + index); - // Steps 10.c and 10.e-g. - uint32_t chunk = decode4Chars(chars + index); + // Steps 10.h-i. (Not applicable in this loop.) - // Steps 10.h-i. (Not applicable in this loop.) + // Steps 10.d and 10.j-l. + if (MOZ_LIKELY(int32_t(chunk) >= 0)) { + // Step 10.j-l. + decodeChunk(chunk); - // Steps 10.d and 10.j-l. - if (MOZ_LIKELY(int32_t(chunk) >= 0)) { - // Step 10.j-l. - decodeChunk(chunk); + // Step 10.d. + index += 4; + continue; + } - // Step 10.d. - index += 4; - continue; - } + // Slow path: Read four characters, ignoring whitespace. - // Slow path: Read four characters, ignoring whitespace. + // Steps 10.a and 10.b. + CharT part[4]; + size_t i = index; + size_t j = 0; + while (i < length && j < 4) { + auto ch = chars[i++]; - // Steps 10.a and 10.b. - CharT part[4]; - size_t i = index; - size_t j = 0; - while (i < length && j < 4) { - auto ch = chars[i++]; + // Step 10.a. + if (mozilla::IsAsciiWhitespace(ch)) { + continue; + } - // Step 10.a. - if (mozilla::IsAsciiWhitespace(ch)) { - continue; + // Step 10.c. + part[j++] = ch; } - // Step 10.c. - part[j++] = ch; - } + // Steps 10.d-l. + if (MOZ_LIKELY(j == 4)) { + // Steps 10.e-g. + uint32_t chunk = decode4Chars(part); - // Steps 10.d-l. - if (MOZ_LIKELY(j == 4)) { - // Steps 10.e-g. - uint32_t chunk = decode4Chars(part); + // Steps 10.h-i. (Not applicable in this loop.) - // Steps 10.h-i. (Not applicable in this loop.) + // Steps 10.d and 10.j-l. + if (MOZ_LIKELY(int32_t(chunk) >= 0)) { + // Step 10.j-l. + decodeChunk(chunk); - // Steps 10.d and 10.j-l. - if (MOZ_LIKELY(int32_t(chunk) >= 0)) { - // Step 10.j-l. - decodeChunk(chunk); - - // Step 10.d. - index = i; - continue; + // Step 10.d. + index = i; + continue; + } } + + // Padding or invalid characters, or end of input. The next loop will + // process any characters left in the input. + break; } - // Padding or invalid characters, or end of input. The next loop will - // process any characters left in the input. - break; - } + // Step 10.b.ii. + if (index == length) { + return Base64Result::Ok(length, written()); + } - // Step 10.b.ii. - if (index == length) { - return Base64Result::Ok(length, written()); + // Step 10.l.v. (Reordered) + if (!canAppend(1)) { + MOZ_ASSERT(written() > 0); + return Base64Result::Ok(index, written()); + } } // Step 4. @@ -4848,12 +4855,6 @@ static auto FromBase64(const CharT* chars, size_t length, Alphabet alphabet, // String index after the last fully read base64 chunk. size_t read = index; - // Step 10.l.v. (Reordered) - if (!canAppend(1)) { - MOZ_ASSERT(written() > 0); - return Base64Result::Ok(read, written()); - } - // Step 5. (Not applicable in our implementation.) // Step 6.