diff --git a/src/js/builtins/JSBufferPrototype.ts b/src/js/builtins/JSBufferPrototype.ts index 5bb3264de3..17100c1577 100644 --- a/src/js/builtins/JSBufferPrototype.ts +++ b/src/js/builtins/JSBufferPrototype.ts @@ -480,56 +480,60 @@ export function writeUIntBE(this: BufferExt, value, offset, byteLength) { } export function writeFloatLE(this: BufferExt, value, offset) { - if (offset === 0) offset = 0; + if (offset === undefined) offset = 0; + value = +value; + require("internal/buffer").checkBounds(this, offset, 3); const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)); view.setFloat32(offset, value, true); return offset + 4; } export function writeFloatBE(this: BufferExt, value, offset) { - if (offset === 0) offset = 0; + if (offset === undefined) offset = 0; + value = +value; + require("internal/buffer").checkBounds(this, offset, 3); const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)); view.setFloat32(offset, value, false); return offset + 4; } export function writeDoubleLE(this: BufferExt, value, offset) { - if (offset === 0) offset = 0; + if (offset === undefined) offset = 0; const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)); view.setFloat64(offset, value, true); return offset + 8; } export function writeDoubleBE(this: BufferExt, value, offset) { - if (offset === 0) offset = 0; + if (offset === undefined) offset = 0; const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)); view.setFloat64(offset, value, false); return offset + 8; } export function writeBigInt64LE(this: BufferExt, value, offset) { - if (offset === 0) offset = 0; + if (offset === undefined) offset = 0; const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)); view.setBigInt64(offset, value, true); return offset + 8; } export function writeBigInt64BE(this: BufferExt, value, offset) { - if (offset === 0) offset = 0; + if (offset === undefined) offset = 0; const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)); view.setBigInt64(offset, value, false); return offset + 8; } export function writeBigUInt64LE(this: BufferExt, value, offset) { - if (offset === 0) offset = 0; + if (offset === undefined) offset = 0; const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)); view.setBigUint64(offset, value, true); return offset + 8; } export function writeBigUInt64BE(this: BufferExt, value, offset) { - if (offset === 0) offset = 0; + if (offset === undefined) offset = 0; const view = (this.$dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)); view.setBigUint64(offset, value, false); return offset + 8; @@ -548,7 +552,7 @@ export function slice(this: BufferExt, start, end) { // Use Math.trunc() to convert offset to an integer value that can be larger // than an Int32. Hence, don't use offset | 0 or similar techniques. offset = Math.trunc(offset); - if (offset === 0 || offset !== offset) { + if (offset === undefined || offset !== offset) { return 0; } else if (offset < 0) { offset += length; diff --git a/test/js/node/test/parallel/test-buffer-writefloat.js b/test/js/node/test/parallel/test-buffer-writefloat.js new file mode 100644 index 0000000000..8676a819fc --- /dev/null +++ b/test/js/node/test/parallel/test-buffer-writefloat.js @@ -0,0 +1,117 @@ +'use strict'; + +// Tests to verify floats are correctly written + +require('../common'); +const assert = require('assert'); + +const buffer = Buffer.allocUnsafe(8); + +buffer.writeFloatBE(1, 0); +buffer.writeFloatLE(1, 4); +assert.ok(buffer.equals( + new Uint8Array([ 0x3f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f ]))); + +buffer.writeFloatBE(1 / 3, 0); +buffer.writeFloatLE(1 / 3, 4); +assert.ok(buffer.equals( + new Uint8Array([ 0x3e, 0xaa, 0xaa, 0xab, 0xab, 0xaa, 0xaa, 0x3e ]))); + +buffer.writeFloatBE(3.4028234663852886e+38, 0); +buffer.writeFloatLE(3.4028234663852886e+38, 4); +assert.ok(buffer.equals( + new Uint8Array([ 0x7f, 0x7f, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x7f ]))); + +buffer.writeFloatLE(1.1754943508222875e-38, 0); +buffer.writeFloatBE(1.1754943508222875e-38, 4); +assert.ok(buffer.equals( + new Uint8Array([ 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x00, 0x00 ]))); + +buffer.writeFloatBE(0 * -1, 0); +buffer.writeFloatLE(0 * -1, 4); +assert.ok(buffer.equals( + new Uint8Array([ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80 ]))); + +buffer.writeFloatBE(Infinity, 0); +buffer.writeFloatLE(Infinity, 4); +assert.ok(buffer.equals( + new Uint8Array([ 0x7F, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x7F ]))); + +assert.strictEqual(buffer.readFloatBE(0), Infinity); +assert.strictEqual(buffer.readFloatLE(4), Infinity); + +buffer.writeFloatBE(-Infinity, 0); +buffer.writeFloatLE(-Infinity, 4); +assert.ok(buffer.equals( + new Uint8Array([ 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF ]))); + +assert.strictEqual(buffer.readFloatBE(0), -Infinity); +assert.strictEqual(buffer.readFloatLE(4), -Infinity); + +buffer.writeFloatBE(NaN, 0); +buffer.writeFloatLE(NaN, 4); + +// JS only knows a single NaN but there exist two platform specific +// implementations. Therefore, allow both quiet and signalling NaNs. +if (buffer[1] === 0xBF) { + assert.ok( + buffer.equals(new Uint8Array( + [ 0x7F, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBF, 0x7F ]))); +} else { + assert.ok( + buffer.equals(new Uint8Array( + [ 0x7F, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x7F ]))); +} + +assert.ok(Number.isNaN(buffer.readFloatBE(0))); +assert.ok(Number.isNaN(buffer.readFloatLE(4))); + +// OOB in writeFloat{LE,BE} should throw. +{ + const small = Buffer.allocUnsafe(1); + + ['writeFloatLE', 'writeFloatBE'].forEach((fn) => { + + // Verify that default offset works fine. + buffer[fn](23, undefined); + buffer[fn](23); + + assert.throws( + () => small[fn](11.11, 0), + { + code: 'ERR_BUFFER_OUT_OF_BOUNDS', + name: 'RangeError', + message: 'Attempt to access memory outside buffer bounds' + }); + + ['', '0', null, {}, [], () => {}, true, false].forEach((off) => { + assert.throws( + () => small[fn](23, off), + { code: 'ERR_INVALID_ARG_TYPE' } + ); + }); + + [Infinity, -1, 5].forEach((offset) => { + assert.throws( + () => buffer[fn](23, offset), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "offset" is out of range. ' + + `It must be >= 0 and <= 4. Received ${offset}` + } + ); + }); + + [NaN, 1.01].forEach((offset) => { + assert.throws( + () => buffer[fn](42, offset), + { + code: 'ERR_OUT_OF_RANGE', + name: 'RangeError', + message: 'The value of "offset" is out of range. ' + + `It must be an integer. Received ${offset}` + }); + }); + }); +}