diff --git a/.changeset/strange-ducks-float.md b/.changeset/strange-ducks-float.md new file mode 100644 index 0000000000..39ff32f010 --- /dev/null +++ b/.changeset/strange-ducks-float.md @@ -0,0 +1,13 @@ +--- +"@latticexyz/common": minor +--- + +`TableId.toHex()` now truncates name/namespace to 16 bytes each, to properly fit into a `bytes32` hex string. + +Also adds a few utils we'll need in the indexer: + +- `bigIntMin` is similar to `Math.min` but for `bigint`s +- `bigIntMax` is similar to `Math.max` but for `bigint`s +- `bigIntSort` for sorting an array of `bigint`s +- `chunk` to split an array into chunks +- `wait` returns a `Promise` that resolves after specified number of milliseconds diff --git a/packages/common/src/TableId.test.ts b/packages/common/src/TableId.test.ts index d7ce5df7ef..74bc35ec18 100644 --- a/packages/common/src/TableId.test.ts +++ b/packages/common/src/TableId.test.ts @@ -9,14 +9,20 @@ describe("TableId", () => { ); }); - it("throws when converting namespaces >16 bytes", () => { + it("truncates namespaces >16 bytes", () => { const tableId = new TableId("AVeryLongNamespace", "name"); - expect(() => tableId.toHex()).toThrow("Size cannot exceed 16 bytes. Given size: 18 bytes."); + expect(tableId.toHex()).toMatchInlineSnapshot( + '"0x41566572794c6f6e674e616d657370616e616d65000000000000000000000000"' + ); + expect(TableId.fromHex(tableId.toHex()).namespace).toMatchInlineSnapshot('"AVeryLongNamespa"'); }); - it("throws when converting names >16 bytes", () => { + it("truncates names >16 bytes", () => { const tableId = new TableId("namespace", "AnUnnecessarilyLongName"); - expect(() => tableId.toHex()).toThrow("Size cannot exceed 16 bytes. Given size: 23 bytes."); + expect(tableId.toHex()).toMatchInlineSnapshot( + '"0x6e616d65737061636500000000000000416e556e6e65636573736172696c794c"' + ); + expect(TableId.fromHex(tableId.toHex()).name).toMatchInlineSnapshot('"AnUnnecessarilyL"'); }); it("can convert from hex string", () => { diff --git a/packages/common/src/TableId.ts b/packages/common/src/TableId.ts index afde342885..e67c787f4f 100644 --- a/packages/common/src/TableId.ts +++ b/packages/common/src/TableId.ts @@ -1,12 +1,12 @@ import { Hex, stringToHex, hexToString, sliceHex, concatHex } from "viem"; export class TableId { - namespace: string; - name: string; + readonly namespace: string; + readonly name: string; constructor(namespace: string, name: string) { - this.namespace = namespace; - this.name = name; + this.namespace = namespace.substring(0, 16); + this.name = name.substring(0, 16); } toString(): string { diff --git a/packages/common/src/utils/bigIntMax.ts b/packages/common/src/utils/bigIntMax.ts new file mode 100644 index 0000000000..9a1a4a6367 --- /dev/null +++ b/packages/common/src/utils/bigIntMax.ts @@ -0,0 +1,3 @@ +export function bigIntMax(...args: bigint[]): bigint { + return args.reduce((m, e) => (e > m ? e : m)); +} diff --git a/packages/common/src/utils/bigIntMin.ts b/packages/common/src/utils/bigIntMin.ts new file mode 100644 index 0000000000..75e6c83792 --- /dev/null +++ b/packages/common/src/utils/bigIntMin.ts @@ -0,0 +1,3 @@ +export function bigIntMin(...args: bigint[]): bigint { + return args.reduce((m, e) => (e < m ? e : m)); +} diff --git a/packages/common/src/utils/bigIntSort.ts b/packages/common/src/utils/bigIntSort.ts new file mode 100644 index 0000000000..d87cb3d843 --- /dev/null +++ b/packages/common/src/utils/bigIntSort.ts @@ -0,0 +1,3 @@ +export function bigIntSort(a: bigint, b: bigint): -1 | 0 | 1 { + return a < b ? -1 : a > b ? 1 : 0; +} diff --git a/packages/common/src/utils/chunk.test.ts b/packages/common/src/utils/chunk.test.ts new file mode 100644 index 0000000000..b159bc9950 --- /dev/null +++ b/packages/common/src/utils/chunk.test.ts @@ -0,0 +1,94 @@ +import { describe, it, expect } from "vitest"; +import { chunk } from "./chunk"; + +describe("chunk", () => { + it("splits an array into chunks", () => { + expect(Array.from(chunk([1, 2, 3, 4, 5, 6, 7, 8, 9], 3))).toMatchInlineSnapshot(` + [ + [ + 1, + 2, + 3, + ], + [ + 4, + 5, + 6, + ], + [ + 7, + 8, + 9, + ], + ] + `); + + expect(Array.from(chunk([1, 2, 3, 4, 5, 6, 7, 8, 9], 5))).toMatchInlineSnapshot(` + [ + [ + 1, + 2, + 3, + 4, + 5, + ], + [ + 6, + 7, + 8, + 9, + ], + ] + `); + + expect(Array.from(chunk([1, 2, 3, 4, 5, 6, 7, 8, 9], 8))).toMatchInlineSnapshot(` + [ + [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + ], + [ + 9, + ], + ] + `); + + expect(Array.from(chunk([1, 2, 3, 4, 5, 6, 7, 8, 9], 9))).toMatchInlineSnapshot(` + [ + [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + ], + ] + `); + + expect(Array.from(chunk([1, 2, 3, 4, 5, 6, 7, 8, 9], 10))).toMatchInlineSnapshot(` + [ + [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + ], + ] + `); + }); +}); diff --git a/packages/common/src/utils/chunk.ts b/packages/common/src/utils/chunk.ts new file mode 100644 index 0000000000..796b8ebca7 --- /dev/null +++ b/packages/common/src/utils/chunk.ts @@ -0,0 +1,5 @@ +export function* chunk(arr: T[], n: number): Generator { + for (let i = 0; i < arr.length; i += n) { + yield arr.slice(i, i + n); + } +} diff --git a/packages/common/src/utils/index.ts b/packages/common/src/utils/index.ts index b2231070d3..7ba406179b 100644 --- a/packages/common/src/utils/index.ts +++ b/packages/common/src/utils/index.ts @@ -1,4 +1,9 @@ export * from "./assertExhaustive"; +export * from "./bigIntMax"; +export * from "./bigIntMin"; +export * from "./bigIntSort"; +export * from "./chunk"; export * from "./curry"; export * from "./isDefined"; export * from "./isNotNull"; +export * from "./wait"; diff --git a/packages/common/src/utils/wait.ts b/packages/common/src/utils/wait.ts new file mode 100644 index 0000000000..7b18fbe5dd --- /dev/null +++ b/packages/common/src/utils/wait.ts @@ -0,0 +1,3 @@ +export function wait(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)); +}