[js] add UUID and type-handlers
Signed-off-by: szdytom <szdytom@qq.com>
This commit is contained in:
parent
7c156dacb7
commit
a03cf1b2a1
1
shared/bstruct/index.mjs
Normal file
1
shared/bstruct/index.mjs
Normal file
@ -0,0 +1 @@
|
||||
import { VirtualMethodNotImplementedError } from '@og/error-utils';
|
9
shared/bstruct/package.json
Normal file
9
shared/bstruct/package.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "@og/binary-struct",
|
||||
"type": "module",
|
||||
"main": "index.mjs",
|
||||
"dependencies": {
|
||||
"@og/error-utils": "file:../error-utils",
|
||||
"@og/uuid": "file:../uuid"
|
||||
}
|
||||
}
|
455
shared/bstruct/type-handler.mjs
Normal file
455
shared/bstruct/type-handler.mjs
Normal file
@ -0,0 +1,455 @@
|
||||
import { VirtualMethodNotImplementedError } from '@og/error-utils';
|
||||
|
||||
export class BasicTypeHandler {
|
||||
sizeof(value) {
|
||||
throw new VirtualMethodNotImplementedError();
|
||||
}
|
||||
|
||||
serialize(view, offset, value) {
|
||||
throw new VirtualMethodNotImplementedError();
|
||||
}
|
||||
|
||||
deserialize(view, offset) {
|
||||
throw new VirtualMethodNotImplementedError();
|
||||
}
|
||||
};
|
||||
|
||||
export class Int8Handler extends BasicTypeHandler {
|
||||
sizeof(_value) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {DataView} view
|
||||
* @param {number} offset
|
||||
* @param {number} value
|
||||
*/
|
||||
serialize(view, offset, value) {
|
||||
view.setInt8(offset, value);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {DataView} view
|
||||
* @param {number} offset
|
||||
* @returns {number}
|
||||
*/
|
||||
deserialize(view, offset) {
|
||||
return view.getInt8(offset);
|
||||
}
|
||||
}
|
||||
|
||||
export class Int16Handler extends BasicTypeHandler {
|
||||
/**
|
||||
* @param {number} _value
|
||||
* @returns {number}
|
||||
*/
|
||||
sizeof(_value) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DataView} view
|
||||
* @param {number} offset
|
||||
* @param {number} value
|
||||
*/
|
||||
serialize(view, offset, value) {
|
||||
view.setInt16(offset, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DataView} view
|
||||
* @param {number} offset
|
||||
* @returns {number}
|
||||
*/
|
||||
deserialize(view, offset) {
|
||||
return view.getInt16(offset);
|
||||
}
|
||||
}
|
||||
|
||||
export class Int32Handler 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.setInt32(offset, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DataView} view
|
||||
* @param {number} offset
|
||||
* @returns {number}
|
||||
*/
|
||||
deserialize(view, offset) {
|
||||
return view.getInt32(offset);
|
||||
}
|
||||
}
|
||||
|
||||
export class Int64Handler extends BasicTypeHandler {
|
||||
/**
|
||||
* @param {number} _value
|
||||
* @returns {number}
|
||||
*/
|
||||
sizeof(_value) {
|
||||
return 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DataView} view
|
||||
* @param {number} offset
|
||||
* @param {BigInt} value
|
||||
*/
|
||||
serialize(view, offset, value) {
|
||||
view.setBigInt64(offset, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DataView} view
|
||||
* @param {number} offset
|
||||
* @returns {BigInt}
|
||||
*/
|
||||
deserialize(view, offset) {
|
||||
return view.getBigInt64(offset);
|
||||
}
|
||||
}
|
||||
|
||||
export class Uint8Handler extends BasicTypeHandler {
|
||||
/**
|
||||
* @param {number} _value
|
||||
* @returns {number}
|
||||
*/
|
||||
sizeof(_value) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DataView} view
|
||||
* @param {number} offset
|
||||
* @param {number} value
|
||||
*/
|
||||
serialize(view, offset, value) {
|
||||
view.setUint8(offset, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DataView} view
|
||||
* @param {number} offset
|
||||
* @returns {number}
|
||||
*/
|
||||
deserialize(view, offset) {
|
||||
return view.getUint8(offset);
|
||||
}
|
||||
}
|
||||
|
||||
export class Uint16Handler extends BasicTypeHandler {
|
||||
/**
|
||||
* @param {number} _value
|
||||
* @returns {number}
|
||||
*/
|
||||
sizeof(_value) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DataView} view
|
||||
* @param {number} offset
|
||||
* @param {number} value
|
||||
*/
|
||||
serialize(view, offset, value) {
|
||||
view.setUint16(offset, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DataView} view
|
||||
* @param {number} offset
|
||||
* @returns {number}
|
||||
*/
|
||||
deserialize(view, offset) {
|
||||
return view.getUint16(offset);
|
||||
}
|
||||
}
|
||||
|
||||
export class Uint32Handler 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.setUint32(offset, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DataView} view
|
||||
* @param {number} offset
|
||||
* @returns {number}
|
||||
*/
|
||||
deserialize(view, offset) {
|
||||
return view.getUint32(offset);
|
||||
}
|
||||
}
|
||||
|
||||
export class Uint64Handler extends BasicTypeHandler {
|
||||
/**
|
||||
* @param {number} _value
|
||||
* @returns {number}
|
||||
*/
|
||||
sizeof(_value) {
|
||||
return 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DataView} view
|
||||
* @param {number} offset
|
||||
* @param {BigInt} value
|
||||
*/
|
||||
serialize(view, offset, value) {
|
||||
view.setBigUint64(offset, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
|
||||
export class BoolHandler extends BasicTypeHandler {
|
||||
/**
|
||||
* @param {boolean} _value
|
||||
* @returns {number}
|
||||
*/
|
||||
sizeof(_value) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DataView} view
|
||||
* @param {number} offset
|
||||
* @param {boolean} value
|
||||
*/
|
||||
serialize(view, offset, value) {
|
||||
view.setUint8(offset, value ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DataView} view
|
||||
* @param {number} offset
|
||||
* @returns {boolean}
|
||||
*/
|
||||
deserialize(view, offset) {
|
||||
return view.getUint8(offset) !== 0;
|
||||
}
|
||||
}
|
||||
|
||||
export class VoidHandler extends BasicTypeHandler {
|
||||
/**
|
||||
* @returns {number}
|
||||
*/
|
||||
sizeof() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DataView} _view
|
||||
* @param {number} _offset
|
||||
* @param {undefined} _value
|
||||
*/
|
||||
serialize(_view, _offset, _value) {
|
||||
// Do nothing for Void
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DataView} _view
|
||||
* @param {number} _offset
|
||||
* @returns {undefined}
|
||||
*/
|
||||
deserialize(_view, _offset) {
|
||||
// Do nothing for Void
|
||||
}
|
||||
}
|
||||
|
||||
export class UUIDHandler extends BasicTypeHandler {
|
||||
/**
|
||||
* @param {UUID} _value
|
||||
* @returns {number}
|
||||
*/
|
||||
sizeof(_value) {
|
||||
return 16;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes a string to UTF-8 bytes.
|
||||
* @param {string} str
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
encodeUTF8(str) {
|
||||
const encoder = new TextEncoder();
|
||||
return encoder.encode(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes UTF-8 bytes to a string.
|
||||
* @param {Uint8Array} bytes
|
||||
* @returns {string}
|
||||
*/
|
||||
decodeUTF8(bytes) {
|
||||
const decoder = new TextDecoder();
|
||||
return decoder.decode(bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the byte length of a string encoded in UTF-8.
|
||||
* @param {string} str
|
||||
* @returns {number}
|
||||
*/
|
||||
utf8ByteLength(str) {
|
||||
return new TextEncoder().encode(str).length;
|
||||
}
|
||||
}
|
5
shared/error-utils/index.mjs
Normal file
5
shared/error-utils/index.mjs
Normal file
@ -0,0 +1,5 @@
|
||||
export class VirtualMethodNotImplementedError extends Error {
|
||||
constructor() {
|
||||
super('virtual method not implemented.');
|
||||
}
|
||||
};
|
6
shared/error-utils/package.json
Normal file
6
shared/error-utils/package.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "@og/error-utils",
|
||||
"type": "module",
|
||||
"main": "index.mjs",
|
||||
"dependencies": {}
|
||||
}
|
50
shared/uuid/index.mjs
Normal file
50
shared/uuid/index.mjs
Normal file
@ -0,0 +1,50 @@
|
||||
import { v4 as uuidv4, v5 as uuidv5, stringify, parse } from 'uuid';
|
||||
import { Buffer } from 'buffer';
|
||||
|
||||
/**
|
||||
* Represents a UUID (Universally Unique Identifier) object.
|
||||
*/
|
||||
export class UUID {
|
||||
/**
|
||||
* Constructs a UUID object.
|
||||
* @param {string|ArrayBuffer} input - The input to construct the UUID from. It can be either a hexadecimal string or an ArrayBuffer.
|
||||
* @throws {Error} Will throw an error if the input type is invalid. Input must be either a string or an ArrayBuffer.
|
||||
*/
|
||||
constructor(input) {
|
||||
if (typeof input === 'string') {
|
||||
this.buffer = parse(input);
|
||||
} else if (input instanceof ArrayBuffer) {
|
||||
this.buffer = Buffer.from(input);
|
||||
} else if (input instanceof UUID) {
|
||||
this.buffer = Buffer.from(input.buffer);
|
||||
} else {
|
||||
throw new Error('Invalid input type. Input must be either a string or an ArrayBuffer.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random UUID using version 4 (random).
|
||||
* @returns {UUID} A UUID object representing a randomly generated UUID.
|
||||
*/
|
||||
static v4() {
|
||||
return new UUID(uuidv4());
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a UUID using version 5 (namespace-based) with the specified namespace and name.
|
||||
* @param {string} name - The name to be used for generating the UUID.
|
||||
* @param {string|Buffer|UUID} namespace - The namespace UUID in string or Buffer format.
|
||||
* @returns {UUID} A UUID object representing a version 5 UUID.
|
||||
*/
|
||||
static v5(name, namespace) {
|
||||
return new UUID(uuidv5(name, new UUID(namespace).toString()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the UUID object to a string representation.
|
||||
* @returns {string} A hexadecimal string representation of the UUID.
|
||||
*/
|
||||
toString() {
|
||||
return stringify(this.buffer);
|
||||
}
|
||||
};
|
9
shared/uuid/package.json
Normal file
9
shared/uuid/package.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "@og/uuid",
|
||||
"type": "module",
|
||||
"main": "index.mjs",
|
||||
"dependencies": {
|
||||
"buffer": "^6.0.3",
|
||||
"uuid": "^9.0.1"
|
||||
}
|
||||
}
|
51
shared/uuid/test/uuid.test.js
Normal file
51
shared/uuid/test/uuid.test.js
Normal file
@ -0,0 +1,51 @@
|
||||
import { UUID } from '../index.mjs';
|
||||
import assert from 'node:assert';
|
||||
|
||||
describe('UUID', () => {
|
||||
describe('#constructor()', () => {
|
||||
it('should create UUID from string', () => {
|
||||
const uuid_str = '123e4567-e89b-12d3-a456-426614171440';
|
||||
const uuid = new UUID(uuid_str);
|
||||
assert.strictEqual(uuid.toString(), uuid_str);
|
||||
});
|
||||
|
||||
it('should create UUID from ArrayBuffer', () => {
|
||||
const buffer = new Uint8Array([
|
||||
18, 62, 69, 103, 232, 155, 18, 211,
|
||||
164, 86, 66, 102, 20, 23, 20, 64
|
||||
]).buffer;
|
||||
const uuid = new UUID(buffer);
|
||||
assert.strictEqual(uuid.toString(), '123e4567-e89b-12d3-a456-426614171440');
|
||||
});
|
||||
|
||||
it('should throw error for invalid input type', () => {
|
||||
assert.throws(() => {
|
||||
new UUID({}); // passing invalid input
|
||||
}, /^Error: Invalid input type/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#v4()', () => {
|
||||
it('should generate a version 4 UUID', () => {
|
||||
const uuid = UUID.v4();
|
||||
assert.strictEqual(uuid.toString().length, 36);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#v5()', () => {
|
||||
it('should generate a version 5 UUID', () => {
|
||||
const namespace = '123e4567-e89b-12d3-a456-426614171440'; // Sample namespace
|
||||
const name = 'test';
|
||||
const uuid = UUID.v5(name, namespace);
|
||||
assert.strictEqual(uuid.toString().length, 36);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#toString()', () => {
|
||||
it('should convert UUID to string', () => {
|
||||
const uuid_str = '123e4567-e89b-12d3-a456-426614174000';
|
||||
const uuid = new UUID(uuid_str);
|
||||
assert.strictEqual(uuid.toString(), uuid_str);
|
||||
});
|
||||
});
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user