23
23
24
24
const {
25
25
Array,
26
- ArrayBufferIsView ,
26
+ ArrayFrom ,
27
27
ArrayIsArray,
28
28
ArrayPrototypeForEach,
29
+ ArrayPrototypeIndexOf,
29
30
MathFloor,
30
31
MathMin,
31
32
MathTrunc,
32
- NumberIsInteger,
33
33
NumberIsNaN,
34
34
NumberMAX_SAFE_INTEGER,
35
35
NumberMIN_SAFE_INTEGER,
@@ -43,10 +43,10 @@ const {
43
43
StringPrototypeTrim,
44
44
SymbolSpecies,
45
45
SymbolToPrimitive,
46
- TypedArrayPrototypeFill,
47
46
TypedArrayPrototypeGetBuffer,
48
47
TypedArrayPrototypeGetByteLength,
49
48
TypedArrayPrototypeGetByteOffset,
49
+ TypedArrayPrototypeFill,
50
50
TypedArrayPrototypeGetLength,
51
51
TypedArrayPrototypeSet,
52
52
TypedArrayPrototypeSlice,
@@ -58,7 +58,6 @@ const {
58
58
byteLengthUtf8,
59
59
compare : _compare ,
60
60
compareOffset,
61
- copy : _copy ,
62
61
createFromString,
63
62
fill : bindingFill ,
64
63
isAscii : bindingIsAscii ,
@@ -71,9 +70,10 @@ const {
71
70
swap64 : _swap64 ,
72
71
kMaxLength,
73
72
kStringMaxLength,
74
- atob : _atob ,
75
- btoa : _btoa ,
76
73
} = internalBinding ( 'buffer' ) ;
74
+
75
+ const bufferBinding = internalBinding ( 'buffer' ) ;
76
+
77
77
const {
78
78
constants : {
79
79
ALL_PROPERTIES ,
@@ -88,7 +88,6 @@ const {
88
88
normalizeEncoding,
89
89
kIsEncodingSymbol,
90
90
defineLazyProperties,
91
- encodingsMap,
92
91
} = require ( 'internal/util' ) ;
93
92
const {
94
93
isAnyArrayBuffer,
@@ -99,15 +98,16 @@ const {
99
98
const {
100
99
inspect : utilInspect ,
101
100
} = require ( 'internal/util/inspect' ) ;
101
+ const { encodings } = internalBinding ( 'string_decoder' ) ;
102
102
103
103
const {
104
104
codes : {
105
105
ERR_BUFFER_OUT_OF_BOUNDS ,
106
106
ERR_INVALID_ARG_TYPE ,
107
107
ERR_INVALID_ARG_VALUE ,
108
108
ERR_INVALID_BUFFER_SIZE ,
109
- ERR_MISSING_ARGS ,
110
109
ERR_OUT_OF_RANGE ,
110
+ ERR_MISSING_ARGS ,
111
111
ERR_UNKNOWN_ENCODING ,
112
112
} ,
113
113
genericNodeError,
@@ -152,6 +152,10 @@ const constants = ObjectDefineProperties({}, {
152
152
Buffer . poolSize = 8 * 1024 ;
153
153
let poolSize , poolOffset , allocPool ;
154
154
155
+ const encodingsMap = { __proto__ : null } ;
156
+ for ( let i = 0 ; i < encodings . length ; ++ i )
157
+ encodingsMap [ encodings [ i ] ] = i ;
158
+
155
159
function createPool ( ) {
156
160
poolSize = Buffer . poolSize ;
157
161
allocPool = createUnsafeBuffer ( poolSize ) . buffer ;
@@ -202,55 +206,55 @@ function toInteger(n, defaultVal) {
202
206
return defaultVal ;
203
207
}
204
208
205
- function copyImpl ( source , target , targetStart , sourceStart , sourceEnd ) {
206
- if ( ! ArrayBufferIsView ( source ) )
209
+ function _copy ( source , target , targetStart , sourceStart , sourceEnd ) {
210
+ if ( ! isUint8Array ( source ) )
207
211
throw new ERR_INVALID_ARG_TYPE ( 'source' , [ 'Buffer' , 'Uint8Array' ] , source ) ;
208
- if ( ! ArrayBufferIsView ( target ) )
212
+ if ( ! isUint8Array ( target ) )
209
213
throw new ERR_INVALID_ARG_TYPE ( 'target' , [ 'Buffer' , 'Uint8Array' ] , target ) ;
210
214
211
215
if ( targetStart === undefined ) {
212
216
targetStart = 0 ;
213
217
} else {
214
- targetStart = NumberIsInteger ( targetStart ) ? targetStart : toInteger ( targetStart , 0 ) ;
218
+ targetStart = toInteger ( targetStart , 0 ) ;
215
219
if ( targetStart < 0 )
216
220
throw new ERR_OUT_OF_RANGE ( 'targetStart' , '>= 0' , targetStart ) ;
217
221
}
218
222
219
223
if ( sourceStart === undefined ) {
220
224
sourceStart = 0 ;
221
225
} else {
222
- sourceStart = NumberIsInteger ( sourceStart ) ? sourceStart : toInteger ( sourceStart , 0 ) ;
223
- if ( sourceStart < 0 || sourceStart > source . byteLength )
224
- throw new ERR_OUT_OF_RANGE ( 'sourceStart' , `>= 0 && <= ${ source . byteLength } ` , sourceStart ) ;
226
+ sourceStart = toInteger ( sourceStart , 0 ) ;
227
+ if ( sourceStart < 0 || sourceStart > source . length )
228
+ throw new ERR_OUT_OF_RANGE ( 'sourceStart' , `>= 0 && <= ${ source . length } ` , sourceStart ) ;
225
229
}
226
230
227
231
if ( sourceEnd === undefined ) {
228
- sourceEnd = source . byteLength ;
232
+ sourceEnd = source . length ;
229
233
} else {
230
- sourceEnd = NumberIsInteger ( sourceEnd ) ? sourceEnd : toInteger ( sourceEnd , 0 ) ;
234
+ sourceEnd = toInteger ( sourceEnd , 0 ) ;
231
235
if ( sourceEnd < 0 )
232
236
throw new ERR_OUT_OF_RANGE ( 'sourceEnd' , '>= 0' , sourceEnd ) ;
233
237
}
234
238
235
- if ( targetStart >= target . byteLength || sourceStart >= sourceEnd )
239
+ if ( targetStart >= target . length || sourceStart >= sourceEnd )
236
240
return 0 ;
237
241
238
242
return _copyActual ( source , target , targetStart , sourceStart , sourceEnd ) ;
239
243
}
240
244
241
245
function _copyActual ( source , target , targetStart , sourceStart , sourceEnd ) {
242
- if ( sourceEnd - sourceStart > target . byteLength - targetStart )
243
- sourceEnd = sourceStart + target . byteLength - targetStart ;
246
+ if ( sourceEnd - sourceStart > target . length - targetStart )
247
+ sourceEnd = sourceStart + target . length - targetStart ;
244
248
245
249
let nb = sourceEnd - sourceStart ;
246
- const sourceLen = source . byteLength - sourceStart ;
250
+ const sourceLen = source . length - sourceStart ;
247
251
if ( nb > sourceLen )
248
252
nb = sourceLen ;
249
253
250
- if ( nb <= 0 )
251
- return 0 ;
254
+ if ( sourceStart !== 0 || sourceEnd < source . length )
255
+ source = new Uint8Array ( source . buffer , source . byteOffset + sourceStart , nb ) ;
252
256
253
- _copy ( source , target , targetStart , sourceStart , nb ) ;
257
+ TypedArrayPrototypeSet ( target , source , targetStart ) ;
254
258
255
259
return nb ;
256
260
}
@@ -620,7 +624,7 @@ const encodingOps = {
620
624
encoding : 'utf8' ,
621
625
encodingVal : encodingsMap . utf8 ,
622
626
byteLength : byteLengthUtf8 ,
623
- write : ( buf , string , offset , len ) => buf . utf8Write ( string , offset , len ) ,
627
+ write : ( buf , string , offset , len ) => bufferBinding . utf8WriteStatic ( buf , string , offset , len ) ,
624
628
slice : ( buf , start , end ) => buf . utf8Slice ( start , end ) ,
625
629
indexOf : ( buf , val , byteOffset , dir ) =>
626
630
indexOfString ( buf , val , byteOffset , encodingsMap . utf8 , dir ) ,
@@ -647,7 +651,7 @@ const encodingOps = {
647
651
encoding : 'latin1' ,
648
652
encodingVal : encodingsMap . latin1 ,
649
653
byteLength : ( string ) => string . length ,
650
- write : ( buf , string , offset , len ) => buf . latin1Write ( string , offset , len ) ,
654
+ write : ( buf , string , offset , len ) => bufferBinding . latin1WriteStatic ( buf , string , offset , len ) ,
651
655
slice : ( buf , start , end ) => buf . latin1Slice ( start , end ) ,
652
656
indexOf : ( buf , val , byteOffset , dir ) =>
653
657
indexOfString ( buf , val , byteOffset , encodingsMap . latin1 , dir ) ,
@@ -656,7 +660,7 @@ const encodingOps = {
656
660
encoding : 'ascii' ,
657
661
encodingVal : encodingsMap . ascii ,
658
662
byteLength : ( string ) => string . length ,
659
- write : ( buf , string , offset , len ) => buf . asciiWrite ( string , offset , len ) ,
663
+ write : ( buf , string , offset , len ) => bufferBinding . asciiWriteStatic ( buf , string , offset , len ) ,
660
664
slice : ( buf , start , end ) => buf . asciiSlice ( start , end ) ,
661
665
indexOf : ( buf , val , byteOffset , dir ) =>
662
666
indexOfBuffer ( buf ,
@@ -804,7 +808,7 @@ ObjectDefineProperty(Buffer.prototype, 'offset', {
804
808
805
809
Buffer . prototype . copy =
806
810
function copy ( target , targetStart , sourceStart , sourceEnd ) {
807
- return copyImpl ( this , target , targetStart , sourceStart , sourceEnd ) ;
811
+ return _copy ( this , target , targetStart , sourceStart , sourceEnd ) ;
808
812
} ;
809
813
810
814
// No need to verify that "buf.length <= MAX_UINT32" since it's a read-only
@@ -1253,41 +1257,102 @@ function btoa(input) {
1253
1257
if ( arguments . length === 0 ) {
1254
1258
throw new ERR_MISSING_ARGS ( 'input' ) ;
1255
1259
}
1256
- const result = _btoa ( `${ input } ` ) ;
1257
- if ( result === - 1 ) {
1258
- throw lazyDOMException ( 'Invalid character' , 'InvalidCharacterError' ) ;
1260
+ input = `${ input } ` ;
1261
+ for ( let n = 0 ; n < input . length ; n ++ ) {
1262
+ if ( input [ n ] . charCodeAt ( 0 ) > 0xff )
1263
+ throw lazyDOMException ( 'Invalid character' , 'InvalidCharacterError' ) ;
1259
1264
}
1260
- return result ;
1265
+ const buf = Buffer . from ( input , 'latin1' ) ;
1266
+ return buf . toString ( 'base64' ) ;
1261
1267
}
1262
1268
1269
+ // Refs: https://infra.spec.whatwg.org/#forgiving-base64-decode
1270
+ const kForgivingBase64AllowedChars = [
1271
+ // ASCII whitespace
1272
+ // Refs: https://infra.spec.whatwg.org/#ascii-whitespace
1273
+ 0x09 , 0x0A , 0x0C , 0x0D , 0x20 ,
1274
+
1275
+ // Uppercase letters
1276
+ ...ArrayFrom ( { length : 26 } , ( _ , i ) => StringPrototypeCharCodeAt ( 'A' ) + i ) ,
1277
+
1278
+ // Lowercase letters
1279
+ ...ArrayFrom ( { length : 26 } , ( _ , i ) => StringPrototypeCharCodeAt ( 'a' ) + i ) ,
1280
+
1281
+ // Decimal digits
1282
+ ...ArrayFrom ( { length : 10 } , ( _ , i ) => StringPrototypeCharCodeAt ( '0' ) + i ) ,
1283
+
1284
+ 0x2B , // +
1285
+ 0x2F , // /
1286
+ 0x3D , // =
1287
+ ] ;
1288
+ const kEqualSignIndex = ArrayPrototypeIndexOf ( kForgivingBase64AllowedChars ,
1289
+ 0x3D ) ;
1290
+
1263
1291
function atob ( input ) {
1292
+ // The implementation here has not been performance optimized in any way and
1293
+ // should not be.
1294
+ // Refs: https://github.com/nodejs/node/pull/38433#issuecomment-828426932
1264
1295
if ( arguments . length === 0 ) {
1265
1296
throw new ERR_MISSING_ARGS ( 'input' ) ;
1266
1297
}
1267
1298
1268
- const result = _atob ( `${ input } ` ) ;
1299
+ input = `${ input } ` ;
1300
+ let nonAsciiWhitespaceCharCount = 0 ;
1301
+ let equalCharCount = 0 ;
1302
+
1303
+ for ( let n = 0 ; n < input . length ; n ++ ) {
1304
+ const index = ArrayPrototypeIndexOf (
1305
+ kForgivingBase64AllowedChars ,
1306
+ StringPrototypeCharCodeAt ( input , n ) ) ;
1307
+
1308
+ if ( index > 4 ) {
1309
+ // The first 5 elements of `kForgivingBase64AllowedChars` are
1310
+ // ASCII whitespace char codes.
1311
+ nonAsciiWhitespaceCharCount ++ ;
1312
+
1313
+ if ( index === kEqualSignIndex ) {
1314
+ equalCharCount ++ ;
1315
+ } else if ( equalCharCount ) {
1316
+ // The `=` char is only allowed at the end.
1317
+ throw lazyDOMException ( 'Invalid character' , 'InvalidCharacterError' ) ;
1318
+ }
1269
1319
1270
- switch ( result ) {
1271
- case - 2 : // Invalid character
1320
+ if ( equalCharCount > 2 ) {
1321
+ // Only one more `=` is permitted after the first equal sign.
1322
+ throw lazyDOMException ( 'Invalid character' , 'InvalidCharacterError' ) ;
1323
+ }
1324
+ } else if ( index === - 1 ) {
1272
1325
throw lazyDOMException ( 'Invalid character' , 'InvalidCharacterError' ) ;
1273
- case - 1 : // Single character remained
1274
- throw lazyDOMException (
1275
- 'The string to be decoded is not correctly encoded.' ,
1276
- 'InvalidCharacterError' ) ;
1277
- case - 3 : // Possible overflow
1278
- // TODO(@anonrig): Throw correct error in here.
1279
- throw lazyDOMException ( 'The input causes overflow.' , 'InvalidCharacterError' ) ;
1280
- default :
1281
- return result ;
1326
+ }
1282
1327
}
1328
+
1329
+ let reminder = nonAsciiWhitespaceCharCount % 4 ;
1330
+
1331
+ // See #2, #3, #4 - https://infra.spec.whatwg.org/#forgiving-base64
1332
+ if ( ! reminder ) {
1333
+ // Remove all trailing `=` characters and get the new reminder.
1334
+ reminder = ( nonAsciiWhitespaceCharCount - equalCharCount ) % 4 ;
1335
+ } else if ( equalCharCount ) {
1336
+ // `=` should not in the input if there's a reminder.
1337
+ throw lazyDOMException ( 'Invalid character' , 'InvalidCharacterError' ) ;
1338
+ }
1339
+
1340
+ // See #3 - https://infra.spec.whatwg.org/#forgiving-base64
1341
+ if ( reminder === 1 ) {
1342
+ throw lazyDOMException (
1343
+ 'The string to be decoded is not correctly encoded.' ,
1344
+ 'InvalidCharacterError' ) ;
1345
+ }
1346
+
1347
+ return Buffer . from ( input , 'base64' ) . toString ( 'latin1' ) ;
1283
1348
}
1284
1349
1285
1350
function isUtf8 ( input ) {
1286
1351
if ( isTypedArray ( input ) || isAnyArrayBuffer ( input ) ) {
1287
1352
return bindingIsUtf8 ( input ) ;
1288
1353
}
1289
1354
1290
- throw new ERR_INVALID_ARG_TYPE ( 'input' , [ 'ArrayBuffer ' , 'Buffer' , 'TypedArray '] , input ) ;
1355
+ throw new ERR_INVALID_ARG_TYPE ( 'input' , [ 'TypedArray ' , 'Buffer' ] , input ) ;
1291
1356
}
1292
1357
1293
1358
function isAscii ( input ) {
0 commit comments