diff --git a/packages/string-store/src/lib/schema/Schema.ts b/packages/string-store/src/lib/schema/Schema.ts index adb54fd4df..46d4147090 100644 --- a/packages/string-store/src/lib/schema/Schema.ts +++ b/packages/string-store/src/lib/schema/Schema.ts @@ -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 { readonly #id: Id; @@ -63,6 +63,32 @@ export class Schema 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>, 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>, defaultMaximumArrayLength = 100): UnalignedUint16Array { + const buffer = new UnalignedUint16Array(this.totalBitSize ?? defaultMaximumArrayLength); + this.serializeInto(buffer, value); + return buffer; + } + /** * Serialize a value into a buffer. * @@ -74,7 +100,7 @@ export class Schema * The schema's ID is written to the buffer first, followed by each property * in the schema. */ - public serialize(buffer: UnalignedUint16Array, value: Readonly>): void { + public serializeInto(buffer: UnalignedUint16Array, value: Readonly>): void { buffer.writeInt16(this.#id); for (const [name, type] of this) { (type as IType).serialize(buffer, (value as any)[name]); @@ -90,15 +116,16 @@ export class Schema * * @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 { - const ptr = Pointer.from(pointer); + public deserialize(buffer: UnalignedUint16Array | string, pointer: PointerLike): UnwrapSchemaEntries { + buffer = UnalignedUint16Array.from(buffer); + pointer = Pointer.from(pointer); const result = Object.create(null) as UnwrapSchemaEntries; 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; } diff --git a/packages/string-store/src/lib/schema/SchemaStore.ts b/packages/string-store/src/lib/schema/SchemaStore.ts index baccce8ecb..1105e188d3 100644 --- a/packages/string-store/src/lib/schema/SchemaStore.ts +++ b/packages/string-store/src/lib/schema/SchemaStore.ts @@ -74,9 +74,7 @@ export class SchemaStore { */ public serializeRaw>(id: Id, value: SerializeValue): UnalignedUint16Array { const schema = this.get(id) as Schema; - const buffer = new UnalignedUint16Array(schema.totalBitSize ?? this.defaultMaximumArrayLength); - schema.serialize(buffer, value); - return buffer; + return schema.serializeRaw(value, this.defaultMaximumArrayLength); } /** diff --git a/packages/string-store/tests/lib/Schema.test.ts b/packages/string-store/tests/lib/Schema.test.ts index 0f7ec00c5d..0b04841ea5 100644 --- a/packages/string-store/tests/lib/Schema.test.ts +++ b/packages/string-store/tests/lib/Schema.test.ts @@ -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) //