88
99'use strict'
1010
11- const base64 = require ( 'base64-js' )
1211const ieee754 = require ( 'ieee754' )
1312const customInspectSymbol =
1413 ( typeof Symbol === 'function' && typeof Symbol [ 'for' ] === 'function' ) // eslint-disable-line dot-notation
@@ -38,10 +37,54 @@ exports.constants = {
3837 MAX_STRING_LENGTH : K_STRING_MAX_LENGTH
3938}
4039
41- exports . Blob = global . Blob
42- exports . File = global . File
43- exports . atob = global . atob
44- exports . btoa = global . btoa
40+ exports . Blob = typeof Blob !== 'undefined' ? Blob : undefined
41+ exports . File = typeof File !== 'undefined' ? File : undefined
42+ exports . atob = typeof atob !== 'undefined' ? atob : undefined
43+ exports . btoa = typeof btoa !== 'undefined' ? btoa : undefined
44+
45+ /**
46+ * The `atob` and `btoa` functions are unoptimized in node.js[1][2].
47+ * As a result of this, we call out to Buffer directly when running
48+ * inside of node.js. Unfortunately, detecting node.js is tricky:
49+ *
50+ * We can't check `process.browser` because it will cause browserify
51+ * to pull in the entire `process` module.
52+ *
53+ * Instead, we check for a global `Buffer` object with `asciiSlice`
54+ * defined on the prototype. This undocumented method has been
55+ * defined on the node.js Buffer prototype since the _very_ early
56+ * days of node.js (as early as 0.4.0) and is still defined to this
57+ * day (but is not defined on _our_ Buffer prototype).
58+ *
59+ * Because our `Buffer` constructor is hoisted, we can't check for
60+ * `typeof Buffer === 'function'`. Instead, we need to access `global`.
61+ *
62+ * Unfortunately, we can't assume `global` exists as there may be a
63+ * non-browserify bundler which supports CJS but not a full node.js
64+ * environment which includes `global`.
65+ *
66+ * As an added bonus, this hack also accounts for nodes prior to
67+ * v16.0.0 (when `atob` and `btoa` were first exposed globally).
68+ *
69+ * [1] https://github.com/feross/buffer/issues/339
70+ * [2] https://github.com/nodejs/node/pull/38433
71+ */
72+ let _atob = exports . atob
73+ let _btoa = exports . btoa
74+
75+ if ( typeof global !== 'undefined' && global && global . Math === Math &&
76+ typeof global . Buffer === 'function' && global . Buffer . prototype &&
77+ typeof global . Buffer . prototype . asciiSlice === 'function' ) {
78+ const NodeBuffer = global . Buffer
79+
80+ _atob = function atob ( str ) {
81+ return NodeBuffer . from ( str , 'base64' ) . toString ( 'binary' )
82+ }
83+
84+ _btoa = function btoa ( str ) {
85+ return NodeBuffer . from ( str , 'binary' ) . toString ( 'base64' )
86+ }
87+ }
4588
4689/**
4790 * If `Buffer.TYPED_ARRAY_SUPPORT`:
@@ -398,6 +441,7 @@ Buffer.isEncoding = function isEncoding (encoding) {
398441 case 'latin1' :
399442 case 'binary' :
400443 case 'base64' :
444+ case 'base64url' :
401445 case 'ucs2' :
402446 case 'ucs-2' :
403447 case 'utf16le' :
@@ -489,7 +533,8 @@ function byteLength (string, encoding) {
489533 case 'hex' :
490534 return len >>> 1
491535 case 'base64' :
492- return base64ToBytes ( string ) . length
536+ case 'base64url' :
537+ return base64ByteLength ( string , len )
493538 default :
494539 if ( loweredCase ) {
495540 return mustMatch ? - 1 : utf8ByteLength ( string ) // assume utf8
@@ -557,6 +602,9 @@ function slowToString (encoding, start, end) {
557602 case 'base64' :
558603 return base64Slice ( this , start , end )
559604
605+ case 'base64url' :
606+ return base64UrlSlice ( this , start , end )
607+
560608 case 'ucs2' :
561609 case 'ucs-2' :
562610 case 'utf16le' :
@@ -1020,7 +1068,14 @@ function asciiWrite (buf, string, offset, length) {
10201068}
10211069
10221070function base64Write ( buf , string , offset , length ) {
1023- return blitBuffer ( base64ToBytes ( string ) , buf , offset , length )
1071+ try {
1072+ // Parse optimistically as base64.
1073+ string = _atob ( string )
1074+ } catch ( e ) {
1075+ // Fall back to full preprocessing.
1076+ string = _atob ( base64clean ( string ) )
1077+ }
1078+ return asciiWrite ( buf , string , offset , length )
10241079}
10251080
10261081function ucs2Write ( buf , string , offset , length ) {
@@ -1096,6 +1151,7 @@ Buffer.prototype.write = function write (string, offset, length, encoding) {
10961151 return asciiWrite ( this , string , offset , length )
10971152
10981153 case 'base64' :
1154+ case 'base64url' :
10991155 // Warning: maxLength not taken into account in base64Write
11001156 return base64Write ( this , string , offset , length )
11011157
@@ -1121,11 +1177,11 @@ Buffer.prototype.toJSON = function toJSON () {
11211177}
11221178
11231179function base64Slice ( buf , start , end ) {
1124- if ( start === 0 && end === buf . length ) {
1125- return base64 . fromByteArray ( buf )
1126- } else {
1127- return base64 . fromByteArray ( buf . slice ( start , end ) )
1128- }
1180+ return _btoa ( latin1Slice ( buf , start , end ) )
1181+ }
1182+
1183+ function base64UrlSlice ( buf , start , end ) {
1184+ return base64convert ( base64Slice ( buf , start , end ) )
11291185}
11301186
11311187function utf8Slice ( buf , start , end ) {
@@ -2109,23 +2165,62 @@ function boundsError (value, length, type) {
21092165// ================
21102166
21112167const INVALID_BASE64_RE = / [ ^ + / 0 - 9 A - Z a - z - _ ] / g
2168+ const BASE64URL_62 = / - / g
2169+ const BASE64URL_63 = / _ / g
21122170
21132171function base64clean ( str ) {
2114- // Node takes equal signs as end of the Base64 encoding
2115- str = str . split ( '=' ) [ 0 ]
2116- // Node strips out invalid characters like \n and \t from the string, base64-js does not
2117- str = str . trim ( ) . replace ( INVALID_BASE64_RE , '' )
2172+ // Node takes equal signs as end of the encoding
2173+ const index = str . indexOf ( '=' )
2174+
2175+ if ( index >= 0 ) {
2176+ str = str . slice ( 0 , index )
2177+ }
2178+
2179+ // Node strips out invalid characters, atob does not
2180+ str = str . replace ( INVALID_BASE64_RE , '' )
2181+
21182182 // Node converts strings with length < 2 to ''
21192183 if ( str . length < 2 ) return ''
2120- // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not
2121- while ( str . length % 4 !== 0 ) {
2122- str = str + '='
2184+
2185+ // Node handles base64-url, atob does not
2186+ str = str . replace ( BASE64URL_62 , '+' )
2187+ str = str . replace ( BASE64URL_63 , '/' )
2188+
2189+ // Node allows for non-padded strings, atob _may_ not
2190+ while ( str . length & 3 ) {
2191+ str += '='
2192+ }
2193+
2194+ return str
2195+ }
2196+
2197+ const BASE64_62 = / \+ / g
2198+ const BASE64_63 = / \/ / g
2199+
2200+ function base64convert ( str ) {
2201+ // Convert base64 to base64-url.
2202+ let len = str . length
2203+
2204+ if ( len > 0 && str [ len - 1 ] === '=' ) len --
2205+ if ( len > 0 && str [ len - 1 ] === '=' ) len --
2206+
2207+ if ( len !== str . length ) {
2208+ str = str . slice ( 0 , len )
21232209 }
2210+
2211+ str = str . replace ( BASE64_62 , '-' )
2212+ str = str . replace ( BASE64_63 , '_' )
2213+
21242214 return str
21252215}
21262216
2127- function base64ToBytes ( str ) {
2128- return base64 . toByteArray ( base64clean ( str ) )
2217+ function base64ByteLength ( str , bytes ) {
2218+ // Handle padding
2219+ if ( bytes > 0 && str . charCodeAt ( bytes - 1 ) === 0x3d ) bytes --
2220+ if ( bytes > 1 && str . charCodeAt ( bytes - 1 ) === 0x3d ) bytes --
2221+
2222+ // Base64 ratio: 3/4
2223+ return ( bytes * 3 ) >>> 2
21292224}
21302225
21312226function writeInvalid ( buf , pos ) {
@@ -2136,15 +2231,6 @@ function writeInvalid (buf, pos) {
21362231 return pos
21372232}
21382233
2139- function blitBuffer ( src , dst , offset , length ) {
2140- let i
2141- for ( i = 0 ; i < length ; ++ i ) {
2142- if ( ( i + offset >= dst . length ) || ( i >= src . length ) ) break
2143- dst [ i + offset ] = src [ i ]
2144- }
2145- return i
2146- }
2147-
21482234// ArrayBuffer or Uint8Array objects from other contexts (i.e. iframes) do not pass
21492235// the `instanceof` check but they should be treated as of that type.
21502236// See: https://github.com/feross/buffer/issues/166
0 commit comments