diff --git a/doc/api/buffer.md b/doc/api/buffer.md index c07443601c8b67d..356f62c31db0f62 100644 --- a/doc/api/buffer.md +++ b/doc/api/buffer.md @@ -415,6 +415,21 @@ function: * [`Buffer.from(arrayBuffer[, byteOffset[, length]])`][`Buffer.from(arrayBuf)`] * [`Buffer.from(string[, encoding])`][`Buffer.from(string)`] +### Buffer methods are callable with `Uint8Array` instances + +All methods on the Buffer prototype are callable with a `Uint8Array` instance. + +```js +const { toString, write } = Buffer.prototype; + +const uint8array = new Uint8Array(5); + +write.call(uint8array, 'hello', 0, 5, 'utf8'); // 5 +// + +toString.call(uint8array, 'utf8'); // 'hello' +``` + ## Buffers and iteration `Buffer` instances can be iterated over using `for..of` syntax: diff --git a/lib/buffer.js b/lib/buffer.js index 22bbb95eaf79e6c..6ed58118fcc7a1a 100644 --- a/lib/buffer.js +++ b/lib/buffer.js @@ -26,6 +26,7 @@ const { ArrayBufferIsView, ArrayIsArray, ArrayPrototypeForEach, + FunctionPrototypeCall, MathFloor, MathMin, MathTrunc, @@ -135,6 +136,23 @@ FastBuffer.prototype.constructor = Buffer; Buffer.prototype = FastBuffer.prototype; addBufferPrototypeMethods(Buffer.prototype); +const { + asciiWrite, + latin1Write, + utf8Write, + asciiSlice, + base64Slice, + base64urlSlice, + latin1Slice, + hexSlice, + ucs2Slice, + utf8Slice, + base64Write, + base64urlWrite, + hexWrite, + ucs2Write, +} = Buffer.prototype; + const constants = ObjectDefineProperties({}, { MAX_LENGTH: { __proto__: null, @@ -633,8 +651,8 @@ const encodingOps = { encoding: 'utf8', encodingVal: encodingsMap.utf8, byteLength: byteLengthUtf8, - write: (buf, string, offset, len) => buf.utf8Write(string, offset, len), - slice: (buf, start, end) => buf.utf8Slice(start, end), + write: (buf, string, offset, len) => FunctionPrototypeCall(utf8Write, buf, string, offset, len), + slice: (buf, start, end) => FunctionPrototypeCall(utf8Slice, buf, start, end), indexOf: (buf, val, byteOffset, dir) => indexOfString(buf, val, byteOffset, encodingsMap.utf8, dir), }, @@ -642,8 +660,8 @@ const encodingOps = { encoding: 'ucs2', encodingVal: encodingsMap.utf16le, byteLength: (string) => string.length * 2, - write: (buf, string, offset, len) => buf.ucs2Write(string, offset, len), - slice: (buf, start, end) => buf.ucs2Slice(start, end), + write: (buf, string, offset, len) => FunctionPrototypeCall(ucs2Write, buf, string, offset, len), + slice: (buf, start, end) => FunctionPrototypeCall(ucs2Slice, buf, start, end), indexOf: (buf, val, byteOffset, dir) => indexOfString(buf, val, byteOffset, encodingsMap.utf16le, dir), }, @@ -651,8 +669,8 @@ const encodingOps = { encoding: 'utf16le', encodingVal: encodingsMap.utf16le, byteLength: (string) => string.length * 2, - write: (buf, string, offset, len) => buf.ucs2Write(string, offset, len), - slice: (buf, start, end) => buf.ucs2Slice(start, end), + write: (buf, string, offset, len) => FunctionPrototypeCall(ucs2Write, buf, string, offset, len), + slice: (buf, start, end) => FunctionPrototypeCall(ucs2Slice, buf, start, end), indexOf: (buf, val, byteOffset, dir) => indexOfString(buf, val, byteOffset, encodingsMap.utf16le, dir), }, @@ -660,8 +678,8 @@ const encodingOps = { encoding: 'latin1', encodingVal: encodingsMap.latin1, byteLength: (string) => string.length, - write: (buf, string, offset, len) => buf.latin1Write(string, offset, len), - slice: (buf, start, end) => buf.latin1Slice(start, end), + write: (buf, string, offset, len) => FunctionPrototypeCall(latin1Write, buf, string, offset, len), + slice: (buf, start, end) => FunctionPrototypeCall(latin1Slice, buf, start, end), indexOf: (buf, val, byteOffset, dir) => indexOfString(buf, val, byteOffset, encodingsMap.latin1, dir), }, @@ -669,8 +687,8 @@ const encodingOps = { encoding: 'ascii', encodingVal: encodingsMap.ascii, byteLength: (string) => string.length, - write: (buf, string, offset, len) => buf.asciiWrite(string, offset, len), - slice: (buf, start, end) => buf.asciiSlice(start, end), + write: (buf, string, offset, len) => FunctionPrototypeCall(asciiWrite, buf, string, offset, len), + slice: (buf, start, end) => FunctionPrototypeCall(asciiSlice, buf, start, end), indexOf: (buf, val, byteOffset, dir) => indexOfBuffer(buf, fromStringFast(val, encodingOps.ascii), @@ -682,8 +700,8 @@ const encodingOps = { encoding: 'base64', encodingVal: encodingsMap.base64, byteLength: (string) => base64ByteLength(string, string.length), - write: (buf, string, offset, len) => buf.base64Write(string, offset, len), - slice: (buf, start, end) => buf.base64Slice(start, end), + write: (buf, string, offset, len) => FunctionPrototypeCall(base64Write, buf, string, offset, len), + slice: (buf, start, end) => FunctionPrototypeCall(base64Slice, buf, start, end), indexOf: (buf, val, byteOffset, dir) => indexOfBuffer(buf, fromStringFast(val, encodingOps.base64), @@ -696,8 +714,8 @@ const encodingOps = { encodingVal: encodingsMap.base64url, byteLength: (string) => base64ByteLength(string, string.length), write: (buf, string, offset, len) => - buf.base64urlWrite(string, offset, len), - slice: (buf, start, end) => buf.base64urlSlice(start, end), + FunctionPrototypeCall(base64urlWrite, buf, string, offset, len), + slice: (buf, start, end) => FunctionPrototypeCall(base64urlSlice, buf, start, end), indexOf: (buf, val, byteOffset, dir) => indexOfBuffer(buf, fromStringFast(val, encodingOps.base64url), @@ -709,8 +727,8 @@ const encodingOps = { encoding: 'hex', encodingVal: encodingsMap.hex, byteLength: (string) => string.length >>> 1, - write: (buf, string, offset, len) => buf.hexWrite(string, offset, len), - slice: (buf, start, end) => buf.hexSlice(start, end), + write: (buf, string, offset, len) => FunctionPrototypeCall(hexWrite, buf, string, offset, len), + slice: (buf, start, end) => FunctionPrototypeCall(hexSlice, buf, start, end), indexOf: (buf, val, byteOffset, dir) => indexOfBuffer(buf, fromStringFast(val, encodingOps.hex), @@ -835,7 +853,7 @@ Buffer.prototype.copy = // to their upper/lower bounds if the value passed is out of range. Buffer.prototype.toString = function toString(encoding, start, end) { if (arguments.length === 0) { - return this.utf8Slice(0, this.length); + return FunctionPrototypeCall(utf8Slice, this, 0, this.length); } const len = this.length; @@ -856,7 +874,7 @@ Buffer.prototype.toString = function toString(encoding, start, end) { return ''; if (encoding === undefined) - return this.utf8Slice(start, end); + return FunctionPrototypeCall(utf8Slice, this, start, end); const ops = getEncodingOps(encoding); if (ops === undefined) @@ -887,7 +905,7 @@ Buffer.prototype[customInspectSymbol] = function inspect(recurseTimes, ctx) { const actualMax = MathMin(max, this.length); const remaining = this.length - max; let str = StringPrototypeTrim(RegExpPrototypeSymbolReplace( - /(.{2})/g, this.hexSlice(0, actualMax), '$1 ')); + /(.{2})/g, FunctionPrototypeCall(hexSlice, this, 0, actualMax), '$1 ')); if (remaining > 0) str += ` ... ${remaining} more byte${remaining > 1 ? 's' : ''}`; // Inspect special properties as well, if possible. @@ -1026,7 +1044,7 @@ Buffer.prototype.lastIndexOf = function lastIndexOf(val, byteOffset, encoding) { }; Buffer.prototype.includes = function includes(val, byteOffset, encoding) { - return this.indexOf(val, byteOffset, encoding) !== -1; + return bidirectionalIndexOf(this, val, byteOffset, encoding) !== -1; }; // Usage: @@ -1111,7 +1129,7 @@ function _fill(buf, value, offset, end, encoding) { Buffer.prototype.write = function write(string, offset, length, encoding) { // Buffer#write(string); if (offset === undefined) { - return this.utf8Write(string, 0, this.length); + return FunctionPrototypeCall(utf8Write, this, string, 0, this.length); } // Buffer#write(string, encoding) if (length === undefined && typeof offset === 'string') { @@ -1138,9 +1156,9 @@ Buffer.prototype.write = function write(string, offset, length, encoding) { } if (!encoding || encoding === 'utf8') - return this.utf8Write(string, offset, length); + return FunctionPrototypeCall(utf8Write, this, string, offset, length); if (encoding === 'ascii') - return this.asciiWrite(string, offset, length); + return FunctionPrototypeCall(asciiWrite, this, string, offset, length); const ops = getEncodingOps(encoding); if (ops === undefined) diff --git a/lib/internal/buffer.js b/lib/internal/buffer.js index dc21fb3e3345885..f2a066e7f6dce17 100644 --- a/lib/internal/buffer.js +++ b/lib/internal/buffer.js @@ -4,6 +4,7 @@ const { BigInt, Float32Array, Float64Array, + FunctionPrototypeCall, MathFloor, Number, Uint8Array, @@ -177,11 +178,11 @@ function readUIntLE(offset, byteLength) { if (byteLength === 3) return readUInt24LE(this, offset); if (byteLength === 4) - return this.readUInt32LE(offset); + return FunctionPrototypeCall(readUInt32LE, this, offset); if (byteLength === 2) - return this.readUInt16LE(offset); + return FunctionPrototypeCall(readUInt16LE, this, offset); if (byteLength === 1) - return this.readUInt8(offset); + return FunctionPrototypeCall(readUInt8, this, offset); boundsError(byteLength, 6, 'byteLength'); } @@ -266,11 +267,11 @@ function readUIntBE(offset, byteLength) { if (byteLength === 3) return readUInt24BE(this, offset); if (byteLength === 4) - return this.readUInt32BE(offset); + return FunctionPrototypeCall(readUInt32BE, this, offset); if (byteLength === 2) - return this.readUInt16BE(offset); + return FunctionPrototypeCall(readUInt16BE, this, offset); if (byteLength === 1) - return this.readUInt8(offset); + return FunctionPrototypeCall(readUInt8, this, offset); boundsError(byteLength, 6, 'byteLength'); } @@ -346,11 +347,11 @@ function readIntLE(offset, byteLength) { if (byteLength === 3) return readInt24LE(this, offset); if (byteLength === 4) - return this.readInt32LE(offset); + return FunctionPrototypeCall(readInt32LE, this, offset); if (byteLength === 2) - return this.readInt16LE(offset); + return FunctionPrototypeCall(readInt16LE, this, offset); if (byteLength === 1) - return this.readInt8(offset); + return FunctionPrototypeCall(readInt8, this, offset); boundsError(byteLength, 6, 'byteLength'); } @@ -438,11 +439,11 @@ function readIntBE(offset, byteLength) { if (byteLength === 3) return readInt24BE(this, offset); if (byteLength === 4) - return this.readInt32BE(offset); + return FunctionPrototypeCall(readInt32BE, this, offset); if (byteLength === 2) - return this.readInt16BE(offset); + return FunctionPrototypeCall(readInt16BE, this, offset); if (byteLength === 1) - return this.readInt8(offset); + return FunctionPrototypeCall(readInt8, this, offset); boundsError(byteLength, 6, 'byteLength'); } diff --git a/test/parallel/test-buffer-generic-methods.js b/test/parallel/test-buffer-generic-methods.js new file mode 100644 index 000000000000000..7b607a9a3907661 --- /dev/null +++ b/test/parallel/test-buffer-generic-methods.js @@ -0,0 +1,1118 @@ +'use strict'; +require('../common'); +const assert = require('assert'); + +function arrayOfNumbers(length) { + return Array.from({ length }, (_, i) => i); +} + +const customInspectSymbol = Symbol.for('nodejs.util.inspect.custom'); + +// Methods that are either internal or do not make sense to be generic. +const ignoredMethods = [ + 'constructor', + 'asciiSlice', + 'base64Slice', + 'base64urlSlice', + 'latin1Slice', + 'hexSlice', + 'ucs2Slice', + 'utf8Slice', + 'asciiWrite', + 'base64Write', + 'base64urlWrite', + 'latin1Write', + 'hexWrite', + 'ucs2Write', + 'utf8Write', + 'inspect', +]; + +// Tested methods +const testedMethods = [ + 'compare', + 'copy', + 'equals', + 'fill', + 'includes', + 'indexOf', + 'lastIndexOf', + 'readBigInt64BE', + 'readBigInt64LE', + 'readBigUInt64BE', + 'readBigUInt64LE', + 'readDoubleBE', + 'readDoubleLE', + 'readFloatBE', + 'readFloatLE', + 'readInt8', + 'readInt16BE', + 'readInt16LE', + 'readInt32BE', + 'readInt32LE', + 'readIntBE', + 'readIntLE', + 'readUInt8', + 'readUInt16BE', + 'readUInt16LE', + 'readUInt32BE', + 'readUInt32LE', + 'readUIntBE', + 'readUIntLE', + 'subarray', + 'slice', + 'swap16', + 'swap32', + 'swap64', + 'toJSON', + 'toString', + 'toLocaleString', + 'write', + 'writeBigInt64BE', + 'writeBigInt64LE', + 'writeBigUInt64BE', + 'writeBigUInt64LE', + 'writeDoubleBE', + 'writeDoubleLE', + 'writeFloatBE', + 'writeFloatLE', + 'writeInt8', + 'writeInt16BE', + 'writeInt16LE', + 'writeInt32BE', + 'writeInt32LE', + 'writeIntBE', + 'writeIntLE', + 'writeUInt8', + 'writeUInt16BE', + 'writeUInt16LE', + 'writeUInt32BE', + 'writeUInt32LE', + 'writeUIntBE', + 'writeUIntLE', + customInspectSymbol, +] + +const expectedMethods = [ + ...ignoredMethods, + ...testedMethods, +]; + +const isMethod = (method) => typeof Buffer.prototype[method] === 'function'; +const addUnique = (names, newName) => { + const nameMatches = (name) => name.toLowerCase() === newName.toLowerCase(); + if (!names.some(nameMatches)) names.push(newName); + return names; +} +const actualMethods = [ + // The readXInt methods are provided as both upper & lower case names; reducing to only consider each once. + ...Object.getOwnPropertyNames(Buffer.prototype).filter(isMethod).reduce(addUnique, []), + ...Object.getOwnPropertySymbols(Buffer.prototype).filter(isMethod), +]; + +// If this fails, there is a new method on Buffer that should be tested in this file to be generic. +// Then update the manually managed list above. +assert.deepStrictEqual(new Set(actualMethods), new Set(expectedMethods)); + +const { + compare, + copy, + equals, + fill, + includes, + indexOf, + lastIndexOf, + readBigInt64BE, + readBigInt64LE, + readBigUInt64BE, + readBigUInt64LE, + readDoubleBE, + readDoubleLE, + readFloatBE, + readFloatLE, + readInt8, + readInt16BE, + readInt16LE, + readInt32BE, + readInt32LE, + readIntBE, + readIntLE, + readUInt8, + readUInt16BE, + readUInt16LE, + readUInt32BE, + readUInt32LE, + readUIntBE, + readUIntLE, + subarray, + slice, + swap16, + swap32, + swap64, + toJSON, + toString, + write, + writeBigInt64BE, + writeBigInt64LE, + writeBigUInt64BE, + writeBigUInt64LE, + writeDoubleBE, + writeDoubleLE, + writeFloatBE, + writeFloatLE, + writeInt8, + writeInt16BE, + writeInt16LE, + writeInt32BE, + writeInt32LE, + writeIntBE, + writeIntLE, + writeUInt8, + writeUInt16BE, + writeUInt16LE, + writeUInt32BE, + writeUInt32LE, + writeUIntBE, + writeUIntLE, + [customInspectSymbol]: customInspect, +} = Buffer.prototype; + +/** + * Assert the Uint8Array is not converted to a Buffer + * in the process of calling a Buffer method on it. + */ +function assertContentEqual(actualUint8Array, expectedBuffer) { + assert.ok(!Buffer.isBuffer(actualUint8Array)); + assert.ok(Buffer.isBuffer(expectedBuffer)); + assert.strictEqual(actualUint8Array.length, expectedBuffer.length); + for (let i = 0; i < actualUint8Array.length; i++) { + assert.strictEqual(actualUint8Array[i], expectedBuffer[i], `Uint8Array and Buffer differ at ${i}`); + } +} + +// Buffer Methods: + +{ + // buf.compare(target[, targetStart[, targetEnd[, sourceStart[, sourceEnd]]]]) + + const buf = Uint8Array.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + const target = Uint8Array.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + + assert.strictEqual(compare.call(buf, target, 0, 4, 0, 4), 0); + assert.strictEqual(compare.call(buf, target, 0, 4, 1, 5), 1); + assert.strictEqual(compare.call(buf, target, 1, 5, 0, 4), -1); +} + +{ + // buf.copy(target[, targetStart[, sourceStart[, sourceEnd]]]) + + const sourceUint8Array = Uint8Array.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9); + + const destination = new Uint8Array(4); + + copy.call(sourceUint8Array, destination, 1, 7, 10); + assertContentEqual(destination, Buffer.of(0, 7, 8, 9)); +} + +{ + // buf.equals(otherBufferLike) + + const emptyUint8Array = new Uint8Array(0); + const emptyBuffer = Buffer.alloc(0); + + assert.ok(equals.call(emptyUint8Array, emptyBuffer)); + assert.ok(equals.call(emptyUint8Array, emptyUint8Array)); + assert.ok(equals.call(emptyUint8Array, new Uint8Array(0))); + + const largeUint8Array = new Uint8Array(arrayOfNumbers(9000)); + const largeBuffer = Buffer.from(arrayOfNumbers(9000)); + + assert.ok(equals.call(largeUint8Array, largeBuffer)); + assert.ok(equals.call(largeUint8Array, largeUint8Array)); + assert.ok(equals.call(largeUint8Array, new Uint8Array(arrayOfNumbers(9000)))); +} + +{ + // buf.fill(value[, byteOffset][, encoding]) + + const uint8array = new Uint8Array(10); + const buffer = Buffer.alloc(10); + + assertContentEqual( + fill.call(uint8array, '\u03a3', 0, 'utf16le'), + buffer.fill('\u03a3', 0, 'utf16le') + ); +} + +{ + // buf.includes(value[, byteOffset][, encoding]) + + const uint8array = Uint8Array.of(154, 3, 145, 3, 163, 3, 163, 3, 149, 3); + const buffer = Buffer.of(154, 3, 145, 3, 163, 3, 163, 3, 149, 3); + + assert.strictEqual( + includes.call(uint8array, '\u03a3', 0, 'utf16le'), + buffer.includes('\u03a3', 0, 'utf16le') + ); +} + + +{ + // buf.indexOf(value[, byteOffset][, encoding]) + + const uint8array = Uint8Array.of(154, 3, 145, 3, 163, 3, 163, 3, 149, 3); + const buffer = Buffer.of(154, 3, 145, 3, 163, 3, 163, 3, 149, 3); + + assert.strictEqual( + indexOf.call(uint8array, '\u03a3', 0, 'utf16le'), + buffer.indexOf('\u03a3', 0, 'utf16le') + ); +} + +{ + // buf.lastIndexOf(value[, byteOffset][, encoding]) + + const uint8array = Uint8Array.of(154, 3, 145, 3, 163, 3, 163, 3, 149, 3); + const buffer = Buffer.of(154, 3, 145, 3, 163, 3, 163, 3, 149, 3); + + assert.strictEqual( + lastIndexOf.call(uint8array, '\u03a3', -5, 'utf16le'), + buffer.lastIndexOf('\u03a3', -5, 'utf16le') + ); +} + + +{ + // buf.readBigInt64BE([offset]) + + const uint8array = Uint8Array.of(0, 0, 1, 0, 0, 0, 1, 0); + const buffer = Buffer.of(0, 0, 1, 0, 0, 0, 1, 0); + + assert.strictEqual( + readBigInt64BE.call(uint8array, 0), + buffer.readBigInt64BE(0) + ); +} + +{ + // buf.readBigInt64LE([offset]) + + const uint8array = Uint8Array.of(0, 0, 1, 0, 0, 0, 1, 0); + const buffer = Buffer.of(0, 0, 1, 0, 0, 0, 1, 0); + + assert.strictEqual( + readBigInt64LE.call(uint8array, 0), + buffer.readBigInt64LE(0) + ); +} + +{ + // buf.readBigUInt64BE([offset]) + + const uint8array = Uint8Array.of(0, 0, 1, 0, 0, 0, 1, 0); + const buffer = Buffer.of(0, 0, 1, 0, 0, 0, 1, 0); + + assert.strictEqual( + readBigUInt64BE.call(uint8array, 0), + buffer.readBigUInt64BE(0) + ); +} + +{ + // buf.readBigUInt64LE([offset]) + + const uint8array = Uint8Array.of(0, 0, 1, 0, 0, 0, 1, 0); + const buffer = Buffer.of(0, 0, 1, 0, 0, 0, 1, 0); + + assert.strictEqual( + readBigUInt64LE.call(uint8array, 0), + buffer.readBigUInt64LE(0) + ); +} + +{ + // buf.readDoubleBE([offset]) + + const uint8array = Uint8Array.of(0, 0, 1, 0, 0, 0, 1, 0); + const buffer = Buffer.of(0, 0, 1, 0, 0, 0, 1, 0); + + assert.strictEqual( + readDoubleBE.call(uint8array, 0), + buffer.readDoubleBE(0) + ); +} + +{ + // buf.readDoubleLE([offset]) + + const uint8array = Uint8Array.of(0, 0, 1, 0, 0, 0, 1, 0); + const buffer = Buffer.of(0, 0, 1, 0, 0, 0, 1, 0); + + assert.strictEqual( + readDoubleLE.call(uint8array, 0), + buffer.readDoubleLE(0) + ); +} + +{ + // buf.readFloatBE([offset]) + + const uint8array = Uint8Array.of(0, 0, 1, 0); + const buffer = Buffer.of(0, 0, 1, 0); + + assert.strictEqual( + readFloatBE.call(uint8array, 0), + buffer.readFloatBE(0) + ); +} + +{ + // buf.readFloatLE([offset]) + + const uint8array = Uint8Array.of(0, 0, 1, 0); + const buffer = Buffer.of(0, 0, 1, 0); + + assert.strictEqual( + readFloatLE.call(uint8array, 0), + buffer.readFloatLE(0) + ); +} + +{ + // buf.readInt8([offset]) + + const uint8array = Uint8Array.of(0, 0, 1, 0); + const buffer = Buffer.of(0, 0, 1, 0); + + assert.strictEqual( + readInt8.call(uint8array, 2), + buffer.readInt8(2) + ); +} + +{ + // buf.readInt16BE([offset]) + + const uint8array = Uint8Array.of(0, 0, 1, 0); + const buffer = Buffer.of(0, 0, 1, 0); + + assert.strictEqual( + readInt16BE.call(uint8array, 2), + buffer.readInt16BE(2) + ); +} + +{ + // buf.readInt16LE([offset]) + + const uint8array = Uint8Array.of(0, 0, 1, 0); + const buffer = Buffer.of(0, 0, 1, 0); + + assert.strictEqual( + readInt16LE.call(uint8array, 2), + buffer.readInt16LE(2) + ); +} + +{ + // buf.readInt32BE([offset]) + + const uint8array = Uint8Array.of(0, 0, 1, 0); + const buffer = Buffer.of(0, 0, 1, 0); + + assert.strictEqual( + readInt32BE.call(uint8array, 0), + buffer.readInt32BE(0) + ); +} + +{ + // buf.readInt32LE([offset]) + + const uint8array = Uint8Array.of(0, 0, 1, 0); + const buffer = Buffer.of(0, 0, 1, 0); + + assert.strictEqual( + readInt32LE.call(uint8array, 0), + buffer.readInt32LE(0) + ); +} + +{ + // buf.readIntBE(offset, byteLength) + + const uint8array = Uint8Array.of(0, 0, 1, 0); + const buffer = Buffer.of(0, 0, 1, 0); + + assert.strictEqual( + readIntBE.call(uint8array, 2, 2), + buffer.readIntBE(2, 2) + ); +} + +{ + // buf.readIntLE(offset, byteLength) + + const uint8array = Uint8Array.of(0, 0, 1, 0); + const buffer = Buffer.of(0, 0, 1, 0); + + assert.strictEqual( + readIntLE.call(uint8array, 2, 2), + buffer.readIntLE(2, 2) + ); +} + +{ + // buf.readUInt8([offset]) + + const uint8array = Uint8Array.of(0, 0, 1, 0); + const buffer = Buffer.of(0, 0, 1, 0); + + assert.strictEqual( + readUInt8.call(uint8array, 2), + buffer.readUInt8(2) + ); +} + +{ + // buf.readUInt16BE([offset]) + + const uint8array = Uint8Array.of(0, 0, 1, 0); + const buffer = Buffer.of(0, 0, 1, 0); + + assert.strictEqual( + readUInt16BE.call(uint8array, 2), + buffer.readUInt16BE(2) + ); +} + +{ + // buf.readUInt16LE([offset]) + + const uint8array = Uint8Array.of(0, 0, 1, 0); + const buffer = Buffer.of(0, 0, 1, 0); + + assert.strictEqual( + readUInt16LE.call(uint8array, 2), + buffer.readUInt16LE(2) + ); +} + +{ + // buf.readUInt32BE([offset]) + + const uint8array = Uint8Array.of(0, 0, 1, 0); + const buffer = Buffer.of(0, 0, 1, 0); + + assert.strictEqual( + readUInt32BE.call(uint8array, 0), + buffer.readUInt32BE(0) + ); +} + +{ + // buf.readUInt32LE([offset]) + + const uint8array = Uint8Array.of(0, 0, 1, 0); + const buffer = Buffer.of(0, 0, 1, 0); + + assert.strictEqual( + readUInt32LE.call(uint8array, 0), + buffer.readUInt32LE(0) + ); +} + +{ + // buf.readUIntBE(offset, byteLength) + + const uint8array = Uint8Array.of(0, 0, 1, 0); + const buffer = Buffer.of(0, 0, 1, 0); + + assert.strictEqual( + readUIntBE.call(uint8array, 2, 2), + buffer.readUIntBE(2, 2) + ); +} + +{ + // buf.readUIntLE(offset, byteLength) + + const uint8array = Uint8Array.of(0, 0, 1, 0); + const buffer = Buffer.of(0, 0, 1, 0); + + assert.strictEqual( + readUIntLE.call(uint8array, 2, 2), + buffer.readUIntLE(2, 2) + ); +} + +{ + // buf.subarray() + + const uint8array = new Uint8Array(arrayOfNumbers(8)); + const buffer = Buffer.from(arrayOfNumbers(8)); + + const array = subarray.call(uint8array, 2, 5); + assert.ok(Buffer.isBuffer(array)); // Does convert the output type because it makes a new FastBuffer + assert.deepStrictEqual( + array, + buffer.subarray(2, 5) + ); +} + +{ + // buf.slice() + + const uint8array = new Uint8Array(arrayOfNumbers(8)); + const buffer = Buffer.from(arrayOfNumbers(8)); + + assertContentEqual( + slice.call(uint8array, 2, 5), // Does not convert the output type because it uses "this.subarray" internally + buffer.slice(2, 5) + ); +} + +{ + // buf.swap16() + + const smallUint8array = new Uint8Array(arrayOfNumbers(2)); + const smallBuffer = Buffer.from(arrayOfNumbers(2)); + + assertContentEqual( + swap16.call(smallUint8array), + smallBuffer.swap16() + ); + + const largeUint8array = new Uint8Array(arrayOfNumbers(2 * 500)); + const largeBuffer = Buffer.from(arrayOfNumbers(2 * 500)); + + assertContentEqual( + swap16.call(largeUint8array), + largeBuffer.swap16() + ); +} + +{ + // buf.swap32() + + const smallUint8array = new Uint8Array(arrayOfNumbers(4)); + const smallBuffer = Buffer.from(arrayOfNumbers(4)); + + assertContentEqual( + swap32.call(smallUint8array), + smallBuffer.swap32() + ); + + const largeUint8array = new Uint8Array(arrayOfNumbers(4 * 500)); + const largeBuffer = Buffer.from(arrayOfNumbers(4 * 500)); + + assertContentEqual( + swap32.call(largeUint8array), + largeBuffer.swap32() + ); +} + +{ + // buf.swap64() + + const smallUint8array = new Uint8Array(arrayOfNumbers(8)); + const smallBuffer = Buffer.from(arrayOfNumbers(8)); + + assertContentEqual( + swap64.call(smallUint8array), + smallBuffer.swap64() + ); + + const largeUint8array = new Uint8Array(arrayOfNumbers(8 * 500)); + const largeBuffer = Buffer.from(arrayOfNumbers(8 * 500)); + + assertContentEqual( + swap64.call(largeUint8array), + largeBuffer.swap64() + ); +} + +{ + // buf.toJSON() + + const uint8array = Uint8Array.of(1, 2, 3, 4); + const buffer = Buffer.of(1, 2, 3, 4); + + assert.deepStrictEqual( + toJSON.call(uint8array), + buffer.toJSON() + ); +} + +{ + // buf.toString([encoding[, start[, end]]]) + assert.strictEqual(Buffer.prototype.toLocaleString, toString); + + const uint8array = Uint8Array.of(1, 2, 3, 4); + const buffer = Buffer.of(1, 2, 3, 4); + + assert.deepStrictEqual( + toString.call(uint8array), + buffer.toString() + ); + + assert.deepStrictEqual( + toString.call(uint8array, 'utf8'), + buffer.toString('utf8') + ); + + assert.deepStrictEqual( + toString.call(uint8array, 'utf16le'), + buffer.toString('utf16le') + ); + + assert.deepStrictEqual( + toString.call(uint8array, 'latin1'), + buffer.toString('latin1') + ); + + assert.deepStrictEqual( + toString.call(uint8array, 'base64'), + buffer.toString('base64') + ); + + assert.deepStrictEqual( + toString.call(uint8array, 'base64url'), + buffer.toString('base64url') + ); + + assert.deepStrictEqual( + toString.call(uint8array, 'hex'), + buffer.toString('hex') + ); + + assert.deepStrictEqual( + toString.call(uint8array, 'ascii'), + buffer.toString('ascii') + ); + + assert.deepStrictEqual( + toString.call(uint8array, 'binary'), + buffer.toString('binary') + ); + + assert.deepStrictEqual( + toString.call(uint8array, 'ucs2'), + buffer.toString('ucs2') + ); +} + +{ + // buf.write(string[, offset[, length]][, encoding]) + const bufferSize = 16; + // UTF-8 encoding + { + const testString = 'Hello, world!'; + const uint8array = new Uint8Array(bufferSize); + const buffer = Buffer.alloc(bufferSize); + + const returnValue = write.call(uint8array, testString, 0, 'utf8'); + const expectedReturnValue = buffer.write(testString, 0, 'utf8'); + + assert.strictEqual(returnValue, expectedReturnValue); + assertContentEqual(uint8array, buffer); + } + + // Hex encoding + { + const testString = 'a1b2c3d4e5'; + const uint8array = new Uint8Array(bufferSize); + const buffer = Buffer.alloc(bufferSize); + + const returnValue = write.call(uint8array, testString, 0, 'hex'); + const expectedReturnValue = buffer.write(testString, 0, 'hex'); + + assert.strictEqual(returnValue, expectedReturnValue); + assertContentEqual(uint8array, buffer); + } + + // Base64 encoding + { + const testString = 'SGVsbG8gd29ybGQ='; + const uint8array = new Uint8Array(bufferSize); + const buffer = Buffer.alloc(bufferSize); + + const returnValue = write.call(uint8array, testString, 0, 'base64'); + const expectedReturnValue = buffer.write(testString, 0, 'base64'); + + assert.strictEqual(returnValue, expectedReturnValue); + assertContentEqual(uint8array, buffer); + } + + // Latin1 encoding + { + const testString = '¡Hola!'; + const uint8array = new Uint8Array(bufferSize); + const buffer = Buffer.alloc(bufferSize); + + const returnValue = write.call(uint8array, testString, 0, 'latin1'); + const expectedReturnValue = buffer.write(testString, 0, 'latin1'); + + assert.strictEqual(returnValue, expectedReturnValue); + assertContentEqual(uint8array, buffer); + } + + // Utf16le encoding + { + const testString = '\uD835\uDC9C\uD835\uDCB7\uD835\uDCB8'; // Unicode string + const uint8array = new Uint8Array(bufferSize); + const buffer = Buffer.alloc(bufferSize); + + const returnValue = write.call(uint8array, testString, 0, 'utf16le'); + const expectedReturnValue = buffer.write(testString, 0, 'utf16le'); + + assert.strictEqual(returnValue, expectedReturnValue); + assertContentEqual(uint8array, buffer); + } + + // Binary encoding + { + const testString = '\x00\x01\x02\x03'; + const uint8array = new Uint8Array(bufferSize); + const buffer = Buffer.alloc(bufferSize); + + const returnValue = write.call(uint8array, testString, 0, 'binary'); + const expectedReturnValue = buffer.write(testString, 0, 'binary'); + + assert.strictEqual(returnValue, expectedReturnValue); + assertContentEqual(uint8array, buffer); + } + + // Base64url encoding + { + const testString = 'SGVsbG9fV29ybGQ'; // Valid base64url string + const uint8array = new Uint8Array(bufferSize); + const buffer = Buffer.alloc(bufferSize); + + const returnValue = write.call(uint8array, testString, 0, 'base64url'); + const expectedReturnValue = buffer.write(testString, 0, 'base64url'); + + assert.strictEqual(returnValue, expectedReturnValue); + assertContentEqual(uint8array, buffer); + } + + // Ascii encoding + { + const testString = 'ASCII!'; + const uint8array = new Uint8Array(bufferSize); + const buffer = Buffer.alloc(bufferSize); + + const returnValue = write.call(uint8array, testString, 0, 'ascii'); + const expectedReturnValue = buffer.write(testString, 0, 'ascii'); + + assert.strictEqual(returnValue, expectedReturnValue); + assertContentEqual(uint8array, buffer); + } + + // Ucs2 encoding + { + const testString = 'A\uD83D\uDC96B'; + const uint8array = new Uint8Array(bufferSize); + const buffer = Buffer.alloc(bufferSize); + + const returnValue = write.call(uint8array, testString, 0, 'ucs2'); + const expectedReturnValue = buffer.write(testString, 0, 'ucs2'); + + assert.strictEqual(returnValue, expectedReturnValue); + assertContentEqual(uint8array, buffer); + } + +} + +{ + // buf.writeBigInt64BE(value[, offset]) + + const uint8array = new Uint8Array(8); + const buffer = Buffer.alloc(8); + + writeBigInt64BE.call(uint8array, 1234567890123456789n, 0); + buffer.writeBigInt64BE(1234567890123456789n, 0); + + assertContentEqual(uint8array, buffer); +} + +{ + // buf.writeBigInt64LE(value[, offset]) + + const uint8array = new Uint8Array(8); + const buffer = Buffer.alloc(8); + + writeBigInt64LE.call(uint8array, 1234567890123456789n, 0); + buffer.writeBigInt64LE(1234567890123456789n, 0); + + assertContentEqual(uint8array, buffer); +} + +{ + // buf.writeBigUInt64BE(value[, offset]) + + const uint8array = new Uint8Array(8); + const buffer = Buffer.alloc(8); + + writeBigUInt64BE.call(uint8array, 12345678901234567890n, 0); + buffer.writeBigUInt64BE(12345678901234567890n, 0); + + assertContentEqual(uint8array, buffer); +} + +{ + // buf.writeBigUInt64LE(value[, offset]) + + const uint8array = new Uint8Array(8); + const buffer = Buffer.alloc(8); + + writeBigUInt64LE.call(uint8array, 12345678901234567890n, 0); + buffer.writeBigUInt64LE(12345678901234567890n, 0); + + assertContentEqual(uint8array, buffer); +} + +{ + // buf.writeDoubleBE(value[, offset]) + + const uint8array = new Uint8Array(8); + const buffer = Buffer.alloc(8); + + writeDoubleBE.call(uint8array, 12345.6789, 0); + buffer.writeDoubleBE(12345.6789, 0); + + assertContentEqual(uint8array, buffer); +} + +{ + // buf.writeDoubleLE(value[, offset]) + + const uint8array = new Uint8Array(8); + const buffer = Buffer.alloc(8); + + writeDoubleLE.call(uint8array, 12345.6789, 0); + buffer.writeDoubleLE(12345.6789, 0); + + assertContentEqual(uint8array, buffer); +} + +{ + // buf.writeFloatBE(value[, offset]) + + const uint8array = new Uint8Array(4); + const buffer = Buffer.alloc(4); + + writeFloatBE.call(uint8array, 12345.67, 0); + buffer.writeFloatBE(12345.67, 0); + + assertContentEqual(uint8array, buffer); +} + +{ + // buf.writeFloatLE(value[, offset]) + + const uint8array = new Uint8Array(4); + const buffer = Buffer.alloc(4); + + writeFloatLE.call(uint8array, 12345.67, 0); + buffer.writeFloatLE(12345.67, 0); + + assertContentEqual(uint8array, buffer); +} + +{ + // buf.writeInt8(value[, offset]) + + const uint8array = new Uint8Array(1); + const buffer = Buffer.alloc(1); + + writeInt8.call(uint8array, -123, 0); + buffer.writeInt8(-123, 0); + + assertContentEqual(uint8array, buffer); +} + +{ + // buf.writeInt16BE(value[, offset]) + + const uint8array = new Uint8Array(2); + const buffer = Buffer.alloc(2); + + writeInt16BE.call(uint8array, -12345, 0); + buffer.writeInt16BE(-12345, 0); + + assertContentEqual(uint8array, buffer); +} + +{ + // buf.writeInt16LE(value[, offset]) + + const uint8array = new Uint8Array(2); + const buffer = Buffer.alloc(2); + + writeInt16LE.call(uint8array, -12345, 0); + buffer.writeInt16LE(-12345, 0); + + assertContentEqual(uint8array, buffer); +} + +{ + // buf.writeInt32BE(value[, offset]) + + const uint8array = new Uint8Array(4); + const buffer = Buffer.alloc(4); + + writeInt32BE.call(uint8array, -123456789, 0); + buffer.writeInt32BE(-123456789, 0); + + assertContentEqual(uint8array, buffer); +} + +{ + // buf.writeInt32LE(value[, offset]) + + const uint8array = new Uint8Array(4); + const buffer = Buffer.alloc(4); + + writeInt32LE.call(uint8array, -123456789, 0); + buffer.writeInt32LE(-123456789, 0); + + assertContentEqual(uint8array, buffer); +} + +{ + // buf.writeIntBE(value, offset, byteLength) + + const uint8array = new Uint8Array(3); + const buffer = Buffer.alloc(3); + + writeIntBE.call(uint8array, -1234567, 0, 3); + buffer.writeIntBE(-1234567, 0, 3); + + assertContentEqual(uint8array, buffer); +} + +{ + // buf.writeIntLE(value, offset, byteLength) + + const uint8array = new Uint8Array(3); + const buffer = Buffer.alloc(3); + + writeIntLE.call(uint8array, -1234567, 0, 3); + buffer.writeIntLE(-1234567, 0, 3); + + assertContentEqual(uint8array, buffer); +} + +{ + // buf.writeUInt8(value[, offset]) + + const uint8array = new Uint8Array(1); + const buffer = Buffer.alloc(1); + + writeUInt8.call(uint8array, 255, 0); + buffer.writeUInt8(255, 0); + + assertContentEqual(uint8array, buffer); +} + +{ + // buf.writeUInt16BE(value[, offset]) + + const uint8array = new Uint8Array(2); + const buffer = Buffer.alloc(2); + + writeUInt16BE.call(uint8array, 65535, 0); + buffer.writeUInt16BE(65535, 0); + + assertContentEqual(uint8array, buffer); +} + +{ + // buf.writeUInt16LE(value[, offset]) + + const uint8array = new Uint8Array(2); + const buffer = Buffer.alloc(2); + + writeUInt16LE.call(uint8array, 65535, 0); + buffer.writeUInt16LE(65535, 0); + + assertContentEqual(uint8array, buffer); +} + +{ + // buf.writeUInt32BE(value[, offset]) + + const uint8array = new Uint8Array(4); + const buffer = Buffer.alloc(4); + + writeUInt32BE.call(uint8array, 4294967295, 0); + buffer.writeUInt32BE(4294967295, 0); + + assertContentEqual(uint8array, buffer); +} + +{ + // buf.writeUInt32LE(value[, offset]) + + const uint8array = new Uint8Array(4); + const buffer = Buffer.alloc(4); + + writeUInt32LE.call(uint8array, 4294967295, 0); + buffer.writeUInt32LE(4294967295, 0); + + assertContentEqual(uint8array, buffer); +} + +{ + // buf.writeUIntBE(value, offset, byteLength) + + const uint8array = new Uint8Array(3); + const buffer = Buffer.alloc(3); + + writeUIntBE.call(uint8array, 1234567, 0, 3); + buffer.writeUIntBE(1234567, 0, 3); + + assertContentEqual(uint8array, buffer); +} + +{ + // buf.writeUIntLE(value, offset, byteLength) + + const uint8array = new Uint8Array(3); + const buffer = Buffer.alloc(3); + + writeUIntLE.call(uint8array, 1234567, 0, 3); + buffer.writeUIntLE(1234567, 0, 3); + + assertContentEqual(uint8array, buffer); +} + +{ + // buf[Symbol.for('nodejs.util.inspect.custom')]() + assert.strictEqual(Buffer.prototype.inspect, customInspect); // Ensure these methods are exactly the same. + + const emptyUint8Array = new Uint8Array(0); + const emptyBuffer = Buffer.alloc(0); + + assert.strictEqual( + customInspect.call(emptyUint8Array), + emptyBuffer[customInspectSymbol]().replace('Buffer', 'Uint8Array') + ); + + const smallUint8Array = Uint8Array.of(1, 2, 3, 4, 5, 6, 7, 8); + const smallBuffer = Buffer.of(1, 2, 3, 4, 5, 6, 7, 8); + + assert.strictEqual( + customInspect.call(smallUint8Array), + smallBuffer[customInspectSymbol]().replace('Buffer', 'Uint8Array') + ); + + const largeUint8Array = new Uint8Array(arrayOfNumbers(9000)); + const largeBuffer = Buffer.from(arrayOfNumbers(9000)); + + assert.strictEqual( + customInspect.call(largeUint8Array), + largeBuffer[customInspectSymbol]().replace('Buffer', 'Uint8Array') + ); +}