[js][bstruct]add handler for type Buffer and Map

This commit is contained in:
方而静 2024-02-11 12:51:05 +08:00
parent 9bc48fc0c0
commit cdd6564b51
Signed by: szTom
GPG Key ID: 072D999D60C6473C
4 changed files with 171 additions and 13 deletions

View File

@ -1,6 +1,6 @@
import { BaseTypeHandler, CompoundTypeHandler } from './type-handler.mjs';
export { BASIC_TYPES, FixedArrayHandler, DynamicArrayHandler, CompoundTypeHandler } from './type-handler.mjs';
export { BASIC_TYPES } from './type-handler.mjs';
/**
* Serializes JavaScript value to binary.

View File

@ -4,7 +4,8 @@
"main": "index.mjs",
"dependencies": {
"@og/error-utils": "file:../error-utils",
"@og/utility": "file:../utility",
"@og/uuid": "file:../uuid",
"@og/utility": "file:../utility"
"buffer": "^6.0.3"
}
}
}

View File

@ -1,4 +1,4 @@
import { BASIC_TYPES, FixedArrayHandler, DynamicArrayHandler, CompoundTypeHandler, serializeToBinary, deserializeFromBinary } from '../index.mjs';
import { BASIC_TYPES, serializeToBinary, deserializeFromBinary } from '../index.mjs';
import assert from 'node:assert/strict';
import { areArrayBuffersEqual } from '@og/utility';
import { UUID } from '@og/uuid';
@ -82,13 +82,13 @@ describe('binary-struct', () => {
});
it('type u16[]', () => {
let res = serializeToBinary([0x1770, 0x3c3a, 0x9012], new DynamicArrayHandler(BASIC_TYPES.u16));
let res = serializeToBinary([0x1770, 0x3c3a, 0x9012], BASIC_TYPES.array(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 res = serializeToBinary([0x1770, 0x3c3a, 0x9012], BASIC_TYPES.FixedArray(3, BASIC_TYPES.u16));
let ans = new Uint8Array([0x70, 0x17, 0x3a, 0x3c, 0x12, 0x90]);
assert.ok(areArrayBuffersEqual(res, ans));
});
@ -104,6 +104,18 @@ describe('binary-struct', () => {
let ans = new Uint8Array([0x80, 0x30, 0x18, 0x46, 0, 0, 0, 0]);
assert.ok(areArrayBuffersEqual(res, ans));
});
it('type Buffer', () => {
let ans = new Uint8Array([0x3c, 0x3d, 0x3e, 0x3f]).buffer;
let res = serializeToBinary(Buffer.from(ans), BASIC_TYPES.raw(4));
assert.ok(areArrayBuffersEqual(res, ans));
});
it('type StringMap', () => {
let res = serializeToBinary(new Map([['aa', 'b'], ['hi', 'hello']]), BASIC_TYPES.StringMap);
let ans = new Uint8Array([0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x61, 0x61, 0x01, 0x00, 0x00, 0x00, 0x62, 0x02, 0x00, 0x00, 0x00, 0x68, 0x69, 0x05, 0x00, 0x00, 0x00, 0x68, 0x65, 0x6c, 0x6c, 0x6f]).buffer;
assert.ok(areArrayBuffersEqual(res, ans));
});
});
describe('deserializeFromBinary', () => {
@ -179,14 +191,14 @@ describe('binary-struct', () => {
it('type u16[]', () => {
let binary_data = new Uint8Array([0x03, 0x00, 0x00, 0x00, 0x70, 0x17, 0x3a, 0x3c, 0x12, 0x90]).buffer;
let res = deserializeFromBinary(new DataView(binary_data), new DynamicArrayHandler(BASIC_TYPES.u16));
let res = deserializeFromBinary(new DataView(binary_data), BASIC_TYPES.array(BASIC_TYPES.u16));
let ans = [0x1770, 0x3c3a, 0x9012];
assert.deepEqual(res, ans);
});
it('type u16[3]', () => {
let binary_data = new Uint8Array([0x70, 0x17, 0x3a, 0x3c, 0x12, 0x90]).buffer;
let res = deserializeFromBinary(new DataView(binary_data), new FixedArrayHandler(3, BASIC_TYPES.u16));
let res = deserializeFromBinary(new DataView(binary_data), BASIC_TYPES.FixedArray(3, BASIC_TYPES.u16));
let ans = [0x1770, 0x3c3a, 0x9012];
assert.deepEqual(res, ans);
});
@ -206,5 +218,18 @@ describe('binary-struct', () => {
let ans = new Date("2007-04-08");
assert.equal(ans.toUTCString(), res.toUTCString());
});
it('type Buffer', () => {
let binary_data = new Uint8Array([0x3c, 0x3d, 0x3e, 0x3f]).buffer;
let res = deserializeFromBinary(new DataView(binary_data), BASIC_TYPES.raw(4));
assert.deepEqual(Buffer.from(binary_data), res);
});
it('type StringMap', () => {
let binary_data = new Uint8Array([0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x61, 0x61, 0x01, 0x00, 0x00, 0x00, 0x62, 0x02, 0x00, 0x00, 0x00, 0x68, 0x69, 0x05, 0x00, 0x00, 0x00, 0x68, 0x65, 0x6c, 0x6c, 0x6f]).buffer;
let res = deserializeFromBinary(new DataView(binary_data), BASIC_TYPES.StringMap);
let ans = new Map([['aa', 'b'], ['hi', 'hello']]);
assert.deepEqual(res, ans);
});
});
});

View File

@ -1,5 +1,6 @@
import { VirtualMethodNotImplementedError } from '@og/error-utils';
import { UUID } from '@og/uuid';
import { Buffer } from 'buffer';
/**
* Represents the result of deserialization.
@ -551,6 +552,13 @@ export class VoidHandler extends BaseTypeHandler {
}
}
function getHandlerObject(type) {
if (type instanceof BaseTypeHandler) {
return type;
}
return new CompoundTypeHandler(type);
}
/**
* Handles array of a fixed length with elements of the same type.
* @extends {BaseTypeHandler}
@ -565,7 +573,7 @@ export class FixedArrayHandler extends BaseTypeHandler {
constructor(n, element_handler) {
super();
this.n = n;
this.element_handler = element_handler;
this.element_handler = getHandlerObject(element_handler);
}
/**
@ -612,6 +620,56 @@ export class FixedArrayHandler extends BaseTypeHandler {
}
}
/**
* Handles raw binary buffer of a fixed length.
* @extends {BaseTypeHandler}
* @class
*/
export class RawBufferHandler extends BaseTypeHandler {
/**
* Constructor for RawBufferHandler.
* @param {number} n - The fixed length of the buffer.
*/
constructor(n) {
super();
this.n = n;
}
/**
* Gets the size of the serialized fixed-length buffer in bytes.
* @param {Buffer} value - The array to calculate the size for.
* @returns {number} - The size of the serialized fixed-length array in bytes.
*/
sizeof(value) {
return value.byteLength;
}
/**
* Serializes the fixed-length buffer 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 {Buffer} value - The fixed-length buffer to be serialized.
* @returns {number} - The new offset after serialization.
*/
serialize(view, offset, value) {
for (let i = 0; i < this.n; i += 1) {
view.setUint8(offset + i, value.readUInt8(offset + i));
}
return offset + this.n;
}
/**
* 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) {
const res = Buffer.from(view.buffer, offset, this.n);
return new DeserializedResult(res, offset + this.n);
}
}
/**
* Handles dynamic arrays with elements of the same type.
* @extends {BaseTypeHandler}
@ -624,7 +682,7 @@ export class DynamicArrayHandler extends BaseTypeHandler {
*/
constructor(element_handler) {
super();
this.element_handler = element_handler;
this.element_handler = getHandlerObject(element_handler);
}
/**
@ -675,6 +733,74 @@ export class DynamicArrayHandler extends BaseTypeHandler {
}
}
/**
* Handles map with keys and values are of the same type.
* @extends {BaseTypeHandler}
* @class
*/
export class MapHandler extends BaseTypeHandler {
/**
* Constructor for MapHandler.
* @param {BaseTypeHandler} key_handler - The handler for keys of the map.
* @param {BaseTypeHandler} value_handler - The handler for values of the map.
*/
constructor(key_handler, value_handler) {
super();
this.key_handler = getHandlerObject(key_handler);
this.value_handler = getHandlerObject(value_handler);
}
/**
* Gets the size of the serialized map in bytes.
* @param {Map} value - The map to calculate the size for.
* @returns {number} - The size of the serialized map in bytes.
*/
sizeof(value) {
let res = 4;
for (const [k, v] of value) {
res += this.key_handler.sizeof(k);
res += this.value_handler.sizeof(v);
}
return res;
}
/**
* Serializes the map 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 {Map} value - The map to be serialized.
* @returns {number} - The new offset after serialization.
*/
serialize(view, offset, value) {
view.setUint32(offset, value.size, true);
offset += 4;
for (const [k, v] of value) {
offset = this.key_handler.serialize(view, offset, k);
offset = this.value_handler.serialize(view, offset, v);
}
return offset;
}
/**
* Deserializes the map 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 map and a new offset.
*/
deserialize(view, offset) {
const size = view.getUint32(offset, true);
offset += 4;
const res = new Map();
for (let i = 0; i < size; i += 1) {
const resk = this.key_handler.deserialize(view, offset);
const resv = this.value_handler.deserialize(view, resk.offset);
offset = resv.offset;
res.set(resk.value, resv.value);
}
return new DeserializedResult(res, offset);
}
}
/**
* Handles storage and serialization of UUID objects using a 16-byte buffer.
* @extends {BaseTypeHandler}
@ -848,7 +974,7 @@ export class CompoundTypeHandler extends BaseTypeHandler {
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].type;
const field_handler = getHandlerObject(this.typedef[i].type);
res += field_handler.sizeof(value[field_name]);
}
return res;
@ -864,7 +990,7 @@ export class CompoundTypeHandler extends BaseTypeHandler {
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].type;
const field_handler = getHandlerObject(this.typedef[i].type);
offset = field_handler.serialize(view, offset, value[field_name]);
}
return offset;
@ -880,7 +1006,7 @@ export class CompoundTypeHandler extends BaseTypeHandler {
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].type;
const field_handler = getHandlerObject(this.typedef[i].type);
const tmp = field_handler.deserialize(view, offset);
res[field_name] = tmp.value;
offset = tmp.offset;
@ -908,4 +1034,10 @@ export const BASIC_TYPES = {
uuid: new UUIDHandler(),
str: new StringHandler(),
DateTime: new DateHandler(),
array: (type) => new DynamicArrayHandler(type),
FixedArray: (n, type) => new FixedArrayHandler(n, type),
raw: (n) => new RawBufferHandler(n),
map: (k, v) => new MapHandler(k, v),
StringMap: new MapHandler(new StringHandler(), new StringHandler()),
};