1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
9 #include "mozilla/ArrayUtils.h"
10 #include "mozilla/UniquePtrExtensions.h"
11 #include "nsIInputStream.h"
19 // BEGIN base64 encode code copied and modified from NSPR
20 const unsigned char* const base
=
21 (unsigned char*)"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
22 "abcdefghijklmnopqrstuvwxyz"
25 // The Base64 encoder assumes all characters are less than 256; for 16-bit
26 // strings, that means assuming that all characters are within range, and
27 // masking off high bits if necessary.
29 uint8_t CharTo8Bit(T aChar
) {
30 return uint8_t(aChar
);
33 template <typename SrcT
, typename DestT
>
34 static void Encode3to4(const SrcT
* aSrc
, DestT
* aDest
) {
35 uint32_t b32
= (uint32_t)0;
38 for (i
= 0; i
< 3; ++i
) {
40 b32
|= CharTo8Bit(aSrc
[i
]);
43 for (i
= 0; i
< 4; ++i
) {
44 aDest
[i
] = base
[(uint32_t)((b32
>> j
) & 0x3F)];
49 template <typename SrcT
, typename DestT
>
50 static void Encode2to4(const SrcT
* aSrc
, DestT
* aDest
) {
51 uint8_t src0
= CharTo8Bit(aSrc
[0]);
52 uint8_t src1
= CharTo8Bit(aSrc
[1]);
53 aDest
[0] = base
[(uint32_t)((src0
>> 2) & 0x3F)];
54 aDest
[1] = base
[(uint32_t)(((src0
& 0x03) << 4) | ((src1
>> 4) & 0x0F))];
55 aDest
[2] = base
[(uint32_t)((src1
& 0x0F) << 2)];
56 aDest
[3] = DestT('=');
59 template <typename SrcT
, typename DestT
>
60 static void Encode1to4(const SrcT
* aSrc
, DestT
* aDest
) {
61 uint8_t src0
= CharTo8Bit(aSrc
[0]);
62 aDest
[0] = base
[(uint32_t)((src0
>> 2) & 0x3F)];
63 aDest
[1] = base
[(uint32_t)((src0
& 0x03) << 4)];
64 aDest
[2] = DestT('=');
65 aDest
[3] = DestT('=');
68 template <typename SrcT
, typename DestT
>
69 static void Encode(const SrcT
* aSrc
, uint32_t aSrcLen
, DestT
* aDest
) {
70 while (aSrcLen
>= 3) {
71 Encode3to4(aSrc
, aDest
);
79 Encode2to4(aSrc
, aDest
);
82 Encode1to4(aSrc
, aDest
);
87 MOZ_ASSERT_UNREACHABLE("coding error");
91 // END base64 encode code copied and modified from NSPR.
94 struct EncodeInputStream_State
{
97 typename
T::char_type
* buffer
;
100 template <typename T
>
101 nsresult
EncodeInputStream_Encoder(nsIInputStream
* aStream
, void* aClosure
,
102 const char* aFromSegment
, uint32_t aToOffset
,
103 uint32_t aCount
, uint32_t* aWriteCount
) {
104 MOZ_ASSERT(aCount
> 0, "Er, what?");
106 EncodeInputStream_State
<T
>* state
=
107 static_cast<EncodeInputStream_State
<T
>*>(aClosure
);
109 // We consume the whole data always.
110 *aWriteCount
= aCount
;
112 // If we have any data left from last time, encode it now.
113 uint32_t countRemaining
= aCount
;
114 const unsigned char* src
= (const unsigned char*)aFromSegment
;
115 if (state
->charsOnStack
) {
116 MOZ_ASSERT(state
->charsOnStack
== 1 || state
->charsOnStack
== 2);
118 // Not enough data to compose a triple.
119 if (state
->charsOnStack
== 1 && countRemaining
== 1) {
120 state
->charsOnStack
= 2;
121 state
->c
[1] = src
[0];
125 uint32_t consumed
= 0;
126 unsigned char firstSet
[4];
127 if (state
->charsOnStack
== 1) {
128 firstSet
[0] = state
->c
[0];
129 firstSet
[1] = src
[0];
130 firstSet
[2] = src
[1];
133 } else /* state->charsOnStack == 2 */ {
134 firstSet
[0] = state
->c
[0];
135 firstSet
[1] = state
->c
[1];
136 firstSet
[2] = src
[0];
141 Encode(firstSet
, 3, state
->buffer
);
143 countRemaining
-= consumed
;
145 state
->charsOnStack
= 0;
148 if (!countRemaining
) {
153 // Encode as many full triplets as possible.
154 uint32_t encodeLength
= countRemaining
- countRemaining
% 3;
155 MOZ_ASSERT(encodeLength
% 3 == 0, "Should have an exact number of triplets!");
156 Encode(src
, encodeLength
, state
->buffer
);
157 state
->buffer
+= (encodeLength
/ 3) * 4;
159 countRemaining
-= encodeLength
;
161 if (countRemaining
) {
162 // We should never have a full triplet left at this point.
163 MOZ_ASSERT(countRemaining
< 3, "We should have encoded more!");
164 state
->c
[0] = src
[0];
165 state
->c
[1] = (countRemaining
== 2) ? src
[1] : '\0';
166 state
->charsOnStack
= countRemaining
;
172 mozilla::Result
<uint32_t, nsresult
> CalculateBase64EncodedLength(
173 const size_t aBinaryLen
, const uint32_t aPrefixLen
= 0) {
174 mozilla::CheckedUint32 res
= aBinaryLen
;
175 // base 64 encoded length is 4/3rds the length of the input data, rounded up
180 if (!res
.isValid()) {
181 return mozilla::Err(NS_ERROR_FAILURE
);
186 template <typename T
>
187 nsresult
EncodeInputStream(nsIInputStream
* aInputStream
, T
& aDest
,
188 uint32_t aCount
, uint32_t aOffset
) {
190 uint64_t count64
= aCount
;
193 rv
= aInputStream
->Available(&count64
);
194 if (NS_WARN_IF(NS_FAILED(rv
))) {
197 // if count64 is over 4GB, it will be failed at the below condition,
198 // then will return NS_ERROR_OUT_OF_MEMORY
199 aCount
= (uint32_t)count64
;
202 const auto base64LenOrErr
= CalculateBase64EncodedLength(count64
, aOffset
);
203 if (base64LenOrErr
.isErr()) {
204 // XXX For some reason, it was NS_ERROR_OUT_OF_MEMORY here instead of
205 // NS_ERROR_FAILURE, so we keep that.
206 return NS_ERROR_OUT_OF_MEMORY
;
209 auto handleOrErr
= aDest
.BulkWrite(base64LenOrErr
.inspect(), aOffset
, false);
210 if (handleOrErr
.isErr()) {
211 return handleOrErr
.unwrapErr();
214 auto handle
= handleOrErr
.unwrap();
216 EncodeInputStream_State
<T
> state
{
217 .c
= {'\0', '\0', '\0'},
219 .buffer
= handle
.Elements() + aOffset
,
225 rv
= aInputStream
->ReadSegments(&EncodeInputStream_Encoder
<T
>,
226 (void*)&state
, aCount
, &read
);
228 if (rv
== NS_BASE_STREAM_WOULD_BLOCK
) {
229 MOZ_CRASH("Not implemented for async streams!");
231 if (rv
== NS_ERROR_NOT_IMPLEMENTED
) {
232 MOZ_CRASH("Requires a stream that implements ReadSegments!");
244 // Finish encoding if anything is left
245 if (state
.charsOnStack
) {
246 Encode(state
.c
, state
.charsOnStack
, state
.buffer
);
250 // If we encountered EOF before reading aCount bytes, the resulting string
251 // could be shorter than predicted, so determine the length from the state.
252 size_t trueLength
= state
.buffer
- handle
.Elements();
253 handle
.Finish(trueLength
, false);
258 // Maps an encoded character to a value in the Base64 alphabet, per
259 // RFC 4648, Table 1. Invalid input characters map to UINT8_MAX.
261 static const uint8_t kBase64DecodeTable
[] = {
263 /* 0 */ 255, 255, 255, 255, 255, 255, 255, 255,
264 /* 8 */ 255, 255, 255, 255, 255, 255, 255, 255,
265 /* 16 */ 255, 255, 255, 255, 255, 255, 255, 255,
266 /* 24 */ 255, 255, 255, 255, 255, 255, 255, 255,
267 /* 32 */ 255, 255, 255, 255, 255, 255, 255, 255,
268 /* 40 */ 255, 255, 255,
273 /* 48 */ /* 0 - 9 */ 52, 53, 54, 55, 56, 57, 58, 59,
274 /* 56 */ 60, 61, 255, 255, 255, 255, 255, 255,
276 /* 64 */ 255, /* A - Z */ 0, 1, 2, 3, 4, 5, 6,
277 /* 72 */ 7, 8, 9, 10, 11, 12, 13, 14,
278 /* 80 */ 15, 16, 17, 18, 19, 20, 21, 22,
279 /* 88 */ 23, 24, 25, 255, 255, 255, 255, 255,
280 /* 96 */ 255, /* a - z */ 26, 27, 28, 29, 30, 31, 32,
281 /* 104 */ 33, 34, 35, 36, 37, 38, 39, 40,
282 /* 112 */ 41, 42, 43, 44, 45, 46, 47, 48,
283 /* 120 */ 49, 50, 51, 255, 255, 255, 255, 255,
285 static_assert(std::size(kBase64DecodeTable
) == 0x80);
288 template <typename T
>
289 [[nodiscard
]] bool Base64CharToValue(T aChar
, uint8_t* aValue
) {
290 size_t index
= static_cast<uint8_t>(aChar
);
291 if (index
>= std::size(kBase64DecodeTable
)) {
295 *aValue
= kBase64DecodeTable
[index
];
296 return *aValue
!= 255;
299 static const char kBase64URLAlphabet
[] =
300 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
301 static_assert(std::size(kBase64URLAlphabet
) == 0x41);
303 // Maps an encoded character to a value in the Base64 URL alphabet, per
304 // RFC 4648, Table 2. Invalid input characters map to UINT8_MAX.
305 static const uint8_t kBase64URLDecodeTable
[] = {
307 255, 255, 255, 255, 255, 255, 255, 255,
308 255, 255, 255, 255, 255, 255, 255, 255,
309 255, 255, 255, 255, 255, 255, 255, 255,
310 255, 255, 255, 255, 255, 255, 255, 255,
311 255, 255, 255, 255, 255, 255, 255, 255,
312 255, 255, 255, 255, 255,
315 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, /* 0 - 9 */
316 255, 255, 255, 255, 255, 255, 255,
317 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
318 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, /* A - Z */
322 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
323 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, /* a - z */
324 255, 255, 255, 255, 255,
326 static_assert(std::size(kBase64URLDecodeTable
) == 0x80);
329 bool Base64URLCharToValue(char aChar
, uint8_t* aValue
) {
330 uint8_t index
= static_cast<uint8_t>(aChar
);
331 if (index
>= std::size(kBase64URLDecodeTable
)) {
335 *aValue
= kBase64URLDecodeTable
[index
];
336 return *aValue
!= 255;
343 nsresult
Base64EncodeInputStream(nsIInputStream
* aInputStream
,
344 nsACString
& aDest
, uint32_t aCount
,
346 return EncodeInputStream
<nsACString
>(aInputStream
, aDest
, aCount
, aOffset
);
349 nsresult
Base64EncodeInputStream(nsIInputStream
* aInputStream
, nsAString
& aDest
,
350 uint32_t aCount
, uint32_t aOffset
) {
351 return EncodeInputStream
<nsAString
>(aInputStream
, aDest
, aCount
, aOffset
);
354 nsresult
Base64Encode(const char* aBinary
, uint32_t aBinaryLen
,
356 if (aBinaryLen
== 0) {
357 *aBase64
= (char*)moz_xmalloc(1);
358 (*aBase64
)[0] = '\0';
362 const auto base64LenOrErr
= CalculateBase64EncodedLength(aBinaryLen
);
363 if (base64LenOrErr
.isErr()) {
364 return base64LenOrErr
.inspectErr();
366 const uint32_t base64Len
= base64LenOrErr
.inspect();
370 // Add one byte for null termination.
371 UniqueFreePtr
<char[]> base64((char*)malloc(base64Len
+ 1));
373 return NS_ERROR_OUT_OF_MEMORY
;
376 Encode(aBinary
, aBinaryLen
, base64
.get());
377 base64
[base64Len
] = '\0';
379 *aBase64
= base64
.release();
383 template <bool Append
= false, typename T
, typename U
>
384 static nsresult
Base64EncodeHelper(const T
* const aBinary
,
385 const size_t aBinaryLen
, U
& aBase64
) {
386 if (aBinaryLen
== 0) {
393 const uint32_t prefixLen
= Append
? aBase64
.Length() : 0;
394 const auto base64LenOrErr
=
395 CalculateBase64EncodedLength(aBinaryLen
, prefixLen
);
396 if (base64LenOrErr
.isErr()) {
397 return base64LenOrErr
.inspectErr();
399 const uint32_t base64Len
= base64LenOrErr
.inspect();
401 auto handleOrErr
= aBase64
.BulkWrite(base64Len
, prefixLen
, false);
402 if (handleOrErr
.isErr()) {
403 return handleOrErr
.unwrapErr();
406 auto handle
= handleOrErr
.unwrap();
408 Encode(aBinary
, aBinaryLen
, handle
.Elements() + prefixLen
);
409 handle
.Finish(base64Len
, false);
413 nsresult
Base64EncodeAppend(const char* aBinary
, uint32_t aBinaryLen
,
414 nsAString
& aBase64
) {
415 return Base64EncodeHelper
<true>(aBinary
, aBinaryLen
, aBase64
);
418 nsresult
Base64EncodeAppend(const char* aBinary
, uint32_t aBinaryLen
,
419 nsACString
& aBase64
) {
420 return Base64EncodeHelper
<true>(aBinary
, aBinaryLen
, aBase64
);
423 nsresult
Base64EncodeAppend(const nsACString
& aBinary
, nsACString
& aBase64
) {
424 return Base64EncodeHelper
<true>(aBinary
.BeginReading(), aBinary
.Length(),
428 nsresult
Base64EncodeAppend(const nsACString
& aBinary
, nsAString
& aBase64
) {
429 return Base64EncodeHelper
<true>(aBinary
.BeginReading(), aBinary
.Length(),
433 nsresult
Base64Encode(const char* aBinary
, uint32_t aBinaryLen
,
434 nsACString
& aBase64
) {
435 return Base64EncodeHelper(aBinary
, aBinaryLen
, aBase64
);
438 nsresult
Base64Encode(const char* aBinary
, uint32_t aBinaryLen
,
439 nsAString
& aBase64
) {
440 return Base64EncodeHelper(aBinary
, aBinaryLen
, aBase64
);
443 nsresult
Base64Encode(const nsACString
& aBinary
, nsACString
& aBase64
) {
444 return Base64EncodeHelper(aBinary
.BeginReading(), aBinary
.Length(), aBase64
);
447 nsresult
Base64Encode(const nsACString
& aBinary
, nsAString
& aBase64
) {
448 return Base64EncodeHelper(aBinary
.BeginReading(), aBinary
.Length(), aBase64
);
451 nsresult
Base64Encode(const nsAString
& aBinary
, nsAString
& aBase64
) {
452 return Base64EncodeHelper(aBinary
.BeginReading(), aBinary
.Length(), aBase64
);
455 template <typename T
, typename U
, typename Decoder
>
456 static bool Decode4to3(const T
* aSrc
, U
* aDest
, Decoder aToVal
) {
458 if (!aToVal(aSrc
[0], &w
) || !aToVal(aSrc
[1], &x
) || !aToVal(aSrc
[2], &y
) ||
459 !aToVal(aSrc
[3], &z
)) {
462 aDest
[0] = U(uint8_t(w
<< 2 | x
>> 4));
463 aDest
[1] = U(uint8_t(x
<< 4 | y
>> 2));
464 aDest
[2] = U(uint8_t(y
<< 6 | z
));
468 template <typename T
, typename U
, typename Decoder
>
469 static bool Decode3to2(const T
* aSrc
, U
* aDest
, Decoder aToVal
) {
471 if (!aToVal(aSrc
[0], &w
) || !aToVal(aSrc
[1], &x
) || !aToVal(aSrc
[2], &y
)) {
474 aDest
[0] = U(uint8_t(w
<< 2 | x
>> 4));
475 aDest
[1] = U(uint8_t(x
<< 4 | y
>> 2));
479 template <typename T
, typename U
, typename Decoder
>
480 static bool Decode2to1(const T
* aSrc
, U
* aDest
, Decoder aToVal
) {
482 if (!aToVal(aSrc
[0], &w
) || !aToVal(aSrc
[1], &x
)) {
485 aDest
[0] = U(uint8_t(w
<< 2 | x
>> 4));
489 template <typename SrcT
, typename DestT
>
490 static nsresult
Base64DecodeHelper(const SrcT
* aBase64
, uint32_t aBase64Len
,
491 DestT
* aBinary
, uint32_t* aBinaryLen
) {
494 const SrcT
* input
= aBase64
;
495 uint32_t inputLength
= aBase64Len
;
496 DestT
* binary
= aBinary
;
497 uint32_t binaryLength
= 0;
499 // Handle trailing '=' characters.
500 if (inputLength
&& (inputLength
% 4 == 0)) {
501 if (aBase64
[inputLength
- 1] == SrcT('=')) {
502 if (aBase64
[inputLength
- 2] == SrcT('=')) {
510 while (inputLength
>= 4) {
511 if (!Decode4to3(input
, binary
, Base64CharToValue
<SrcT
>)) {
512 return NS_ERROR_INVALID_ARG
;
521 switch (inputLength
) {
523 if (!Decode3to2(input
, binary
, Base64CharToValue
<SrcT
>)) {
524 return NS_ERROR_INVALID_ARG
;
529 if (!Decode2to1(input
, binary
, Base64CharToValue
<SrcT
>)) {
530 return NS_ERROR_INVALID_ARG
;
535 return NS_ERROR_INVALID_ARG
;
539 MOZ_CRASH("Too many characters leftover");
542 aBinary
[binaryLength
] = DestT('\0');
543 *aBinaryLen
= binaryLength
;
548 nsresult
Base64Decode(const char* aBase64
, uint32_t aBase64Len
, char** aBinary
,
549 uint32_t* aBinaryLen
) {
550 // Check for overflow.
551 if (aBase64Len
> UINT32_MAX
/ 3) {
552 return NS_ERROR_FAILURE
;
555 // Don't ask PR_Base64Decode to decode the empty string.
556 if (aBase64Len
== 0) {
557 *aBinary
= (char*)moz_xmalloc(1);
558 (*aBinary
)[0] = '\0';
564 *aBinaryLen
= (aBase64Len
* 3) / 4;
566 // Add one byte for null termination.
567 UniqueFreePtr
<char[]> binary((char*)malloc(*aBinaryLen
+ 1));
569 return NS_ERROR_OUT_OF_MEMORY
;
573 Base64DecodeHelper(aBase64
, aBase64Len
, binary
.get(), aBinaryLen
);
578 *aBinary
= binary
.release();
582 template <typename T
, typename U
>
583 static nsresult
Base64DecodeString(const T
& aBase64
, U
& aBinary
) {
586 // Check for overflow.
587 if (aBase64
.Length() > UINT32_MAX
/ 3) {
588 return NS_ERROR_FAILURE
;
591 // Don't decode the empty string
592 if (aBase64
.IsEmpty()) {
596 uint32_t binaryLen
= ((aBase64
.Length() * 3) / 4);
598 auto handleOrErr
= aBinary
.BulkWrite(binaryLen
, 0, false);
599 if (handleOrErr
.isErr()) {
600 // Must not touch the handle if failing here, but we
601 // already truncated the string at the top, so it's
603 return handleOrErr
.unwrapErr();
606 auto handle
= handleOrErr
.unwrap();
608 nsresult rv
= Base64DecodeHelper(aBase64
.BeginReading(), aBase64
.Length(),
609 handle
.Elements(), &binaryLen
);
611 // Retruncate to match old semantics of this method.
612 handle
.Finish(0, true);
616 handle
.Finish(binaryLen
, true);
620 nsresult
Base64Decode(const nsACString
& aBase64
, nsACString
& aBinary
) {
621 return Base64DecodeString(aBase64
, aBinary
);
624 nsresult
Base64Decode(const nsAString
& aBase64
, nsAString
& aBinary
) {
625 return Base64DecodeString(aBase64
, aBinary
);
628 nsresult
Base64Decode(const nsAString
& aBase64
, nsACString
& aBinary
) {
629 return Base64DecodeString(aBase64
, aBinary
);
632 nsresult
Base64URLDecode(const nsACString
& aBase64
,
633 Base64URLDecodePaddingPolicy aPaddingPolicy
,
634 FallibleTArray
<uint8_t>& aBinary
) {
635 // Don't decode empty strings.
636 if (aBase64
.IsEmpty()) {
641 // Check for overflow.
642 uint32_t base64Len
= aBase64
.Length();
643 if (base64Len
> UINT32_MAX
/ 3) {
644 return NS_ERROR_FAILURE
;
646 const char* base64
= aBase64
.BeginReading();
648 // The decoded length may be 1-2 bytes over, depending on the final quantum.
649 uint32_t binaryLen
= (base64Len
* 3) / 4;
651 // Determine whether to check for and ignore trailing padding.
652 bool maybePadded
= false;
653 switch (aPaddingPolicy
) {
654 case Base64URLDecodePaddingPolicy::Require
:
656 // Padded input length must be a multiple of 4.
657 return NS_ERROR_INVALID_ARG
;
662 case Base64URLDecodePaddingPolicy::Ignore
:
663 // Check for padding only if the length is a multiple of 4.
664 maybePadded
= !(base64Len
% 4);
667 // If we're expecting unpadded input, no need for additional checks.
668 // `=` isn't in the decode table, so padded strings will fail to decode.
670 MOZ_FALLTHROUGH_ASSERT("Invalid decode padding policy");
671 case Base64URLDecodePaddingPolicy::Reject
:
674 if (maybePadded
&& base64
[base64Len
- 1] == '=') {
675 if (base64
[base64Len
- 2] == '=') {
682 if (NS_WARN_IF(!aBinary
.SetCapacity(binaryLen
, mozilla::fallible
))) {
683 return NS_ERROR_OUT_OF_MEMORY
;
685 aBinary
.SetLengthAndRetainStorage(binaryLen
);
686 uint8_t* binary
= aBinary
.Elements();
688 for (; base64Len
>= 4; base64Len
-= 4) {
689 if (!Decode4to3(base64
, binary
, Base64URLCharToValue
)) {
690 return NS_ERROR_INVALID_ARG
;
696 if (base64Len
== 3) {
697 if (!Decode3to2(base64
, binary
, Base64URLCharToValue
)) {
698 return NS_ERROR_INVALID_ARG
;
701 } else if (base64Len
== 2) {
702 if (!Decode2to1(base64
, binary
, Base64URLCharToValue
)) {
703 return NS_ERROR_INVALID_ARG
;
706 } else if (base64Len
) {
707 return NS_ERROR_INVALID_ARG
;
710 // Set the length to the actual number of decoded bytes.
711 aBinary
.TruncateLength(binary
- aBinary
.Elements());
715 nsresult
Base64URLEncode(uint32_t aBinaryLen
, const uint8_t* aBinary
,
716 Base64URLEncodePaddingPolicy aPaddingPolicy
,
717 nsACString
& aBase64
) {
719 // Don't encode empty strings.
720 if (aBinaryLen
== 0) {
724 // Allocate a buffer large enough to hold the encoded string with padding.
725 const auto base64LenOrErr
= CalculateBase64EncodedLength(aBinaryLen
);
726 if (base64LenOrErr
.isErr()) {
727 return base64LenOrErr
.inspectErr();
729 const uint32_t base64Len
= base64LenOrErr
.inspect();
731 auto handleOrErr
= aBase64
.BulkWrite(base64Len
, 0, false);
732 if (handleOrErr
.isErr()) {
733 return handleOrErr
.unwrapErr();
736 auto handle
= handleOrErr
.unwrap();
738 char* base64
= handle
.Elements();
741 for (; index
+ 3 <= aBinaryLen
; index
+= 3) {
742 *base64
++ = kBase64URLAlphabet
[aBinary
[index
] >> 2];
743 *base64
++ = kBase64URLAlphabet
[((aBinary
[index
] & 0x3) << 4) |
744 (aBinary
[index
+ 1] >> 4)];
745 *base64
++ = kBase64URLAlphabet
[((aBinary
[index
+ 1] & 0xf) << 2) |
746 (aBinary
[index
+ 2] >> 6)];
747 *base64
++ = kBase64URLAlphabet
[aBinary
[index
+ 2] & 0x3f];
750 uint32_t remaining
= aBinaryLen
- index
;
751 if (remaining
== 1) {
752 *base64
++ = kBase64URLAlphabet
[aBinary
[index
] >> 2];
753 *base64
++ = kBase64URLAlphabet
[((aBinary
[index
] & 0x3) << 4)];
754 } else if (remaining
== 2) {
755 *base64
++ = kBase64URLAlphabet
[aBinary
[index
] >> 2];
756 *base64
++ = kBase64URLAlphabet
[((aBinary
[index
] & 0x3) << 4) |
757 (aBinary
[index
+ 1] >> 4)];
758 *base64
++ = kBase64URLAlphabet
[((aBinary
[index
+ 1] & 0xf) << 2)];
761 uint32_t length
= base64
- handle
.Elements();
762 if (aPaddingPolicy
== Base64URLEncodePaddingPolicy::Include
) {
763 if (length
% 4 == 2) {
767 } else if (length
% 4 == 3) {
772 MOZ_ASSERT(aPaddingPolicy
== Base64URLEncodePaddingPolicy::Omit
,
773 "Invalid encode padding policy");
776 handle
.Finish(length
, false);
780 } // namespace mozilla