diff --git a/.evergreen/config.yml b/.evergreen/config.yml index 2bceb640..e1d5e19d 100644 --- a/.evergreen/config.yml +++ b/.evergreen/config.yml @@ -263,6 +263,10 @@ buildvariants: display_name: RHEL 8.0 run_on: rhel80-small tasks: [".node", ".web", "check-eslint-plugin"] + # - name: linux-zseries + # display_name: RHEL 8.3 zSeries + # run_on: rhel83-zseries-small + # tasks: [".node", ".web"] - name: lint display_name: lint run_on: rhel80-small diff --git a/src/utils/number_utils.ts b/src/utils/number_utils.ts index a3ccc8e2..66dd4ff5 100644 --- a/src/utils/number_utils.ts +++ b/src/utils/number_utils.ts @@ -1,6 +1,11 @@ const FLOAT = new Float64Array(1); const FLOAT_BYTES = new Uint8Array(FLOAT.buffer, 0, 8); +FLOAT[0] = -1; +// Little endian [0, 0, 0, 0, 0, 0, 240, 191] +// Big endian [191, 240, 0, 0, 0, 0, 0, 0] +const isBigEndian = FLOAT_BYTES[7] === 0; + /** * Number parsing and serializing utilities. * @@ -50,17 +55,29 @@ export const NumberUtils = { }, /** Reads a little-endian 64-bit float from source */ - getFloat64LE(source: Uint8Array, offset: number): number { - FLOAT_BYTES[0] = source[offset]; - FLOAT_BYTES[1] = source[offset + 1]; - FLOAT_BYTES[2] = source[offset + 2]; - FLOAT_BYTES[3] = source[offset + 3]; - FLOAT_BYTES[4] = source[offset + 4]; - FLOAT_BYTES[5] = source[offset + 5]; - FLOAT_BYTES[6] = source[offset + 6]; - FLOAT_BYTES[7] = source[offset + 7]; - return FLOAT[0]; - }, + getFloat64LE: isBigEndian + ? (source: Uint8Array, offset: number) => { + FLOAT_BYTES[7] = source[offset]; + FLOAT_BYTES[6] = source[offset + 1]; + FLOAT_BYTES[5] = source[offset + 2]; + FLOAT_BYTES[4] = source[offset + 3]; + FLOAT_BYTES[3] = source[offset + 4]; + FLOAT_BYTES[2] = source[offset + 5]; + FLOAT_BYTES[1] = source[offset + 6]; + FLOAT_BYTES[0] = source[offset + 7]; + return FLOAT[0]; + } + : (source: Uint8Array, offset: number) => { + FLOAT_BYTES[0] = source[offset]; + FLOAT_BYTES[1] = source[offset + 1]; + FLOAT_BYTES[2] = source[offset + 2]; + FLOAT_BYTES[3] = source[offset + 3]; + FLOAT_BYTES[4] = source[offset + 4]; + FLOAT_BYTES[5] = source[offset + 5]; + FLOAT_BYTES[6] = source[offset + 6]; + FLOAT_BYTES[7] = source[offset + 7]; + return FLOAT[0]; + }, /** Writes a big-endian 32-bit integer to destination, can be signed or unsigned */ setInt32BE(destination: Uint8Array, offset: number, value: number): 4 { @@ -120,16 +137,29 @@ export const NumberUtils = { }, /** Writes a little-endian 64-bit float to destination */ - setFloat64LE(destination: Uint8Array, offset: number, value: number): 8 { - FLOAT[0] = value; - destination[offset] = FLOAT_BYTES[0]; - destination[offset + 1] = FLOAT_BYTES[1]; - destination[offset + 2] = FLOAT_BYTES[2]; - destination[offset + 3] = FLOAT_BYTES[3]; - destination[offset + 4] = FLOAT_BYTES[4]; - destination[offset + 5] = FLOAT_BYTES[5]; - destination[offset + 6] = FLOAT_BYTES[6]; - destination[offset + 7] = FLOAT_BYTES[7]; - return 8; - } + setFloat64LE: isBigEndian + ? (destination: Uint8Array, offset: number, value: number) => { + FLOAT[0] = value; + destination[offset] = FLOAT_BYTES[7]; + destination[offset + 1] = FLOAT_BYTES[6]; + destination[offset + 2] = FLOAT_BYTES[5]; + destination[offset + 3] = FLOAT_BYTES[4]; + destination[offset + 4] = FLOAT_BYTES[3]; + destination[offset + 5] = FLOAT_BYTES[2]; + destination[offset + 6] = FLOAT_BYTES[1]; + destination[offset + 7] = FLOAT_BYTES[0]; + return 8; + } + : (destination: Uint8Array, offset: number, value: number) => { + FLOAT[0] = value; + destination[offset] = FLOAT_BYTES[0]; + destination[offset + 1] = FLOAT_BYTES[1]; + destination[offset + 2] = FLOAT_BYTES[2]; + destination[offset + 3] = FLOAT_BYTES[3]; + destination[offset + 4] = FLOAT_BYTES[4]; + destination[offset + 5] = FLOAT_BYTES[5]; + destination[offset + 6] = FLOAT_BYTES[6]; + destination[offset + 7] = FLOAT_BYTES[7]; + return 8; + } }; diff --git a/test/node/double.test.ts b/test/node/double.test.ts index a91e442f..baf27b8d 100644 --- a/test/node/double.test.ts +++ b/test/node/double.test.ts @@ -3,6 +3,15 @@ import { BSON, Double } from '../register-bson'; import { BSON_DATA_NUMBER, BSON_DATA_INT } from '../../src/constants'; import { inspect } from 'node:util'; +import { bufferFromHexArray } from './tools/utils'; + +const FLOAT = new Float64Array(1); +const FLOAT_BYTES = new Uint8Array(FLOAT.buffer, 0, 8); + +FLOAT[0] = -1; +// Little endian [0, 0, 0, 0, 0, 0, 240, 191] +// Big endian [191, 240, 0, 0, 0, 0, 0, 0] +const isBigEndian = FLOAT_BYTES[7] === 0; describe('BSON Double Precision', function () { context('class Double', function () { @@ -297,4 +306,22 @@ describe('BSON Double Precision', function () { }); }); }); + + context(`handles ${isBigEndian ? 'big' : 'little'} endianness correctly`, () => { + const bsonWithFloat = bufferFromHexArray([ + '01', // double + '6100', // 'a' + '00'.repeat(6) + 'f0bf' // 8 byte LE float equal to -1 + ]); + + it('deserialize should return -1', () => { + const res = BSON.deserialize(bsonWithFloat); + expect(res).to.have.property('a', -1); + }); + + it('serialize should set bytes to -1 in little endian format', () => { + const res = BSON.serialize({ a: new Double(-1) }); + expect(res).to.deep.equal(bsonWithFloat); + }); + }); }); diff --git a/test/node/utils/number_utils.test.ts b/test/node/utils/number_utils.test.ts index 00d9c84a..86d86f88 100644 --- a/test/node/utils/number_utils.test.ts +++ b/test/node/utils/number_utils.test.ts @@ -1,7 +1,32 @@ import { expect } from 'chai'; import { NumberUtils } from '../../../src/utils/number_utils'; +const FLOAT = new Float64Array(1); +const FLOAT_BYTES = new Uint8Array(FLOAT.buffer, 0, 8); + +FLOAT[0] = -1; +// Little endian [0, 0, 0, 0, 0, 0, 240, 191] +// Big endian [191, 240, 0, 0, 0, 0, 0, 0] +const isBigEndian = FLOAT_BYTES[7] === 0; + describe('NumberUtils', () => { + context(`handles ${isBigEndian ? 'big' : 'little'} endianness correctly`, () => { + context('getFloat64LE()', () => { + it('should return -1', () => { + const res = NumberUtils.getFloat64LE(new Uint8Array([0, 0, 0, 0, 0, 0, 240, 191]), 0); + expect(res).to.equal(-1); + }); + }); + + context('setFloat64LE()', () => { + it('should return -1 as little endian bytes', () => { + const buf = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0]); + NumberUtils.setFloat64LE(buf, 0, -1); + expect(buf).to.deep.equal(new Uint8Array([0, 0, 0, 0, 0, 0, 240, 191])); + }); + }); + }); + /** Make a Uint8Array in a less verbose way */ const b = (...values) => new Uint8Array(values);