From 68ee882a8b13efcfb8d5bd01fabc23600f711e46 Mon Sep 17 00:00:00 2001 From: Aura Date: Sat, 27 Jul 2024 09:36:48 +0200 Subject: [PATCH] feat: add signed ints (#778) --- packages/string-store/README.md | 130 ++++++++-- packages/string-store/src/index.ts | 18 +- .../src/lib/UnalignedUint16Array.ts | 151 +++++++---- .../string-store/src/lib/schema/Schema.ts | 212 +++++++++++++--- packages/string-store/src/lib/types/Array.ts | 2 +- .../string-store/src/lib/types/BigUint32.ts | 11 + .../string-store/src/lib/types/BigUint64.ts | 11 + .../string-store/src/lib/types/Snowflake.ts | 2 +- packages/string-store/src/lib/types/Uint16.ts | 11 + packages/string-store/src/lib/types/Uint2.ts | 11 + packages/string-store/src/lib/types/Uint32.ts | 11 + packages/string-store/src/lib/types/Uint4.ts | 11 + packages/string-store/src/lib/types/Uint64.ts | 11 + packages/string-store/src/lib/types/Uint8.ts | 11 + packages/string-store/src/lib/types/index.ts | 80 ++++++ .../string-store/tests/lib/Schema.test.ts | 209 ++++++++++----- .../tests/lib/UnalignedUint16Array.test.ts | 28 +- packages/string-store/tests/lib/types.test.ts | 239 +++++++++++++----- 18 files changed, 910 insertions(+), 249 deletions(-) create mode 100644 packages/string-store/src/lib/types/BigUint32.ts create mode 100644 packages/string-store/src/lib/types/BigUint64.ts create mode 100644 packages/string-store/src/lib/types/Uint16.ts create mode 100644 packages/string-store/src/lib/types/Uint2.ts create mode 100644 packages/string-store/src/lib/types/Uint32.ts create mode 100644 packages/string-store/src/lib/types/Uint4.ts create mode 100644 packages/string-store/src/lib/types/Uint64.ts create mode 100644 packages/string-store/src/lib/types/Uint8.ts create mode 100644 packages/string-store/src/lib/types/index.ts diff --git a/packages/string-store/README.md b/packages/string-store/README.md index 0dc9ece90b..3a457f9965 100644 --- a/packages/string-store/README.md +++ b/packages/string-store/README.md @@ -66,9 +66,9 @@ const buffer = store.serialize(Id.AgeUpdate, { age: 20 }).toString(); > [!Tip] > The serialized string is encoded in UTF-16, meaning it can store 16 bits per character. Each type stores a different number of bits, for example, a single character can store: > - 16 booleans -> - 8 2-bit integers (0-3) -> - 4 4-bit integers (0-15) -> - 2 8-bit integers (0-255) +> - 8 2-bit unsigned integers (0-3) +> - 4 4-bit unsigned integers (0-15) +> - 2 8-bit unsigned integers (0-255) > - 1 16-bit integer (0-65535) > > As a use-case, Discord's `custom_id` field in message components can store up to **100** UTF-16 characters, which means it has a storage of **1600 bits**, below you can see the supported types and their storage in bits. Keep in mind that the schema ID is stored as a [16-bit](#int16) integer, and that the property names are **not** stored. @@ -137,92 +137,184 @@ const schema = new Schema(Id.Planet).bit('isHabitable'); ### `int2` -Adds a 2-bit integer to the schema. It can store values from 0 to 3 (`0b11`), inclusive. +Adds a 2-bit signed integer to the schema. It can store values from -2 to 1, inclusive. ```ts -// A schema with a single field `type` that is a 2-bit integer: +// A schema with a single field `type` that is a 2-bit signed integer: const schema = new Schema(Id.Planet).int2('type'); +// → Schema +``` + +### `uint2` + +Adds a 2-bit unsigned integer to the schema. It can store values from 0 to 3, inclusive. + +```ts +// A schema with a single field `type` that is a 2-bit unsigned integer: + +const schema = new Schema(Id.Planet).uint2('type'); // → Schema ``` ### `int4` -Adds a 4-bit integer to the schema. It can store values from 0 to 15 (`0b1111`), inclusive. +Adds a 4-bit signed integer to the schema. It can store values from -8 to 7, inclusive. ```ts -// A schema with a single field `type` that is a 4-bit integer: +// A schema with a single field `type` that is a 4-bit signed integer: const schema = new Schema(Id.Planet).int4('type'); +// → Schema +``` + +### `uint4` + +Adds a 4-bit unsigned integer to the schema. It can store values from 0 to 15, inclusive. + +```ts +// A schema with a single field `type` that is a 4-bit unsigned integer: + +const schema = new Schema(Id.Planet).uint4('type'); // → Schema ``` ### `int8` -Adds an 8-bit integer to the schema. It can store values from 0 to 255 (`0b1111_1111`), inclusive. +Adds an 8-bit signed integer to the schema. It can store values from -128 to 127, inclusive. ```ts -// A schema with a single field `type` that is an 8-bit integer: +// A schema with a single field `type` that is an 8-bit signed integer: const schema = new Schema(Id.Planet).int8('type'); +// → Schema +``` + +### `uint8` + +Adds an 8-bit unsigned integer to the schema. It can store values from 0 to 255, inclusive. + +```ts +// A schema with a single field `type` that is an 8-bit unsigned integer: + +const schema = new Schema(Id.Planet).uint8('type'); // → Schema ``` ### `int16` -Adds a 16-bit integer to the schema. It can store values from 0 to 65535 (`0xFFFF`), inclusive. +Adds a 16-bit signed integer to the schema. It can store values from -32768 to 32767, inclusive. ```ts -// A schema with a single field `type` that is a 16-bit integer: +// A schema with a single field `type` that is a 16-bit signed integer: const schema = new Schema(Id.Planet).int16('type'); +// → Schema +``` + +### `uint16` + +Adds a 16-bit unsigned integer to the schema. It can store values from 0 to 65535, inclusive. + +```ts +// A schema with a single field `type` that is a 16-bit unsigned integer: + +const schema = new Schema(Id.Planet).uint16('type'); // → Schema ``` ### `int32` -Adds a 32-bit integer to the schema. It can store values from 0 to 4294967295 (`0xFFFFFFFF`), inclusive. +Adds a 32-bit signed integer to the schema. It can store values from 2,147,483,648 to 2,147,483,647, inclusive. ```ts -// A schema with a single field `type` that is a 32-bit integer: +// A schema with a single field `type` that is a 32-bit signed integer: const schema = new Schema(Id.Planet).int32('type'); +// → Schema +``` + +### `uint32` + +Adds a 32-bit unsigned integer to the schema. It can store values from 0 to 4,294,967,295, inclusive. + +```ts +// A schema with a single field `type` that is a 32-bit unsigned integer: + +const schema = new Schema(Id.Planet).uint32('type'); // → Schema ``` ### `int64` -Adds a 64-bit integer to the schema. It can store values from 0 to 9007199254740991 (`Number.MAX_SAFE_INTEGER`), inclusive. +Adds a 64-bit signed integer to the schema. It can store values from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807, inclusive. + +**Note**: values smaller than `Number.MIN_SAFE_INTEGER` or larger than `Number.MAX_SAFE_INTEGER` will lose precision, if you need to store larger numbers, consider using [`bigInt64`](#bigint64). ```ts -// A schema with a single field `type` that is a 64-bit integer: +// A schema with a single field `type` that is a 64-bit signed integer: const schema = new Schema(Id.Planet).int64('type'); -// → Schema +// → Schema +``` + +### `uint64` + +Adds a 64-bit unsigned integer to the schema. It can store values from 0 to 18,446,744,073,709,551,615, inclusive. + +**Note**: values larger than 9,007,199,254,740,991 (`Number.MAX_SAFE_INTEGER`) will lose precision, if you need to store larger numbers, use [`bigUint64`](#bigUint64). + +```ts +// A schema with a single field `type` that is a 64-bit unsigned integer: + +const schema = new Schema(Id.Planet).uint64('type'); +// → Schema ``` **Note**: values larger than `Number.MAX_SAFE_INTEGER` will be truncated. ### `bigInt32` -Alternative to [`int32`](#int32) that uses `BigInt`. +Alternative to [`int32`](#int32) that uses `BigInt`. It can store values from 2,147,483,648 to 2,147,483,647, inclusive. ```ts // A schema with a single field `type` that is a 32-bit integer: const schema = new Schema(Id.Planet).bigInt32('type'); +// → Schema +``` + +### `bigUint32` + +Alternative to [`uint32`](#uint32) that uses `BigInt`. It can store values from 0 to 4,294,967,295, inclusive. + +```ts +// A schema with a single field `type` that is a 32-bit integer: + +const schema = new Schema(Id.Planet).bigUint32('type'); // → Schema ``` ### `bigInt64` -Alternative to [`int64`](#int64) that uses `BigInt`. +Alternative to [`int64`](#int64) that uses `BigInt`. It can store values from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807, inclusive. ```ts // A schema with a single field `type` that is a 64-bit integer: const schema = new Schema(Id.Planet).bigInt64('type'); -// → Schema +// → Schema +``` + +### `bigUint64` + +Alternative to [`uint64`](#uint64) that uses `BigInt`. It can store values from 0 to 18,446,744,073,709,551,615, inclusive. + +```ts +// A schema with a single field `type` that is a 64-bit integer: + +const schema = new Schema(Id.Planet).bigUint64('type'); +// → Schema ``` ### `float32` diff --git a/packages/string-store/src/index.ts b/packages/string-store/src/index.ts index 06f60e5a5d..36b0accd31 100644 --- a/packages/string-store/src/index.ts +++ b/packages/string-store/src/index.ts @@ -1,21 +1,5 @@ export * from './lib/schema/Schema'; export * from './lib/schema/SchemaStore'; export * from './lib/shared/Pointer'; -export * from './lib/types/Array'; -export type * from './lib/types/base/IType'; -export * from './lib/types/BigInt32'; -export * from './lib/types/BigInt64'; -export * from './lib/types/Bit'; -export * from './lib/types/Boolean'; -export * from './lib/types/FixedLengthArray'; -export * from './lib/types/Float32'; -export * from './lib/types/Float64'; -export * from './lib/types/Int16'; -export * from './lib/types/Int2'; -export * from './lib/types/Int32'; -export * from './lib/types/Int4'; -export * from './lib/types/Int64'; -export * from './lib/types/Int8'; -export * from './lib/types/Snowflake'; -export * from './lib/types/String'; +export * from './lib/types'; export * from './lib/UnalignedUint16Array'; diff --git a/packages/string-store/src/lib/UnalignedUint16Array.ts b/packages/string-store/src/lib/UnalignedUint16Array.ts index 5d6c09612a..ffb42cec50 100644 --- a/packages/string-store/src/lib/UnalignedUint16Array.ts +++ b/packages/string-store/src/lib/UnalignedUint16Array.ts @@ -1,8 +1,15 @@ import { Pointer, type PointerLike } from './shared/Pointer'; -const Converter8 = new Uint8Array(8); -const ConverterFloat = new Float32Array(Converter8.buffer); -const ConverterDouble = new Float64Array(Converter8.buffer); +const ConverterUint8 = new Uint8Array(8); +const ConverterUint16 = new Uint16Array(ConverterUint8.buffer); +const ConverterUint32 = new Uint32Array(ConverterUint8.buffer); +const ConverterUint64 = new BigUint64Array(ConverterUint8.buffer); + +const ConverterInt32 = new Int32Array(ConverterUint8.buffer); +const ConverterInt64 = new BigInt64Array(ConverterUint8.buffer); + +const ConverterFloat = new Float32Array(ConverterUint8.buffer); +const ConverterDouble = new Float64Array(ConverterUint8.buffer); export class UnalignedUint16Array { #buffer: Uint16Array; @@ -75,22 +82,22 @@ export class UnalignedUint16Array { public writeFloat32(value: number): void { ConverterFloat[0] = value; - this.writeInt8(Converter8[0]); - this.writeInt8(Converter8[1]); - this.writeInt8(Converter8[2]); - this.writeInt8(Converter8[3]); + this.writeInt8(ConverterUint8[0]); + this.writeInt8(ConverterUint8[1]); + this.writeInt8(ConverterUint8[2]); + this.writeInt8(ConverterUint8[3]); } public writeFloat64(value: number): void { ConverterDouble[0] = value; - this.writeInt8(Converter8[0]); - this.writeInt8(Converter8[1]); - this.writeInt8(Converter8[2]); - this.writeInt8(Converter8[3]); - this.writeInt8(Converter8[4]); - this.writeInt8(Converter8[5]); - this.writeInt8(Converter8[6]); - this.writeInt8(Converter8[7]); + this.writeInt8(ConverterUint8[0]); + this.writeInt8(ConverterUint8[1]); + this.writeInt8(ConverterUint8[2]); + this.writeInt8(ConverterUint8[3]); + this.writeInt8(ConverterUint8[4]); + this.writeInt8(ConverterUint8[5]); + this.writeInt8(ConverterUint8[6]); + this.writeInt8(ConverterUint8[7]); } public readBit(offset: PointerLike): 0 | 1 { @@ -99,76 +106,95 @@ export class UnalignedUint16Array { } public readInt2(offset: PointerLike): number { + // Bit shifting to convert 2-bit signed integer to 32-bit signed integer + // 0b01 → 0b0100_0000_0000_0000 → 0b000_0000_0000_0001 (1) + // 0b10 → 0b1000_0000_0000_0000 → 0b111_1111_1111_1110 (-2) + return (this.readUint2(offset) << 30) >> 30; + } + + public readUint2(offset: PointerLike): number { const ptr = Pointer.from(offset); return this.#readBit(ptr) | (this.#readBit(ptr) << 1); } public readInt4(offset: PointerLike): number { + // Bit shifting to convert 4-bit signed integer to 32-bit signed integer, + // as shown in `readInt2`. + return (this.readUint4(offset) << 28) >> 28; + } + + public readUint4(offset: PointerLike): number { const ptr = Pointer.from(offset); return this.#readBit(ptr) | (this.#readBit(ptr) << 1) | (this.#readBit(ptr) << 2) | (this.#readBit(ptr) << 3); } public readInt8(offset: PointerLike): number { + // Bit shifting to convert 8-bit signed integer to 32-bit signed integer, + // as shown in `readInt2`. + return (this.readUint8(offset) << 24) >> 24; + } + + public readUint8(offset: PointerLike): number { const ptr = Pointer.from(offset); return this.#readByte(ptr); } public readInt16(offset: PointerLike): number { - const ptr = Pointer.from(offset); - return this.#readByte(ptr) | (this.#readByte(ptr) << 8); + // Bit shifting to convert 8-bit signed integer to 32-bit signed integer, + // as shown in `readInt2`. + return (this.readUint16(offset) << 16) >> 16; + } + + public readUint16(offset: PointerLike): number { + this.#bufferRead16(Pointer.from(offset)); + return ConverterUint16[0]; } public readInt32(offset: PointerLike): number { - return Number(this.readBigInt32(offset)); + this.#bufferRead32(Pointer.from(offset)); + return ConverterInt32[0]; + } + + public readUint32(offset: PointerLike): number { + this.#bufferRead32(Pointer.from(offset)); + return ConverterUint32[0]; } - public readInt64(offset: PointerLike) { + public readInt64(offset: PointerLike): number { return Number(this.readBigInt64(offset)); } + public readUint64(offset: PointerLike) { + return Number(this.readBigUint64(offset)); + } + public readBigInt32(offset: PointerLike): bigint { - const ptr = Pointer.from(offset); - return ( - BigInt(this.#readByte(ptr)) | - (BigInt(this.#readByte(ptr)) << 8n) | - (BigInt(this.#readByte(ptr)) << 16n) | - (BigInt(this.#readByte(ptr)) << 24n) - ); + this.#bufferRead32(Pointer.from(offset)); + return BigInt(ConverterInt32[0]); + } + + public readBigUint32(offset: PointerLike): bigint { + this.#bufferRead32(Pointer.from(offset)); + return BigInt(ConverterUint32[0]); } public readBigInt64(offset: PointerLike): bigint { - const ptr = Pointer.from(offset); - return ( - BigInt(this.#readByte(ptr)) | - (BigInt(this.#readByte(ptr)) << 8n) | - (BigInt(this.#readByte(ptr)) << 16n) | - (BigInt(this.#readByte(ptr)) << 24n) | - (BigInt(this.#readByte(ptr)) << 32n) | - (BigInt(this.#readByte(ptr)) << 40n) | - (BigInt(this.#readByte(ptr)) << 48n) | - (BigInt(this.#readByte(ptr)) << 56n) - ); + this.#bufferRead64(Pointer.from(offset)); + return ConverterInt64[0]; + } + + public readBigUint64(offset: PointerLike): bigint { + this.#bufferRead64(Pointer.from(offset)); + return ConverterUint64[0]; } public readFloat32(offset: PointerLike): number { - const ptr = Pointer.from(offset); - Converter8[0] = this.#readByte(ptr); - Converter8[1] = this.#readByte(ptr); - Converter8[2] = this.#readByte(ptr); - Converter8[3] = this.#readByte(ptr); + this.#bufferRead32(Pointer.from(offset)); return ConverterFloat[0]; } public readFloat64(offset: PointerLike): number { - const ptr = Pointer.from(offset); - Converter8[0] = this.#readByte(ptr); - Converter8[1] = this.#readByte(ptr); - Converter8[2] = this.#readByte(ptr); - Converter8[3] = this.#readByte(ptr); - Converter8[4] = this.#readByte(ptr); - Converter8[5] = this.#readByte(ptr); - Converter8[6] = this.#readByte(ptr); - Converter8[7] = this.#readByte(ptr); + this.#bufferRead64(Pointer.from(offset)); return ConverterDouble[0]; } @@ -206,6 +232,29 @@ export class UnalignedUint16Array { ); } + #bufferRead16(ptr: Pointer): void { + ConverterUint8[0] = this.#readByte(ptr); + ConverterUint8[1] = this.#readByte(ptr); + } + + #bufferRead32(ptr: Pointer): void { + ConverterUint8[0] = this.#readByte(ptr); + ConverterUint8[1] = this.#readByte(ptr); + ConverterUint8[2] = this.#readByte(ptr); + ConverterUint8[3] = this.#readByte(ptr); + } + + #bufferRead64(ptr: Pointer): void { + ConverterUint8[0] = this.#readByte(ptr); + ConverterUint8[1] = this.#readByte(ptr); + ConverterUint8[2] = this.#readByte(ptr); + ConverterUint8[3] = this.#readByte(ptr); + ConverterUint8[4] = this.#readByte(ptr); + ConverterUint8[5] = this.#readByte(ptr); + ConverterUint8[6] = this.#readByte(ptr); + ConverterUint8[7] = this.#readByte(ptr); + } + #writeBit(value: number) { if (this.#wordIndex === this.maxLength) { throw new RangeError(`The buffer is full`); diff --git a/packages/string-store/src/lib/schema/Schema.ts b/packages/string-store/src/lib/schema/Schema.ts index 82b3f2f950..b3eda1d9d5 100644 --- a/packages/string-store/src/lib/schema/Schema.ts +++ b/packages/string-store/src/lib/schema/Schema.ts @@ -1,21 +1,5 @@ import { Pointer, type PointerLike } from '../shared/Pointer'; -import { ArrayType } from '../types/Array'; -import type { IType } from '../types/base/IType'; -import { BigInt32Type } from '../types/BigInt32'; -import { BigInt64Type } from '../types/BigInt64'; -import { BitType } from '../types/Bit'; -import { BooleanType } from '../types/Boolean'; -import { FixedLengthArrayType } from '../types/FixedLengthArray'; -import { Float32Type } from '../types/Float32'; -import { Float64Type } from '../types/Float64'; -import { Int16Type } from '../types/Int16'; -import { Int2Type } from '../types/Int2'; -import { Int32Type } from '../types/Int32'; -import { Int4Type } from '../types/Int4'; -import { Int64Type } from '../types/Int64'; -import { Int8Type } from '../types/Int8'; -import { SnowflakeType } from '../types/Snowflake'; -import { StringType } from '../types/String'; +import { t, type IType } from '../types/index'; import type { UnalignedUint16Array } from '../UnalignedUint16Array'; export class Schema { @@ -120,7 +104,7 @@ export class Schema name: Name, type: IType ) { - return this.#addType(name, ArrayType(type)); + return this.#addType(name, t.array(type)); } /** @@ -138,7 +122,7 @@ export class Schema type: IType, length: number ) { - return this.#addType(name, FixedLengthArrayType(type, length)); + return this.#addType(name, t.fixedLengthArray(type, length)); } /** @@ -148,7 +132,7 @@ export class Schema * @returns The modified schema */ public string(name: Name) { - return this.#addType(name, StringType); + return this.#addType(name, t.string); } /** @@ -158,7 +142,7 @@ export class Schema * @returns The modified schema */ public boolean(name: Name) { - return this.#addType(name, BooleanType); + return this.#addType(name, t.boolean); } /** @@ -168,117 +152,277 @@ export class Schema * @returns The modified schema */ public bit(name: Name) { - return this.#addType(name, BitType); + return this.#addType(name, t.bit); } /** * Adds a 2-bit integer property to the schema. * + * @remarks + * + * The range of values is from -2 to 1, inclusive. + * * @param name The name of the property * @returns The modified schema */ public int2(name: Name) { - return this.#addType(name, Int2Type); + return this.#addType(name, t.int2); + } + + /** + * Adds a 2-bit unsigned integer property to the schema. + * + * @remarks + * + * The range of values is from 0 to 3, inclusive. + * + * @param name The name of the property + * @returns The modified schema + */ + public uint2(name: Name) { + return this.#addType(name, t.uint2); } /** * Adds a 4-bit integer property to the schema. * + * @remarks + * + * The range of values is from -8 to 7, inclusive. + * * @param name The name of the property * @returns The modified schema */ public int4(name: Name) { - return this.#addType(name, Int4Type); + return this.#addType(name, t.int4); + } + + /** + * Adds a 4-bit unsigned integer property to the schema. + * + * @remarks + * + * The range of values is from 0 to 15, inclusive. + * + * @param name The name of the property + * @returns The modified schema + */ + public uint4(name: Name) { + return this.#addType(name, t.uint4); } /** * Adds a 8-bit integer property to the schema. * + * @remarks + * + * The range of values is from -128 to 127, inclusive. + * * @param name The name of the property * @returns The modified schema */ public int8(name: Name) { - return this.#addType(name, Int8Type); + return this.#addType(name, t.int8); + } + + /** + * Adds a 8-bit unsigned integer property to the schema. + * + * @remarks + * + * The range of values is from 0 to 255, inclusive. + * + * @param name The name of the property + * @returns The modified schema + */ + public uint8(name: Name) { + return this.#addType(name, t.uint8); } /** * Adds a 16-bit integer property to the schema. * + * @remarks + * + * The range of values is from -32768 to 32767, inclusive. + * * @param name The name of the property * @returns The modified schema */ public int16(name: Name) { - return this.#addType(name, Int16Type); + return this.#addType(name, t.int16); + } + + /** + * Adds a 16-bit unsigned integer property to the schema. + * + * @remarks + * + * The range of values is from 0 to 65535, inclusive. + * + * @param name The name of the property + * @returns The modified schema + */ + public uint16(name: Name) { + return this.#addType(name, t.uint16); } /** * Adds a 32-bit integer property to the schema. * + * @remarks + * + * The range of values is from -2_147_483_648 to 2_147_483_647, inclusive. + * * @param name The name of the property * @returns The modified schema */ public int32(name: Name) { - return this.#addType(name, Int32Type); + return this.#addType(name, t.int32); + } + + /** + * Adds a 32-bit unsigned integer property to the schema. + * + * @remarks + * + * The range of values is from 0 to 4_294_967_295, inclusive. + * + * @param name The name of the property + * @returns The modified schema + */ + public uint32(name: Name) { + return this.#addType(name, t.uint32); } /** * Adds a 64-bit integer property to the schema. * + * @remarks + * + * The range of values is from -9_223_372_036_854_775_808 to 9_223_372_036_854_775_807, inclusive. + * + * However, it may run into precision issues past the range of `-9_007_199_254_740_991` to `9_007_199_254_740_991` + * * @param name The name of the property * @returns The modified schema */ public int64(name: Name) { - return this.#addType(name, Int64Type); + return this.#addType(name, t.int64); + } + + /** + * Adds a 64-bit unsigned integer property to the schema. + * + * @remarks + * + * The range of values is from 0 to 18_446_744_073_709_551_615, inclusive. + * + * However, it may run into precision issues past `9_007_199_254_740_991` + * + * @param name The name of the property + * @returns The modified schema + */ + public uint64(name: Name) { + return this.#addType(name, t.uint64); } /** * Adds a 32-bit big integer property to the schema. * + * @remarks + * + * The range of values is from -2_147_483_648n to 2_147_483_647n, inclusive. + * * @param name The name of the property * @returns The modified schema */ public bigInt32(name: Name) { - return this.#addType(name, BigInt32Type); + return this.#addType(name, t.bigInt32); + } + + /** + * Adds a 32-bit big integer property to the schema. + * + * @remarks + * + * The range of values is from 0n to 4_294_967_295n, inclusive. + * + * @param name The name of the property + * @returns The modified schema + */ + public bigUint32(name: Name) { + return this.#addType(name, t.bigUint32); } /** * Adds a 64-bit big integer property to the schema. * + * @remarks + * + * The range of values is from -9_223_372_036_854_775_808n to 9_223_372_036_854_775_807n, inclusive. + * * @param name The name of the property * @returns The modified schema */ public bigInt64(name: Name) { - return this.#addType(name, BigInt64Type); + return this.#addType(name, t.bigInt64); + } + + /** + * Adds a 64-bit big integer property to the schema. + * + * @remarks + * + * The range of values is from 0n to 18_446_744_073_709_551_615n, inclusive. + * + * @param name The name of the property + * @returns The modified schema + */ + public bigUint64(name: Name) { + return this.#addType(name, t.bigUint64); } /** * Adds a 32-bit floating point number property to the schema. * + * @remarks + * + * The range of values is from -3.4028234663852886e+38 to 3.4028234663852886e+38, inclusive. + * * @param name The name of the property * @returns The modified schema */ public float32(name: Name) { - return this.#addType(name, Float32Type); + return this.#addType(name, t.float32); } /** * Adds a 64-bit floating point number property to the schema. * + * @remarks + * + * The range of values is from -1.7976931348623157e+308 to 1.7976931348623157e+308, inclusive. + * * @param name The name of the property * @returns The modified schema */ public float64(name: Name) { - return this.#addType(name, Float64Type); + return this.#addType(name, t.float64); } /** - * Adds a 64-bit big integer property to the schema, similar to {@link Schema.bigInt64}. + * Adds a 64-bit big integer property to the schema, similar to {@link Schema.bigUint64}. + * + * @remarks + * + * The range of values is from 0n to 18_446_744_073_709_551_615n, inclusive. * * @param name The name of the property * @returns The modified schema */ public snowflake(name: Name) { - return this.#addType(name, SnowflakeType); + return this.#addType(name, t.snowflake); } /** diff --git a/packages/string-store/src/lib/types/Array.ts b/packages/string-store/src/lib/types/Array.ts index e4834cf80d..2e0aaa29a3 100644 --- a/packages/string-store/src/lib/types/Array.ts +++ b/packages/string-store/src/lib/types/Array.ts @@ -14,7 +14,7 @@ export function ArrayType(type: I } }, deserialize(buffer, pointer) { - const length = buffer.readInt16(pointer); + const length = buffer.readUint16(pointer); const value = []; for (let i = 0; i < length; i++) { value.push(type.deserialize(buffer, pointer)); diff --git a/packages/string-store/src/lib/types/BigUint32.ts b/packages/string-store/src/lib/types/BigUint32.ts new file mode 100644 index 0000000000..35e76d3560 --- /dev/null +++ b/packages/string-store/src/lib/types/BigUint32.ts @@ -0,0 +1,11 @@ +import type { IType } from './base/IType'; + +export const BigUint32Type: IType = { + serialize(buffer, value) { + buffer.writeBigInt32(value); + }, + deserialize(buffer, pointer) { + return buffer.readBigUint32(pointer); + }, + BIT_SIZE: 32 +}; diff --git a/packages/string-store/src/lib/types/BigUint64.ts b/packages/string-store/src/lib/types/BigUint64.ts new file mode 100644 index 0000000000..d048f37fa4 --- /dev/null +++ b/packages/string-store/src/lib/types/BigUint64.ts @@ -0,0 +1,11 @@ +import type { IType } from './base/IType'; + +export const BigUint64Type: IType = { + serialize(buffer, value) { + buffer.writeBigInt64(value); + }, + deserialize(buffer, pointer) { + return buffer.readBigUint64(pointer); + }, + BIT_SIZE: 64 +}; diff --git a/packages/string-store/src/lib/types/Snowflake.ts b/packages/string-store/src/lib/types/Snowflake.ts index 258858ab09..a270c9ed1b 100644 --- a/packages/string-store/src/lib/types/Snowflake.ts +++ b/packages/string-store/src/lib/types/Snowflake.ts @@ -5,7 +5,7 @@ export const SnowflakeType = { buffer.writeBigInt64(BigInt(value)); }, deserialize(buffer, offset) { - return buffer.readBigInt64(offset); + return buffer.readBigUint64(offset); }, BIT_SIZE: 64 } as const satisfies IType; diff --git a/packages/string-store/src/lib/types/Uint16.ts b/packages/string-store/src/lib/types/Uint16.ts new file mode 100644 index 0000000000..13a26e8c31 --- /dev/null +++ b/packages/string-store/src/lib/types/Uint16.ts @@ -0,0 +1,11 @@ +import type { IType } from './base/IType'; + +export const Uint16Type: IType = { + serialize(buffer, value) { + buffer.writeInt16(value); + }, + deserialize(buffer, pointer) { + return buffer.readUint16(pointer); + }, + BIT_SIZE: 16 +}; diff --git a/packages/string-store/src/lib/types/Uint2.ts b/packages/string-store/src/lib/types/Uint2.ts new file mode 100644 index 0000000000..3ff49aeb34 --- /dev/null +++ b/packages/string-store/src/lib/types/Uint2.ts @@ -0,0 +1,11 @@ +import type { IType } from './base/IType'; + +export const Uint2Type: IType = { + serialize(buffer, value) { + buffer.writeInt2(value); + }, + deserialize(buffer, pointer) { + return buffer.readUint2(pointer); + }, + BIT_SIZE: 2 +}; diff --git a/packages/string-store/src/lib/types/Uint32.ts b/packages/string-store/src/lib/types/Uint32.ts new file mode 100644 index 0000000000..c8c3ff2696 --- /dev/null +++ b/packages/string-store/src/lib/types/Uint32.ts @@ -0,0 +1,11 @@ +import type { IType } from './base/IType'; + +export const Uint32Type: IType = { + serialize(buffer, value) { + buffer.writeInt32(value); + }, + deserialize(buffer, pointer) { + return buffer.readUint32(pointer); + }, + BIT_SIZE: 32 +}; diff --git a/packages/string-store/src/lib/types/Uint4.ts b/packages/string-store/src/lib/types/Uint4.ts new file mode 100644 index 0000000000..c09ecbba3f --- /dev/null +++ b/packages/string-store/src/lib/types/Uint4.ts @@ -0,0 +1,11 @@ +import type { IType } from './base/IType'; + +export const Uint4Type: IType = { + serialize(buffer, value) { + buffer.writeInt4(value); + }, + deserialize(buffer, pointer) { + return buffer.readUint4(pointer); + }, + BIT_SIZE: 4 +}; diff --git a/packages/string-store/src/lib/types/Uint64.ts b/packages/string-store/src/lib/types/Uint64.ts new file mode 100644 index 0000000000..5cae3c7bf4 --- /dev/null +++ b/packages/string-store/src/lib/types/Uint64.ts @@ -0,0 +1,11 @@ +import type { IType } from './base/IType'; + +export const Uint64Type: IType = { + serialize(buffer, value) { + buffer.writeInt64(value); + }, + deserialize(buffer, pointer) { + return buffer.readUint64(pointer); + }, + BIT_SIZE: 64 +}; diff --git a/packages/string-store/src/lib/types/Uint8.ts b/packages/string-store/src/lib/types/Uint8.ts new file mode 100644 index 0000000000..aa5366b5d0 --- /dev/null +++ b/packages/string-store/src/lib/types/Uint8.ts @@ -0,0 +1,11 @@ +import type { IType } from './base/IType'; + +export const Uint8Type: IType = { + serialize(buffer, value) { + buffer.writeInt8(value); + }, + deserialize(buffer, pointer) { + return buffer.readUint8(pointer); + }, + BIT_SIZE: 8 +}; diff --git a/packages/string-store/src/lib/types/index.ts b/packages/string-store/src/lib/types/index.ts new file mode 100644 index 0000000000..330b488cb3 --- /dev/null +++ b/packages/string-store/src/lib/types/index.ts @@ -0,0 +1,80 @@ +import { ArrayType } from './Array'; +import type { IType } from './base/IType'; +import { BigInt32Type } from './BigInt32'; +import { BigInt64Type } from './BigInt64'; +import { BigUint32Type } from './BigUint32'; +import { BigUint64Type } from './BigUint64'; +import { BitType } from './Bit'; +import { BooleanType } from './Boolean'; +import { FixedLengthArrayType } from './FixedLengthArray'; +import { Float32Type } from './Float32'; +import { Float64Type } from './Float64'; +import { Int16Type } from './Int16'; +import { Int2Type } from './Int2'; +import { Int32Type } from './Int32'; +import { Int4Type } from './Int4'; +import { Int64Type } from './Int64'; +import { Int8Type } from './Int8'; +import { SnowflakeType } from './Snowflake'; +import { StringType } from './String'; +import { Uint16Type } from './Uint16'; +import { Uint2Type } from './Uint2'; +import { Uint32Type } from './Uint32'; +import { Uint4Type } from './Uint4'; +import { Uint64Type } from './Uint64'; +import { Uint8Type } from './Uint8'; + +export const t = { + array: ArrayType, + bigInt32: BigInt32Type, + bigInt64: BigInt64Type, + bigUint32: BigUint32Type, + bigUint64: BigUint64Type, + bit: BitType, + boolean: BooleanType, + fixedLengthArray: FixedLengthArrayType, + float32: Float32Type, + float64: Float64Type, + int16: Int16Type, + int2: Int2Type, + int32: Int32Type, + int4: Int4Type, + int64: Int64Type, + int8: Int8Type, + snowflake: SnowflakeType, + string: StringType, + uint16: Uint16Type, + uint2: Uint2Type, + uint32: Uint32Type, + uint4: Uint4Type, + uint64: Uint64Type, + uint8: Uint8Type +}; + +export { + ArrayType, + BigInt32Type, + BigInt64Type, + BigUint32Type, + BigUint64Type, + BitType, + BooleanType, + FixedLengthArrayType, + Float32Type, + Float64Type, + Int16Type, + Int2Type, + Int32Type, + Int4Type, + Int64Type, + Int8Type, + SnowflakeType, + StringType, + Uint16Type, + Uint2Type, + Uint32Type, + Uint4Type, + Uint64Type, + Uint8Type, + type IType +}; diff --git a/packages/string-store/tests/lib/Schema.test.ts b/packages/string-store/tests/lib/Schema.test.ts index d6301c0bbe..2b5fa25c3d 100644 --- a/packages/string-store/tests/lib/Schema.test.ts +++ b/packages/string-store/tests/lib/Schema.test.ts @@ -1,23 +1,4 @@ -import { - BigInt32Type, - BigInt64Type, - BitType, - BooleanType, - Float32Type, - Float64Type, - Int16Type, - Int2Type, - Int32Type, - Int4Type, - Int64Type, - Int8Type, - Pointer, - Schema, - SnowflakeType, - StringType, - UnalignedUint16Array, - type IType -} from '../../src'; +import { Schema, SnowflakeType, StringType, t, UnalignedUint16Array, type IType } from '../../src'; describe('Schema', () => { test('GIVEN a new instance THEN it has the correct properties', () => { @@ -39,7 +20,7 @@ describe('Schema', () => { describe('types', () => { test('GIVEN a schema with a boolean property THEN it has the correct properties and types', () => { type Key = 'a'; - type Value = typeof BooleanType; + type Value = typeof t.boolean; type Entry = readonly [Key, Value]; const schema = new Schema(1).boolean('a'); @@ -47,13 +28,13 @@ describe('Schema', () => { expect(schema.bitSize).toBe(1); expect([...schema.keys()]).toEqual(['a']); - expect([...schema.values()]).toEqual([BooleanType]); - expect([...schema.entries()]).toEqual([['a', BooleanType]]); + expect([...schema.values()]).toEqual([t.boolean]); + expect([...schema.entries()]).toEqual([['a', t.boolean]]); }); test('GIVEN a schema with a bit property THEN it has the correct properties and types', () => { type Key = 'a'; - type Value = typeof BitType; + type Value = typeof t.bit; type Entry = readonly [Key, Value]; const schema = new Schema(1).bit('a'); @@ -61,13 +42,13 @@ describe('Schema', () => { expect(schema.bitSize).toBe(1); expect([...schema.keys()]).toEqual(['a']); - expect([...schema.values()]).toEqual([BitType]); - expect([...schema.entries()]).toEqual([['a', BitType]]); + expect([...schema.values()]).toEqual([t.bit]); + expect([...schema.entries()]).toEqual([['a', t.bit]]); }); test('GIVEN a schema with an int2 property THEN it has the correct properties and types', () => { type Key = 'a'; - type Value = typeof Int2Type; + type Value = typeof t.int2; type Entry = readonly [Key, Value]; const schema = new Schema(1).int2('a'); @@ -75,13 +56,27 @@ describe('Schema', () => { expect(schema.bitSize).toBe(2); expect([...schema.keys()]).toEqual(['a']); - expect([...schema.values()]).toEqual([Int2Type]); - expect([...schema.entries()]).toEqual([['a', Int2Type]]); + expect([...schema.values()]).toEqual([t.int2]); + expect([...schema.entries()]).toEqual([['a', t.int2]]); + }); + + test('GIVEN a schema with an uint2 property THEN it has the correct properties and types', () => { + type Key = 'a'; + type Value = typeof t.uint2; + type Entry = readonly [Key, Value]; + + const schema = new Schema(1).uint2('a'); + expectTypeOf>(schema); + expect(schema.bitSize).toBe(2); + + expect([...schema.keys()]).toEqual(['a']); + expect([...schema.values()]).toEqual([t.uint2]); + expect([...schema.entries()]).toEqual([['a', t.uint2]]); }); test('GIVEN a schema with an int4 property THEN it has the correct properties and types', () => { type Key = 'a'; - type Value = typeof Int4Type; + type Value = typeof t.int4; type Entry = readonly [Key, Value]; const schema = new Schema(1).int4('a'); @@ -89,13 +84,27 @@ describe('Schema', () => { expect(schema.bitSize).toBe(4); expect([...schema.keys()]).toEqual(['a']); - expect([...schema.values()]).toEqual([Int4Type]); - expect([...schema.entries()]).toEqual([['a', Int4Type]]); + expect([...schema.values()]).toEqual([t.int4]); + expect([...schema.entries()]).toEqual([['a', t.int4]]); + }); + + test('GIVEN a schema with an uint4 property THEN it has the correct properties and types', () => { + type Key = 'a'; + type Value = typeof t.uint4; + type Entry = readonly [Key, Value]; + + const schema = new Schema(1).uint4('a'); + expectTypeOf>(schema); + expect(schema.bitSize).toBe(4); + + expect([...schema.keys()]).toEqual(['a']); + expect([...schema.values()]).toEqual([t.uint4]); + expect([...schema.entries()]).toEqual([['a', t.uint4]]); }); test('GIVEN a schema with an int8 property THEN it has the correct properties and types', () => { type Key = 'a'; - type Value = typeof Int8Type; + type Value = typeof t.int8; type Entry = readonly [Key, Value]; const schema = new Schema(1).int8('a'); @@ -103,13 +112,27 @@ describe('Schema', () => { expect(schema.bitSize).toBe(8); expect([...schema.keys()]).toEqual(['a']); - expect([...schema.values()]).toEqual([Int8Type]); - expect([...schema.entries()]).toEqual([['a', Int8Type]]); + expect([...schema.values()]).toEqual([t.int8]); + expect([...schema.entries()]).toEqual([['a', t.int8]]); + }); + + test('GIVEN a schema with an uint8 property THEN it has the correct properties and types', () => { + type Key = 'a'; + type Value = typeof t.uint8; + type Entry = readonly [Key, Value]; + + const schema = new Schema(1).uint8('a'); + expectTypeOf>(schema); + expect(schema.bitSize).toBe(8); + + expect([...schema.keys()]).toEqual(['a']); + expect([...schema.values()]).toEqual([t.uint8]); + expect([...schema.entries()]).toEqual([['a', t.uint8]]); }); test('GIVEN a schema with an int16 property THEN it has the correct properties and types', () => { type Key = 'a'; - type Value = typeof Int16Type; + type Value = typeof t.int16; type Entry = readonly [Key, Value]; const schema = new Schema(1).int16('a'); @@ -117,13 +140,27 @@ describe('Schema', () => { expect(schema.bitSize).toBe(16); expect([...schema.keys()]).toEqual(['a']); - expect([...schema.values()]).toEqual([Int16Type]); - expect([...schema.entries()]).toEqual([['a', Int16Type]]); + expect([...schema.values()]).toEqual([t.int16]); + expect([...schema.entries()]).toEqual([['a', t.int16]]); + }); + + test('GIVEN a schema with an uint16 property THEN it has the correct properties and types', () => { + type Key = 'a'; + type Value = typeof t.uint16; + type Entry = readonly [Key, Value]; + + const schema = new Schema(1).uint16('a'); + expectTypeOf>(schema); + expect(schema.bitSize).toBe(16); + + expect([...schema.keys()]).toEqual(['a']); + expect([...schema.values()]).toEqual([t.uint16]); + expect([...schema.entries()]).toEqual([['a', t.uint16]]); }); test('GIVEN a schema with an int32 property THEN it has the correct properties and types', () => { type Key = 'a'; - type Value = typeof Int32Type; + type Value = typeof t.int32; type Entry = readonly [Key, Value]; const schema = new Schema(1).int32('a'); @@ -131,13 +168,27 @@ describe('Schema', () => { expect(schema.bitSize).toBe(32); expect([...schema.keys()]).toEqual(['a']); - expect([...schema.values()]).toEqual([Int32Type]); - expect([...schema.entries()]).toEqual([['a', Int32Type]]); + expect([...schema.values()]).toEqual([t.int32]); + expect([...schema.entries()]).toEqual([['a', t.int32]]); + }); + + test('GIVEN a schema with an uint32 property THEN it has the correct properties and types', () => { + type Key = 'a'; + type Value = typeof t.uint32; + type Entry = readonly [Key, Value]; + + const schema = new Schema(1).uint32('a'); + expectTypeOf>(schema); + expect(schema.bitSize).toBe(32); + + expect([...schema.keys()]).toEqual(['a']); + expect([...schema.values()]).toEqual([t.uint32]); + expect([...schema.entries()]).toEqual([['a', t.uint32]]); }); test('GIVEN a schema with an int64 property THEN it has the correct properties and types', () => { type Key = 'a'; - type Value = typeof Int64Type; + type Value = typeof t.int64; type Entry = readonly [Key, Value]; const schema = new Schema(1).int64('a'); @@ -145,13 +196,27 @@ describe('Schema', () => { expect(schema.bitSize).toBe(64); expect([...schema.keys()]).toEqual(['a']); - expect([...schema.values()]).toEqual([Int64Type]); - expect([...schema.entries()]).toEqual([['a', Int64Type]]); + expect([...schema.values()]).toEqual([t.int64]); + expect([...schema.entries()]).toEqual([['a', t.int64]]); + }); + + test('GIVEN a schema with an uint64 property THEN it has the correct properties and types', () => { + type Key = 'a'; + type Value = typeof t.uint64; + type Entry = readonly [Key, Value]; + + const schema = new Schema(1).uint64('a'); + expectTypeOf>(schema); + expect(schema.bitSize).toBe(64); + + expect([...schema.keys()]).toEqual(['a']); + expect([...schema.values()]).toEqual([t.uint64]); + expect([...schema.entries()]).toEqual([['a', t.uint64]]); }); test('GIVEN a schema with a bigInt32 property THEN it has the correct properties and types', () => { type Key = 'a'; - type Value = typeof BigInt32Type; + type Value = typeof t.bigInt32; type Entry = readonly [Key, Value]; const schema = new Schema(1).bigInt32('a'); @@ -159,13 +224,27 @@ describe('Schema', () => { expect(schema.bitSize).toBe(32); expect([...schema.keys()]).toEqual(['a']); - expect([...schema.values()]).toEqual([BigInt32Type]); - expect([...schema.entries()]).toEqual([['a', BigInt32Type]]); + expect([...schema.values()]).toEqual([t.bigInt32]); + expect([...schema.entries()]).toEqual([['a', t.bigInt32]]); + }); + + test('GIVEN a schema with a bigUint32 property THEN it has the correct properties and types', () => { + type Key = 'a'; + type Value = typeof t.bigUint32; + type Entry = readonly [Key, Value]; + + const schema = new Schema(1).bigUint32('a'); + expectTypeOf>(schema); + expect(schema.bitSize).toBe(32); + + expect([...schema.keys()]).toEqual(['a']); + expect([...schema.values()]).toEqual([t.bigUint32]); + expect([...schema.entries()]).toEqual([['a', t.bigUint32]]); }); test('GIVEN a schema with a bigInt64 property THEN it has the correct properties and types', () => { type Key = 'a'; - type Value = typeof BigInt64Type; + type Value = typeof t.bigInt64; type Entry = readonly [Key, Value]; const schema = new Schema(1).bigInt64('a'); @@ -173,13 +252,27 @@ describe('Schema', () => { expect(schema.bitSize).toBe(64); expect([...schema.keys()]).toEqual(['a']); - expect([...schema.values()]).toEqual([BigInt64Type]); - expect([...schema.entries()]).toEqual([['a', BigInt64Type]]); + expect([...schema.values()]).toEqual([t.bigInt64]); + expect([...schema.entries()]).toEqual([['a', t.bigInt64]]); + }); + + test('GIVEN a schema with a bigUint64 property THEN it has the correct properties and types', () => { + type Key = 'a'; + type Value = typeof t.bigUint64; + type Entry = readonly [Key, Value]; + + const schema = new Schema(1).bigUint64('a'); + expectTypeOf>(schema); + expect(schema.bitSize).toBe(64); + + expect([...schema.keys()]).toEqual(['a']); + expect([...schema.values()]).toEqual([t.bigUint64]); + expect([...schema.entries()]).toEqual([['a', t.bigUint64]]); }); test('GIVEN a schema with a float32 property THEN it has the correct properties and types', () => { type Key = 'a'; - type Value = typeof Float32Type; + type Value = typeof t.float32; type Entry = readonly [Key, Value]; const schema = new Schema(1).float32('a'); @@ -187,13 +280,13 @@ describe('Schema', () => { expect(schema.bitSize).toBe(32); expect([...schema.keys()]).toEqual(['a']); - expect([...schema.values()]).toEqual([Float32Type]); - expect([...schema.entries()]).toEqual([['a', Float32Type]]); + expect([...schema.values()]).toEqual([t.float32]); + expect([...schema.entries()]).toEqual([['a', t.float32]]); }); test('GIVEN a schema with a float64 property THEN it has the correct properties and types', () => { type Key = 'a'; - type Value = typeof Float64Type; + type Value = typeof t.float64; type Entry = readonly [Key, Value]; const schema = new Schema(1).float64('a'); @@ -201,8 +294,8 @@ describe('Schema', () => { expect(schema.bitSize).toBe(64); expect([...schema.keys()]).toEqual(['a']); - expect([...schema.values()]).toEqual([Float64Type]); - expect([...schema.entries()]).toEqual([['a', Float64Type]]); + expect([...schema.values()]).toEqual([t.float64]); + expect([...schema.entries()]).toEqual([['a', t.float64]]); }); test('GIVEN a schema with a string property THEN it has the correct properties and types', () => { @@ -234,7 +327,7 @@ describe('Schema', () => { }); test('GIVEN a schema with an array THEN it has the correct properties and types', () => { - const schema = new Schema(1).array('a', BitType); + const schema = new Schema(1).array('a', t.bit); expectTypeOf>(schema); expect(schema.bitSize).toBeNull(); @@ -250,7 +343,7 @@ describe('Schema', () => { }); test('GIVEN a schema with a fixed-length array THEN it has the correct properties and types', () => { - const schema = new Schema(1).fixedLengthArray('a', BitType, 2); + const schema = new Schema(1).fixedLengthArray('a', t.bit, 2); expectTypeOf>(schema); expect(schema.bitSize).toBe(2); diff --git a/packages/string-store/tests/lib/UnalignedUint16Array.test.ts b/packages/string-store/tests/lib/UnalignedUint16Array.test.ts index 001a44a6ed..1dfe2ac916 100644 --- a/packages/string-store/tests/lib/UnalignedUint16Array.test.ts +++ b/packages/string-store/tests/lib/UnalignedUint16Array.test.ts @@ -32,19 +32,21 @@ describe('UnalignedUint16Array', () => { expect(buffer.toString()).toBe('\u{3}'); // Verify that the data can be deserialized correctly - expect(buffer.readInt2(0)).toBe(3); + expect(buffer.readInt2(0)).toBe(-1); + expect(buffer.readUint2(0)).toBe(3); }); test('GIVEN an instance with a 4-bit integer written THEN it has the correct properties', () => { const buffer = new UnalignedUint16Array(10); - buffer.writeInt4(5); + buffer.writeInt4(15); expect(buffer.length).toBe(1); expect(buffer.bitLength).toBe(4); - expect(buffer.toArray()).toEqual(new Uint16Array([5])); - expect(buffer.toString()).toBe('\u{5}'); + expect(buffer.toArray()).toEqual(new Uint16Array([15])); + expect(buffer.toString()).toBe('\u{f}'); // Verify that the data can be deserialized correctly - expect(buffer.readInt4(0)).toBe(5); + expect(buffer.readInt4(0)).toBe(-1); + expect(buffer.readUint4(0)).toBe(15); }); test('GIVEN an instance with an 8-bit integer written THEN it has the correct properties', () => { @@ -56,7 +58,8 @@ describe('UnalignedUint16Array', () => { expect(buffer.toString()).toBe('\u{ff}'); // Verify that the data can be deserialized correctly - expect(buffer.readInt8(0)).toBe(255); + expect(buffer.readInt8(0)).toBe(-1); + expect(buffer.readUint8(0)).toBe(255); }); test('GIVEN an instance with a 16-bit integer written THEN it has the correct properties', () => { @@ -68,7 +71,8 @@ describe('UnalignedUint16Array', () => { expect(buffer.toString()).toBe('\u{ffff}'); // Verify that the data can be deserialized correctly - expect(buffer.readInt16(0)).toBe(65535); + expect(buffer.readInt16(0)).toBe(-1); + expect(buffer.readUint16(0)).toBe(65535); }); test('GIVEN an instance with a 32-bit integer written THEN it has the correct properties', () => { @@ -80,7 +84,8 @@ describe('UnalignedUint16Array', () => { expect(buffer.toString()).toBe('\u{ffff}\u{ffff}'); // Verify that the data can be deserialized correctly - expect(buffer.readInt32(0)).toBe(4294967295); + expect(buffer.readInt32(0)).toBe(-1); + expect(buffer.readUint32(0)).toBe(4294967295); }); test('GIVEN an instance with a 64-bit integer written THEN it has the correct properties', () => { @@ -93,6 +98,7 @@ describe('UnalignedUint16Array', () => { // Verify that the data can be deserialized correctly expect(buffer.readInt64(0)).toBe(3058204829589); + expect(buffer.readUint64(0)).toBe(3058204829589); }); test('GIVEN an instance with a 32-bit big integer written THEN it has the correct properties', () => { @@ -104,7 +110,8 @@ describe('UnalignedUint16Array', () => { expect(buffer.toString()).toBe('\u{ffff}\u{ffff}'); // Verify that the data can be deserialized correctly - expect(buffer.readBigInt32(0)).toBe(4294967295n); + expect(buffer.readBigInt32(0)).toBe(-1n); + expect(buffer.readBigUint32(0)).toBe(4294967295n); }); test('GIVEN an instance with a 64-bit big integer written THEN it has the correct properties', () => { @@ -116,7 +123,8 @@ describe('UnalignedUint16Array', () => { expect(buffer.toString()).toBe('\u{ffff}\u{ffff}\u{ffff}\u{ffff}'); // Verify that the data can be deserialized correctly - expect(buffer.readBigInt64(0)).toBe(18446744073709551615n); + expect(buffer.readBigInt64(0)).toBe(-1n); + expect(buffer.readBigUint64(0)).toBe(18446744073709551615n); }); test('GIVEN an instance with a 32-bit float written THEN it has the correct properties', () => { diff --git a/packages/string-store/tests/lib/types.test.ts b/packages/string-store/tests/lib/types.test.ts index 6bc9bc00a7..46b11c9adf 100644 --- a/packages/string-store/tests/lib/types.test.ts +++ b/packages/string-store/tests/lib/types.test.ts @@ -1,27 +1,8 @@ -import { - ArrayType, - BigInt32Type, - BigInt64Type, - BitType, - BooleanType, - FixedLengthArrayType, - Float32Type, - Float64Type, - Int16Type, - Int2Type, - Int32Type, - Int4Type, - Int64Type, - Int8Type, - Pointer, - SnowflakeType, - StringType, - UnalignedUint16Array -} from '../../src'; +import { Pointer, SnowflakeType, StringType, t, UnalignedUint16Array } from '../../src'; describe('types', () => { describe('Boolean', () => { - const type = BooleanType; + const type = t.boolean; test('GIVEN type THEN it has the correct properties', () => { expect(type.BIT_SIZE).toBe(1); @@ -38,7 +19,7 @@ describe('types', () => { }); describe('Bit', () => { - const type = BitType; + const type = t.bit; test('GIVEN type THEN it has the correct properties', () => { expect(type.BIT_SIZE).toBe(1); @@ -55,143 +36,285 @@ describe('types', () => { }); describe('Int2', () => { - const type = Int2Type; + const type = t.int2; test('GIVEN type THEN it has the correct properties', () => { expect(type.BIT_SIZE).toBe(2); }); - test('GIVEN a buffer THEN it serializes and deserializes correctly', () => { + test.each([-2, -1, 0, -1])('GIVEN a buffer THEN it serializes and deserializes correctly', (value) => { const buffer = new UnalignedUint16Array(10); - type.serialize(buffer, 3); + type.serialize(buffer, value); + + const deserialized = type.deserialize(buffer, new Pointer()); + expect(deserialized).toEqual(value); + }); + }); + + describe('Uint2', () => { + const type = t.uint2; + + test('GIVEN type THEN it has the correct properties', () => { + expect(type.BIT_SIZE).toBe(2); + }); + + test.each([0, 1, 2, 3])('GIVEN a buffer THEN it serializes and deserializes correctly', (value) => { + const buffer = new UnalignedUint16Array(10); + + type.serialize(buffer, value); const deserialized = type.deserialize(buffer, new Pointer()); - expect(deserialized).toEqual(3); + expect(deserialized).toEqual(value); }); }); describe('Int4', () => { - const type = Int4Type; + const type = t.int4; test('GIVEN type THEN it has the correct properties', () => { expect(type.BIT_SIZE).toBe(4); }); - test('GIVEN a buffer THEN it serializes and deserializes correctly', () => { + test.each([-8, -6, 0, 7])('GIVEN a buffer THEN it serializes and deserializes correctly', (value) => { const buffer = new UnalignedUint16Array(10); - type.serialize(buffer, 5); + type.serialize(buffer, value); + + const deserialized = type.deserialize(buffer, new Pointer()); + expect(deserialized).toEqual(value); + }); + }); + + describe('Uint4', () => { + const type = t.uint4; + + test('GIVEN type THEN it has the correct properties', () => { + expect(type.BIT_SIZE).toBe(4); + }); + + test.each([0, 1, 14, 15])('GIVEN a buffer THEN it serializes and deserializes correctly', (value) => { + const buffer = new UnalignedUint16Array(10); + + type.serialize(buffer, value); const deserialized = type.deserialize(buffer, new Pointer()); - expect(deserialized).toEqual(5); + expect(deserialized).toEqual(value); }); }); describe('Int8', () => { - const type = Int8Type; + const type = t.int8; test('GIVEN type THEN it has the correct properties', () => { expect(type.BIT_SIZE).toBe(8); }); - test('GIVEN a buffer THEN it serializes and deserializes correctly', () => { + test.each([-128, -100, 0, 127])('GIVEN a buffer THEN it serializes and deserializes correctly', (value) => { const buffer = new UnalignedUint16Array(10); - type.serialize(buffer, 255); + type.serialize(buffer, value); + + const deserialized = type.deserialize(buffer, new Pointer()); + expect(deserialized).toEqual(value); + }); + }); + + describe('Uint8', () => { + const type = t.uint8; + + test('GIVEN type THEN it has the correct properties', () => { + expect(type.BIT_SIZE).toBe(8); + }); + + test.each([0, 100, 200, 255])('GIVEN a buffer THEN it serializes and deserializes correctly', (value) => { + const buffer = new UnalignedUint16Array(10); + + type.serialize(buffer, value); const deserialized = type.deserialize(buffer, new Pointer()); - expect(deserialized).toEqual(255); + expect(deserialized).toEqual(value); }); }); describe('Int16', () => { - const type = Int16Type; + const type = t.int16; test('GIVEN type THEN it has the correct properties', () => { expect(type.BIT_SIZE).toBe(16); }); - test('GIVEN a buffer THEN it serializes and deserializes correctly', () => { + test.each([-32768, -100, 10, 32767])('GIVEN a buffer THEN it serializes and deserializes correctly', (value) => { const buffer = new UnalignedUint16Array(10); - type.serialize(buffer, 65535); + type.serialize(buffer, value); + + const deserialized = type.deserialize(buffer, new Pointer()); + expect(deserialized).toEqual(value); + }); + }); + + describe('Uint16', () => { + const type = t.uint16; + + test('GIVEN type THEN it has the correct properties', () => { + expect(type.BIT_SIZE).toBe(16); + }); + + test.each([0, 2500, 30000, 65535])('GIVEN a buffer THEN it serializes and deserializes correctly', (value) => { + const buffer = new UnalignedUint16Array(10); + + type.serialize(buffer, value); const deserialized = type.deserialize(buffer, new Pointer()); - expect(deserialized).toEqual(65535); + expect(deserialized).toEqual(value); }); }); describe('Int32', () => { - const type = Int32Type; + const type = t.int32; test('GIVEN type THEN it has the correct properties', () => { expect(type.BIT_SIZE).toBe(32); }); - test('GIVEN a buffer THEN it serializes and deserializes correctly', () => { + test.each([-2_147_483_648, -52100, 420, 2_147_483_647])('GIVEN a buffer THEN it serializes and deserializes correctly', (value) => { const buffer = new UnalignedUint16Array(10); - type.serialize(buffer, 4294967295); + type.serialize(buffer, value); + + const deserialized = type.deserialize(buffer, new Pointer()); + expect(deserialized).toEqual(value); + }); + }); + + describe('Uint32', () => { + const type = t.uint32; + + test('GIVEN type THEN it has the correct properties', () => { + expect(type.BIT_SIZE).toBe(32); + }); + + test.each([0, 420, 4_294_967_295])('GIVEN a buffer THEN it serializes and deserializes correctly', (value) => { + const buffer = new UnalignedUint16Array(10); + + type.serialize(buffer, value); const deserialized = type.deserialize(buffer, new Pointer()); - expect(deserialized).toEqual(4294967295); + expect(deserialized).toEqual(value); }); }); describe('Int64', () => { - const type = Int64Type; + const type = t.int64; test('GIVEN type THEN it has the correct properties', () => { expect(type.BIT_SIZE).toBe(64); }); - test('GIVEN a buffer THEN it serializes and deserializes correctly', () => { + test.each([-9_007_199_254_740_991, 420_000, 9_007_199_254_740_991])( + 'GIVEN a buffer THEN it serializes and deserializes correctly', + (value) => { + const buffer = new UnalignedUint16Array(10); + + type.serialize(buffer, value); + + const deserialized = type.deserialize(buffer, new Pointer()); + expect(deserialized).toEqual(value); + } + ); + }); + + describe('Uint64', () => { + const type = t.uint64; + + test('GIVEN type THEN it has the correct properties', () => { + expect(type.BIT_SIZE).toBe(64); + }); + + test.each([0, 640_000_420, 9_007_199_254_740_991])('GIVEN a buffer THEN it serializes and deserializes correctly', (value) => { const buffer = new UnalignedUint16Array(10); - type.serialize(buffer, 3058204829589); + type.serialize(buffer, value); const deserialized = type.deserialize(buffer, new Pointer()); - expect(deserialized).toEqual(3058204829589); + expect(deserialized).toEqual(value); }); }); describe('BigInt32', () => { - const type = BigInt32Type; + const type = t.bigInt32; test('GIVEN type THEN it has the correct properties', () => { expect(type.BIT_SIZE).toBe(32); }); - test('GIVEN a buffer THEN it serializes and deserializes correctly', () => { + test.each([-2_147_483_648n, -52100n, 420n, 2_147_483_647n])('GIVEN a buffer THEN it serializes and deserializes correctly', (value) => { const buffer = new UnalignedUint16Array(10); - type.serialize(buffer, 4294967295n); + type.serialize(buffer, value); + + const deserialized = type.deserialize(buffer, new Pointer()); + expect(deserialized).toEqual(value); + }); + }); + + describe('BigUint32', () => { + const type = t.bigUint32; + + test('GIVEN type THEN it has the correct properties', () => { + expect(type.BIT_SIZE).toBe(32); + }); + + test.each([0n, 420n, 4_294_967_295n])('GIVEN a buffer THEN it serializes and deserializes correctly', (value) => { + const buffer = new UnalignedUint16Array(10); + + type.serialize(buffer, value); const deserialized = type.deserialize(buffer, new Pointer()); - expect(deserialized).toEqual(4294967295n); + expect(deserialized).toEqual(value); }); }); describe('BigInt64', () => { - const type = BigInt64Type; + const type = t.bigInt64; test('GIVEN type THEN it has the correct properties', () => { expect(type.BIT_SIZE).toBe(64); }); - test('GIVEN a buffer THEN it serializes and deserializes correctly', () => { + test.each([-9_223_372_036_854_775_808n, -420n, 420n, 9_223_372_036_854_775_807n])( + 'GIVEN a buffer THEN it serializes and deserializes correctly', + (value) => { + const buffer = new UnalignedUint16Array(10); + + type.serialize(buffer, value); + + const deserialized = type.deserialize(buffer, new Pointer()); + expect(deserialized).toEqual(value); + } + ); + }); + + describe('BigUint64', () => { + const type = t.bigUint64; + + test('GIVEN type THEN it has the correct properties', () => { + expect(type.BIT_SIZE).toBe(64); + }); + + test.each([0n, 18_446_744_073_709_551_615n])('GIVEN a buffer THEN it serializes and deserializes correctly', (value) => { const buffer = new UnalignedUint16Array(10); - type.serialize(buffer, 18446744073709551615n); + type.serialize(buffer, value); const deserialized = type.deserialize(buffer, new Pointer()); - expect(deserialized).toEqual(18446744073709551615n); + expect(deserialized).toEqual(value); }); }); describe('Float32', () => { - const type = Float32Type; + const type = t.float32; test('GIVEN type THEN it has the correct properties', () => { expect(type.BIT_SIZE).toBe(32); @@ -208,7 +331,7 @@ describe('types', () => { }); describe('Float64', () => { - const type = Float64Type; + const type = t.float64; test('GIVEN type THEN it has the correct properties', () => { expect(type.BIT_SIZE).toBe(64); @@ -225,7 +348,7 @@ describe('types', () => { }); describe('Array(Bit)', () => { - const type = ArrayType(BitType); + const type = t.array(t.bit); test('GIVEN type THEN it has the correct properties', () => { expect(type.BIT_SIZE).toBeNull(); @@ -251,7 +374,7 @@ describe('types', () => { }); describe('FixedLengthArray(Bit)', () => { - const type = FixedLengthArrayType(BitType, 2); + const type = t.fixedLengthArray(t.bit, 2); test('GIVEN type THEN it has the correct properties', () => { expect(type.BIT_SIZE).toBe(2);