diff --git a/lib/internal/buffer.js b/lib/internal/buffer.js index 2d249ccdda5ae4..b9140c59905d98 100644 --- a/lib/internal/buffer.js +++ b/lib/internal/buffer.js @@ -118,6 +118,12 @@ function readBigUInt64LE(offset = 0) { return BigInt(lo) + (BigInt(hi) << 32n); } +function getBigUInt64LE() { + const val = this.readBigUInt64LE(this.readerIndex); + this.readerIndex += 8; + return val; +} + function readBigUInt64BE(offset = 0) { validateNumber(offset, 'offset'); const first = this[offset]; @@ -138,6 +144,12 @@ function readBigUInt64BE(offset = 0) { return (BigInt(hi) << 32n) + BigInt(lo); } +function getBigUInt64BE() { + const val = this.readBigUInt64BE(this.readerIndex); + this.readerIndex += 8; + return val; +} + function readBigInt64LE(offset = 0) { validateNumber(offset, 'offset'); const first = this[offset]; @@ -156,6 +168,12 @@ function readBigInt64LE(offset = 0) { this[++offset] * 2 ** 24); } +function getBigInt64LE() { + const val = this.readBigInt64LE(this.readerIndex); + this.readerIndex += 8; + return val; +} + function readBigInt64BE(offset = 0) { validateNumber(offset, 'offset'); const first = this[offset]; @@ -174,6 +192,12 @@ function readBigInt64BE(offset = 0) { last); } +function getBigInt64BE() { + const val = this.readBigInt64BE(this.readerIndex); + this.readerIndex += 8; + return val; +} + function readUIntLE(offset, byteLength) { if (offset === undefined) throw new ERR_INVALID_ARG_TYPE('offset', 'number', offset); @@ -193,6 +217,12 @@ function readUIntLE(offset, byteLength) { boundsError(byteLength, 6, 'byteLength'); } +function getUIntLE(byteLength) { + const val = this.readUIntLE(this.readerIndex, byteLength); + this.readerIndex += byteLength; + return val; +} + function readUInt48LE(buf, offset = 0) { validateNumber(offset, 'offset'); const first = buf[offset]; @@ -234,6 +264,12 @@ function readUInt32LE(offset = 0) { last * 2 ** 24; } +function getUint32LE() { + const val = this.readUInt32LE(this.readerIndex); + this.readerIndex += 4; + return val; +} + function readUInt24LE(buf, offset = 0) { validateNumber(offset, 'offset'); const first = buf[offset]; @@ -254,6 +290,12 @@ function readUInt16LE(offset = 0) { return first + last * 2 ** 8; } +function getUint16LE() { + const val = this.readUInt16LE(this.readerIndex); + this.readerIndex += 2; + return val; +} + function readUInt8(offset = 0) { validateNumber(offset, 'offset'); const val = this[offset]; @@ -263,6 +305,12 @@ function readUInt8(offset = 0) { return val; } +function getUint8() { + const val = this.readUInt8(this.readerIndex); + this.readerIndex += 1; + return val; +} + function readUIntBE(offset, byteLength) { if (offset === undefined) throw new ERR_INVALID_ARG_TYPE('offset', 'number', offset); @@ -282,6 +330,12 @@ function readUIntBE(offset, byteLength) { boundsError(byteLength, 6, 'byteLength'); } +function getUIntBE(byteLength) { + const val = this.readUIntBE(this.readerIndex, byteLength); + this.readerIndex += byteLength; + return val; +} + function readUInt48BE(buf, offset = 0) { validateNumber(offset, 'offset'); const first = buf[offset]; @@ -323,6 +377,12 @@ function readUInt32BE(offset = 0) { last; } +function getUint32BE() { + const val = this.readUInt32BE(this.readerIndex); + this.readerIndex += 4; + return val; +} + function readUInt24BE(buf, offset = 0) { validateNumber(offset, 'offset'); const first = buf[offset]; @@ -343,6 +403,12 @@ function readUInt16BE(offset = 0) { return first * 2 ** 8 + last; } +function getUint16BE() { + const val = this.readUInt16BE(this.readerIndex); + this.readerIndex += 2; + return val; +} + function readIntLE(offset, byteLength) { if (offset === undefined) throw new ERR_INVALID_ARG_TYPE('offset', 'number', offset); @@ -362,6 +428,12 @@ function readIntLE(offset, byteLength) { boundsError(byteLength, 6, 'byteLength'); } +function getIntLE(byteLength) { + const val = this.readIntLE(this.readerIndex, byteLength); + this.readerIndex += byteLength; + return val; +} + function readInt48LE(buf, offset = 0) { validateNumber(offset, 'offset'); const first = buf[offset]; @@ -404,6 +476,12 @@ function readInt32LE(offset = 0) { (last << 24); // Overflow } +function getInt32LE() { + const val = this.readInt32LE(this.readerIndex); + this.readerIndex += 4; + return val; +} + function readInt24LE(buf, offset = 0) { validateNumber(offset, 'offset'); const first = buf[offset]; @@ -426,6 +504,12 @@ function readInt16LE(offset = 0) { return val | (val & 2 ** 15) * 0x1fffe; } +function getInt16LE() { + const val = this.readInt16LE(this.readerIndex); + this.readerIndex += 2; + return val; +} + function readInt8(offset = 0) { validateNumber(offset, 'offset'); const val = this[offset]; @@ -435,6 +519,12 @@ function readInt8(offset = 0) { return val | (val & 2 ** 7) * 0x1fffffe; } +function getInt8() { + const val = this.readInt8(this.readerIndex); + this.readerIndex += 1; + return val; +} + function readIntBE(offset, byteLength) { if (offset === undefined) throw new ERR_INVALID_ARG_TYPE('offset', 'number', offset); @@ -454,6 +544,12 @@ function readIntBE(offset, byteLength) { boundsError(byteLength, 6, 'byteLength'); } +function getIntBE(byteLength) { + const val = this.readIntBE(this.readerIndex, byteLength); + this.readerIndex += byteLength; + return val; +} + function readInt48BE(buf, offset = 0) { validateNumber(offset, 'offset'); const first = buf[offset]; @@ -496,6 +592,12 @@ function readInt32BE(offset = 0) { last; } +function getInt32BE() { + const val = this.readInt32BE(this.readerIndex); + this.readerIndex += 4; + return val; +} + function readInt24BE(buf, offset = 0) { validateNumber(offset, 'offset'); const first = buf[offset]; @@ -518,6 +620,12 @@ function readInt16BE(offset = 0) { return val | (val & 2 ** 15) * 0x1fffe; } +function getInt16BE() { + const val = this.readInt16BE(this.readerIndex); + this.readerIndex += 2; + return val; +} + // Read floats function readFloatBackwards(offset = 0) { validateNumber(offset, 'offset'); @@ -960,6 +1068,10 @@ function writeFloatBackwards(val, offset = 0) { return offset; } +function reset() { + this.readerIndex = 0; +} + class FastBuffer extends Uint8Array { // Using an explicit constructor here is necessary to avoid relying on // `Array.prototype[Symbol.iterator]`, which can be mutated by users. @@ -970,12 +1082,20 @@ class FastBuffer extends Uint8Array { } function addBufferPrototypeMethods(proto) { + proto.readerIndex = 0; + proto.readBigUInt64LE = readBigUInt64LE; + proto.getBigUInt64LE = getBigUInt64LE; proto.readBigUInt64BE = readBigUInt64BE; + proto.getBigUInt64BE = getBigUInt64BE; proto.readBigUint64LE = readBigUInt64LE; + proto.getBigUint64LE = getBigUInt64LE; proto.readBigUint64BE = readBigUInt64BE; + proto.getBigUint64BE = getBigUInt64BE; proto.readBigInt64LE = readBigInt64LE; + proto.getBigInt64LE = getBigInt64LE; proto.readBigInt64BE = readBigInt64BE; + proto.getBigInt64BE = getBigInt64BE; proto.writeBigUInt64LE = writeBigUInt64LE; proto.writeBigUInt64BE = writeBigUInt64BE; proto.writeBigUint64LE = writeBigUInt64LE; @@ -984,12 +1104,26 @@ function addBufferPrototypeMethods(proto) { proto.writeBigInt64BE = writeBigInt64BE; proto.readUIntLE = readUIntLE; + proto.getUIntLE = getUIntLE; + proto.getUintLE = getUIntLE; proto.readUInt32LE = readUInt32LE; + proto.getUInt32LE = getUint32LE; + proto.getUint32LE = getUint32LE; proto.readUInt16LE = readUInt16LE; + proto.getUInt16LE = getUint16LE; + proto.getUint16LE = getUint16LE; proto.readUInt8 = readUInt8; + proto.getUInt8 = getUint8; + proto.getUint8 = getUint8; proto.readUIntBE = readUIntBE; + proto.getUIntBE = getUIntBE; + proto.getUIntBE = getUIntBE; proto.readUInt32BE = readUInt32BE; + proto.getUInt32BE = getUint32BE; + proto.getUint32BE = getUint32BE; proto.readUInt16BE = readUInt16BE; + proto.getUInt16BE = getUint16BE; + proto.getUint16BE = getUint16BE; proto.readUintLE = readUIntLE; proto.readUint32LE = readUInt32LE; proto.readUint16LE = readUInt16LE; @@ -998,12 +1132,21 @@ function addBufferPrototypeMethods(proto) { proto.readUint32BE = readUInt32BE; proto.readUint16BE = readUInt16BE; proto.readIntLE = readIntLE; + proto.getIntLE = getIntLE; proto.readInt32LE = readInt32LE; + proto.getInt32LE = getInt32LE; proto.readInt16LE = readInt16LE; + proto.getInt16LE = getInt16LE; proto.readInt8 = readInt8; + proto.getInt8 = getInt8; proto.readIntBE = readIntBE; + proto.getIntBE = getIntBE; proto.readInt32BE = readInt32BE; + proto.getInt32BE = getInt32BE; proto.readInt16BE = readInt16BE; + proto.getInt16BE = getInt16BE; + + proto.reset = reset; proto.writeUIntLE = writeUIntLE; proto.writeUInt32LE = writeUInt32LE; diff --git a/test/parallel/test-buffer-get.js b/test/parallel/test-buffer-get.js new file mode 100644 index 00000000000000..72d8e73fcd7189 --- /dev/null +++ b/test/parallel/test-buffer-get.js @@ -0,0 +1,137 @@ +'use strict'; + +require('../common'); + +const assert = require('assert'); + +const BASE_BUFFER = Buffer.from([0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78]); + +// Sequential-read test cases (multiple reads with advancing positions) +const sequentialTestCases = [ + { + steps: [ + { + method: 'getIntBE', + byteLength: 2, + expected: 0x1234, + }, + { + method: 'getIntBE', + byteLength: 2, + expected: 0x5678, + }, + ], + }, + { + steps: [ + { + method: 'getIntLE', + byteLength: 2, + expected: 0x3412, + }, + { + method: 'getIntLE', + byteLength: 2, + expected: 0x7856, + }, + ], + }, + { + steps: [ + { + method: 'getUIntBE', + byteLength: 2, + expected: 0x1234, + }, + { + method: 'getUIntBE', + byteLength: 2, + expected: 0x5678, + }, + ], + }, + { + steps: [ + { + method: 'getUIntLE', + byteLength: 2, + expected: 0x3412, + }, + { + method: 'getUIntLE', + byteLength: 2, + expected: 0x7856, + }, + ], + }, +]; + +// Mixed-endian test cases (mixing big-endian and little-endian reads) +const mixedEndianTestCases = [ + { + steps: [ + { + method: 'getIntBE', + byteLength: 2, + expected: 0x1234, + }, + { + method: 'getIntLE', + byteLength: 2, + expected: 0x7856, + }, + ], + }, + { + steps: [ + { + method: 'getUIntBE', + byteLength: 2, + expected: 0x1234, + }, + { + method: 'getUIntLE', + byteLength: 2, + expected: 0x7856, + }, + ], + }, +]; + +function runTestCase({ steps }, index) { + // Clone the buffer to avoid side effects + const buffer = Buffer.from(BASE_BUFFER); + + steps.forEach(({ method, byteLength, expected }) => { + assert.strictEqual( + buffer[method](byteLength), + expected, + `Method ${method} at step ${index + 1} failed` + ); + }); +} + +function runTestCases(testCases) { + testCases.forEach((testCase, index) => runTestCase(testCase, index)); +} + +runTestCases(sequentialTestCases); +runTestCases(mixedEndianTestCases); + +// Out-of-bounds test cases +{ + const buffer = Buffer.from([0x01, 0x02]); + const byteLength = 2; + + buffer.getIntBE(1); // Moves reader index to 1 + + const methodsToTest = ['getIntBE', 'getIntLE', 'getUIntBE', 'getUIntLE']; + + methodsToTest.forEach((method) => { + assert.throws(() => buffer[method](byteLength), { name: 'RangeError' }); + }); + + // Check that the reader index is not moved + assert.strictEqual(buffer.readerIndex, 1); + assert.strictEqual(buffer.getIntBE(1), 0x02); +} diff --git a/test/parallel/test-buffer-getint.js b/test/parallel/test-buffer-getint.js new file mode 100644 index 00000000000000..6e446e9067c43e --- /dev/null +++ b/test/parallel/test-buffer-getint.js @@ -0,0 +1,152 @@ +'use strict'; + +require('../common'); +const assert = require('assert'); + +const buffer1 = Buffer.from([ + 0x12, 0x34, 0x56, 0x78, + 0x12, 0x34, 0x56, 0x78, + 0x12, 0x34, 0x56, 0x78, + 0x12, 0x34, 0x56, 0x78, +]); + +// Sequential-read test cases (multiple reads with advancing positions) +const testCases = [ + // 8-bit signed integers + { + steps: [ + { method: 'getInt8', expected: 0x12 }, + { method: 'getInt8', expected: 0x34 }, + ], + }, + // 8-bit unsigned integers + { + steps: [ + { method: 'getUInt8', expected: 0x12 }, + { method: 'getUInt8', expected: 0x34 }, + ], + }, + // 16-bit signed integers + { + steps: [ + { method: 'getInt16BE', expected: 0x1234 }, + { method: 'getInt16BE', expected: 0x5678 }, + ], + }, + { + steps: [ + { method: 'getInt16LE', expected: 0x3412 }, + { method: 'getInt16LE', expected: 0x7856 }, + ], + }, + // 16-bit unsigned integers + { + steps: [ + { method: 'getUInt16BE', expected: 0x1234 }, + { method: 'getUInt16BE', expected: 0x5678 }, + ], + }, + { + steps: [ + { method: 'getUInt16LE', expected: 0x3412 }, + { method: 'getUInt16LE', expected: 0x7856 }, + ], + }, + // 32-bit signed integers + { + steps: [ + { method: 'getInt32BE', expected: 0x12345678 }, + { method: 'getInt32BE', expected: 0x12345678 }, + ], + }, + { + steps: [ + { method: 'getInt32LE', expected: 0x78563412 }, + { method: 'getInt32LE', expected: 0x78563412 }, + ], + }, + // 32-bit unsigned integers + { + steps: [ + { method: 'getUInt32BE', expected: 0x12345678 }, + { method: 'getUInt32BE', expected: 0x12345678 }, + ], + }, + { + steps: [ + { method: 'getUInt32LE', expected: 0x78563412 }, + { method: 'getUInt32LE', expected: 0x78563412 }, + ], + }, + // 64-bit signed integers + { + steps: [ + { method: 'getBigInt64BE', expected: BigInt('0x1234567812345678') }, + { method: 'getBigInt64BE', expected: BigInt('0x1234567812345678') }, + ], + }, + { + steps: [ + { method: 'getBigInt64LE', expected: BigInt('0x7856341278563412') }, + { method: 'getBigInt64LE', expected: BigInt('0x7856341278563412') }, + ], + }, + // 64-bit unsigned integers + { + steps: [ + { method: 'getBigUInt64BE', expected: BigInt('0x1234567812345678') }, + { method: 'getBigUInt64BE', expected: BigInt('0x1234567812345678') }, + ], + }, + { + steps: [ + { method: 'getBigUInt64LE', expected: BigInt('0x7856341278563412') }, + { method: 'getBigUInt64LE', expected: BigInt('0x7856341278563412') }, + ], + }, +]; + +function runTestCase({ steps }, index) { + // Clone the buffer to avoid side effects + const buffer = Buffer.from(buffer1); + + steps.forEach(({ method, expected }) => { + assert.strictEqual( + buffer[method](), + expected, + `Method ${method} at step ${index + 1} failed` + ); + }); +} + +function runTestCases(testCases) { + testCases.forEach((testCase, index) => runTestCase(testCase, index)); +} + +// Out-of-bounds test cases using a empty buffer +{ + const emptyBuffer = Buffer.from([]); + + const methodsToTest = [ + 'getInt8', 'getUInt8', + 'getInt16BE', 'getInt16LE', + 'getUInt16BE', 'getUInt16LE', + 'getInt32BE', 'getInt32LE', + 'getUInt32BE', 'getUInt32LE', + 'getBigInt64BE', 'getBigInt64LE', + 'getBigUInt64BE', 'getBigUInt64LE', + ]; + + methodsToTest.forEach((method) => { + assert.throws(() => emptyBuffer[method](), { + code: 'ERR_BUFFER_OUT_OF_BOUNDS', + name: 'RangeError', + message: 'Attempt to access memory outside buffer bounds', + }); + }); + + // Check that the reader index is not moved + assert.strictEqual(emptyBuffer.readerIndex, 0); +} + +runTestCases(testCases); diff --git a/test/parallel/test-buffer-reset.js b/test/parallel/test-buffer-reset.js new file mode 100644 index 00000000000000..98422ac014edff --- /dev/null +++ b/test/parallel/test-buffer-reset.js @@ -0,0 +1,15 @@ +'use strict'; + +require('../common'); + +const assert = require('assert'); + +const buffer = Buffer.from([0x12, 0x34, 0x56, 0x78]); + +buffer.getInt16BE(); +buffer.getInt16BE(); + +buffer.reset(); + +assert.strictEqual(buffer.getInt16BE(), 0x1234); +assert.strictEqual(buffer.getInt16BE(), 0x5678);