@@ -58,33 +58,38 @@ export function fromBase64url(str, format = 'uint8') {
5858 return fromTypedArray ( fromBase64common ( str , true ) , format )
5959}
6060
61- function fromBase64common ( str , isBase64url ) {
62- if ( Uint8Array . fromBase64 ) {
63- const options = { alphabet : isBase64url ? 'base64url' : 'base64' , lastChunkHandling : 'strict' }
61+ let fromBase64common
62+ if ( Uint8Array . fromBase64 ) {
63+ // NOTICE: this is actually slower than our JS impl in JavaScriptCore and SpiderMonkey (but faster on V8)
64+ fromBase64common = ( str , isBase64url ) => {
65+ const alphabet = isBase64url ? 'base64url' : 'base64'
6466 const padded = str . length % 4 > 0 ? `${ str } ${ '=' . repeat ( 4 - ( str . length % 4 ) ) } ` : str
65- return Uint8Array . fromBase64 ( padded , options )
67+ return Uint8Array . fromBase64 ( padded , { alphabet , lastChunkHandling : 'strict' } )
6668 }
69+ } else {
70+ fromBase64common = ( str , isBase64url ) => {
71+ let arr
72+ if ( ! haveNativeBuffer && atob ) {
73+ // atob is faster than manual parsing on Hermes
74+ const raw = atob ( isBase64url ? str . replaceAll ( '-' , '+' ) . replaceAll ( '_' , '/' ) : str )
75+ arr = new Uint8Array ( raw . length )
76+ for ( let i = 0 ; i < raw . length ; i ++ ) arr [ i ] = raw . charCodeAt ( i )
77+ } else {
78+ // base64url is already checked to have no padding
79+ if ( ! isBase64url ) assert ( ! str . includes ( '=' ) || ! / = [ ^ = ] / iu. test ( str ) , 'Invalid padding' )
80+ arr = haveNativeBuffer ? Buffer . from ( str , 'base64' ) : fromBase64js ( str )
81+ }
6782
68- let arr
69- if ( ! haveNativeBuffer && atob ) {
70- // atob is faster than manual parsing on Hermes
71- const raw = atob ( isBase64url ? str . replaceAll ( '-' , '+' ) . replaceAll ( '_' , '/' ) : str )
72- arr = new Uint8Array ( raw . length )
73- for ( let i = 0 ; i < raw . length ; i ++ ) arr [ i ] = raw . charCodeAt ( i )
74- } else {
75- assert ( ! str . includes ( '=' ) || ! / = [ ^ = ] / iu. test ( str ) , 'Invalid input after padding' )
76- arr = haveNativeBuffer ? Buffer . from ( str , 'base64' ) : fromBase64js ( str )
77- }
83+ if ( arr . length % 3 !== 0 ) {
84+ // Check last chunk to be strict if it was incomplete
85+ const expected = toBase64 ( arr . subarray ( - ( arr . length % 3 ) ) )
86+ const end = str . length % 4 === 0 ? str . slice ( - 4 ) : str . slice ( - ( str . length % 4 ) ) . padEnd ( 4 , '=' )
87+ const actual = isBase64url ? end . replaceAll ( '-' , '+' ) . replaceAll ( '_' , '/' ) : end
88+ if ( expected !== actual ) throw new Error ( 'Invalid last chunk' )
89+ }
7890
79- if ( arr . length % 3 !== 0 ) {
80- // Check last chunk to be strict if it was incomplete
81- const expected = toBase64 ( arr . subarray ( - ( arr . length % 3 ) ) )
82- const last = str . length % 4 === 0 ? str . slice ( - 4 ) : str . slice ( - ( str . length % 4 ) ) . padEnd ( 4 , '=' )
83- const actual = isBase64url ? last . replaceAll ( '-' , '+' ) . replaceAll ( '_' , '/' ) : last
84- if ( expected !== actual ) throw new Error ( 'Invalid last chunk' )
91+ return arr
8592 }
86-
87- return arr
8893}
8994
9095const BASE64 = [ ...'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' ]
@@ -143,8 +148,8 @@ function fromBase64js(str) {
143148 if ( ! fromBase64jsMap ) {
144149 fromBase64jsMap = map
145150 BASE64 . forEach ( ( c , i ) => ( map [ c . charCodeAt ( 0 ) ] = i ) )
146- map [ '-' . charCodeAt ( 0 ) ] = 62 // two last chars of BASE64
147- map [ '_' . charCodeAt ( 0 ) ] = 63 // two last chars of BASE64
151+ map [ '-' . charCodeAt ( 0 ) ] = 62 // two last chars of BASE64URL
152+ map [ '_' . charCodeAt ( 0 ) ] = 63 // two last chars of BASE64URL
148153 }
149154
150155 let inputLength = str . length
0 commit comments