11const assert = require ( 'assert' )
2- const { atob } = require ( 'buffer' )
32const { isomorphicDecode } = require ( './util' )
43
54const encoder = new TextEncoder ( )
@@ -8,7 +7,8 @@ const encoder = new TextEncoder()
87 * @see https://mimesniff.spec.whatwg.org/#http-token-code-point
98 */
109const HTTP_TOKEN_CODEPOINTS = / ^ [ ! # $ % & ' * + - . ^ _ | ~ A - Z a - z 0 - 9 ] + $ /
11- const HTTP_WHITESPACE_REGEX = / ( \u000A | \u000D | \u0009 | \u0020 ) / // eslint-disable-line
10+ const HTTP_WHITESPACE_REGEX = / [ \u000A | \u000D | \u0009 | \u0020 ] / // eslint-disable-line
11+ const ASCII_WHITESPACE_REPLACE_REGEX = / [ \u0009 \u000A \u000C \u000D \u0020 ] / g // eslint-disable-line
1212/**
1313 * @see https://mimesniff.spec.whatwg.org/#http-quoted-string-token-code-point
1414 */
@@ -188,20 +188,26 @@ function stringPercentDecode (input) {
188188 return percentDecode ( bytes )
189189}
190190
191+ function isHexCharByte ( byte ) {
192+ // 0-9 A-F a-f
193+ return ( byte >= 0x30 && byte <= 0x39 ) || ( byte >= 0x41 && byte <= 0x46 ) || ( byte >= 0x61 && byte <= 0x66 )
194+ }
195+
191196// https://url.spec.whatwg.org/#percent-decode
192197/** @param {Uint8Array } input */
193198function percentDecode ( input ) {
199+ const length = input . length
194200 // 1. Let output be an empty byte sequence.
195- /** @type {number[] } */
196- const output = [ ]
197-
201+ /** @type {Uint8Array } */
202+ const output = new Uint8Array ( length )
203+ let j = 0
198204 // 2. For each byte byte in input:
199- for ( let i = 0 ; i < input . length ; i ++ ) {
205+ for ( let i = 0 ; i < length ; ++ i ) {
200206 const byte = input [ i ]
201207
202208 // 1. If byte is not 0x25 (%), then append byte to output.
203209 if ( byte !== 0x25 ) {
204- output . push ( byte )
210+ output [ j ++ ] = byte
205211
206212 // 2. Otherwise, if byte is 0x25 (%) and the next two bytes
207213 // after byte in input are not in the ranges
@@ -210,9 +216,9 @@ function percentDecode (input) {
210216 // to output.
211217 } else if (
212218 byte === 0x25 &&
213- ! / ^ [ 0 - 9 A - F a - f ] { 2 } $ / i . test ( String . fromCharCode ( input [ i + 1 ] , input [ i + 2 ] ) )
219+ ! ( isHexCharByte ( input [ i + 1 ] ) && isHexCharByte ( input [ i + 2 ] ) )
214220 ) {
215- output . push ( 0x25 )
221+ output [ j ++ ] = 0x25
216222
217223 // 3. Otherwise:
218224 } else {
@@ -222,15 +228,15 @@ function percentDecode (input) {
222228 const bytePoint = Number . parseInt ( nextTwoBytes , 16 )
223229
224230 // 2. Append a byte whose value is bytePoint to output.
225- output . push ( bytePoint )
231+ output [ j ++ ] = bytePoint
226232
227233 // 3. Skip the next two bytes in input.
228234 i += 2
229235 }
230236 }
231237
232238 // 3. Return output.
233- return Uint8Array . from ( output )
239+ return length === j ? output : output . subarray ( 0 , j )
234240}
235241
236242// https://mimesniff.spec.whatwg.org/#parse-a-mime-type
@@ -410,19 +416,25 @@ function parseMIMEType (input) {
410416/** @param {string } data */
411417function forgivingBase64 ( data ) {
412418 // 1. Remove all ASCII whitespace from data.
413- data = data . replace ( / [ \u0009 \u000A \u000C \u000D \u0020 ] / g , '' ) // eslint-disable-line
419+ data = data . replace ( ASCII_WHITESPACE_REPLACE_REGEX , '' ) // eslint-disable-line
414420
421+ let dataLength = data . length
415422 // 2. If data’s code point length divides by 4 leaving
416423 // no remainder, then:
417- if ( data . length % 4 === 0 ) {
424+ if ( dataLength % 4 === 0 ) {
418425 // 1. If data ends with one or two U+003D (=) code points,
419426 // then remove them from data.
420- data = data . replace ( / = ? = $ / , '' )
427+ if ( data . charCodeAt ( dataLength - 1 ) === 0x003D ) {
428+ -- dataLength
429+ if ( data . charCodeAt ( dataLength - 1 ) === 0x003D ) {
430+ -- dataLength
431+ }
432+ }
421433 }
422434
423435 // 3. If data’s code point length divides by 4 leaving
424436 // a remainder of 1, then return failure.
425- if ( data . length % 4 === 1 ) {
437+ if ( dataLength % 4 === 1 ) {
426438 return 'failure'
427439 }
428440
@@ -431,18 +443,12 @@ function forgivingBase64 (data) {
431443 // U+002F (/)
432444 // ASCII alphanumeric
433445 // then return failure.
434- if ( / [ ^ + / 0 - 9 A - Z a - z ] / . test ( data ) ) {
446+ if ( / [ ^ + / 0 - 9 A - Z a - z ] / . test ( data . length === dataLength ? data : data . substring ( 0 , dataLength ) ) ) {
435447 return 'failure'
436448 }
437449
438- const binary = atob ( data )
439- const bytes = new Uint8Array ( binary . length )
440-
441- for ( let byte = 0 ; byte < binary . length ; byte ++ ) {
442- bytes [ byte ] = binary . charCodeAt ( byte )
443- }
444-
445- return bytes
450+ const buffer = Buffer . from ( data , 'base64' )
451+ return new Uint8Array ( buffer . buffer , buffer . byteOffset , buffer . byteLength )
446452}
447453
448454// https://fetch.spec.whatwg.org/#collect-an-http-quoted-string
@@ -570,55 +576,54 @@ function serializeAMimeType (mimeType) {
570576
571577/**
572578 * @see https://fetch.spec.whatwg.org/#http-whitespace
573- * @param {string } char
579+ * @param {number } char
574580 */
575581function isHTTPWhiteSpace ( char ) {
576- return char === '\r' || char === '\n' || char === '\t' || char === ' '
582+ // "\r\n\t "
583+ return char === 0x00d || char === 0x00a || char === 0x009 || char === 0x020
577584}
578585
579586/**
580587 * @see https://fetch.spec.whatwg.org/#http-whitespace
581588 * @param {string } str
589+ * @param {boolean } [leading=true]
590+ * @param {boolean } [trailing=true]
582591 */
583592function removeHTTPWhitespace ( str , leading = true , trailing = true ) {
584- let lead = 0
585- let trail = str . length - 1
586-
593+ let i = 0 ; let j = str . length
587594 if ( leading ) {
588- for ( ; lead < str . length && isHTTPWhiteSpace ( str [ lead ] ) ; lead ++ ) ;
595+ while ( j > i && isHTTPWhiteSpace ( str . charCodeAt ( i ) ) ) -- i
589596 }
590-
591597 if ( trailing ) {
592- for ( ; trail > 0 && isHTTPWhiteSpace ( str [ trail ] ) ; trail -- ) ;
598+ while ( j > i && isHTTPWhiteSpace ( str . charCodeAt ( j - 1 ) ) ) -- j
593599 }
594-
595- return str . slice ( lead , trail + 1 )
600+ return i === 0 && j === str . length ? str : str . substring ( i , j )
596601}
597602
598603/**
599604 * @see https://infra.spec.whatwg.org/#ascii-whitespace
600- * @param {string } char
605+ * @param {number } char
601606 */
602607function isASCIIWhitespace ( char ) {
603- return char === '\r' || char === '\n' || char === '\t' || char === '\f' || char === ' '
608+ // "\r\n\t\f "
609+ return char === 0x00d || char === 0x00a || char === 0x009 || char === 0x00c || char === 0x020
604610}
605611
606612/**
607613 * @see https://infra.spec.whatwg.org/#strip-leading-and-trailing-ascii-whitespace
614+ * @param {string } str
615+ * @param {boolean } [leading=true]
616+ * @param {boolean } [trailing=true]
608617 */
609618function removeASCIIWhitespace ( str , leading = true , trailing = true ) {
610- let lead = 0
611- let trail = str . length - 1
612-
619+ let i = 0 ; let j = str . length
613620 if ( leading ) {
614- for ( ; lead < str . length && isASCIIWhitespace ( str [ lead ] ) ; lead ++ ) ;
621+ while ( j > i && isASCIIWhitespace ( str . charCodeAt ( i ) ) ) -- i
615622 }
616-
617623 if ( trailing ) {
618- for ( ; trail > 0 && isASCIIWhitespace ( str [ trail ] ) ; trail -- ) ;
624+ while ( j > i && isASCIIWhitespace ( str . charCodeAt ( j - 1 ) ) ) -- j
619625 }
620-
621- return str . slice ( lead , trail + 1 )
626+ return i === 0 && j === str . length ? str : str . substring ( i , j )
622627}
623628
624629module . exports = {
0 commit comments