From 023e8e34f9ba08e566b70049b5f36d73a8ff2b0c Mon Sep 17 00:00:00 2001 From: szdytom Date: Sat, 10 Feb 2024 15:29:34 +0800 Subject: [PATCH] redesign type-handler --- shared/bstruct/type-handler.mjs | 1050 ++++++++++++++++++++----------- 1 file changed, 684 insertions(+), 366 deletions(-) diff --git a/shared/bstruct/type-handler.mjs b/shared/bstruct/type-handler.mjs index af0c058..fc9d0ff 100644 --- a/shared/bstruct/type-handler.mjs +++ b/shared/bstruct/type-handler.mjs @@ -1,455 +1,773 @@ import { VirtualMethodNotImplementedError } from '@og/error-utils'; +import { UUID } from '@og/uuid'; +/** + * Represents the result of deserialization. + * @class + */ +export class DeserializedResult { + /** + * @constructor + * @param {any} value - The deserialized value. + * @param {number} offset - The offset after deserialization. + */ + constructor(value, offset) { + this.value = value; + this.offset = offset; + } +} + +/** + * Base class for handling basic data types. + * @abstract + * @class + */ export class BasicTypeHandler { + /** + * Gets the size of the serialized value in bytes. + * @abstract + * @param {any} value - The value to be serialized. + * @returns {number} - The size of the serialized value in bytes. + */ sizeof(value) { throw new VirtualMethodNotImplementedError(); } + /** + * Serializes the value and writes it to the DataView at the specified offset. + * @abstract + * @param {DataView} view - The DataView to write to. + * @param {number} offset - The offset to start writing at. + * @param {any} value - The value to be serialized. + * @returns {number} - The new offset after serialization. + */ serialize(view, offset, value) { throw new VirtualMethodNotImplementedError(); } + /** + * Deserializes the value from the DataView at the specified offset. + * @abstract + * @param {DataView} view - The DataView to read from. + * @param {number} offset - The offset to start reading from. + * @returns {DeserializedResult} - The deserialized result. + */ deserialize(view, offset) { throw new VirtualMethodNotImplementedError(); } -}; +} +/** + * Handles 8-bit signed integers (int8). + * @extends {BasicTypeHandler} + * @class + */ export class Int8Handler extends BasicTypeHandler { + /** + * Gets the size of the serialized int8 value in bytes (always 1). + * @param {number} _value - The int8 value. + * @returns {number} - The size of the serialized int8 value in bytes. + */ sizeof(_value) { return 1; } /** - * - * @param {DataView} view - * @param {number} offset - * @param {number} value + * Serializes the int8 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 int8 value to be serialized. + * @returns {number} - The new offset after serialization. */ serialize(view, offset, value) { view.setInt8(offset, value); + return offset + 1; } /** - * - * @param {DataView} view - * @param {number} offset - * @returns {number} + * Deserializes the int8 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 int8 value and a new offset. */ deserialize(view, offset) { - return view.getInt8(offset); + return new DeserializedResult(view.getInt8(offset), offset + 1); } } +/** + * Handles 16-bit signed integers (int16). + * @extends {BasicTypeHandler} + * @class + */ export class Int16Handler extends BasicTypeHandler { - /** - * @param {number} _value - * @returns {number} - */ - sizeof(_value) { - return 2; - } + /** + * Gets the size of the serialized int16 value in bytes (always 2). + * @param {number} _value - The int16 value. + * @returns {number} - The size of the serialized int16 value in bytes. + */ + sizeof(_value) { + return 2; + } - /** - * @param {DataView} view - * @param {number} offset - * @param {number} value - */ - serialize(view, offset, value) { - view.setInt16(offset, value); - } + /** + * Serializes the int16 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 int16 value to be serialized. + * @returns {number} - The new offset after serialization. + */ + serialize(view, offset, value) { + view.setInt16(offset, value); + return offset + 2; + } - /** - * @param {DataView} view - * @param {number} offset - * @returns {number} - */ - deserialize(view, offset) { - return view.getInt16(offset); - } + /** + * Deserializes the int16 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 int16 value and a new offset. + */ + deserialize(view, offset) { + return new DeserializedResult(view.getInt16(offset), offset + 2); + } } +/** + * Handles 32-bit signed integers (int32). + * @extends {BasicTypeHandler} + * @class + */ export class Int32Handler extends BasicTypeHandler { - /** - * @param {number} _value - * @returns {number} - */ - sizeof(_value) { - return 4; - } + /** + * Gets the size of the serialized int32 value in bytes (always 4). + * @param {number} _value - The int32 value. + * @returns {number} - The size of the serialized int32 value in bytes. + */ + sizeof(_value) { + return 4; + } - /** - * @param {DataView} view - * @param {number} offset - * @param {number} value - */ - serialize(view, offset, value) { - view.setInt32(offset, value); - } + /** + * Serializes the int32 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 int32 value to be serialized. + * @returns {number} - The new offset after serialization. + */ + serialize(view, offset, value) { + view.setInt32(offset, value); + return offset + 4; + } - /** - * @param {DataView} view - * @param {number} offset - * @returns {number} - */ - deserialize(view, offset) { - return view.getInt32(offset); - } + /** + * Deserializes the int32 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 int32 value and a new offset. + */ + deserialize(view, offset) { + return new DeserializedResult(view.getInt32(offset), offset + 4); + } } +/** + * Handles 64-bit signed integers (int64). + * @extends {BasicTypeHandler} + * @class + */ export class Int64Handler extends BasicTypeHandler { - /** - * @param {number} _value - * @returns {number} - */ - sizeof(_value) { - return 8; - } + /** + * Gets the size of the serialized int64 value in bytes (always 8). + * @param {BigInt} _value - The int64 value. + * @returns {number} - The size of the serialized int64 value in bytes. + */ + sizeof(_value) { + return 8; + } - /** - * @param {DataView} view - * @param {number} offset - * @param {BigInt} value - */ - serialize(view, offset, value) { - view.setBigInt64(offset, value); - } + /** + * Serializes the int64 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 {BigInt} value - The int64 value to be serialized. + * @returns {number} - The new offset after serialization. + */ + serialize(view, offset, value) { + view.setBigInt64(offset, value); + return offset + 8; + } - /** - * @param {DataView} view - * @param {number} offset - * @returns {BigInt} - */ - deserialize(view, offset) { - return view.getBigInt64(offset); - } + /** + * Deserializes the int64 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 int64 value and a new offset. + */ + deserialize(view, offset) { + return new DeserializedResult(view.getBigInt64(offset), offset + 8); + } } +/** + * Handles 8-bit unsigned integers (uint8). + * @extends {BasicTypeHandler} + * @class + */ export class Uint8Handler extends BasicTypeHandler { - /** - * @param {number} _value - * @returns {number} - */ - sizeof(_value) { - return 1; - } + /** + * Gets the size of the serialized uint8 value in bytes (always 1). + * @param {number} _value - The uint8 value. + * @returns {number} - The size of the serialized uint8 value in bytes. + */ + sizeof(_value) { + return 1; + } - /** - * @param {DataView} view - * @param {number} offset - * @param {number} value - */ - serialize(view, offset, value) { - view.setUint8(offset, value); - } + /** + * Serializes the uint8 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 uint8 value to be serialized. + * @returns {number} - The new offset after serialization. + */ + serialize(view, offset, value) { + view.setUint8(offset, value); + return offset + 1; + } - /** - * @param {DataView} view - * @param {number} offset - * @returns {number} - */ - deserialize(view, offset) { - return view.getUint8(offset); - } + /** + * Deserializes the uint8 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 uint8 value and a new offset. + */ + deserialize(view, offset) { + return new DeserializedResult(view.getUint8(offset), offset + 1); + } } +/** + * Handles 16-bit unsigned integers (uint16). + * @extends {BasicTypeHandler} + * @class + */ export class Uint16Handler extends BasicTypeHandler { - /** - * @param {number} _value - * @returns {number} - */ - sizeof(_value) { - return 2; - } + /** + * Gets the size of the serialized uint16 value in bytes (always 2). + * @param {number} _value - The uint16 value. + * @returns {number} - The size of the serialized uint16 value in bytes. + */ + sizeof(_value) { + return 2; + } - /** - * @param {DataView} view - * @param {number} offset - * @param {number} value - */ - serialize(view, offset, value) { - view.setUint16(offset, value); - } + /** + * Serializes the uint16 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 uint16 value to be serialized. + * @returns {number} - The new offset after serialization. + */ + serialize(view, offset, value) { + view.setUint16(offset, value); + return offset + 2; + } - /** - * @param {DataView} view - * @param {number} offset - * @returns {number} - */ - deserialize(view, offset) { - return view.getUint16(offset); - } + /** + * Deserializes the uint16 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 uint16 value and a new offset. + */ + deserialize(view, offset) { + return new DeserializedResult(view.getUint16(offset), offset + 2); + } } +/** + * Handles 32-bit unsigned integers (uint32). + * @extends {BasicTypeHandler} + * @class + */ export class Uint32Handler extends BasicTypeHandler { - /** - * @param {number} _value - * @returns {number} - */ - sizeof(_value) { - return 4; - } + /** + * Gets the size of the serialized uint32 value in bytes (always 4). + * @param {number} _value - The uint32 value. + * @returns {number} - The size of the serialized uint32 value in bytes. + */ + sizeof(_value) { + return 4; + } - /** - * @param {DataView} view - * @param {number} offset - * @param {number} value - */ - serialize(view, offset, value) { - view.setUint32(offset, value); - } + /** + * Serializes the uint32 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 uint32 value to be serialized. + * @returns {number} - The new offset after serialization. + */ + serialize(view, offset, value) { + view.setUint32(offset, value); + return offset + 4; + } - /** - * @param {DataView} view - * @param {number} offset - * @returns {number} - */ - deserialize(view, offset) { - return view.getUint32(offset); - } + /** + * Deserializes the uint32 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 uint32 value and a new offset. + */ + deserialize(view, offset) { + return new DeserializedResult(view.getUint32(offset), offset + 4); + } } +/** + * Handles 64-bit unsigned integers (uint64). + * @extends {BasicTypeHandler} + * @class + */ export class Uint64Handler extends BasicTypeHandler { - /** - * @param {number} _value - * @returns {number} - */ - sizeof(_value) { - return 8; - } + /** + * Gets the size of the serialized uint64 value in bytes (always 8). + * @param {BigInt} _value - The uint64 value. + * @returns {number} - The size of the serialized uint64 value in bytes. + */ + sizeof(_value) { + return 8; + } - /** - * @param {DataView} view - * @param {number} offset - * @param {BigInt} value - */ - serialize(view, offset, value) { - view.setBigUint64(offset, value); - } + /** + * Serializes the uint64 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 {BigInt} value - The uint64 value to be serialized. + * @returns {number} - The new offset after serialization. + */ + serialize(view, offset, value) { + view.setBigUint64(offset, value); + return offset + 8; + } - /** - * @param {DataView} view - * @param {number} offset - * @returns {BigInt} - */ - deserialize(view, offset) { - return view.getBigUint64(offset); - } -} - -export class Float32Handler extends BasicTypeHandler { - /** - * @param {number} _value - * @returns {number} - */ - sizeof(_value) { - return 4; - } - - /** - * @param {DataView} view - * @param {number} offset - * @param {number} value - */ - serialize(view, offset, value) { - view.setFloat32(offset, value); - } - - /** - * @param {DataView} view - * @param {number} offset - * @returns {number} - */ - deserialize(view, offset) { - return view.getFloat32(offset); - } -} - -export class Float64Handler extends BasicTypeHandler { - /** - * @param {number} _value - * @returns {number} - */ - sizeof(_value) { - return 8; - } - - /** - * @param {DataView} view - * @param {number} offset - * @param {number} value - */ - serialize(view, offset, value) { - view.setFloat64(offset, value); - } - - /** - * @param {DataView} view - * @param {number} offset - * @returns {number} - */ - deserialize(view, offset) { - return view.getFloat64(offset); - } + /** + * Deserializes the uint64 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 uint64 value and a new offset. + */ + deserialize(view, offset) { + return new DeserializedResult(view.getBigUint64(offset), offset + 8); + } } +/** + * Handles boolean values (bool). + * @extends {BasicTypeHandler} + * @class + */ export class BoolHandler extends BasicTypeHandler { - /** - * @param {boolean} _value - * @returns {number} - */ - sizeof(_value) { - return 1; - } + /** + * Gets the size of the serialized bool value in bytes (always 1). + * @param {boolean} _value - The bool value. + * @returns {number} - The size of the serialized bool value in bytes. + */ + sizeof(_value) { + return 1; + } - /** - * @param {DataView} view - * @param {number} offset - * @param {boolean} value - */ - serialize(view, offset, value) { - view.setUint8(offset, value ? 1 : 0); - } + /** + * Serializes the bool 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 {boolean} value - The bool value to be serialized. + * @returns {number} - The new offset after serialization. + */ + serialize(view, offset, value) { + view.setUint8(offset, value ? 1 : 0); + return offset + 1; + } - /** - * @param {DataView} view - * @param {number} offset - * @returns {boolean} - */ - deserialize(view, offset) { - return view.getUint8(offset) !== 0; - } + /** + * Deserializes the bool 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 bool value and a new offset. + */ + deserialize(view, offset) { + return new DeserializedResult(view.getUint8(offset) !== 0, offset + 1); + } } +/** + * Handles void values (void). + * @extends {BasicTypeHandler} + * @class + */ export class VoidHandler extends BasicTypeHandler { - /** - * @returns {number} - */ - sizeof() { - return 0; - } + /** + * Gets the size of the serialized void value in bytes (always 0). + * @param {*} _value - The void value. + * @returns {number} - The size of the serialized void value in bytes (always 0). + */ + sizeof(_value) { + return 0; + } - /** - * @param {DataView} _view - * @param {number} _offset - * @param {undefined} _value - */ - serialize(_view, _offset, _value) { - // Do nothing for Void - } + /** + * Serializes the void value (does nothing). + * @param {DataView} _view - The DataView to write to (not used). + * @param {number} offset - The offset to start writing at (not used). + * @param {*} _value - The void value (not used). + * @returns {number} - The offset unchanged. + */ + serialize(_view, offset, _value) { + return offset; + } - /** - * @param {DataView} _view - * @param {number} _offset - * @returns {undefined} - */ - deserialize(_view, _offset) { - // Do nothing for Void - } + /** + * Deserializes the void value (does nothing). + * @param {DataView} _view - The DataView to read from (not used). + * @param {number} offset - The offset to start reading from (not used). + * @returns {DeserializedResult} - The offset unchanged and undefined as the value. + */ + deserialize(_view, offset) { + return new DeserializedResult(undefined, offset); + } } +/** + * Handles array of a fixed length with elements of the same type. + * @extends {BasicTypeHandler} + * @class + */ +export class FixedArrayHandler extends BasicTypeHandler { + /** + * Constructor for FixedArrayHandler. + * @param {number} n - The fixed length of the array. + * @param {BasicTypeHandler} element_handler - The handler for individual elements of the array. + */ + constructor(n, element_handler) { + super(); + this.n = n; + this.element_handler = element_handler; + } + + /** + * Gets the size of the serialized fixed-length array in bytes. + * @param {Array} value - The array to calculate the size for. + * @returns {number} - The size of the serialized fixed-length array in bytes. + */ + sizeof(value) { + let res = 0; + for (let i = 0; i < this.n; i += 1) { + res += this.element_handler.sizeof(value[i]); + } + return res; + } + + /** + * Serializes the fixed-length array 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 {Array} value - The fixed-length array to be serialized. + * @returns {number} - The new offset after serialization. + */ + serialize(view, offset, value) { + for (let i = 0; i < this.n; i += 1) { + offset = this.element_handler.serialize(view, offset, value[i]); + } + return offset; + } + + /** + * Deserializes the fixed-length array 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 fixed-length array and a new offset. + */ + deserialize(view, offset) { + let res = new Array(this.n); + for (let i = 0; i < this.n; i += 1) { + const tmp = this.element_handler.deserialize(view, offset); + res[i] = tmp.value; + offset = tmp.offset; + } + return new DeserializedResult(res, offset); + } +} + +/** + * Handles dynamic arrays with elements of the same type. + * @extends {BasicTypeHandler} + * @class + */ +export class DynamicArrayHandler extends BasicTypeHandler { + /** + * Constructor for DynamicArrayHandler. + * @param {BasicTypeHandler} element_handler - The handler for individual elements of the array. + */ + constructor(element_handler) { + super(); + this.element_handler = element_handler; + } + + /** + * Gets the size of the serialized dynamic array in bytes. + * @param {Array} value - The array to calculate the size for. + * @returns {number} - The size of the serialized dynamic array in bytes. + */ + sizeof(value) { + let size = 4; // For storing the length of the array + for (const element of value) { + size += this.element_handler.sizeof(element); + } + return size; + } + + /** + * Serializes the dynamic array 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 {Array} value - The dynamic array to be serialized. + * @returns {number} - The new offset after serialization. + */ + serialize(view, offset, value) { + view.setUint32(offset, value.length); + offset += 4; + for (const element of value) { + offset = this.element_handler.serialize(view, offset, element); + } + return offset; + } + + /** + * Deserializes the dynamic array 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 dynamic array and a new offset. + */ + deserialize(view, offset) { + const length = view.getUint32(offset); + offset += 4; + const res = new Array(length); + for (let i = 0; i < length; i++) { + const tmp = this.element_handler.deserialize(view, offset); + res[i] = tmp.value; + offset = tmp.offset; + } + return new DeserializedResult(res, offset); + } +} + +/** + * Handles storage and serialization of UUID objects using a 16-byte buffer. + * @extends {BasicTypeHandler} + * @class + */ export class UUIDHandler extends BasicTypeHandler { - /** - * @param {UUID} _value - * @returns {number} - */ - 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. + * @param {number} offset - The offset to start writing at. + * @param {UUID} value - The UUID object to be serialized. + * @returns {number} - The new offset after serialization. + */ + serialize(view, offset, value) { + // Convert UUID to binary data (16 bytes) + const binary_data = new Uint8Array(value.buffer); - /** - * @param {DataView} view - * @param {number} offset - * @param {UUID} value - */ - serialize(view, offset, value) { - const buffer = value.buffer; - for (let i = 0; i < buffer.length; i++) { - view.setUint8(offset + i, buffer[i]); - } - } + // Write binary data to the buffer + for (let i = 0; i < binary_data.length; i++) { + view.setUint8(offset + i, binary_data[i]); + } - /** - * @param {DataView} view - * @param {number} offset - * @returns {UUID} - */ - deserialize(view, offset) { - const buffer = new Uint8Array(16); - for (let i = 0; i < 16; i++) { - buffer[i] = view.getUint8(offset + i); - } - return new UUID(buffer); - } + return offset + 16; // Move offset to the next position after the UUID + } + + /** + * Deserializes the UUID from the buffer 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 object. + */ + deserialize(view, offset) { + // Read 16 bytes from the buffer starting at the given offset + const binary_data = new Uint8Array(16); + for (let i = 0; i < 16; i++) { + binary_data[i] = view.getUint8(offset + i); + } + + // Create a new UUID object from the binary data + const uuid = new UUID(binary_data.buffer); + return new DeserializedResult(uuid, offset + 16); + } } +/** + * Handles storage and serialization of strings. + * @extends {BasicTypeHandler} + * @class + */ export class StringHandler extends BasicTypeHandler { - /** - * @param {string} _value - * @returns {number} - */ - sizeof(_value) { - // Calculate the size of string in bytes including the 4 bytes for length - return 4 + this.utf8ByteLength(_value); - } + /** + * Constructs a StringHandler object. + */ + constructor() { + super(); + } - /** - * @param {DataView} view - * @param {number} offset - * @param {string} value - */ - serialize(view, offset, value) { - const utf8_bytes = this.encodeUTF8(value); - // Write string length as 4-byte unsigned integer - view.setUint32(offset, utf8_bytes.length, true); - // Write UTF-8 bytes - for (let i = 0; i < utf8_bytes.length; i++) { - view.setUint8(offset + 4 + i, utf8_bytes[i]); - } - } + /** + * Gets the size of the serialized string in bytes. + * @param {string} value - The string to calculate the size for. + * @returns {number} - The size of the serialized string in bytes. + */ + sizeof(value) { + // Convert the string to UTF-8 encoding and calculate its byte length + const encoder = new TextEncoder(); + const encodedString = encoder.encode(value); - /** - * @param {DataView} view - * @param {number} offset - * @returns {string} - */ - deserialize(view, offset) { - // Read string length as 4-byte unsigned integer - const length = view.getUint32(offset, true); - // Read UTF-8 bytes - const utf8_bytes = new Uint8Array(length); - for (let i = 0; i < length; i++) { - utf8_bytes[i] = view.getUint8(offset + 4 + i); - } - // Decode UTF-8 bytes to string - return this.decodeUTF8(utf8_bytes); - } + // Calculate the size of the string (length of UTF-8 encoding) plus 4 bytes for storing the length + return encodedString.byteLength + 4; + } - /** - * Encodes a string to UTF-8 bytes. - * @param {string} str - * @returns {Uint8Array} - */ - encodeUTF8(str) { - const encoder = new TextEncoder(); - return encoder.encode(str); - } + /** + * Serializes the string 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 {string} value - The string to be serialized. + * @returns {number} - The new offset after serialization. + */ + serialize(view, offset, value) { + // Convert the string to UTF-8 encoding + const encoder = new TextEncoder(); + const encoded_string = encoder.encode(value); - /** - * Decodes UTF-8 bytes to a string. - * @param {Uint8Array} bytes - * @returns {string} - */ - decodeUTF8(bytes) { - const decoder = new TextDecoder(); - return decoder.decode(bytes); - } + // Write the length of the string as a uint32 at the specified offset + view.setUint32(offset, encoded_string.length); + offset += 4; - /** - * Calculates the byte length of a string encoded in UTF-8. - * @param {string} str - * @returns {number} - */ - utf8ByteLength(str) { - return new TextEncoder().encode(str).length; - } + // Write the UTF-8 encoded string to the DataView starting at offset + 4 (after the length) + for (let i = 0; i < encoded_string.length; i++) { + view.setUint8(offset + i, encoded_string[i]); + } + + // Return the new offset after serialization + return offset + encoded_string.length; + } + + /** + * Deserializes the string 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 string and a new offset. + */ + deserialize(view, offset) { + // Read the length of the string as a uint32 at the specified offset + const length = view.getUint32(offset); + offset += 4; + + // Read the UTF-8 encoded string from the DataView starting at offset + 4 (after the length) + const encoded_string = new Uint8Array(view.buffer, offset, length); + + // Convert the UTF-8 encoded string to a JavaScript string + const decoder = new TextDecoder(); + const decodedString = decoder.decode(encoded_string); + + // Return the deserialized string and the new offset + return new DeserializedResult(decodedString, offset + length); + } +} + +/** + * Handles the serialization and deserialization of a compound type composed of various fields. + * @extends {BasicTypeHandler} + * @class + */ +export class CompoundTypeHandler extends BasicTypeHandler { + /** + * Constructs a CompoundTypeHandler object for the specified type. + * @param {Function} type - The class representing the compound type. + * @example + * // Define a class Point + * class Point { + * constructor(x, y) { + * this.x = x; + * this.y = y; + * } + * } + * + * // Define the type definition for Point + * Point.prototype.typedef = [ + * {field: 'x', handler: new Float32Handler()}, + * {field: 'y', handler: new Float32Handler()} + * ]; + * + * // Create a CompoundTypeHandler for Point + * const pointHandler = new CompoundTypeHandler(Point); + */ + constructor(type) { + super(); + /** + * The class representing the compound type. + * @type {Function} + * @private + */ + this.type = type; + /** + * The type definition specifying the fields and their handlers. + * @type {Array<{field: string, handler: BasicTypeHandler}>} + * @private + */ + this.typedef = type.typedef; + } + + /** + * Gets the size of the serialized compound type in bytes. + * @param {Object} value - The instance of the compound type to calculate the size for. + * @returns {number} - The size of the serialized compound type in bytes. + */ + sizeof(value) { + 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; + res += field_handler.sizeof(value[field_name]); + } + return res; + } + + /** + * Serializes the compound type 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 {Object} value - The instance of the compound type to be serialized. + * @returns {number} - The new offset after serialization. + */ + 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; + offset = field_handler.serialize(view, offset, value[field_name]); + } + return offset; + } + + /** + * Deserializes the compound type 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 compound type and a new offset. + */ + deserialize(view, offset) { + 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 tmp = field_handler.deserialize(view, offset); + res[field_name] = tmp.value; + offset = tmp.offset; + } + return new DeserializedResult(res, offset); + } }