• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright Joyent, Inc. and other Node contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the
5// "Software"), to deal in the Software without restriction, including
6// without limitation the rights to use, copy, modify, merge, publish,
7// distribute, sublicense, and/or sell copies of the Software, and to permit
8// persons to whom the Software is furnished to do so, subject to the
9// following conditions:
10//
11// The above copyright notice and this permission notice shall be included
12// in all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20// USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22'use strict';
23
24const {
25  Array,
26  ArrayIsArray,
27  ArrayPrototypeForEach,
28  Error,
29  MathFloor,
30  MathMin,
31  MathTrunc,
32  NumberIsNaN,
33  NumberMAX_SAFE_INTEGER,
34  NumberMIN_SAFE_INTEGER,
35  ObjectCreate,
36  ObjectDefineProperties,
37  ObjectDefineProperty,
38  ObjectSetPrototypeOf,
39  StringPrototypeCharCodeAt,
40  StringPrototypeReplace,
41  StringPrototypeSlice,
42  StringPrototypeToLowerCase,
43  StringPrototypeTrim,
44  SymbolSpecies,
45  SymbolToPrimitive,
46  TypedArrayPrototypeGetByteLength,
47  TypedArrayPrototypeFill,
48  TypedArrayPrototypeSet,
49  Uint8Array,
50  Uint8ArrayPrototype,
51} = primordials;
52
53const {
54  byteLengthUtf8,
55  compare: _compare,
56  compareOffset,
57  createFromString,
58  fill: bindingFill,
59  indexOfBuffer,
60  indexOfNumber,
61  indexOfString,
62  swap16: _swap16,
63  swap32: _swap32,
64  swap64: _swap64,
65  kMaxLength,
66  kStringMaxLength,
67  zeroFill: bindingZeroFill
68} = internalBinding('buffer');
69const {
70  getOwnNonIndexProperties,
71  propertyFilter: {
72    ALL_PROPERTIES,
73    ONLY_ENUMERABLE
74  },
75} = internalBinding('util');
76const {
77  customInspectSymbol,
78  isInsideNodeModules,
79  normalizeEncoding,
80  kIsEncodingSymbol
81} = require('internal/util');
82const {
83  isAnyArrayBuffer,
84  isArrayBufferView,
85  isUint8Array
86} = require('internal/util/types');
87const {
88  inspect: utilInspect
89} = require('internal/util/inspect');
90const { encodings } = internalBinding('string_decoder');
91
92const {
93  codes: {
94    ERR_BUFFER_OUT_OF_BOUNDS,
95    ERR_INVALID_ARG_TYPE,
96    ERR_INVALID_ARG_VALUE,
97    ERR_INVALID_BUFFER_SIZE,
98    ERR_INVALID_OPT_VALUE,
99    ERR_OUT_OF_RANGE,
100    ERR_UNKNOWN_ENCODING
101  },
102  hideStackFrames
103} = require('internal/errors');
104const {
105  validateBuffer,
106  validateInteger,
107  validateString
108} = require('internal/validators');
109// Provide validateInteger() but with kMaxLength as the default maximum value.
110const validateOffset = (value, name, min = 0, max = kMaxLength) =>
111  validateInteger(value, name, min, max);
112
113const {
114  FastBuffer,
115  markAsUntransferable,
116  addBufferPrototypeMethods
117} = require('internal/buffer');
118
119const {
120  Blob,
121} = require('internal/blob');
122
123FastBuffer.prototype.constructor = Buffer;
124Buffer.prototype = FastBuffer.prototype;
125addBufferPrototypeMethods(Buffer.prototype);
126
127const constants = ObjectDefineProperties({}, {
128  MAX_LENGTH: {
129    value: kMaxLength,
130    writable: false,
131    enumerable: true
132  },
133  MAX_STRING_LENGTH: {
134    value: kStringMaxLength,
135    writable: false,
136    enumerable: true
137  }
138});
139
140Buffer.poolSize = 8 * 1024;
141let poolSize, poolOffset, allocPool;
142
143// A toggle used to access the zero fill setting of the array buffer allocator
144// in C++.
145// |zeroFill| can be undefined when running inside an isolate where we
146// do not own the ArrayBuffer allocator.  Zero fill is always on in that case.
147const zeroFill = bindingZeroFill || [0];
148
149const encodingsMap = ObjectCreate(null);
150for (let i = 0; i < encodings.length; ++i)
151  encodingsMap[encodings[i]] = i;
152
153function createUnsafeBuffer(size) {
154  zeroFill[0] = 0;
155  try {
156    return new FastBuffer(size);
157  } finally {
158    zeroFill[0] = 1;
159  }
160}
161
162function createPool() {
163  poolSize = Buffer.poolSize;
164  allocPool = createUnsafeBuffer(poolSize).buffer;
165  markAsUntransferable(allocPool);
166  poolOffset = 0;
167}
168createPool();
169
170function alignPool() {
171  // Ensure aligned slices
172  if (poolOffset & 0x7) {
173    poolOffset |= 0x7;
174    poolOffset++;
175  }
176}
177
178let bufferWarningAlreadyEmitted = false;
179let nodeModulesCheckCounter = 0;
180const bufferWarning = 'Buffer() is deprecated due to security and usability ' +
181                      'issues. Please use the Buffer.alloc(), ' +
182                      'Buffer.allocUnsafe(), or Buffer.from() methods instead.';
183
184function showFlaggedDeprecation() {
185  if (bufferWarningAlreadyEmitted ||
186      ++nodeModulesCheckCounter > 10000 ||
187      (!require('internal/options').getOptionValue('--pending-deprecation') &&
188       isInsideNodeModules())) {
189    // We don't emit a warning, because we either:
190    // - Already did so, or
191    // - Already checked too many times whether a call is coming
192    //   from node_modules and want to stop slowing down things, or
193    // - We aren't running with `--pending-deprecation` enabled,
194    //   and the code is inside `node_modules`.
195    return;
196  }
197
198  process.emitWarning(bufferWarning, 'DeprecationWarning', 'DEP0005');
199  bufferWarningAlreadyEmitted = true;
200}
201
202function toInteger(n, defaultVal) {
203  n = +n;
204  if (!NumberIsNaN(n) &&
205      n >= NumberMIN_SAFE_INTEGER &&
206      n <= NumberMAX_SAFE_INTEGER) {
207    return ((n % 1) === 0 ? n : MathFloor(n));
208  }
209  return defaultVal;
210}
211
212function _copy(source, target, targetStart, sourceStart, sourceEnd) {
213  if (!isUint8Array(source))
214    throw new ERR_INVALID_ARG_TYPE('source', ['Buffer', 'Uint8Array'], source);
215  if (!isUint8Array(target))
216    throw new ERR_INVALID_ARG_TYPE('target', ['Buffer', 'Uint8Array'], target);
217
218  if (targetStart === undefined) {
219    targetStart = 0;
220  } else {
221    targetStart = toInteger(targetStart, 0);
222    if (targetStart < 0)
223      throw new ERR_OUT_OF_RANGE('targetStart', '>= 0', targetStart);
224  }
225
226  if (sourceStart === undefined) {
227    sourceStart = 0;
228  } else {
229    sourceStart = toInteger(sourceStart, 0);
230    if (sourceStart < 0)
231      throw new ERR_OUT_OF_RANGE('sourceStart', '>= 0', sourceStart);
232  }
233
234  if (sourceEnd === undefined) {
235    sourceEnd = source.length;
236  } else {
237    sourceEnd = toInteger(sourceEnd, 0);
238    if (sourceEnd < 0)
239      throw new ERR_OUT_OF_RANGE('sourceEnd', '>= 0', sourceEnd);
240  }
241
242  if (targetStart >= target.length || sourceStart >= sourceEnd)
243    return 0;
244
245  if (sourceStart > source.length) {
246    throw new ERR_OUT_OF_RANGE('sourceStart',
247                               `<= ${source.length}`,
248                               sourceStart);
249  }
250
251  return _copyActual(source, target, targetStart, sourceStart, sourceEnd);
252}
253
254function _copyActual(source, target, targetStart, sourceStart, sourceEnd) {
255  if (sourceEnd - sourceStart > target.length - targetStart)
256    sourceEnd = sourceStart + target.length - targetStart;
257
258  let nb = sourceEnd - sourceStart;
259  const sourceLen = source.length - sourceStart;
260  if (nb > sourceLen)
261    nb = sourceLen;
262
263  if (sourceStart !== 0 || sourceEnd < source.length)
264    source = new Uint8Array(source.buffer, source.byteOffset + sourceStart, nb);
265
266  TypedArrayPrototypeSet(target, source, targetStart);
267
268  return nb;
269}
270
271/**
272 * The Buffer() constructor is deprecated in documentation and should not be
273 * used moving forward. Rather, developers should use one of the three new
274 * factory APIs: Buffer.from(), Buffer.allocUnsafe() or Buffer.alloc() based on
275 * their specific needs. There is no runtime deprecation because of the extent
276 * to which the Buffer constructor is used in the ecosystem currently -- a
277 * runtime deprecation would introduce too much breakage at this time. It's not
278 * likely that the Buffer constructors would ever actually be removed.
279 * Deprecation Code: DEP0005
280 */
281function Buffer(arg, encodingOrOffset, length) {
282  showFlaggedDeprecation();
283  // Common case.
284  if (typeof arg === 'number') {
285    if (typeof encodingOrOffset === 'string') {
286      throw new ERR_INVALID_ARG_TYPE('string', 'string', arg);
287    }
288    return Buffer.alloc(arg);
289  }
290  return Buffer.from(arg, encodingOrOffset, length);
291}
292
293ObjectDefineProperty(Buffer, SymbolSpecies, {
294  enumerable: false,
295  configurable: true,
296  get() { return FastBuffer; }
297});
298
299/**
300 * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError
301 * if value is a number.
302 * Buffer.from(str[, encoding])
303 * Buffer.from(array)
304 * Buffer.from(buffer)
305 * Buffer.from(arrayBuffer[, byteOffset[, length]])
306 */
307Buffer.from = function from(value, encodingOrOffset, length) {
308  if (typeof value === 'string')
309    return fromString(value, encodingOrOffset);
310
311  if (typeof value === 'object' && value !== null) {
312    if (isAnyArrayBuffer(value))
313      return fromArrayBuffer(value, encodingOrOffset, length);
314
315    const valueOf = value.valueOf && value.valueOf();
316    if (valueOf != null &&
317        valueOf !== value &&
318        (typeof valueOf === 'string' || typeof valueOf === 'object')) {
319      return from(valueOf, encodingOrOffset, length);
320    }
321
322    const b = fromObject(value);
323    if (b)
324      return b;
325
326    if (typeof value[SymbolToPrimitive] === 'function') {
327      const primitive = value[SymbolToPrimitive]('string');
328      if (typeof primitive === 'string') {
329        return fromString(primitive, encodingOrOffset);
330      }
331    }
332  }
333
334  throw new ERR_INVALID_ARG_TYPE(
335    'first argument',
336    ['string', 'Buffer', 'ArrayBuffer', 'Array', 'Array-like Object'],
337    value
338  );
339};
340
341// Identical to the built-in %TypedArray%.of(), but avoids using the deprecated
342// Buffer() constructor. Must use arrow function syntax to avoid automatically
343// adding a `prototype` property and making the function a constructor.
344//
345// Refs: https://tc39.github.io/ecma262/#sec-%typedarray%.of
346// Refs: https://esdiscuss.org/topic/isconstructor#content-11
347const of = (...items) => {
348  const newObj = createUnsafeBuffer(items.length);
349  for (let k = 0; k < items.length; k++)
350    newObj[k] = items[k];
351  return newObj;
352};
353Buffer.of = of;
354
355ObjectSetPrototypeOf(Buffer, Uint8Array);
356
357// The 'assertSize' method will remove itself from the callstack when an error
358// occurs. This is done simply to keep the internal details of the
359// implementation from bleeding out to users.
360const assertSize = hideStackFrames((size) => {
361  if (typeof size !== 'number') {
362    throw new ERR_INVALID_ARG_TYPE('size', 'number', size);
363  }
364  if (!(size >= 0 && size <= kMaxLength)) {
365    throw new ERR_INVALID_OPT_VALUE.RangeError('size', size);
366  }
367});
368
369/**
370 * Creates a new filled Buffer instance.
371 * alloc(size[, fill[, encoding]])
372 */
373Buffer.alloc = function alloc(size, fill, encoding) {
374  assertSize(size);
375  if (fill !== undefined && fill !== 0 && size > 0) {
376    const buf = createUnsafeBuffer(size);
377    return _fill(buf, fill, 0, buf.length, encoding);
378  }
379  return new FastBuffer(size);
380};
381
382/**
383 * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer
384 * instance. If `--zero-fill-buffers` is set, will zero-fill the buffer.
385 */
386Buffer.allocUnsafe = function allocUnsafe(size) {
387  assertSize(size);
388  return allocate(size);
389};
390
391/**
392 * Equivalent to SlowBuffer(num), by default creates a non-zero-filled
393 * Buffer instance that is not allocated off the pre-initialized pool.
394 * If `--zero-fill-buffers` is set, will zero-fill the buffer.
395 */
396Buffer.allocUnsafeSlow = function allocUnsafeSlow(size) {
397  assertSize(size);
398  return createUnsafeBuffer(size);
399};
400
401// If --zero-fill-buffers command line argument is set, a zero-filled
402// buffer is returned.
403function SlowBuffer(length) {
404  assertSize(length);
405  return createUnsafeBuffer(length);
406}
407
408ObjectSetPrototypeOf(SlowBuffer.prototype, Uint8ArrayPrototype);
409ObjectSetPrototypeOf(SlowBuffer, Uint8Array);
410
411function allocate(size) {
412  if (size <= 0) {
413    return new FastBuffer();
414  }
415  if (size < (Buffer.poolSize >>> 1)) {
416    if (size > (poolSize - poolOffset))
417      createPool();
418    const b = new FastBuffer(allocPool, poolOffset, size);
419    poolOffset += size;
420    alignPool();
421    return b;
422  }
423  return createUnsafeBuffer(size);
424}
425
426function fromStringFast(string, ops) {
427  const length = ops.byteLength(string);
428
429  if (length >= (Buffer.poolSize >>> 1))
430    return createFromString(string, ops.encodingVal);
431
432  if (length > (poolSize - poolOffset))
433    createPool();
434  let b = new FastBuffer(allocPool, poolOffset, length);
435  const actual = ops.write(b, string, 0, length);
436  if (actual !== length) {
437    // byteLength() may overestimate. That's a rare case, though.
438    b = new FastBuffer(allocPool, poolOffset, actual);
439  }
440  poolOffset += actual;
441  alignPool();
442  return b;
443}
444
445function fromString(string, encoding) {
446  let ops;
447  if (typeof encoding !== 'string' || encoding.length === 0) {
448    if (string.length === 0)
449      return new FastBuffer();
450    ops = encodingOps.utf8;
451    encoding = undefined;
452  } else {
453    ops = getEncodingOps(encoding);
454    if (ops === undefined)
455      throw new ERR_UNKNOWN_ENCODING(encoding);
456    if (string.length === 0)
457      return new FastBuffer();
458  }
459  return fromStringFast(string, ops);
460}
461
462function fromArrayBuffer(obj, byteOffset, length) {
463  // Convert byteOffset to integer
464  if (byteOffset === undefined) {
465    byteOffset = 0;
466  } else {
467    byteOffset = +byteOffset;
468    if (NumberIsNaN(byteOffset))
469      byteOffset = 0;
470  }
471
472  const maxLength = obj.byteLength - byteOffset;
473
474  if (maxLength < 0)
475    throw new ERR_BUFFER_OUT_OF_BOUNDS('offset');
476
477  if (length === undefined) {
478    length = maxLength;
479  } else {
480    // Convert length to non-negative integer.
481    length = +length;
482    if (length > 0) {
483      if (length > maxLength)
484        throw new ERR_BUFFER_OUT_OF_BOUNDS('length');
485    } else {
486      length = 0;
487    }
488  }
489
490  return new FastBuffer(obj, byteOffset, length);
491}
492
493function fromArrayLike(obj) {
494  if (obj.length <= 0)
495    return new FastBuffer();
496  if (obj.length < (Buffer.poolSize >>> 1)) {
497    if (obj.length > (poolSize - poolOffset))
498      createPool();
499    const b = new FastBuffer(allocPool, poolOffset, obj.length);
500    TypedArrayPrototypeSet(b, obj, 0);
501    poolOffset += obj.length;
502    alignPool();
503    return b;
504  }
505  return new FastBuffer(obj);
506}
507
508function fromObject(obj) {
509  if (obj.length !== undefined || isAnyArrayBuffer(obj.buffer)) {
510    if (typeof obj.length !== 'number') {
511      return new FastBuffer();
512    }
513    return fromArrayLike(obj);
514  }
515
516  if (obj.type === 'Buffer' && ArrayIsArray(obj.data)) {
517    return fromArrayLike(obj.data);
518  }
519}
520
521// Static methods
522
523Buffer.isBuffer = function isBuffer(b) {
524  return b instanceof Buffer;
525};
526
527Buffer.compare = function compare(buf1, buf2) {
528  if (!isUint8Array(buf1)) {
529    throw new ERR_INVALID_ARG_TYPE('buf1', ['Buffer', 'Uint8Array'], buf1);
530  }
531
532  if (!isUint8Array(buf2)) {
533    throw new ERR_INVALID_ARG_TYPE('buf2', ['Buffer', 'Uint8Array'], buf2);
534  }
535
536  if (buf1 === buf2) {
537    return 0;
538  }
539
540  return _compare(buf1, buf2);
541};
542
543Buffer.isEncoding = function isEncoding(encoding) {
544  return typeof encoding === 'string' && encoding.length !== 0 &&
545         normalizeEncoding(encoding) !== undefined;
546};
547Buffer[kIsEncodingSymbol] = Buffer.isEncoding;
548
549Buffer.concat = function concat(list, length) {
550  if (!ArrayIsArray(list)) {
551    throw new ERR_INVALID_ARG_TYPE('list', 'Array', list);
552  }
553
554  if (list.length === 0)
555    return new FastBuffer();
556
557  if (length === undefined) {
558    length = 0;
559    for (let i = 0; i < list.length; i++) {
560      if (list[i].length) {
561        length += list[i].length;
562      }
563    }
564  } else {
565    validateOffset(length, 'length');
566  }
567
568  const buffer = Buffer.allocUnsafe(length);
569  let pos = 0;
570  for (let i = 0; i < list.length; i++) {
571    const buf = list[i];
572    if (!isUint8Array(buf)) {
573      // TODO(BridgeAR): This should not be of type ERR_INVALID_ARG_TYPE.
574      // Instead, find the proper error code for this.
575      throw new ERR_INVALID_ARG_TYPE(
576        `list[${i}]`, ['Buffer', 'Uint8Array'], list[i]);
577    }
578    pos += _copyActual(buf, buffer, pos, 0, buf.length);
579  }
580
581  // Note: `length` is always equal to `buffer.length` at this point
582  if (pos < length) {
583    // Zero-fill the remaining bytes if the specified `length` was more than
584    // the actual total length, i.e. if we have some remaining allocated bytes
585    // there were not initialized.
586    TypedArrayPrototypeFill(buffer, 0, pos, length);
587  }
588
589  return buffer;
590};
591
592function base64ByteLength(str, bytes) {
593  // Handle padding
594  if (StringPrototypeCharCodeAt(str, bytes - 1) === 0x3D)
595    bytes--;
596  if (bytes > 1 && StringPrototypeCharCodeAt(str, bytes - 1) === 0x3D)
597    bytes--;
598
599  // Base64 ratio: 3/4
600  return (bytes * 3) >>> 2;
601}
602
603const encodingOps = {
604  utf8: {
605    encoding: 'utf8',
606    encodingVal: encodingsMap.utf8,
607    byteLength: byteLengthUtf8,
608    write: (buf, string, offset, len) => buf.utf8Write(string, offset, len),
609    slice: (buf, start, end) => buf.utf8Slice(start, end),
610    indexOf: (buf, val, byteOffset, dir) =>
611      indexOfString(buf, val, byteOffset, encodingsMap.utf8, dir)
612  },
613  ucs2: {
614    encoding: 'ucs2',
615    encodingVal: encodingsMap.utf16le,
616    byteLength: (string) => string.length * 2,
617    write: (buf, string, offset, len) => buf.ucs2Write(string, offset, len),
618    slice: (buf, start, end) => buf.ucs2Slice(start, end),
619    indexOf: (buf, val, byteOffset, dir) =>
620      indexOfString(buf, val, byteOffset, encodingsMap.utf16le, dir)
621  },
622  utf16le: {
623    encoding: 'utf16le',
624    encodingVal: encodingsMap.utf16le,
625    byteLength: (string) => string.length * 2,
626    write: (buf, string, offset, len) => buf.ucs2Write(string, offset, len),
627    slice: (buf, start, end) => buf.ucs2Slice(start, end),
628    indexOf: (buf, val, byteOffset, dir) =>
629      indexOfString(buf, val, byteOffset, encodingsMap.utf16le, dir)
630  },
631  latin1: {
632    encoding: 'latin1',
633    encodingVal: encodingsMap.latin1,
634    byteLength: (string) => string.length,
635    write: (buf, string, offset, len) => buf.latin1Write(string, offset, len),
636    slice: (buf, start, end) => buf.latin1Slice(start, end),
637    indexOf: (buf, val, byteOffset, dir) =>
638      indexOfString(buf, val, byteOffset, encodingsMap.latin1, dir)
639  },
640  ascii: {
641    encoding: 'ascii',
642    encodingVal: encodingsMap.ascii,
643    byteLength: (string) => string.length,
644    write: (buf, string, offset, len) => buf.asciiWrite(string, offset, len),
645    slice: (buf, start, end) => buf.asciiSlice(start, end),
646    indexOf: (buf, val, byteOffset, dir) =>
647      indexOfBuffer(buf,
648                    fromStringFast(val, encodingOps.ascii),
649                    byteOffset,
650                    encodingsMap.ascii,
651                    dir)
652  },
653  base64: {
654    encoding: 'base64',
655    encodingVal: encodingsMap.base64,
656    byteLength: (string) => base64ByteLength(string, string.length),
657    write: (buf, string, offset, len) => buf.base64Write(string, offset, len),
658    slice: (buf, start, end) => buf.base64Slice(start, end),
659    indexOf: (buf, val, byteOffset, dir) =>
660      indexOfBuffer(buf,
661                    fromStringFast(val, encodingOps.base64),
662                    byteOffset,
663                    encodingsMap.base64,
664                    dir)
665  },
666  base64url: {
667    encoding: 'base64url',
668    encodingVal: encodingsMap.base64url,
669    byteLength: (string) => base64ByteLength(string, string.length),
670    write: (buf, string, offset, len) =>
671      buf.base64urlWrite(string, offset, len),
672    slice: (buf, start, end) => buf.base64urlSlice(start, end),
673    indexOf: (buf, val, byteOffset, dir) =>
674      indexOfBuffer(buf,
675                    fromStringFast(val, encodingOps.base64url),
676                    byteOffset,
677                    encodingsMap.base64url,
678                    dir)
679  },
680  hex: {
681    encoding: 'hex',
682    encodingVal: encodingsMap.hex,
683    byteLength: (string) => string.length >>> 1,
684    write: (buf, string, offset, len) => buf.hexWrite(string, offset, len),
685    slice: (buf, start, end) => buf.hexSlice(start, end),
686    indexOf: (buf, val, byteOffset, dir) =>
687      indexOfBuffer(buf,
688                    fromStringFast(val, encodingOps.hex),
689                    byteOffset,
690                    encodingsMap.hex,
691                    dir)
692  }
693};
694function getEncodingOps(encoding) {
695  encoding += '';
696  switch (encoding.length) {
697    case 4:
698      if (encoding === 'utf8') return encodingOps.utf8;
699      if (encoding === 'ucs2') return encodingOps.ucs2;
700      encoding = StringPrototypeToLowerCase(encoding);
701      if (encoding === 'utf8') return encodingOps.utf8;
702      if (encoding === 'ucs2') return encodingOps.ucs2;
703      break;
704    case 5:
705      if (encoding === 'utf-8') return encodingOps.utf8;
706      if (encoding === 'ascii') return encodingOps.ascii;
707      if (encoding === 'ucs-2') return encodingOps.ucs2;
708      encoding = StringPrototypeToLowerCase(encoding);
709      if (encoding === 'utf-8') return encodingOps.utf8;
710      if (encoding === 'ascii') return encodingOps.ascii;
711      if (encoding === 'ucs-2') return encodingOps.ucs2;
712      break;
713    case 7:
714      if (encoding === 'utf16le' ||
715          StringPrototypeToLowerCase(encoding) === 'utf16le')
716        return encodingOps.utf16le;
717      break;
718    case 8:
719      if (encoding === 'utf-16le' ||
720          StringPrototypeToLowerCase(encoding) === 'utf-16le')
721        return encodingOps.utf16le;
722      break;
723    case 6:
724      if (encoding === 'latin1' || encoding === 'binary')
725        return encodingOps.latin1;
726      if (encoding === 'base64') return encodingOps.base64;
727      encoding = StringPrototypeToLowerCase(encoding);
728      if (encoding === 'latin1' || encoding === 'binary')
729        return encodingOps.latin1;
730      if (encoding === 'base64') return encodingOps.base64;
731      break;
732    case 3:
733      if (encoding === 'hex' || StringPrototypeToLowerCase(encoding) === 'hex')
734        return encodingOps.hex;
735      break;
736    case 9:
737      if (encoding === 'base64url' ||
738          StringPrototypeToLowerCase(encoding) === 'base64url')
739        return encodingOps.base64url;
740      break;
741  }
742}
743
744function byteLength(string, encoding) {
745  if (typeof string !== 'string') {
746    if (isArrayBufferView(string) || isAnyArrayBuffer(string)) {
747      return string.byteLength;
748    }
749
750    throw new ERR_INVALID_ARG_TYPE(
751      'string', ['string', 'Buffer', 'ArrayBuffer'], string
752    );
753  }
754
755  const len = string.length;
756  const mustMatch = (arguments.length > 2 && arguments[2] === true);
757  if (!mustMatch && len === 0)
758    return 0;
759
760  if (!encoding)
761    return (mustMatch ? -1 : byteLengthUtf8(string));
762
763  const ops = getEncodingOps(encoding);
764  if (ops === undefined)
765    return (mustMatch ? -1 : byteLengthUtf8(string));
766  return ops.byteLength(string);
767}
768
769Buffer.byteLength = byteLength;
770
771// For backwards compatibility.
772ObjectDefineProperty(Buffer.prototype, 'parent', {
773  enumerable: true,
774  get() {
775    if (!(this instanceof Buffer))
776      return undefined;
777    return this.buffer;
778  }
779});
780ObjectDefineProperty(Buffer.prototype, 'offset', {
781  enumerable: true,
782  get() {
783    if (!(this instanceof Buffer))
784      return undefined;
785    return this.byteOffset;
786  }
787});
788
789Buffer.prototype.copy =
790  function copy(target, targetStart, sourceStart, sourceEnd) {
791    return _copy(this, target, targetStart, sourceStart, sourceEnd);
792  };
793
794// No need to verify that "buf.length <= MAX_UINT32" since it's a read-only
795// property of a typed array.
796// This behaves neither like String nor Uint8Array in that we set start/end
797// to their upper/lower bounds if the value passed is out of range.
798Buffer.prototype.toString = function toString(encoding, start, end) {
799  if (arguments.length === 0) {
800    return this.utf8Slice(0, this.length);
801  }
802
803  const len = this.length;
804
805  if (start <= 0)
806    start = 0;
807  else if (start >= len)
808    return '';
809  else
810    start |= 0;
811
812  if (end === undefined || end > len)
813    end = len;
814  else
815    end |= 0;
816
817  if (end <= start)
818    return '';
819
820  if (encoding === undefined)
821    return this.utf8Slice(start, end);
822
823  const ops = getEncodingOps(encoding);
824  if (ops === undefined)
825    throw new ERR_UNKNOWN_ENCODING(encoding);
826
827  return ops.slice(this, start, end);
828};
829
830Buffer.prototype.equals = function equals(otherBuffer) {
831  if (!isUint8Array(otherBuffer)) {
832    throw new ERR_INVALID_ARG_TYPE(
833      'otherBuffer', ['Buffer', 'Uint8Array'], otherBuffer);
834  }
835
836  if (this === otherBuffer)
837    return true;
838
839  if (this.byteLength !== otherBuffer.byteLength)
840    return false;
841
842  return this.byteLength === 0 || _compare(this, otherBuffer) === 0;
843};
844
845let INSPECT_MAX_BYTES = 50;
846// Override how buffers are presented by util.inspect().
847Buffer.prototype[customInspectSymbol] = function inspect(recurseTimes, ctx) {
848  const max = INSPECT_MAX_BYTES;
849  const actualMax = MathMin(max, this.length);
850  const remaining = this.length - max;
851  let str = StringPrototypeTrim(StringPrototypeReplace(
852    this.hexSlice(0, actualMax), /(.{2})/g, '$1 '));
853  if (remaining > 0)
854    str += ` ... ${remaining} more byte${remaining > 1 ? 's' : ''}`;
855  // Inspect special properties as well, if possible.
856  if (ctx) {
857    let extras = false;
858    const filter = ctx.showHidden ? ALL_PROPERTIES : ONLY_ENUMERABLE;
859    const obj = ObjectCreate(null);
860    ArrayPrototypeForEach(getOwnNonIndexProperties(this, filter),
861                          (key) => {
862                            extras = true;
863                            obj[key] = this[key];
864                          });
865    if (extras) {
866      if (this.length !== 0)
867        str += ', ';
868      // '[Object: null prototype] {'.length === 26
869      // This is guarded with a test.
870      str += StringPrototypeSlice(utilInspect(obj, {
871        ...ctx,
872        breakLength: Infinity,
873        compact: true
874      }), 27, -2);
875    }
876  }
877  return `<${this.constructor.name} ${str}>`;
878};
879Buffer.prototype.inspect = Buffer.prototype[customInspectSymbol];
880
881Buffer.prototype.compare = function compare(target,
882                                            targetStart,
883                                            targetEnd,
884                                            sourceStart,
885                                            sourceEnd) {
886  if (!isUint8Array(target)) {
887    throw new ERR_INVALID_ARG_TYPE('target', ['Buffer', 'Uint8Array'], target);
888  }
889  if (arguments.length === 1)
890    return _compare(this, target);
891
892  if (targetStart === undefined)
893    targetStart = 0;
894  else
895    validateOffset(targetStart, 'targetStart');
896
897  if (targetEnd === undefined)
898    targetEnd = target.length;
899  else
900    validateOffset(targetEnd, 'targetEnd', 0, target.length);
901
902  if (sourceStart === undefined)
903    sourceStart = 0;
904  else
905    validateOffset(sourceStart, 'sourceStart');
906
907  if (sourceEnd === undefined)
908    sourceEnd = this.length;
909  else
910    validateOffset(sourceEnd, 'sourceEnd', 0, this.length);
911
912  if (sourceStart >= sourceEnd)
913    return (targetStart >= targetEnd ? 0 : -1);
914  if (targetStart >= targetEnd)
915    return 1;
916
917  return compareOffset(this, target, targetStart, sourceStart, targetEnd,
918                       sourceEnd);
919};
920
921// Finds either the first index of `val` in `buffer` at offset >= `byteOffset`,
922// OR the last index of `val` in `buffer` at offset <= `byteOffset`.
923//
924// Arguments:
925// - buffer - a Buffer to search
926// - val - a string, Buffer, or number
927// - byteOffset - an index into `buffer`; will be clamped to an int32
928// - encoding - an optional encoding, relevant if val is a string
929// - dir - true for indexOf, false for lastIndexOf
930function bidirectionalIndexOf(buffer, val, byteOffset, encoding, dir) {
931  validateBuffer(buffer);
932
933  if (typeof byteOffset === 'string') {
934    encoding = byteOffset;
935    byteOffset = undefined;
936  } else if (byteOffset > 0x7fffffff) {
937    byteOffset = 0x7fffffff;
938  } else if (byteOffset < -0x80000000) {
939    byteOffset = -0x80000000;
940  }
941  // Coerce to Number. Values like null and [] become 0.
942  byteOffset = +byteOffset;
943  // If the offset is undefined, "foo", {}, coerces to NaN, search whole buffer.
944  if (NumberIsNaN(byteOffset)) {
945    byteOffset = dir ? 0 : (buffer.length || buffer.byteLength);
946  }
947  dir = !!dir;  // Cast to bool.
948
949  if (typeof val === 'number')
950    return indexOfNumber(buffer, val >>> 0, byteOffset, dir);
951
952  let ops;
953  if (encoding === undefined)
954    ops = encodingOps.utf8;
955  else
956    ops = getEncodingOps(encoding);
957
958  if (typeof val === 'string') {
959    if (ops === undefined)
960      throw new ERR_UNKNOWN_ENCODING(encoding);
961    return ops.indexOf(buffer, val, byteOffset, dir);
962  }
963
964  if (isUint8Array(val)) {
965    const encodingVal =
966      (ops === undefined ? encodingsMap.utf8 : ops.encodingVal);
967    return indexOfBuffer(buffer, val, byteOffset, encodingVal, dir);
968  }
969
970  throw new ERR_INVALID_ARG_TYPE(
971    'value', ['number', 'string', 'Buffer', 'Uint8Array'], val
972  );
973}
974
975Buffer.prototype.indexOf = function indexOf(val, byteOffset, encoding) {
976  return bidirectionalIndexOf(this, val, byteOffset, encoding, true);
977};
978
979Buffer.prototype.lastIndexOf = function lastIndexOf(val, byteOffset, encoding) {
980  return bidirectionalIndexOf(this, val, byteOffset, encoding, false);
981};
982
983Buffer.prototype.includes = function includes(val, byteOffset, encoding) {
984  return this.indexOf(val, byteOffset, encoding) !== -1;
985};
986
987// Usage:
988//    buffer.fill(number[, offset[, end]])
989//    buffer.fill(buffer[, offset[, end]])
990//    buffer.fill(string[, offset[, end]][, encoding])
991Buffer.prototype.fill = function fill(value, offset, end, encoding) {
992  return _fill(this, value, offset, end, encoding);
993};
994
995function _fill(buf, value, offset, end, encoding) {
996  if (typeof value === 'string') {
997    if (offset === undefined || typeof offset === 'string') {
998      encoding = offset;
999      offset = 0;
1000      end = buf.length;
1001    } else if (typeof end === 'string') {
1002      encoding = end;
1003      end = buf.length;
1004    }
1005
1006    const normalizedEncoding = normalizeEncoding(encoding);
1007    if (normalizedEncoding === undefined) {
1008      validateString(encoding, 'encoding');
1009      throw new ERR_UNKNOWN_ENCODING(encoding);
1010    }
1011
1012    if (value.length === 0) {
1013      // If value === '' default to zero.
1014      value = 0;
1015    } else if (value.length === 1) {
1016      // Fast path: If `value` fits into a single byte, use that numeric value.
1017      if (normalizedEncoding === 'utf8') {
1018        const code = StringPrototypeCharCodeAt(value, 0);
1019        if (code < 128) {
1020          value = code;
1021        }
1022      } else if (normalizedEncoding === 'latin1') {
1023        value = StringPrototypeCharCodeAt(value, 0);
1024      }
1025    }
1026  } else {
1027    encoding = undefined;
1028  }
1029
1030  if (offset === undefined) {
1031    offset = 0;
1032    end = buf.length;
1033  } else {
1034    validateOffset(offset, 'offset');
1035    // Invalid ranges are not set to a default, so can range check early.
1036    if (end === undefined) {
1037      end = buf.length;
1038    } else {
1039      validateOffset(end, 'end', 0, buf.length);
1040    }
1041    if (offset >= end)
1042      return buf;
1043  }
1044
1045
1046  if (typeof value === 'number') {
1047    // OOB check
1048    const byteLen = TypedArrayPrototypeGetByteLength(buf);
1049    const fillLength = end - offset;
1050    if (offset > end || fillLength + offset > byteLen)
1051      throw new ERR_BUFFER_OUT_OF_BOUNDS();
1052
1053    TypedArrayPrototypeFill(buf, value, offset, end);
1054  } else {
1055    const res = bindingFill(buf, value, offset, end, encoding);
1056    if (res < 0) {
1057      if (res === -1)
1058        throw new ERR_INVALID_ARG_VALUE('value', value);
1059      throw new ERR_BUFFER_OUT_OF_BOUNDS();
1060    }
1061  }
1062
1063  return buf;
1064}
1065
1066Buffer.prototype.write = function write(string, offset, length, encoding) {
1067  // Buffer#write(string);
1068  if (offset === undefined) {
1069    return this.utf8Write(string, 0, this.length);
1070  }
1071  // Buffer#write(string, encoding)
1072  if (length === undefined && typeof offset === 'string') {
1073    encoding = offset;
1074    length = this.length;
1075    offset = 0;
1076
1077  // Buffer#write(string, offset[, length][, encoding])
1078  } else {
1079    validateOffset(offset, 'offset', 0, this.length);
1080
1081    const remaining = this.length - offset;
1082
1083    if (length === undefined) {
1084      length = remaining;
1085    } else if (typeof length === 'string') {
1086      encoding = length;
1087      length = remaining;
1088    } else {
1089      validateOffset(length, 'length', 0, this.length);
1090      if (length > remaining)
1091        length = remaining;
1092    }
1093  }
1094
1095  if (!encoding)
1096    return this.utf8Write(string, offset, length);
1097
1098  const ops = getEncodingOps(encoding);
1099  if (ops === undefined)
1100    throw new ERR_UNKNOWN_ENCODING(encoding);
1101  return ops.write(this, string, offset, length);
1102};
1103
1104Buffer.prototype.toJSON = function toJSON() {
1105  if (this.length > 0) {
1106    const data = new Array(this.length);
1107    for (let i = 0; i < this.length; ++i)
1108      data[i] = this[i];
1109    return { type: 'Buffer', data };
1110  }
1111  return { type: 'Buffer', data: [] };
1112};
1113
1114function adjustOffset(offset, length) {
1115  // Use Math.trunc() to convert offset to an integer value that can be larger
1116  // than an Int32. Hence, don't use offset | 0 or similar techniques.
1117  offset = MathTrunc(offset);
1118  if (offset === 0) {
1119    return 0;
1120  }
1121  if (offset < 0) {
1122    offset += length;
1123    return offset > 0 ? offset : 0;
1124  }
1125  if (offset < length) {
1126    return offset;
1127  }
1128  return NumberIsNaN(offset) ? 0 : length;
1129}
1130
1131Buffer.prototype.slice = function slice(start, end) {
1132  const srcLength = this.length;
1133  start = adjustOffset(start, srcLength);
1134  end = end !== undefined ? adjustOffset(end, srcLength) : srcLength;
1135  const newLength = end > start ? end - start : 0;
1136  return new FastBuffer(this.buffer, this.byteOffset + start, newLength);
1137};
1138
1139function swap(b, n, m) {
1140  const i = b[n];
1141  b[n] = b[m];
1142  b[m] = i;
1143}
1144
1145Buffer.prototype.swap16 = function swap16() {
1146  // For Buffer.length < 128, it's generally faster to
1147  // do the swap in javascript. For larger buffers,
1148  // dropping down to the native code is faster.
1149  const len = this.length;
1150  if (len % 2 !== 0)
1151    throw new ERR_INVALID_BUFFER_SIZE('16-bits');
1152  if (len < 128) {
1153    for (let i = 0; i < len; i += 2)
1154      swap(this, i, i + 1);
1155    return this;
1156  }
1157  return _swap16(this);
1158};
1159
1160Buffer.prototype.swap32 = function swap32() {
1161  // For Buffer.length < 192, it's generally faster to
1162  // do the swap in javascript. For larger buffers,
1163  // dropping down to the native code is faster.
1164  const len = this.length;
1165  if (len % 4 !== 0)
1166    throw new ERR_INVALID_BUFFER_SIZE('32-bits');
1167  if (len < 192) {
1168    for (let i = 0; i < len; i += 4) {
1169      swap(this, i, i + 3);
1170      swap(this, i + 1, i + 2);
1171    }
1172    return this;
1173  }
1174  return _swap32(this);
1175};
1176
1177Buffer.prototype.swap64 = function swap64() {
1178  // For Buffer.length < 192, it's generally faster to
1179  // do the swap in javascript. For larger buffers,
1180  // dropping down to the native code is faster.
1181  const len = this.length;
1182  if (len % 8 !== 0)
1183    throw new ERR_INVALID_BUFFER_SIZE('64-bits');
1184  if (len < 192) {
1185    for (let i = 0; i < len; i += 8) {
1186      swap(this, i, i + 7);
1187      swap(this, i + 1, i + 6);
1188      swap(this, i + 2, i + 5);
1189      swap(this, i + 3, i + 4);
1190    }
1191    return this;
1192  }
1193  return _swap64(this);
1194};
1195
1196Buffer.prototype.toLocaleString = Buffer.prototype.toString;
1197
1198let transcode;
1199if (internalBinding('config').hasIntl) {
1200  const {
1201    icuErrName,
1202    transcode: _transcode
1203  } = internalBinding('icu');
1204
1205  // Transcodes the Buffer from one encoding to another, returning a new
1206  // Buffer instance.
1207  transcode = function transcode(source, fromEncoding, toEncoding) {
1208    if (!isUint8Array(source)) {
1209      throw new ERR_INVALID_ARG_TYPE('source',
1210                                     ['Buffer', 'Uint8Array'], source);
1211    }
1212    if (source.length === 0) return Buffer.alloc(0);
1213
1214    fromEncoding = normalizeEncoding(fromEncoding) || fromEncoding;
1215    toEncoding = normalizeEncoding(toEncoding) || toEncoding;
1216    const result = _transcode(source, fromEncoding, toEncoding);
1217    if (typeof result !== 'number')
1218      return result;
1219
1220    const code = icuErrName(result);
1221    // eslint-disable-next-line no-restricted-syntax
1222    const err = new Error(`Unable to transcode Buffer [${code}]`);
1223    err.code = code;
1224    err.errno = result;
1225    throw err;
1226  };
1227}
1228
1229let DOMException;
1230
1231const lazyInvalidCharError = hideStackFrames((message, name) => {
1232  if (DOMException === undefined)
1233    DOMException = internalBinding('messaging').DOMException;
1234  throw new DOMException('Invalid character', 'InvalidCharacterError');
1235});
1236
1237function btoa(input) {
1238  // The implementation here has not been performance optimized in any way and
1239  // should not be.
1240  // Refs: https://github.com/nodejs/node/pull/38433#issuecomment-828426932
1241  input = `${input}`;
1242  for (let n = 0; n < input.length; n++) {
1243    if (input[n].charCodeAt(0) > 0xff)
1244      lazyInvalidCharError();
1245  }
1246  const buf = Buffer.from(input, 'latin1');
1247  return buf.toString('base64');
1248}
1249
1250const kBase64Digits =
1251  'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
1252
1253function atob(input) {
1254  // The implementation here has not been performance optimized in any way and
1255  // should not be.
1256  // Refs: https://github.com/nodejs/node/pull/38433#issuecomment-828426932
1257  input = `${input}`;
1258  for (let n = 0; n < input.length; n++) {
1259    if (!kBase64Digits.includes(input[n]))
1260      lazyInvalidCharError();
1261  }
1262  return Buffer.from(input, 'base64').toString('latin1');
1263}
1264
1265module.exports = {
1266  Blob,
1267  Buffer,
1268  SlowBuffer,
1269  transcode,
1270  // Legacy
1271  kMaxLength,
1272  kStringMaxLength,
1273  btoa,
1274  atob,
1275};
1276
1277ObjectDefineProperties(module.exports, {
1278  constants: {
1279    configurable: false,
1280    enumerable: true,
1281    value: constants
1282  },
1283  INSPECT_MAX_BYTES: {
1284    configurable: true,
1285    enumerable: true,
1286    get() { return INSPECT_MAX_BYTES; },
1287    set(val) { INSPECT_MAX_BYTES = val; }
1288  }
1289});
1290