Skip to content

Commit

Permalink
feat(Schema): add serialization helpers (#864)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: `serialize` has been renamed to `serializeInto`
  • Loading branch information
kyranet authored Jan 30, 2025
1 parent 318b047 commit 8a922c4
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 11 deletions.
39 changes: 33 additions & 6 deletions packages/string-store/src/lib/schema/Schema.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Pointer, type PointerLike } from '../shared/Pointer';
import { t, type IType } from '../types/index';
import type { UnalignedUint16Array } from '../UnalignedUint16Array';
import { UnalignedUint16Array } from '../UnalignedUint16Array';

export class Schema<Id extends number = number, Entries extends object = object> {
readonly #id: Id;
Expand Down Expand Up @@ -63,6 +63,32 @@ export class Schema<Id extends number = number, Entries extends object = object>
return type;
}

/**
* Create a buffer and serialize a value into it, then convert it to a string
*
* @param value The value to serialize into the buffer
* @param defaultMaximumArrayLength The default maximum array length, if any
* @returns The newly created string.
*
* @seealso This method calls {@link Schema.serializeRaw} before calling `toString()` to its result.
*/
public serialize(value: Readonly<SerializeValueEntries<Entries>>, defaultMaximumArrayLength = 100): string {
return this.serializeRaw(value, defaultMaximumArrayLength).toString();
}

/**
* Create a buffer and serialize a value into it.
*
* @param value The value to serialize into the buffer
* @param defaultMaximumArrayLength The default maximum array length, if any
* @returns The newly created buffer.
*/
public serializeRaw(value: Readonly<SerializeValueEntries<Entries>>, defaultMaximumArrayLength = 100): UnalignedUint16Array {
const buffer = new UnalignedUint16Array(this.totalBitSize ?? defaultMaximumArrayLength);
this.serializeInto(buffer, value);
return buffer;
}

/**
* Serialize a value into a buffer.
*
Expand All @@ -74,7 +100,7 @@ export class Schema<Id extends number = number, Entries extends object = object>
* The schema's ID is written to the buffer first, followed by each property
* in the schema.
*/
public serialize(buffer: UnalignedUint16Array, value: Readonly<SerializeValueEntries<Entries>>): void {
public serializeInto(buffer: UnalignedUint16Array, value: Readonly<SerializeValueEntries<Entries>>): void {
buffer.writeInt16(this.#id);
for (const [name, type] of this) {
(type as IType<any, number | null>).serialize(buffer, (value as any)[name]);
Expand All @@ -90,15 +116,16 @@ export class Schema<Id extends number = number, Entries extends object = object>
*
* @remarks
*
* Unlike {@link Schema.serialize}, this method does not read the schema's ID
* Unlike {@link Schema.serializeInto}, this method does not read the schema's ID
* from the buffer, that is reserved for the {@link SchemaStore}.
*/
public deserialize(buffer: UnalignedUint16Array, pointer: PointerLike): UnwrapSchemaEntries<Entries> {
const ptr = Pointer.from(pointer);
public deserialize(buffer: UnalignedUint16Array | string, pointer: PointerLike): UnwrapSchemaEntries<Entries> {
buffer = UnalignedUint16Array.from(buffer);
pointer = Pointer.from(pointer);
const result = Object.create(null) as UnwrapSchemaEntries<Entries>;
for (const [name, type] of this) {
// @ts-expect-error Complex types
result[name] = type.deserialize(buffer, ptr);
result[name] = type.deserialize(buffer, pointer);
}
return result;
}
Expand Down
4 changes: 1 addition & 3 deletions packages/string-store/src/lib/schema/SchemaStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,7 @@ export class SchemaStore<Entries extends object = object> {
*/
public serializeRaw<const Id extends KeyOfStore<this>>(id: Id, value: SerializeValue<Entries[Id] & object>): UnalignedUint16Array {
const schema = this.get(id) as Schema<Id, object>;
const buffer = new UnalignedUint16Array(schema.totalBitSize ?? this.defaultMaximumArrayLength);
schema.serialize(buffer, value);
return buffer;
return schema.serializeRaw(value, this.defaultMaximumArrayLength);
}

/**
Expand Down
38 changes: 36 additions & 2 deletions packages/string-store/tests/lib/Schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -408,11 +408,45 @@ describe('Schema', () => {
});

describe('serialization', () => {
test('GIVEN a schema with a boolean property THEN it serializes correctly', () => {
test('GIVEN a schema with a boolean property THEN it serializes correctly (serializeRaw)', () => {
const schema = new Schema(4).boolean('a').int16('b');
const buffer = schema.serialize({ a: true, b: 15234 }, 3);

// The buffer has 3 values:
// - 4 (schema:id) & 0xffff (mask)
//
// - 1 (prop:a) & 0b0001 (mask)
// | 15234 (prop:b) & 0xffff (mask) << 1 (offset)
//
// - 15234 (prop:b) & 0xffff (mask) >> 15 (shift)
expect(buffer).toBe('\x04\u{7705}\0');

const value = schema.deserialize(buffer, 16);
expect<{ a: boolean }>(value).toEqual({ a: true, b: 15234 });
});

test('GIVEN a schema with a boolean property THEN it serializes correctly (serializeRaw)', () => {
const schema = new Schema(4).boolean('a').int16('b');
const buffer = schema.serializeRaw({ a: true, b: 15234 }, 3);

// The buffer has 3 values:
// - 4 (schema:id) & 0xffff (mask)
//
// - 1 (prop:a) & 0b0001 (mask)
// | 15234 (prop:b) & 0xffff (mask) << 1 (offset)
//
// - 15234 (prop:b) & 0xffff (mask) >> 15 (shift)
expect(buffer.toArray()).toEqual(new Uint16Array([4, 30469, 0]));

const value = schema.deserialize(buffer, 16);
expect<{ a: boolean }>(value).toEqual({ a: true, b: 15234 });
});

test('GIVEN a schema with a boolean property THEN it serializes correctly (serializeInto)', () => {
const buffer = new UnalignedUint16Array(3);
const schema = new Schema(4).boolean('a').int16('b');

schema.serialize(buffer, { a: true, b: 15234 });
schema.serializeInto(buffer, { a: true, b: 15234 });
// The buffer has 3 values:
// - 4 (schema:id) & 0xffff (mask)
//
Expand Down

0 comments on commit 8a922c4

Please sign in to comment.