@@ -125,6 +125,8 @@ export function toBase64(arr, isURL, padding) {
125125
126126// TODO: can this be optimized? This only affects non-Hermes barebone engines though
127127const mapSize = nativeEncoder ? 128 : 65_536 // we have to store 64 KiB map or recheck everything if we can't decode to byte array
128+ const _AA = 0x4141 // 'AA' string in hex, the only allowed char pair to generate 12 zero bits
129+ const _zz = 0x7a7a // 'zz' string in hex, max allowed char pair
128130
129131export function fromBase64 ( str , isURL ) {
130132 let inputLength = str . length
@@ -152,21 +154,60 @@ export function fromBase64(str, isURL) {
152154 let i = 0
153155
154156 if ( nativeEncoder ) {
157+ if ( ! helpers . fromMap16 ) {
158+ helpers . fromMap16 = new Uint16Array ( _zz + 1 ) // Warning: 64 KiB
159+ const u8 = new Uint8Array ( 2 )
160+ const u16 = new Uint16Array ( u8 . buffer , u8 . byteOffset , 1 ) // for endianess-agnostic transform
161+ alphabet . forEach ( ( c0 , i0 ) => {
162+ u8 [ 0 ] = c0 . charCodeAt ( 0 ) // FIXME, we should avoid calling charCodeAt in a loop
163+ alphabet . forEach ( ( c1 , i1 ) => {
164+ u8 [ 1 ] = c1 . charCodeAt ( 0 )
165+ helpers . fromMap16 [ u16 [ 0 ] ] = ( i0 << 6 ) | i1
166+ } )
167+ } )
168+ }
169+ const m16 = helpers . fromMap16
170+
155171 const codes = nativeEncoder . encode ( str )
172+ const mainLength16 = mainLength >> 1
173+ const codes16 = new Uint16Array ( codes . buffer , codes . byteOffset , mainLength16 )
156174 if ( codes . length !== str . length ) throw new SyntaxError ( E_CHAR ) // non-ascii
157- while ( i < mainLength ) {
158- const c0 = codes [ i ]
159- const c1 = codes [ i + 1 ]
160- const c2 = codes [ i + 2 ]
161- const c3 = codes [ i + 3 ]
175+
176+ // Optional fast loop
177+ for ( const mainLength16_2 = mainLength16 - 2 ; i < mainLength16_2 ; ) {
178+ const c01 = codes16 [ i ]
179+ const c23 = codes16 [ i + 1 ]
180+ const c45 = codes16 [ i + 2 ]
181+ const c67 = codes16 [ i + 3 ]
182+ const x01 = m16 [ c01 ]
183+ const x23 = m16 [ c23 ]
184+ const x45 = m16 [ c45 ]
185+ const x67 = m16 [ c67 ]
186+ if ( ! x01 && c01 !== _AA || ! x23 && c23 !== _AA ) throw new SyntaxError ( E_CHAR )
187+ if ( ! x45 && c45 !== _AA || ! x67 && c67 !== _AA ) throw new SyntaxError ( E_CHAR )
188+ arr [ at ] = x01 >> 4
189+ arr [ at + 1 ] = ( ( x01 & 0xf ) << 4 ) | ( x23 >> 8 )
190+ arr [ at + 2 ] = x23 & 0xff
191+ arr [ at + 3 ] = x45 >> 4
192+ arr [ at + 4 ] = ( ( x45 & 0xf ) << 4 ) | ( x67 >> 8 )
193+ arr [ at + 5 ] = x67 & 0xff
162194 i += 4
163- const a = ( m [ c0 ] << 18 ) | ( m [ c1 ] << 12 ) | ( m [ c2 ] << 6 ) | m [ c3 ]
164- if ( a < 0 ) throw new SyntaxError ( E_CHAR )
165- arr [ at ] = a >> 16
166- arr [ at + 1 ] = ( a >> 8 ) & 0xff
167- arr [ at + 2 ] = a & 0xff
195+ at += 6
196+ }
197+
198+ while ( i < mainLength16 ) {
199+ const c01 = codes16 [ i ]
200+ const c23 = codes16 [ i + 1 ]
201+ const x01 = m16 [ c01 ]
202+ const x23 = m16 [ c23 ]
203+ if ( ! x01 && c01 !== _AA || ! x23 && c23 !== _AA ) throw new SyntaxError ( E_CHAR )
204+ arr [ at ] = x01 >> 4
205+ arr [ at + 1 ] = ( ( x01 & 0xf ) << 4 ) | ( x23 >> 8 )
206+ arr [ at + 2 ] = x23 & 0xff
207+ i += 2
168208 at += 3
169209 }
210+ i *= 2
170211 } else {
171212 while ( i < mainLength ) {
172213 const c0 = str . charCodeAt ( i )
0 commit comments