From 1378dbf10aa4b3b672cfb63e75f27869b4b948ce Mon Sep 17 00:00:00 2001 From: szdytom Date: Sat, 10 Feb 2024 19:49:08 +0800 Subject: [PATCH] [js]add tests for bstruct --- shared/bstruct/index.mjs | 36 ++++- shared/bstruct/package.json | 3 +- shared/bstruct/test/index.test.js | 197 +++++++++++++++++++++++++ shared/bstruct/type-handler.mjs | 224 +++++++++++++++++++++-------- shared/utility/buffer.mjs | 23 +++ shared/utility/index.mjs | 1 + shared/utility/package.json | 6 + shared/utility/test/buffer.test.js | 48 +++++++ shared/uuid/test/uuid.test.js | 2 +- 9 files changed, 474 insertions(+), 66 deletions(-) create mode 100644 shared/bstruct/test/index.test.js create mode 100644 shared/utility/buffer.mjs create mode 100644 shared/utility/index.mjs create mode 100644 shared/utility/package.json create mode 100644 shared/utility/test/buffer.test.js diff --git a/shared/bstruct/index.mjs b/shared/bstruct/index.mjs index 4dc4363..841c26e 100644 --- a/shared/bstruct/index.mjs +++ b/shared/bstruct/index.mjs @@ -1 +1,35 @@ -import { VirtualMethodNotImplementedError } from '@og/error-utils'; +import { BaseTypeHandler, CompoundTypeHandler } from './type-handler.mjs'; + +export { BASIC_TYPES, FixedArrayHandler, DynamicArrayHandler, CompoundTypeHandler } from './type-handler.mjs'; + +/** + * Serializes JavaScript value to binary. + * @param {any} value - value to serialize. + * @param {BaseTypeHandler} type - type handler of the value. + * @returns {ArrayBuffer} - the serialized binary buffer. + */ +export function serializeToBinary(value, type) { + if (!(type instanceof BaseTypeHandler)) { + type = new CompoundTypeHandler(type); + } + + const res = new ArrayBuffer(type.sizeof(value)); + const view = new DataView(res); + type.serialize(view, 0, value); + return res; +} + +/** + * Deserializes binary back to JavaScript value. + * @param {DataView} view - buffer to deserialize. + * @param {BaseTypeHandler} type - type handler of the desired value. + * @returns {any} - the deserialized JavaScript value. + */ +export function deserializeFromBinary(view, type) { + if (!(type instanceof BaseTypeHandler)) { + type = new CompoundTypeHandler(type); + } + + const tmp = type.deserialize(view, 0); + return tmp.value; +} diff --git a/shared/bstruct/package.json b/shared/bstruct/package.json index 8b8fa8c..3d3c2d2 100644 --- a/shared/bstruct/package.json +++ b/shared/bstruct/package.json @@ -4,6 +4,7 @@ "main": "index.mjs", "dependencies": { "@og/error-utils": "file:../error-utils", - "@og/uuid": "file:../uuid" + "@og/uuid": "file:../uuid", + "@og/utility": "file:../utility" } } \ No newline at end of file diff --git a/shared/bstruct/test/index.test.js b/shared/bstruct/test/index.test.js new file mode 100644 index 0000000..c9383b0 --- /dev/null +++ b/shared/bstruct/test/index.test.js @@ -0,0 +1,197 @@ +import { BASIC_TYPES, FixedArrayHandler, DynamicArrayHandler, CompoundTypeHandler, serializeToBinary, deserializeFromBinary } from '../index.mjs'; +import assert from 'node:assert/strict'; +import { areArrayBuffersEqual } from '@og/utility'; +import { UUID } from '@og/uuid'; + +class Point { + constructor(x, y) { + this.x = x; + this.y = y; + } + + foo() { + return this.x + this.y; + } +} + +Point.typedef = [ + { field: 'x', type: BASIC_TYPES.u8 }, + { field: 'y', type: BASIC_TYPES.u8 }, +]; + +describe('binary-struct', () => { + describe('serializeToBinary', () => { + it('type i8', () => { + let res = serializeToBinary(-123, BASIC_TYPES.i8); // Example value for i8 + let ans = new Uint8Array([0x85]); // Byte representation of -123 as i8 + assert.ok(areArrayBuffersEqual(res, ans)); + }); + + it('type i16', () => { + let res = serializeToBinary(-12345, BASIC_TYPES.i16); // Example value for i16 + let ans = new Uint8Array([0xc7, 0xcf]); // Byte representation of -12345 as i16 + assert.ok(areArrayBuffersEqual(res, ans)); + }); + + it('type i32', () => { + let res = serializeToBinary(-1234567890, BASIC_TYPES.i32); // Example value for i32 + let ans = new Uint8Array([0x2e, 0xfd, 0x69, 0xb6]); // Byte representation of -1234567890 as i32 + assert.ok(areArrayBuffersEqual(res, ans)); + }); + + it('type i64', () => { + let res = serializeToBinary(-1234567890123456789n, BASIC_TYPES.i64); // Example value for i64 + let ans = new Uint8Array([0xeb, 0x7e, 0x16, 0x82, 0x0b, 0xef, 0xdd, 0xee]); // Byte representation of -1234567890123456789 as i64 + assert.ok(areArrayBuffersEqual(res, ans)); + }); + + it('type u8', () => { + let res = serializeToBinary(60, BASIC_TYPES.u8); // Example value for u8 + let ans = new Uint8Array([0x3c]); // Byte representation of 60 as u8 + assert.ok(areArrayBuffersEqual(res, ans)); + }); + + it('type u16', () => { + let res = serializeToBinary(56789, BASIC_TYPES.u16); // Example value for u16 + let ans = new Uint8Array([0xd5, 0xdd]); // Byte representation of 56789 as u16 + assert.ok(areArrayBuffersEqual(res, ans)); + }); + + it('type u32', () => { + let res = serializeToBinary(1234567890, BASIC_TYPES.u32); // Example value for u32 + let ans = new Uint8Array([0xd2, 0x02, 0x96, 0x49]); // Byte representation of 1234567890 as u32 + assert.ok(areArrayBuffersEqual(res, ans)); + }); + + it('type u64', () => { + let res = serializeToBinary(123456789009876543n, BASIC_TYPES.u64); // Example value for u64 + let ans = new Uint8Array([0x3f, 0x46, 0x0b, 0xa6, 0x4b, 0x9b, 0xb6, 0x01]); // Byte representation of 123456789009876543 as u64 + assert.ok(areArrayBuffersEqual(res, ans)); + }); + + it('type uuid', () => { + let res = serializeToBinary(new UUID('f5b3ad32-2a77-4f2e-a3b8-3eb8a8546f2b'), BASIC_TYPES.uuid); + let ans = new Uint8Array([0xf5, 0xb3, 0xad, 0x32, 0x2a, 0x77, 0x4f, 0x2e, 0xa3, 0xb8, 0x3e, 0xb8, 0xa8, 0x54, 0x6f, 0x2b]); + assert.ok(areArrayBuffersEqual(res, ans)); + }); + + it('type string', () => { + let res = serializeToBinary('hello world', BASIC_TYPES.str); + let ans = new Uint8Array([0x0b, 0x00, 0x00, 0x00, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64]); + assert.ok(areArrayBuffersEqual(res, ans)); + }); + + it('type u16[]', () => { + let res = serializeToBinary([0x1770, 0x3c3a, 0x9012], new DynamicArrayHandler(BASIC_TYPES.u16)); + let ans = new Uint8Array([0x03, 0x00, 0x00, 0x00, 0x70, 0x17, 0x3a, 0x3c, 0x12, 0x90]); + assert.ok(areArrayBuffersEqual(res, ans)); + }); + + it('type u16[3]', () => { + let res = serializeToBinary([0x1770, 0x3c3a, 0x9012], new FixedArrayHandler(3, BASIC_TYPES.u16)); + let ans = new Uint8Array([0x70, 0x17, 0x3a, 0x3c, 0x12, 0x90]); + assert.ok(areArrayBuffersEqual(res, ans)); + }); + + it('type Point', () => { + let res = serializeToBinary(new Point(2, 3), Point); + let ans = new Uint8Array([0x02, 0x03]); + assert.ok(areArrayBuffersEqual(res, ans)); + }); + }); + + describe('deserializeFromBinary', () => { + it('type i8', () => { + let binaryData = new Uint8Array([0x85]).buffer; + let res = deserializeFromBinary(new DataView(binaryData), BASIC_TYPES.i8); + let ans = -123; + assert.equal(res, ans); + }); + + it('type i16', () => { + let binaryData = new Uint8Array([0xc7, 0xcf]).buffer; + let res = deserializeFromBinary(new DataView(binaryData), BASIC_TYPES.i16); + let ans = -12345; + assert.equal(res, ans); + }); + + it('type i32', () => { + let binaryData = new Uint8Array([0x2e, 0xfd, 0x69, 0xb6]).buffer; + let res = deserializeFromBinary(new DataView(binaryData), BASIC_TYPES.i32); + let ans = -1234567890; + assert.equal(res, ans); + }); + + it('type i64', () => { + let binaryData = new Uint8Array([0xeb, 0x7e, 0x16, 0x82, 0x0b, 0xef, 0xdd, 0xee]).buffer; + let res = deserializeFromBinary(new DataView(binaryData), BASIC_TYPES.i64); + let ans = BigInt('-1234567890123456789'); + assert.equal(res, ans); + }); + + it('type u8', () => { + let binaryData = new Uint8Array([0x3c]).buffer; + let res = deserializeFromBinary(new DataView(binaryData), BASIC_TYPES.u8); + let ans = 60; + assert.equal(res, ans); + }); + + it('type u16', () => { + let binaryData = new Uint8Array([0xd5, 0xdd]).buffer; + let res = deserializeFromBinary(new DataView(binaryData), BASIC_TYPES.u16); + let ans = 56789; + assert.equal(res, ans); + }); + + it('type u32', () => { + let binaryData = new Uint8Array([0xd2, 0x02, 0x96, 0x49]).buffer; + let res = deserializeFromBinary(new DataView(binaryData), BASIC_TYPES.u32); + let ans = 1234567890; + assert.equal(res, ans); + }); + + it('type u64', () => { + let binaryData = new Uint8Array([0x3f, 0x46, 0x0b, 0xa6, 0x4b, 0x9b, 0xb6, 0x01]).buffer; + let res = deserializeFromBinary(new DataView(binaryData), BASIC_TYPES.u64); + let ans = BigInt('123456789009876543'); + assert.equal(res, ans); + }); + + it('type uuid', () => { + let binaryData = new Uint8Array([0xf5, 0xb3, 0xad, 0x32, 0x2a, 0x77, 0x4f, 0x2e, 0xa3, 0xb8, 0x3e, 0xb8, 0xa8, 0x54, 0x6f, 0x2b]).buffer; + let res = deserializeFromBinary(new DataView(binaryData), BASIC_TYPES.uuid); + let ans = 'f5b3ad32-2a77-4f2e-a3b8-3eb8a8546f2b'; + assert.equal(res.toString(), ans); + }); + + it('type string', () => { + let binaryData = new Uint8Array([0x0b, 0x00, 0x00, 0x00, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64]).buffer; + let res = deserializeFromBinary(new DataView(binaryData), BASIC_TYPES.str); + let ans = 'hello world'; + assert.equal(res, ans); + }); + + it('type u16[]', () => { + let binaryData = new Uint8Array([0x03, 0x00, 0x00, 0x00, 0x70, 0x17, 0x3a, 0x3c, 0x12, 0x90]).buffer; + let res = deserializeFromBinary(new DataView(binaryData), new DynamicArrayHandler(BASIC_TYPES.u16)); + let ans = [0x1770, 0x3c3a, 0x9012]; + assert.deepEqual(res, ans); + }); + + it('type u16[3]', () => { + let binaryData = new Uint8Array([0x70, 0x17, 0x3a, 0x3c, 0x12, 0x90]).buffer; + let res = deserializeFromBinary(new DataView(binaryData), new FixedArrayHandler(3, BASIC_TYPES.u16)); + let ans = [0x1770, 0x3c3a, 0x9012]; + assert.deepEqual(res, ans); + }); + + it('type Point', () => { + let binaryData = new Uint8Array([0x02, 0x03]).buffer; + let res = deserializeFromBinary(new DataView(binaryData), Point); + let ans = new Point(2, 3); + assert.equal(res.x, ans.x); + assert.equal(res.y, ans.y); + assert.equal(res.foo(), ans.foo()) + }); + }); +}); diff --git a/shared/bstruct/type-handler.mjs b/shared/bstruct/type-handler.mjs index fc9d0ff..8120a50 100644 --- a/shared/bstruct/type-handler.mjs +++ b/shared/bstruct/type-handler.mjs @@ -22,7 +22,7 @@ export class DeserializedResult { * @abstract * @class */ -export class BasicTypeHandler { +export class BaseTypeHandler { /** * Gets the size of the serialized value in bytes. * @abstract @@ -59,10 +59,10 @@ export class BasicTypeHandler { /** * Handles 8-bit signed integers (int8). - * @extends {BasicTypeHandler} + * @extends {BaseTypeHandler} * @class */ -export class Int8Handler extends BasicTypeHandler { +export class Int8Handler extends BaseTypeHandler { /** * Gets the size of the serialized int8 value in bytes (always 1). * @param {number} _value - The int8 value. @@ -97,10 +97,10 @@ export class Int8Handler extends BasicTypeHandler { /** * Handles 16-bit signed integers (int16). - * @extends {BasicTypeHandler} + * @extends {BaseTypeHandler} * @class */ -export class Int16Handler extends BasicTypeHandler { +export class Int16Handler extends BaseTypeHandler { /** * Gets the size of the serialized int16 value in bytes (always 2). * @param {number} _value - The int16 value. @@ -118,7 +118,7 @@ export class Int16Handler extends BasicTypeHandler { * @returns {number} - The new offset after serialization. */ serialize(view, offset, value) { - view.setInt16(offset, value); + view.setInt16(offset, value, true); return offset + 2; } @@ -129,16 +129,16 @@ export class Int16Handler extends BasicTypeHandler { * @returns {DeserializedResult} - The deserialized result containing the int16 value and a new offset. */ deserialize(view, offset) { - return new DeserializedResult(view.getInt16(offset), offset + 2); + return new DeserializedResult(view.getInt16(offset, true), offset + 2); } } /** * Handles 32-bit signed integers (int32). - * @extends {BasicTypeHandler} + * @extends {BaseTypeHandler} * @class */ -export class Int32Handler extends BasicTypeHandler { +export class Int32Handler extends BaseTypeHandler { /** * Gets the size of the serialized int32 value in bytes (always 4). * @param {number} _value - The int32 value. @@ -156,7 +156,7 @@ export class Int32Handler extends BasicTypeHandler { * @returns {number} - The new offset after serialization. */ serialize(view, offset, value) { - view.setInt32(offset, value); + view.setInt32(offset, value, true); return offset + 4; } @@ -167,16 +167,16 @@ export class Int32Handler extends BasicTypeHandler { * @returns {DeserializedResult} - The deserialized result containing the int32 value and a new offset. */ deserialize(view, offset) { - return new DeserializedResult(view.getInt32(offset), offset + 4); + return new DeserializedResult(view.getInt32(offset, true), offset + 4); } } /** * Handles 64-bit signed integers (int64). - * @extends {BasicTypeHandler} + * @extends {BaseTypeHandler} * @class */ -export class Int64Handler extends BasicTypeHandler { +export class Int64Handler extends BaseTypeHandler { /** * Gets the size of the serialized int64 value in bytes (always 8). * @param {BigInt} _value - The int64 value. @@ -194,7 +194,7 @@ export class Int64Handler extends BasicTypeHandler { * @returns {number} - The new offset after serialization. */ serialize(view, offset, value) { - view.setBigInt64(offset, value); + view.setBigInt64(offset, value, true); return offset + 8; } @@ -205,16 +205,16 @@ export class Int64Handler extends BasicTypeHandler { * @returns {DeserializedResult} - The deserialized result containing the int64 value and a new offset. */ deserialize(view, offset) { - return new DeserializedResult(view.getBigInt64(offset), offset + 8); + return new DeserializedResult(view.getBigInt64(offset, true), offset + 8); } } /** * Handles 8-bit unsigned integers (uint8). - * @extends {BasicTypeHandler} + * @extends {BaseTypeHandler} * @class */ -export class Uint8Handler extends BasicTypeHandler { +export class Uint8Handler extends BaseTypeHandler { /** * Gets the size of the serialized uint8 value in bytes (always 1). * @param {number} _value - The uint8 value. @@ -249,10 +249,10 @@ export class Uint8Handler extends BasicTypeHandler { /** * Handles 16-bit unsigned integers (uint16). - * @extends {BasicTypeHandler} + * @extends {BaseTypeHandler} * @class */ -export class Uint16Handler extends BasicTypeHandler { +export class Uint16Handler extends BaseTypeHandler { /** * Gets the size of the serialized uint16 value in bytes (always 2). * @param {number} _value - The uint16 value. @@ -270,7 +270,7 @@ export class Uint16Handler extends BasicTypeHandler { * @returns {number} - The new offset after serialization. */ serialize(view, offset, value) { - view.setUint16(offset, value); + view.setUint16(offset, value, true); return offset + 2; } @@ -281,16 +281,16 @@ export class Uint16Handler extends BasicTypeHandler { * @returns {DeserializedResult} - The deserialized result containing the uint16 value and a new offset. */ deserialize(view, offset) { - return new DeserializedResult(view.getUint16(offset), offset + 2); + return new DeserializedResult(view.getUint16(offset, true), offset + 2); } } /** * Handles 32-bit unsigned integers (uint32). - * @extends {BasicTypeHandler} + * @extends {BaseTypeHandler} * @class */ -export class Uint32Handler extends BasicTypeHandler { +export class Uint32Handler extends BaseTypeHandler { /** * Gets the size of the serialized uint32 value in bytes (always 4). * @param {number} _value - The uint32 value. @@ -308,7 +308,7 @@ export class Uint32Handler extends BasicTypeHandler { * @returns {number} - The new offset after serialization. */ serialize(view, offset, value) { - view.setUint32(offset, value); + view.setUint32(offset, value, true); return offset + 4; } @@ -319,16 +319,16 @@ export class Uint32Handler extends BasicTypeHandler { * @returns {DeserializedResult} - The deserialized result containing the uint32 value and a new offset. */ deserialize(view, offset) { - return new DeserializedResult(view.getUint32(offset), offset + 4); + return new DeserializedResult(view.getUint32(offset, true), offset + 4); } } /** * Handles 64-bit unsigned integers (uint64). - * @extends {BasicTypeHandler} + * @extends {BaseTypeHandler} * @class */ -export class Uint64Handler extends BasicTypeHandler { +export class Uint64Handler extends BaseTypeHandler { /** * Gets the size of the serialized uint64 value in bytes (always 8). * @param {BigInt} _value - The uint64 value. @@ -346,7 +346,7 @@ export class Uint64Handler extends BasicTypeHandler { * @returns {number} - The new offset after serialization. */ serialize(view, offset, value) { - view.setBigUint64(offset, value); + view.setBigUint64(offset, value, true); return offset + 8; } @@ -357,16 +357,92 @@ export class Uint64Handler extends BasicTypeHandler { * @returns {DeserializedResult} - The deserialized result containing the uint64 value and a new offset. */ deserialize(view, offset) { - return new DeserializedResult(view.getBigUint64(offset), offset + 8); + return new DeserializedResult(view.getBigUint64(offset, true), offset + 8); + } +} + +/** + * Handles 32-bit floating point numbers (float32). + * @extends {BaseTypeHandler} + * @class + */ +export class Float32Handler extends BaseTypeHandler { + /** + * Gets the size of the serialized float32 value in bytes (always 4). + * @param {number} _value - The float32 value. + * @returns {number} - The size of the serialized float32 value in bytes. + */ + sizeof(_value) { + return 4; + } + + /** + * Serializes the float32 value and writes it to the DataView at the specified offset. + * @param {DataView} view - The DataView to write to. + * @param {number} offset - The offset to start writing at. + * @param {number} value - The float32 value to be serialized. + * @returns {number} - The new offset after serialization. + */ + serialize(view, offset, value) { + view.setFloat32(offset, value); + return offset + 4; + } + + /** + * Deserializes the float32 value from the DataView at the specified offset. + * @param {DataView} view - The DataView to read from. + * @param {number} offset - The offset to start reading from. + * @returns {DeserializedResult} - The deserialized result containing the float32 value and a new offset. + */ + deserialize(view, offset) { + return new DeserializedResult(view.getFloat32(offset), offset + 4); + } +} + +/** + * Handles 64-bit floating point numbers (float64). + * @extends {BaseTypeHandler} + * @class + */ +export class Float64Handler extends BaseTypeHandler { + /** + * Gets the size of the serialized float64 value in bytes (always 8). + * @param {number} _value - The float64 value. + * @returns {number} - The size of the serialized float64 value in bytes. + */ + sizeof(_value) { + return 8; + } + + /** + * Serializes the float64 value and writes it to the DataView at the specified offset. + * @param {DataView} view - The DataView to write to. + * @param {number} offset - The offset to start writing at. + * @param {number} value - The float64 value to be serialized. + * @returns {number} - The new offset after serialization. + */ + serialize(view, offset, value) { + view.setFloat64(offset, value); + return offset + 8; + } + + /** + * Deserializes the float64 value from the DataView at the specified offset. + * @param {DataView} view - The DataView to read from. + * @param {number} offset - The offset to start reading from. + * @returns {DeserializedResult} - The deserialized result containing the float64 value and a new offset. + */ + deserialize(view, offset) { + return new DeserializedResult(view.getFloat64(offset), offset + 8); } } /** * Handles boolean values (bool). - * @extends {BasicTypeHandler} + * @extends {BaseTypeHandler} * @class */ -export class BoolHandler extends BasicTypeHandler { +export class BoolHandler extends BaseTypeHandler { /** * Gets the size of the serialized bool value in bytes (always 1). * @param {boolean} _value - The bool value. @@ -401,10 +477,10 @@ export class BoolHandler extends BasicTypeHandler { /** * Handles void values (void). - * @extends {BasicTypeHandler} + * @extends {BaseTypeHandler} * @class */ -export class VoidHandler extends BasicTypeHandler { +export class VoidHandler extends BaseTypeHandler { /** * Gets the size of the serialized void value in bytes (always 0). * @param {*} _value - The void value. @@ -438,14 +514,14 @@ export class VoidHandler extends BasicTypeHandler { /** * Handles array of a fixed length with elements of the same type. - * @extends {BasicTypeHandler} + * @extends {BaseTypeHandler} * @class */ -export class FixedArrayHandler extends BasicTypeHandler { +export class FixedArrayHandler extends BaseTypeHandler { /** * Constructor for FixedArrayHandler. * @param {number} n - The fixed length of the array. - * @param {BasicTypeHandler} element_handler - The handler for individual elements of the array. + * @param {BaseTypeHandler} element_handler - The handler for individual elements of the array. */ constructor(n, element_handler) { super(); @@ -499,13 +575,13 @@ export class FixedArrayHandler extends BasicTypeHandler { /** * Handles dynamic arrays with elements of the same type. - * @extends {BasicTypeHandler} + * @extends {BaseTypeHandler} * @class */ -export class DynamicArrayHandler extends BasicTypeHandler { +export class DynamicArrayHandler extends BaseTypeHandler { /** * Constructor for DynamicArrayHandler. - * @param {BasicTypeHandler} element_handler - The handler for individual elements of the array. + * @param {BaseTypeHandler} element_handler - The handler for individual elements of the array. */ constructor(element_handler) { super(); @@ -533,7 +609,7 @@ export class DynamicArrayHandler extends BasicTypeHandler { * @returns {number} - The new offset after serialization. */ serialize(view, offset, value) { - view.setUint32(offset, value.length); + view.setUint32(offset, value.length, true); offset += 4; for (const element of value) { offset = this.element_handler.serialize(view, offset, element); @@ -548,7 +624,7 @@ export class DynamicArrayHandler extends BasicTypeHandler { * @returns {DeserializedResult} - The deserialized result containing the dynamic array and a new offset. */ deserialize(view, offset) { - const length = view.getUint32(offset); + const length = view.getUint32(offset, true); offset += 4; const res = new Array(length); for (let i = 0; i < length; i++) { @@ -562,10 +638,19 @@ export class DynamicArrayHandler extends BasicTypeHandler { /** * Handles storage and serialization of UUID objects using a 16-byte buffer. - * @extends {BasicTypeHandler} + * @extends {BaseTypeHandler} * @class */ -export class UUIDHandler extends BasicTypeHandler { +export class UUIDHandler extends BaseTypeHandler { + /** + * Gets the size of the serialized UUID value in bytes (always 16). + * @param {boolean} _value - The UUID value. + * @returns {number} - The size of the serialized UUID value in bytes. + */ + sizeof(_value) { + return 16; + } + /** * Serializes the UUID object and writes it to the buffer at the specified offset. * @param {DataView} view - The DataView to write to. @@ -606,17 +691,10 @@ export class UUIDHandler extends BasicTypeHandler { /** * Handles storage and serialization of strings. - * @extends {BasicTypeHandler} + * @extends {BaseTypeHandler} * @class */ -export class StringHandler extends BasicTypeHandler { - /** - * Constructs a StringHandler object. - */ - constructor() { - super(); - } - +export class StringHandler extends BaseTypeHandler { /** * Gets the size of the serialized string in bytes. * @param {string} value - The string to calculate the size for. @@ -644,7 +722,7 @@ export class StringHandler extends BasicTypeHandler { const encoded_string = encoder.encode(value); // Write the length of the string as a uint32 at the specified offset - view.setUint32(offset, encoded_string.length); + view.setUint32(offset, encoded_string.length, true); offset += 4; // Write the UTF-8 encoded string to the DataView starting at offset + 4 (after the length) @@ -664,7 +742,7 @@ export class StringHandler extends BasicTypeHandler { */ deserialize(view, offset) { // Read the length of the string as a uint32 at the specified offset - const length = view.getUint32(offset); + const length = view.getUint32(offset, true); offset += 4; // Read the UTF-8 encoded string from the DataView starting at offset + 4 (after the length) @@ -681,10 +759,10 @@ export class StringHandler extends BasicTypeHandler { /** * Handles the serialization and deserialization of a compound type composed of various fields. - * @extends {BasicTypeHandler} + * @extends {BaseTypeHandler} * @class */ -export class CompoundTypeHandler extends BasicTypeHandler { +export class CompoundTypeHandler extends BaseTypeHandler { /** * Constructs a CompoundTypeHandler object for the specified type. * @param {Function} type - The class representing the compound type. @@ -698,9 +776,9 @@ export class CompoundTypeHandler extends BasicTypeHandler { * } * * // Define the type definition for Point - * Point.prototype.typedef = [ - * {field: 'x', handler: new Float32Handler()}, - * {field: 'y', handler: new Float32Handler()} + * Point.typedef = [ + * {field: 'x', type: BASIC_TYPES.f32}, + * {field: 'y', type: BASIC_TYPES.f32} * ]; * * // Create a CompoundTypeHandler for Point @@ -716,7 +794,7 @@ export class CompoundTypeHandler extends BasicTypeHandler { this.type = type; /** * The type definition specifying the fields and their handlers. - * @type {Array<{field: string, handler: BasicTypeHandler}>} + * @type {Array<{field: string, type: BaseTypeHandler}>} * @private */ this.typedef = type.typedef; @@ -731,7 +809,7 @@ export class CompoundTypeHandler extends BasicTypeHandler { let res = 0; for (let i = 0; i < this.typedef.length; i += 1) { const field_name = this.typedef[i].field; - const field_handler = this.typedef[i].handler; + const field_handler = this.typedef[i].type; res += field_handler.sizeof(value[field_name]); } return res; @@ -747,7 +825,7 @@ export class CompoundTypeHandler extends BasicTypeHandler { serialize(view, offset, value) { for (let i = 0; i < this.typedef.length; i += 1) { const field_name = this.typedef[i].field; - const field_handler = this.typedef[i].handler; + const field_handler = this.typedef[i].type; offset = field_handler.serialize(view, offset, value[field_name]); } return offset; @@ -763,7 +841,7 @@ export class CompoundTypeHandler extends BasicTypeHandler { let res = new this.type(); for (let i = 0; i < this.typedef.length; i += 1) { const field_name = this.typedef[i].field; - const field_handler = this.typedef[i].handler; + const field_handler = this.typedef[i].type; const tmp = field_handler.deserialize(view, offset); res[field_name] = tmp.value; offset = tmp.offset; @@ -771,3 +849,23 @@ export class CompoundTypeHandler extends BasicTypeHandler { return new DeserializedResult(res, offset); } } + +export const BASIC_TYPES = { + i8: new Int8Handler(), + i16: new Int16Handler(), + i32: new Int32Handler(), + i64: new Int64Handler(), + + u8: new Uint8Handler(), + u16: new Uint16Handler(), + u32: new Uint32Handler(), + u64: new Uint64Handler(), + + f32: new Float64Handler(), + f64: new Float64Handler(), + + bool: new BoolHandler(), + void: new VoidHandler(), + uuid: new UUIDHandler(), + str: new StringHandler(), +}; diff --git a/shared/utility/buffer.mjs b/shared/utility/buffer.mjs new file mode 100644 index 0000000..1bfcf65 --- /dev/null +++ b/shared/utility/buffer.mjs @@ -0,0 +1,23 @@ +/** + * Compares two ArrayBuffer objects byte by byte to check for equality. + * + * @param {ArrayBufferLike | Int32Array | Uint32Array | Int16Array | Uint16Array | Int8Array | Uint8Array | Uint8ClampedArray} buffer1 - The first ArrayBuffer to compare. + * @param {ArrayBufferLike | Int32Array | Uint32Array | Int16Array | Uint16Array | Int8Array | Uint8Array | Uint8ClampedArray} buffer2 - The second ArrayBuffer to compare. + * @returns {boolean} Returns true if the ArrayBuffer objects are equal byte by byte, otherwise false. + */ +export function areArrayBuffersEqual(buffer1, buffer2) { + if (buffer1.byteLength !== buffer2.byteLength) { + return false; + } + + const view1 = new DataView(buffer1.buffer ?? buffer1); + const view2 = new DataView(buffer2.buffer ?? buffer2); + + for (let i = 0; i < buffer1.byteLength; i++) { + if (view1.getUint8(i) !== view2.getUint8(i)) { + return false; + } + } + + return true; +} diff --git a/shared/utility/index.mjs b/shared/utility/index.mjs new file mode 100644 index 0000000..a39de4e --- /dev/null +++ b/shared/utility/index.mjs @@ -0,0 +1 @@ +export { areArrayBuffersEqual } from './buffer.mjs'; diff --git a/shared/utility/package.json b/shared/utility/package.json new file mode 100644 index 0000000..76ce69b --- /dev/null +++ b/shared/utility/package.json @@ -0,0 +1,6 @@ +{ + "name": "@og/utility", + "type": "module", + "main": "index.mjs", + "dependencies": {} +} diff --git a/shared/utility/test/buffer.test.js b/shared/utility/test/buffer.test.js new file mode 100644 index 0000000..7c61778 --- /dev/null +++ b/shared/utility/test/buffer.test.js @@ -0,0 +1,48 @@ +// test.js +import assert from 'node:assert/strict'; +import { areArrayBuffersEqual } from '../buffer.mjs'; + +describe('areArrayBuffersEqual', function () { + it('should return true for equal ArrayBuffers', function () { + const buffer1 = new ArrayBuffer(4); + const view1 = new DataView(buffer1); + view1.setInt8(0, 1); + view1.setInt8(1, 2); + view1.setInt8(2, 3); + view1.setInt8(3, 4); + + const buffer2 = new ArrayBuffer(4); + const view2 = new DataView(buffer2); + view2.setInt8(0, 1); + view2.setInt8(1, 2); + view2.setInt8(2, 3); + view2.setInt8(3, 4); + + assert.equal(areArrayBuffersEqual(buffer1, buffer2), true); + }); + + it('should return false for different length ArrayBuffers', function () { + const buffer1 = new ArrayBuffer(4); + const buffer2 = new ArrayBuffer(8); + + assert.equal(areArrayBuffersEqual(buffer1, buffer2), false); + }); + + it('should return false for different content ArrayBuffers', function () { + const buffer1 = new ArrayBuffer(4); + const view1 = new DataView(buffer1); + view1.setInt8(0, 1); + view1.setInt8(1, 2); + view1.setInt8(2, 3); + view1.setInt8(3, 4); + + const buffer2 = new ArrayBuffer(4); + const view2 = new DataView(buffer2); + view2.setInt8(0, 5); + view2.setInt8(1, 6); + view2.setInt8(2, 7); + view2.setInt8(3, 8); + + assert.equal(areArrayBuffersEqual(buffer1, buffer2), false); + }); +}); diff --git a/shared/uuid/test/uuid.test.js b/shared/uuid/test/uuid.test.js index a931f02..088a978 100644 --- a/shared/uuid/test/uuid.test.js +++ b/shared/uuid/test/uuid.test.js @@ -1,5 +1,5 @@ import { UUID } from '../index.mjs'; -import assert from 'node:assert'; +import assert from 'node:assert/strict'; describe('UUID', () => { describe('#constructor()', () => {