From ac5e801bd088397ccf278c96f08073c6f676b5f8 Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Fri, 13 Sep 2024 07:30:54 +0500 Subject: [PATCH 01/30] feat: rework typescript generator & add suite --- Cargo.lock | 7 + Cargo.toml | 1 + rustfmt.toml | 1 + serde-generate/runtime/typescript/bincode.ts | 29 + .../typescript/bincode/bincodeDeserializer.ts | 23 - .../typescript/bincode/bincodeSerializer.ts | 20 - .../runtime/typescript/bincode/mod.ts | 7 - serde-generate/runtime/typescript/serde.ts | 399 ++++++ .../typescript/serde/binaryDeserializer.ts | 145 -- .../typescript/serde/binarySerializer.ts | 176 --- .../runtime/typescript/serde/deserializer.ts | 53 - .../runtime/typescript/serde/mod.ts | 10 - .../runtime/typescript/serde/serializer.ts | 52 - .../runtime/typescript/serde/types.ts | 27 - serde-generate/src/common.rs | 41 +- serde-generate/src/typescript.rs | 1193 +++++++---------- suite/typescript/.gitignore | 3 + suite/typescript/Cargo.lock | 366 +++++ suite/typescript/Cargo.toml | 14 + suite/typescript/flake.lock | 458 +++++++ suite/typescript/flake.nix | 71 + suite/typescript/package.json | 15 + suite/typescript/pnpm-lock.yaml | 453 +++++++ suite/typescript/readme.md | 30 + suite/typescript/rs/generator.rs | 74 + suite/typescript/schema-proto/main.proto | 52 + suite/typescript/ts/bench.ts | 60 + suite/typescript/ts/bincode/bincode.ts | 1 + suite/typescript/ts/bincode/registry.ts | 162 +++ suite/typescript/ts/bincode/serde.ts | 1 + suite/typescript/ts/data.ts | 31 + suite/typescript/ts/proto/main.ts | 668 +++++++++ suite/typescript/ts/test.ts | 67 + suite/typescript/tsconfig.json | 10 + tsconfig.json | 13 + 35 files changed, 3482 insertions(+), 1251 deletions(-) create mode 100644 serde-generate/runtime/typescript/bincode.ts delete mode 100644 serde-generate/runtime/typescript/bincode/bincodeDeserializer.ts delete mode 100644 serde-generate/runtime/typescript/bincode/bincodeSerializer.ts delete mode 100644 serde-generate/runtime/typescript/bincode/mod.ts create mode 100644 serde-generate/runtime/typescript/serde.ts delete mode 100644 serde-generate/runtime/typescript/serde/binaryDeserializer.ts delete mode 100644 serde-generate/runtime/typescript/serde/binarySerializer.ts delete mode 100644 serde-generate/runtime/typescript/serde/deserializer.ts delete mode 100644 serde-generate/runtime/typescript/serde/mod.ts delete mode 100644 serde-generate/runtime/typescript/serde/serializer.ts delete mode 100644 serde-generate/runtime/typescript/serde/types.ts create mode 100644 suite/typescript/.gitignore create mode 100644 suite/typescript/Cargo.lock create mode 100644 suite/typescript/Cargo.toml create mode 100644 suite/typescript/flake.lock create mode 100644 suite/typescript/flake.nix create mode 100644 suite/typescript/package.json create mode 100644 suite/typescript/pnpm-lock.yaml create mode 100644 suite/typescript/readme.md create mode 100644 suite/typescript/rs/generator.rs create mode 100644 suite/typescript/schema-proto/main.proto create mode 100644 suite/typescript/ts/bench.ts create mode 120000 suite/typescript/ts/bincode/bincode.ts create mode 100644 suite/typescript/ts/bincode/registry.ts create mode 120000 suite/typescript/ts/bincode/serde.ts create mode 100644 suite/typescript/ts/data.ts create mode 100644 suite/typescript/ts/proto/main.ts create mode 100644 suite/typescript/ts/test.ts create mode 100644 suite/typescript/tsconfig.json create mode 100644 tsconfig.json diff --git a/Cargo.lock b/Cargo.lock index 2c491dc46..a4f930d99 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -966,6 +966,12 @@ dependencies = [ "either", ] +[[package]] +name = "indoc" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" + [[package]] name = "itoa" version = "1.0.11" @@ -1696,6 +1702,7 @@ dependencies = [ "heck 0.3.3", "hex", "include_dir", + "indoc", "lazy_static", "maplit", "phf", diff --git a/Cargo.toml b/Cargo.toml index 18b6e7cda..8d9997d15 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ members = [ "serde-reflection", "serde-generate", "serde-generate-bin", + "suite/*" ] resolver = "2" diff --git a/rustfmt.toml b/rustfmt.toml index 80eeb4054..d6a77951c 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,2 +1,3 @@ edition = "2018" use_field_init_shorthand = true +disable_all_formatting = true \ No newline at end of file diff --git a/serde-generate/runtime/typescript/bincode.ts b/serde-generate/runtime/typescript/bincode.ts new file mode 100644 index 000000000..5ff8aeefc --- /dev/null +++ b/serde-generate/runtime/typescript/bincode.ts @@ -0,0 +1,29 @@ +import { BinaryReader, BinaryWriter } from "./serde"; + +export class BincodeReader extends BinaryReader { + readLength() { + return Number(this.readU64()) + } + + public readVariantIndex() { + return this.readU32() + } + + checkThatKeySlicesAreIncreasing(key1: [number, number], key2: [number, number]) { + return + } +} + +export class BincodeWriter extends BinaryWriter { + writeLength(value: number) { + this.writeU64(value) + } + + public writeVariantIndex(value: number) { + this.writeU32(value) + } + + public sortMapEntries(offsets: number[]) { + return + } +} diff --git a/serde-generate/runtime/typescript/bincode/bincodeDeserializer.ts b/serde-generate/runtime/typescript/bincode/bincodeDeserializer.ts deleted file mode 100644 index f8de97ed8..000000000 --- a/serde-generate/runtime/typescript/bincode/bincodeDeserializer.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates - * SPDX-License-Identifier: MIT OR Apache-2.0 - */ - -import { BinaryDeserializer } from "../serde/binaryDeserializer.ts"; - -export class BincodeDeserializer extends BinaryDeserializer { - deserializeLen(): number { - return Number(this.deserializeU64()); - } - - public deserializeVariantIndex(): number { - return this.deserializeU32(); - } - - checkThatKeySlicesAreIncreasing( - key1: [number, number], - key2: [number, number], - ): void { - return; - } -} diff --git a/serde-generate/runtime/typescript/bincode/bincodeSerializer.ts b/serde-generate/runtime/typescript/bincode/bincodeSerializer.ts deleted file mode 100644 index dce23a19f..000000000 --- a/serde-generate/runtime/typescript/bincode/bincodeSerializer.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates - * SPDX-License-Identifier: MIT OR Apache-2.0 - */ - -import { BinarySerializer } from "../serde/binarySerializer.ts"; - -export class BincodeSerializer extends BinarySerializer { - serializeLen(value: number): void { - this.serializeU64(value); - } - - public serializeVariantIndex(value: number): void { - this.serializeU32(value); - } - - public sortMapEntries(offsets: number[]): void { - return; - } -} diff --git a/serde-generate/runtime/typescript/bincode/mod.ts b/serde-generate/runtime/typescript/bincode/mod.ts deleted file mode 100644 index 302340333..000000000 --- a/serde-generate/runtime/typescript/bincode/mod.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates - * SPDX-License-Identifier: MIT OR Apache-2.0 - */ - -export { BincodeSerializer } from "./bincodeSerializer.ts"; -export { BincodeDeserializer } from "./bincodeDeserializer.ts"; diff --git a/serde-generate/runtime/typescript/serde.ts b/serde-generate/runtime/typescript/serde.ts new file mode 100644 index 000000000..3345e6397 --- /dev/null +++ b/serde-generate/runtime/typescript/serde.ts @@ -0,0 +1,399 @@ +export type Optional = T | null +export type Seq = T[] +export type Tuple = T +export type ListTuple = Tuple[] +export type Map = globalThis.Map + +export type unit = null +export type bool = boolean +export type i8 = number +export type i16 = number +export type i32 = number +export type i64 = bigint +export type i128 = bigint +export type u8 = number +export type u16 = number +export type u32 = number +export type u64 = bigint +export type u128 = bigint +export type f32 = number +export type f64 = number +export type char = string +export type str = string +export type bytes = Uint8Array + +export type WrapperOfCase = T extends { $: infer _U extends K } ? T : never + +export interface Reader { + readString(): string + readBytes(): Uint8Array + readBool(): boolean + readUnit(): null + readChar(): string + readF32(): number + readF64(): number + readU8(): number + readU16(): number + readU32(): number + readU64(): bigint + readU128(): bigint + readI8(): number + readI16(): number + readI32(): number + readI64(): bigint + readI128(): bigint + readLength(): number + readVariantIndex(): number + readOptionTag(): boolean + readMap(readKey: () => K, readValue: () => V): Map, + checkThatKeySlicesAreIncreasing(key1: [number, number], key2: [number, number]): void +} + +export interface Writer { + writeString(value: string): void + writeBytes(value: Uint8Array): void + writeBool(value: boolean): void + writeUnit(value: null): void + writeChar(value: string): void + writeF32(value: number): void + writeF64(value: number): void + writeU8(value: number): void + writeU16(value: number): void + writeU32(value: number): void + writeU64(value: bigint | number): void + writeU128(value: bigint | number): void + writeI8(value: number): void + writeI16(value: number): void + writeI32(value: number): void + writeI64(value: bigint | number): void + writeI128(value: bigint | number): void + writeLength(value: number): void + writeVariantIndex(value: number): void + writeOptionTag(value: boolean): void + writeMap(value: Map, writeKey: (key: K) => void, writeValue: (value: V) => void): void + getBytes(): Uint8Array + sortMapEntries(offsets: number[]): void +} + + +export abstract class BinaryWriter implements Writer { + public static readonly BIG_32: bigint = BigInt(32) + public static readonly BIG_64: bigint = BigInt(64) + public static readonly BIG_32Fs: bigint = BigInt("429967295") + public static readonly BIG_64Fs: bigint = BigInt("18446744073709551615") + public static readonly textEncoder = new TextEncoder() + + public buffer: ArrayBuffer + public offset: number + + constructor() { + this.buffer = new ArrayBuffer(64) + this.offset = 0 + } + + private ensureBufferWillHandleSize(bytes: number) { + const wishSize = this.offset + bytes + if (wishSize > this.buffer.byteLength) { + let newBufferLength = this.buffer.byteLength + while (newBufferLength < wishSize) newBufferLength *= 2 + newBufferLength = Math.max(wishSize, newBufferLength) + + // this.buffer.resize(newBufferLength) + const newBuffer = new ArrayBuffer(newBufferLength) + new Uint8Array(newBuffer).set(new Uint8Array(this.buffer)) + this.buffer = newBuffer + } + } + + protected write(values: Uint8Array) { + this.ensureBufferWillHandleSize(values.length) + new Uint8Array(this.buffer, this.offset).set(values) + this.offset += values.length + } + + abstract writeLength(value: number): void + abstract writeVariantIndex(value: number): void + abstract sortMapEntries(offsets: number[]): void + + public writeString(value: string) { + const bytes = value.length * 3 + 8 + this.ensureBufferWillHandleSize(bytes) + const { written } = BinaryWriter.textEncoder.encodeInto(value, new Uint8Array(this.buffer, this.offset + 8)) + this.writeU64(written) + this.offset += written + } + + public writeBytes(value: Uint8Array) { + this.writeLength(value.length) + this.write(value) + } + + public writeBool(value: boolean) { + const byteValue = value ? 1 : 0 + this.write(new Uint8Array([byteValue])) + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars,@typescript-eslint/explicit-module-boundary-types + public writeUnit(_value: null) { + return + } + + private writeWithFunction(fn: (byteOffset: number, value: number, littleEndian: boolean) => void, bytesLength: number, value: number) { + this.ensureBufferWillHandleSize(bytesLength) + const dv = new DataView(this.buffer, this.offset) + fn.apply(dv, [0, value, true]) + this.offset += bytesLength + } + + public writeU8(value: number) { + this.write(new Uint8Array([value])) + } + + public writeU16(value: number) { + this.writeWithFunction(DataView.prototype.setUint16, 2, value) + } + + public writeU32(value: number) { + this.writeWithFunction(DataView.prototype.setUint32, 4, value) + } + + public writeU64(value: bigint | number) { + const low = BigInt(value) & BinaryWriter.BIG_32Fs + const high = BigInt(value) >> BinaryWriter.BIG_32 + + // write little endian number + this.writeU32(Number(low)) + this.writeU32(Number(high)) + } + + public writeU128(value: bigint | number) { + const low = BigInt(value) & BinaryWriter.BIG_64Fs + const high = BigInt(value) >> BinaryWriter.BIG_64 + + // write little endian number + this.writeU64(low) + this.writeU64(high) + } + + public writeI8(value: number) { + const bytes = 1 + this.ensureBufferWillHandleSize(bytes) + new DataView(this.buffer, this.offset).setInt8(0, value) + this.offset += bytes + } + + public writeI16(value: number) { + const bytes = 2 + this.ensureBufferWillHandleSize(bytes) + new DataView(this.buffer, this.offset).setInt16(0, value, true) + this.offset += bytes + } + + public writeI32(value: number) { + const bytes = 4 + this.ensureBufferWillHandleSize(bytes) + new DataView(this.buffer, this.offset).setInt32(0, value, true) + this.offset += bytes + } + + public writeI64(value: bigint | number) { + const low = BigInt(value) & BinaryWriter.BIG_32Fs + const high = BigInt(value) >> BinaryWriter.BIG_32 + + // write little endian number + this.writeI32(Number(low)) + this.writeI32(Number(high)) + } + + public writeI128(value: bigint | number) { + const low = BigInt(value) & BinaryWriter.BIG_64Fs + const high = BigInt(value) >> BinaryWriter.BIG_64 + + // write little endian number + this.writeI64(low) + this.writeI64(high) + } + + public writeOptionTag(value: boolean) { + this.writeBool(value) + } + + public writeMap(map: Map, writeKey: (key: T) => void, writeValue: (value: V) => void): void { + this.writeLength(map.size) + const offsets: number[] = [] + for (const [k, v] of map.entries()) { + offsets.push(this.offset) + writeKey(k) + writeValue(v) + } + this.sortMapEntries(offsets) + } + + public writeF32(value: number) { + const bytes = 4 + this.ensureBufferWillHandleSize(bytes) + new DataView(this.buffer, this.offset).setFloat32(0, value, true) + this.offset += bytes + } + + public writeF64(value: number) { + const bytes = 8 + this.ensureBufferWillHandleSize(bytes) + new DataView(this.buffer, this.offset).setFloat64(0, value, true) + this.offset += bytes + } + + public writeChar(_value: string) { + throw new Error("Method serializeChar not implemented.") + } + + public getBytes() { + return new Uint8Array(this.buffer).slice(0, this.offset) + } +} + +export abstract class BinaryReader implements Reader { + private static readonly BIG_32: bigint = BigInt(32) + private static readonly BIG_64: bigint = BigInt(64) + private static readonly textDecoder = new TextDecoder() + public buffer: ArrayBuffer + public offset: number + + constructor(data: Uint8Array) { + // copies data to prevent outside mutation of buffer. + this.buffer = new ArrayBuffer(data.length) + new Uint8Array(this.buffer).set(data, 0) + this.offset = 0 + } + + private read(length): ArrayBuffer { + const bytes = this.buffer.slice(this.offset, this.offset + length) + this.offset += length + return bytes + } + + abstract readLength(): number + + abstract readVariantIndex(): number + + abstract checkThatKeySlicesAreIncreasing(key1: [number, number], key2: [number, number]): void + + public readString() { + const value = this.readBytes() + return BinaryReader.textDecoder.decode(value) + } + + public readBytes() { + const len = this.readLength() + if (len < 0) { + throw new Error("Length of a bytes array can't be negative") + } + return new Uint8Array(this.read(len)) + } + + public readBool() { + const bool = new Uint8Array(this.read(1))[0] + return bool == 1 + } + + public readUnit() { + return null + } + + public readU8() { + return new DataView(this.read(1)).getUint8(0) + } + + public readU16() { + return new DataView(this.read(2)).getUint16(0, true) + } + + public readU32() { + return new DataView(this.read(4)).getUint32(0, true) + } + + public readU64() { + const low = this.readU32() + const high = this.readU32() + + // combine the two 32-bit values and return (little endian) + return BigInt( + (BigInt(high.toString()) << BinaryReader.BIG_32) | + BigInt(low.toString()), + ) + } + + public readU128() { + const low = this.readU64() + const high = this.readU64() + + // combine the two 64-bit values and return (little endian) + return BigInt( + (BigInt(high.toString()) << BinaryReader.BIG_64) | + BigInt(low.toString()), + ) + } + + public readI8() { + return new DataView(this.read(1)).getInt8(0) + } + + public readI16() { + return new DataView(this.read(2)).getInt16(0, true) + } + + public readI32() { + return new DataView(this.read(4)).getInt32(0, true) + } + + public readI64() { + const low = this.readI32() + const high = this.readI32() + + // combine the two 32-bit values and return (little endian) + return (BigInt(high.toString()) << BinaryReader.BIG_32) | + BigInt(low.toString()) + } + + public readI128() { + const low = this.readI64() + const high = this.readI64() + + // combine the two 64-bit values and return (little endian) + return (BigInt(high.toString()) << BinaryReader.BIG_64) | + BigInt(low.toString()) + } + + public readOptionTag() { + return this.readBool() + } + + public readMap(readKey: () => K, readValue: () => V) { + const length = this.readLength(), obj = new Map() + let previousKeyStart = 0, previousKeyEnd = 0 + for (let i = 0; i < length; i++) { + const keyStart = this.offset, + key = readKey(), + keyEnd = this.offset + if (i > 0) { + this.checkThatKeySlicesAreIncreasing([previousKeyStart, previousKeyEnd], [keyStart, keyEnd]) + } + previousKeyStart = keyStart + previousKeyEnd = keyEnd + obj.set(key, readValue()) + } + return obj + } + + public readChar(): string { + throw new Error("Method readChar not implemented.") + } + + public readF32() { + return new DataView(this.read(4)).getFloat32(0, true) + } + + public readF64() { + return new DataView(this.read(8)).getFloat64(0, true) + } +} diff --git a/serde-generate/runtime/typescript/serde/binaryDeserializer.ts b/serde-generate/runtime/typescript/serde/binaryDeserializer.ts deleted file mode 100644 index a9a1183ae..000000000 --- a/serde-generate/runtime/typescript/serde/binaryDeserializer.ts +++ /dev/null @@ -1,145 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates - * SPDX-License-Identifier: MIT OR Apache-2.0 - */ - -import { Deserializer } from "./deserializer.ts"; -import * as util from "https://deno.land/std@0.85.0/node/util.ts"; - -export abstract class BinaryDeserializer implements Deserializer { - private static readonly BIG_32: bigint = BigInt(32); - private static readonly BIG_64: bigint = BigInt(64); - private static readonly textDecoder = typeof window === "undefined" - ? new util.TextDecoder() - : new TextDecoder(); - public buffer: ArrayBuffer; - public offset: number; - - constructor(data: Uint8Array) { - // copies data to prevent outside mutation of buffer. - this.buffer = new ArrayBuffer(data.length); - new Uint8Array(this.buffer).set(data, 0); - this.offset = 0; - } - - private read(length: number): ArrayBuffer { - const bytes = this.buffer.slice(this.offset, this.offset + length); - this.offset += length; - return bytes; - } - - abstract deserializeLen(): number; - - abstract deserializeVariantIndex(): number; - - abstract checkThatKeySlicesAreIncreasing( - key1: [number, number], - key2: [number, number], - ): void; - - public deserializeStr(): string { - const value = this.deserializeBytes(); - return BinaryDeserializer.textDecoder.decode(value); - } - - public deserializeBytes(): Uint8Array { - const len = this.deserializeLen(); - if (len < 0) { - throw new Error("Length of a bytes array can't be negative"); - } - return new Uint8Array(this.read(len)); - } - - public deserializeBool(): boolean { - const bool = new Uint8Array(this.read(1))[0]; - return bool == 1; - } - - public deserializeUnit(): null { - return null; - } - - public deserializeU8(): number { - return new DataView(this.read(1)).getUint8(0); - } - - public deserializeU16(): number { - return new DataView(this.read(2)).getUint16(0, true); - } - - public deserializeU32(): number { - return new DataView(this.read(4)).getUint32(0, true); - } - - public deserializeU64(): bigint { - const low = this.deserializeU32(); - const high = this.deserializeU32(); - - // combine the two 32-bit values and return (little endian) - return BigInt( - (BigInt(high.toString()) << BinaryDeserializer.BIG_32) | - BigInt(low.toString()), - ); - } - - public deserializeU128(): bigint { - const low = this.deserializeU64(); - const high = this.deserializeU64(); - - // combine the two 64-bit values and return (little endian) - return BigInt( - (BigInt(high.toString()) << BinaryDeserializer.BIG_64) | - BigInt(low.toString()), - ); - } - - public deserializeI8(): number { - return new DataView(this.read(1)).getInt8(0); - } - - public deserializeI16(): number { - return new DataView(this.read(2)).getInt16(0, true); - } - - public deserializeI32(): number { - return new DataView(this.read(4)).getInt32(0, true); - } - - public deserializeI64(): bigint { - const low = this.deserializeI32(); - const high = this.deserializeI32(); - - // combine the two 32-bit values and return (little endian) - return (BigInt(high.toString()) << BinaryDeserializer.BIG_32) | - BigInt(low.toString()); - } - - public deserializeI128(): bigint { - const low = this.deserializeI64(); - const high = this.deserializeI64(); - - // combine the two 64-bit values and return (little endian) - return (BigInt(high.toString()) << BinaryDeserializer.BIG_64) | - BigInt(low.toString()); - } - - public deserializeOptionTag(): boolean { - return this.deserializeBool(); - } - - public getBufferOffset(): number { - return this.offset; - } - - public deserializeChar(): string { - throw new Error("Method deserializeChar not implemented."); - } - - public deserializeF32(): number { - return new DataView(this.read(4)).getFloat32(0, true); - } - - public deserializeF64(): number { - return new DataView(this.read(8)).getFloat64(0, true); - } -} diff --git a/serde-generate/runtime/typescript/serde/binarySerializer.ts b/serde-generate/runtime/typescript/serde/binarySerializer.ts deleted file mode 100644 index 55d244f44..000000000 --- a/serde-generate/runtime/typescript/serde/binarySerializer.ts +++ /dev/null @@ -1,176 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates - * SPDX-License-Identifier: MIT OR Apache-2.0 - */ - -import { Serializer } from "./serializer.ts"; -import * as util from "https://deno.land/std@0.85.0/node/util.ts"; - -export abstract class BinarySerializer implements Serializer { - private static readonly BIG_32: bigint = BigInt(32); - private static readonly BIG_64: bigint = BigInt(64); - - private static readonly BIG_32Fs: bigint = BigInt("4294967295"); - private static readonly BIG_64Fs: bigint = BigInt("18446744073709551615"); - - private static readonly textEncoder = typeof window === "undefined" - ? new util.TextEncoder() - : new TextEncoder(); - - private buffer: ArrayBuffer; - private offset: number; - - constructor() { - this.buffer = new ArrayBuffer(64); - this.offset = 0; - } - - private ensureBufferWillHandleSize(bytes: number) { - while (this.buffer.byteLength < this.offset + bytes) { - const newBuffer = new ArrayBuffer(this.buffer.byteLength * 2); - new Uint8Array(newBuffer).set(new Uint8Array(this.buffer)); - this.buffer = newBuffer; - } - } - - protected serialize(values: Uint8Array) { - this.ensureBufferWillHandleSize(values.length); - new Uint8Array(this.buffer, this.offset).set(values); - this.offset += values.length; - } - - abstract serializeLen(value: number): void; - - abstract serializeVariantIndex(value: number): void; - - abstract sortMapEntries(offsets: number[]): void; - - public serializeStr(value: string): void { - this.serializeBytes(BinarySerializer.textEncoder.encode(value)); - } - - public serializeBytes(value: Uint8Array): void { - this.serializeLen(value.length); - this.serialize(value); - } - - public serializeBool(value: boolean): void { - const byteValue = value ? 1 : 0; - this.serialize(new Uint8Array([byteValue])); - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars,@typescript-eslint/explicit-module-boundary-types - public serializeUnit(_value: null): void { - return; - } - - private serializeWithFunction( - fn: (byteOffset: number, value: number, littleEndian: boolean) => void, - bytesLength: number, - value: number, - ) { - this.ensureBufferWillHandleSize(bytesLength); - const dv = new DataView(this.buffer, this.offset); - fn.apply(dv, [0, value, true]); - this.offset += bytesLength; - } - - public serializeU8(value: number): void { - this.serialize(new Uint8Array([value])); - } - - public serializeU16(value: number): void { - this.serializeWithFunction(DataView.prototype.setUint16, 2, value); - } - - public serializeU32(value: number): void { - this.serializeWithFunction(DataView.prototype.setUint32, 4, value); - } - - public serializeU64(value: BigInt | number): void { - const low = BigInt(value.toString()) & BinarySerializer.BIG_32Fs; - const high = BigInt(value.toString()) >> BinarySerializer.BIG_32; - - // write little endian number - this.serializeU32(Number(low)); - this.serializeU32(Number(high)); - } - - public serializeU128(value: BigInt | number): void { - const low = BigInt(value.toString()) & BinarySerializer.BIG_64Fs; - const high = BigInt(value.toString()) >> BinarySerializer.BIG_64; - - // write little endian number - this.serializeU64(low); - this.serializeU64(high); - } - - public serializeI8(value: number): void { - const bytes = 1; - this.ensureBufferWillHandleSize(bytes); - new DataView(this.buffer, this.offset).setInt8(0, value); - this.offset += bytes; - } - - public serializeI16(value: number): void { - const bytes = 2; - this.ensureBufferWillHandleSize(bytes); - new DataView(this.buffer, this.offset).setInt16(0, value, true); - this.offset += bytes; - } - - public serializeI32(value: number): void { - const bytes = 4; - this.ensureBufferWillHandleSize(bytes); - new DataView(this.buffer, this.offset).setInt32(0, value, true); - this.offset += bytes; - } - - public serializeI64(value: bigint | number): void { - const low = BigInt(value) & BinarySerializer.BIG_32Fs; - const high = BigInt(value) >> BinarySerializer.BIG_32; - - // write little endian number - this.serializeI32(Number(low)); - this.serializeI32(Number(high)); - } - - public serializeI128(value: bigint | number): void { - const low = BigInt(value) & BinarySerializer.BIG_64Fs; - const high = BigInt(value) >> BinarySerializer.BIG_64; - - // write little endian number - this.serializeI64(low); - this.serializeI64(high); - } - - public serializeOptionTag(value: boolean): void { - this.serializeBool(value); - } - - public getBufferOffset(): number { - return this.offset; - } - - public getBytes(): Uint8Array { - return new Uint8Array(this.buffer).slice(0, this.offset); - } - - public serializeChar(_value: string): void { - throw new Error("Method serializeChar not implemented."); - } - - public serializeF32(value: number): void { - const bytes = 4; - this.ensureBufferWillHandleSize(bytes); - new DataView(this.buffer, this.offset).setFloat32(0, value, true); - this.offset += bytes; - } - - public serializeF64(value: number): void { - const bytes = 8; - this.ensureBufferWillHandleSize(bytes); - new DataView(this.buffer, this.offset).setFloat64(0, value, true); - this.offset += bytes; - } -} diff --git a/serde-generate/runtime/typescript/serde/deserializer.ts b/serde-generate/runtime/typescript/serde/deserializer.ts deleted file mode 100644 index 1cd03d094..000000000 --- a/serde-generate/runtime/typescript/serde/deserializer.ts +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates - * SPDX-License-Identifier: MIT OR Apache-2.0 - */ - -export interface Deserializer { - deserializeStr(): string; - - deserializeBytes(): Uint8Array; - - deserializeBool(): boolean; - - deserializeUnit(): null; - - deserializeChar(): string; - - deserializeF32(): number; - - deserializeF64(): number; - - deserializeU8(): number; - - deserializeU16(): number; - - deserializeU32(): number; - - deserializeU64(): bigint; - - deserializeU128(): bigint; - - deserializeI8(): number; - - deserializeI16(): number; - - deserializeI32(): number; - - deserializeI64(): bigint; - - deserializeI128(): bigint; - - deserializeLen(): number; - - deserializeVariantIndex(): number; - - deserializeOptionTag(): boolean; - - getBufferOffset(): number; - - checkThatKeySlicesAreIncreasing( - key1: [number, number], - key2: [number, number], - ): void; -} diff --git a/serde-generate/runtime/typescript/serde/mod.ts b/serde-generate/runtime/typescript/serde/mod.ts deleted file mode 100644 index 4c8a4cf74..000000000 --- a/serde-generate/runtime/typescript/serde/mod.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates - * SPDX-License-Identifier: MIT OR Apache-2.0 - */ - -export * from "./types.ts"; -export * from "./serializer.ts"; -export * from "./deserializer.ts"; -export * from "./binarySerializer.ts"; -export * from "./binaryDeserializer.ts"; diff --git a/serde-generate/runtime/typescript/serde/serializer.ts b/serde-generate/runtime/typescript/serde/serializer.ts deleted file mode 100644 index 361e337a8..000000000 --- a/serde-generate/runtime/typescript/serde/serializer.ts +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates - * SPDX-License-Identifier: MIT OR Apache-2.0 - */ - -export interface Serializer { - serializeStr(value: string): void; - - serializeBytes(value: Uint8Array): void; - - serializeBool(value: boolean): void; - - serializeUnit(value: null): void; - - serializeChar(value: string): void; - - serializeF32(value: number): void; - - serializeF64(value: number): void; - - serializeU8(value: number): void; - - serializeU16(value: number): void; - - serializeU32(value: number): void; - - serializeU64(value: bigint | number): void; - - serializeU128(value: bigint | number): void; - - serializeI8(value: number): void; - - serializeI16(value: number): void; - - serializeI32(value: number): void; - - serializeI64(value: bigint | number): void; - - serializeI128(value: bigint | number): void; - - serializeLen(value: number): void; - - serializeVariantIndex(value: number): void; - - serializeOptionTag(value: boolean): void; - - getBufferOffset(): number; - - getBytes(): Uint8Array; - - sortMapEntries(offsets: number[]): void; -} diff --git a/serde-generate/runtime/typescript/serde/types.ts b/serde-generate/runtime/typescript/serde/types.ts deleted file mode 100644 index ea1049b27..000000000 --- a/serde-generate/runtime/typescript/serde/types.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates - * SPDX-License-Identifier: MIT OR Apache-2.0 - */ - -export type Optional = T | null; -export type Seq = T[]; -export type Tuple = T; -export type ListTuple = Tuple[]; - -export type unit = null; -export type bool = boolean; -export type int8 = number; -export type int16 = number; -export type int32 = number; -export type int64 = bigint; -export type int128 = bigint; -export type uint8 = number; -export type uint16 = number; -export type uint32 = number; -export type uint64 = bigint; -export type uint128 = bigint; -export type float32 = number; -export type float64 = number; -export type char = string; -export type str = string; -export type bytes = Uint8Array; diff --git a/serde-generate/src/common.rs b/serde-generate/src/common.rs index e17bc72e8..36abf08dc 100644 --- a/serde-generate/src/common.rs +++ b/serde-generate/src/common.rs @@ -1,32 +1,29 @@ -// Copyright (c) Facebook, Inc. and its affiliates -// SPDX-License-Identifier: MIT OR Apache-2.0 - use serde_reflection::Format; pub(crate) fn mangle_type(format: &Format) -> String { use Format::*; match format { TypeName(x) => x.to_string(), - Unit => "unit".into(), - Bool => "bool".into(), - I8 => "i8".into(), - I16 => "i16".into(), - I32 => "i32".into(), - I64 => "i64".into(), - I128 => "i128".into(), - U8 => "u8".into(), - U16 => "u16".into(), - U32 => "u32".into(), - U64 => "u64".into(), - U128 => "u128".into(), - F32 => "f32".into(), - F64 => "f64".into(), - Char => "char".into(), - Str => "str".into(), - Bytes => "bytes".into(), + Unit => "unit".into(), + Bool => "bool".into(), + I8 => "i8".into(), + I16 => "i16".into(), + I32 => "i32".into(), + I64 => "i64".into(), + I128 => "i128".into(), + U8 => "u8".into(), + U16 => "u16".into(), + U32 => "u32".into(), + U64 => "u64".into(), + U128 => "u128".into(), + F32 => "f32".into(), + F64 => "f64".into(), + Char => "char".into(), + Str => "str".into(), + Bytes => "bytes".into(), - Option(format) => format!("option_{}", mangle_type(format)), - Seq(format) => format!("vector_{}", mangle_type(format)), + Option(format) => format!("option_{}", mangle_type(format)), + Seq(format) => format!("vector_{}", mangle_type(format)), Map { key, value } => format!("map_{}_to_{}", mangle_type(key), mangle_type(value)), Tuple(formats) => format!( "tuple{}_{}", diff --git a/serde-generate/src/typescript.rs b/serde-generate/src/typescript.rs index be8889d61..d4d520f21 100644 --- a/serde-generate/src/typescript.rs +++ b/serde-generate/src/typescript.rs @@ -1,736 +1,497 @@ -// Copyright (c) Facebook, Inc. and its affiliates -// SPDX-License-Identifier: MIT OR Apache-2.0 - +#![allow(unused)] use crate::{ - common, - indent::{IndentConfig, IndentedWriter}, - CodeGeneratorConfig, + common, + indent::{IndentConfig, IndentedWriter}, + CodeGeneratorConfig, }; use heck::CamelCase; use include_dir::include_dir as include_directory; +use indoc::{formatdoc, indoc, writedoc}; use serde_reflection::{ContainerFormat, Format, FormatHolder, Named, Registry, VariantFormat}; use std::{ - collections::{BTreeMap, HashMap}, - io::{Result, Write}, - path::PathBuf, + collections::{BTreeMap, HashMap}, + io::{Result, Write}, + path::PathBuf, }; /// Main configuration object for code-generation in TypeScript, powered by /// the Deno runtime. pub struct CodeGenerator<'a> { - /// Language-independent configuration. - config: &'a CodeGeneratorConfig, - /// Mapping from external type names to fully-qualified class names (e.g. "MyClass" -> "com.my_org.my_package.MyClass"). - /// Derived from `config.external_definitions`. - external_qualified_names: HashMap, - /// vector of namespaces to import - namespaces_to_import: Vec, + /// Language-independent configuration. + config: &'a CodeGeneratorConfig, + /// Mapping from external type names to fully-qualified class names (e.g. "MyClass" -> "com.my_org.my_package.MyClass"). + /// Derived from `config.external_definitions`. + external_qualified_names: HashMap, + /// vector of namespaces to import + namespaces_to_import: Vec, } /// Shared state for the code generation of a TypeScript source file. struct TypeScriptEmitter<'a, T> { - /// Writer. - out: IndentedWriter, - /// Generator. - generator: &'a CodeGenerator<'a>, + /// Writer. + out: IndentedWriter, + /// Generator. + generator: &'a CodeGenerator<'a>, } impl<'a> CodeGenerator<'a> { - /// Create a TypeScript code generator for the given config. - pub fn new(config: &'a CodeGeneratorConfig) -> Self { - if config.c_style_enums { - panic!("TypeScript does not support generating c-style enums"); - } - let mut external_qualified_names = HashMap::new(); - for (namespace, names) in &config.external_definitions { - for name in names { - external_qualified_names.insert( - name.to_string(), - format!("{}.{}", namespace.to_camel_case(), name), - ); - } - } - Self { - config, - external_qualified_names, - namespaces_to_import: config - .external_definitions - .keys() - .map(|k| k.to_string()) - .collect::>(), - } - } - - /// Output class definitions for `registry` in a single source file. - pub fn output(&self, out: &mut dyn Write, registry: &Registry) -> Result<()> { - let mut emitter = TypeScriptEmitter { - out: IndentedWriter::new(out, IndentConfig::Space(2)), - generator: self, - }; - - emitter.output_preamble()?; - - for (name, format) in registry { - emitter.output_container(name, format)?; - } - - if self.config.serialization { - emitter.output_helpers(registry)?; - } - - Ok(()) - } + /// Create a TypeScript code generator for the given config. + pub fn new(config: &'a CodeGeneratorConfig) -> Self { + if config.c_style_enums { + panic!("TypeScript does not support generating c-style enums"); + } + let mut external_qualified_names = HashMap::new(); + for (namespace, names) in &config.external_definitions { + for name in names { + external_qualified_names.insert( + name.to_string(), + format!("{}.{}", namespace.to_camel_case(), name), + ); + } + } + Self { + config, + external_qualified_names, + namespaces_to_import: config.external_definitions.keys().map(|k| k.to_string()).collect::>(), + } + } + + /// Output class definitions for `registry` in a single source file. + pub fn output(&self, out: &mut dyn Write, registry: &Registry) -> Result<()> { + let mut emitter = TypeScriptEmitter { + out: IndentedWriter::new(out, IndentConfig::Tab), + generator: self, + }; + + emitter.output_preamble()?; + + for (name, format) in registry { + writeln!(emitter.out)?; + emitter.output_container_typedef(name, format)?; + } + for (name, format) in registry { + writeln!(emitter.out)?; + emitter.generate_container(name, format)?; + } + + Ok(()) + } } -impl<'a, T> TypeScriptEmitter<'a, T> -where - T: Write, -{ - fn output_preamble(&mut self) -> Result<()> { - writeln!( - self.out, - r#" -import {{ Serializer, Deserializer }} from '../serde/mod.ts'; -import {{ BcsSerializer, BcsDeserializer }} from '../bcs/mod.ts'; -import {{ Optional, Seq, Tuple, ListTuple, unit, bool, int8, int16, int32, int64, int128, uint8, uint16, uint32, uint64, uint128, float32, float64, char, str, bytes }} from '../serde/mod.ts'; -"#, - )?; - for namespace in self.generator.namespaces_to_import.iter() { - writeln!( - self.out, - "import * as {} from '../{}/mod.ts';\n", - namespace.to_camel_case(), - namespace - )?; - } - - Ok(()) - } - - fn quote_qualified_name(&self, name: &str) -> String { - self.generator - .external_qualified_names - .get(name) - .cloned() - .unwrap_or_else(|| name.to_string()) - } - - fn output_comment(&mut self, name: &str) -> std::io::Result<()> { - let path = vec![name.to_string()]; - if let Some(doc) = self.generator.config.comments.get(&path) { - let text = textwrap::indent(doc, " * ").replace("\n\n", "\n *\n"); - writeln!(self.out, "/**\n{} */", text)?; - } - Ok(()) - } - - fn quote_type(&self, format: &Format) -> String { - use Format::*; - match format { - TypeName(x) => self.quote_qualified_name(x), - Unit => "unit".into(), - Bool => "bool".into(), - I8 => "int8".into(), - I16 => "int16".into(), - I32 => "int32".into(), - I64 => "int64".into(), - I128 => "int128".into(), - U8 => "uint8".into(), - U16 => "uint16".into(), - U32 => "uint32".into(), - U64 => "uint64".into(), - U128 => "uint128".into(), - F32 => "float32".into(), - F64 => "float64".into(), - Char => "char".into(), - Str => "str".into(), - Bytes => "bytes".into(), - - Option(format) => format!("Optional<{}>", self.quote_type(format)), - Seq(format) => format!("Seq<{}>", self.quote_type(format)), - Map { key, value } => { - format!("Map<{},{}>", self.quote_type(key), self.quote_type(value)) - } - Tuple(formats) => format!("Tuple<[{}]>", self.quote_types(formats, ", ")), - TupleArray { - content, - size: _size, - } => format!("ListTuple<[{}]>", self.quote_type(content),), - Variable(_) => panic!("unexpected value"), - } - } - - fn quote_types(&self, formats: &[Format], sep: &str) -> String { - formats - .iter() - .map(|f| self.quote_type(f)) - .collect::>() - .join(sep) - } - - fn output_helpers(&mut self, registry: &Registry) -> Result<()> { - let mut subtypes = BTreeMap::new(); - for format in registry.values() { - format - .visit(&mut |f| { - if Self::needs_helper(f) { - subtypes.insert(common::mangle_type(f), f.clone()); - } - Ok(()) - }) - .unwrap(); - } - - writeln!(self.out, "export class Helpers {{")?; - self.out.indent(); - for (mangled_name, subtype) in &subtypes { - self.output_serialization_helper(mangled_name, subtype)?; - self.output_deserialization_helper(mangled_name, subtype)?; - } - self.out.unindent(); - writeln!(self.out, "}}")?; - writeln!(self.out) - } - - fn needs_helper(format: &Format) -> bool { - use Format::*; - matches!( - format, - Option(_) | Seq(_) | Map { .. } | Tuple(_) | TupleArray { .. } - ) - } - - fn quote_serialize_value(&self, value: &str, format: &Format, use_this: bool) -> String { - use Format::*; - let this_str = if use_this { "this." } else { "" }; - - match format { - TypeName(_) => format!("{}{}.serialize(serializer);", this_str, value), - Unit => format!("serializer.serializeUnit({}{});", this_str, value), - Bool => format!("serializer.serializeBool({}{});", this_str, value), - I8 => format!("serializer.serializeI8({}{});", this_str, value), - I16 => format!("serializer.serializeI16({}{});", this_str, value), - I32 => format!("serializer.serializeI32({}{});", this_str, value), - I64 => format!("serializer.serializeI64({}{});", this_str, value), - I128 => format!("serializer.serializeI128({}{});", this_str, value), - U8 => format!("serializer.serializeU8({}{});", this_str, value), - U16 => format!("serializer.serializeU16({}{});", this_str, value), - U32 => format!("serializer.serializeU32({}{});", this_str, value), - U64 => format!("serializer.serializeU64({}{});", this_str, value), - U128 => format!("serializer.serializeU128({}{});", this_str, value), - F32 => format!("serializer.serializeF32({}{});", this_str, value), - F64 => format!("serializer.serializeF64({}{});", this_str, value), - Char => format!("serializer.serializeChar({}{});", this_str, value), - Str => format!("serializer.serializeStr({}{});", this_str, value), - Bytes => format!("serializer.serializeBytes({}{});", this_str, value), - _ => format!( - "Helpers.serialize{}({}{}, serializer);", - common::mangle_type(format).to_camel_case(), - this_str, - value - ), - } - } - - fn quote_deserialize(&self, format: &Format) -> String { - use Format::*; - match format { - TypeName(name) => format!( - "{}.deserialize(deserializer)", - self.quote_qualified_name(name) - ), - Unit => "deserializer.deserializeUnit()".to_string(), - Bool => "deserializer.deserializeBool()".to_string(), - I8 => "deserializer.deserializeI8()".to_string(), - I16 => "deserializer.deserializeI16()".to_string(), - I32 => "deserializer.deserializeI32()".to_string(), - I64 => "deserializer.deserializeI64()".to_string(), - I128 => "deserializer.deserializeI128()".to_string(), - U8 => "deserializer.deserializeU8()".to_string(), - U16 => "deserializer.deserializeU16()".to_string(), - U32 => "deserializer.deserializeU32()".to_string(), - U64 => "deserializer.deserializeU64()".to_string(), - U128 => "deserializer.deserializeU128()".to_string(), - F32 => "deserializer.deserializeF32()".to_string(), - F64 => "deserializer.deserializeF64()".to_string(), - Char => "deserializer.deserializeChar()".to_string(), - Str => "deserializer.deserializeStr()".to_string(), - Bytes => "deserializer.deserializeBytes()".to_string(), - _ => format!( - "Helpers.deserialize{}(deserializer)", - common::mangle_type(format).to_camel_case(), - ), - } - } - - fn output_serialization_helper(&mut self, name: &str, format0: &Format) -> Result<()> { - use Format::*; - - write!( - self.out, - "static serialize{}(value: {}, serializer: Serializer): void {{", - name.to_camel_case(), - self.quote_type(format0) - )?; - self.out.indent(); - match format0 { - Option(format) => { - write!( - self.out, - r#" -if (value) {{ - serializer.serializeOptionTag(true); - {} -}} else {{ - serializer.serializeOptionTag(false); -}} -"#, - self.quote_serialize_value("value", format, false) - )?; - } - - Seq(format) => { - write!( - self.out, - r#" -serializer.serializeLen(value.length); -value.forEach((item: {}) => {{ - {} -}}); -"#, - self.quote_type(format), - self.quote_serialize_value("item", format, false) - )?; - } - - Map { key, value } => { - write!( - self.out, - r#" -serializer.serializeLen(value.size); -const offsets: number[] = []; -for (const [k, v] of value.entries()) {{ - offsets.push(serializer.getBufferOffset()); - {} - {} -}} -serializer.sortMapEntries(offsets); -"#, - self.quote_serialize_value("k", key, false), - self.quote_serialize_value("v", value, false) - )?; - } - - Tuple(formats) => { - writeln!(self.out)?; - for (index, format) in formats.iter().enumerate() { - let expr = format!("value[{}]", index); - writeln!( - self.out, - "{}", - self.quote_serialize_value(&expr, format, false) - )?; - } - } - - TupleArray { - content, - size: _size, - } => { - write!( - self.out, - r#" -value.forEach((item) =>{{ - {} -}}); -"#, - self.quote_serialize_value("item[0]", content, false) - )?; - } - - _ => panic!("unexpected case"), - } - self.out.unindent(); - writeln!(self.out, "}}\n") - } - - fn output_deserialization_helper(&mut self, name: &str, format0: &Format) -> Result<()> { - use Format::*; - - write!( - self.out, - "static deserialize{}(deserializer: Deserializer): {} {{", - name.to_camel_case(), - self.quote_type(format0), - )?; - self.out.indent(); - match format0 { - Option(format) => { - write!( - self.out, - r#" -const tag = deserializer.deserializeOptionTag(); -if (!tag) {{ - return null; -}} else {{ - return {}; -}} -"#, - self.quote_deserialize(format), - )?; - } - - Seq(format) => { - write!( - self.out, - r#" -const length = deserializer.deserializeLen(); -const list: {} = []; -for (let i = 0; i < length; i++) {{ - list.push({}); -}} -return list; -"#, - self.quote_type(format0), - self.quote_deserialize(format) - )?; - } - - Map { key, value } => { - write!( - self.out, - r#" -const length = deserializer.deserializeLen(); -const obj = new Map<{0}, {1}>(); -let previousKeyStart = 0; -let previousKeyEnd = 0; -for (let i = 0; i < length; i++) {{ - const keyStart = deserializer.getBufferOffset(); - const key = {2}; - const keyEnd = deserializer.getBufferOffset(); - if (i > 0) {{ - deserializer.checkThatKeySlicesAreIncreasing( - [previousKeyStart, previousKeyEnd], - [keyStart, keyEnd]); - }} - previousKeyStart = keyStart; - previousKeyEnd = keyEnd; - const value = {3}; - obj.set(key, value); -}} -return obj; -"#, - self.quote_type(key), - self.quote_type(value), - self.quote_deserialize(key), - self.quote_deserialize(value), - )?; - } - - Tuple(formats) => { - write!( - self.out, - r#" -return [{} -]; -"#, - formats - .iter() - .map(|f| format!("\n {}", self.quote_deserialize(f))) - .collect::>() - .join(",") - )?; - } - - TupleArray { content, size } => { - write!( - self.out, - r#" -const list: {} = []; -for (let i = 0; i < {}; i++) {{ - list.push([{}]); -}} -return list; -"#, - self.quote_type(format0), - size, - self.quote_deserialize(content) - )?; - } - - _ => panic!("unexpected case"), - } - self.out.unindent(); - writeln!(self.out, "}}\n") - } - - fn output_variant( - &mut self, - base: &str, - index: u32, - name: &str, - variant: &VariantFormat, - ) -> Result<()> { - use VariantFormat::*; - let fields = match variant { - Unit => Vec::new(), - NewType(format) => vec![Named { - name: "value".to_string(), - value: format.as_ref().clone(), - }], - Tuple(formats) => formats - .iter() - .enumerate() - .map(|(i, f)| Named { - name: format!("field{}", i), - value: f.clone(), - }) - .collect(), - Struct(fields) => fields.clone(), - Variable(_) => panic!("incorrect value"), - }; - self.output_struct_or_variant_container(Some(base), Some(index), name, &fields) - } - - fn output_variants( - &mut self, - base: &str, - variants: &BTreeMap>, - ) -> Result<()> { - for (index, variant) in variants { - self.output_variant(base, *index, &variant.name, &variant.value)?; - } - Ok(()) - } - - fn output_struct_or_variant_container( - &mut self, - variant_base: Option<&str>, - variant_index: Option, - name: &str, - fields: &[Named], - ) -> Result<()> { - let mut variant_base_name = String::new(); - - // Beginning of class - if let Some(base) = variant_base { - writeln!(self.out)?; - self.output_comment(name)?; - writeln!( - self.out, - "export class {0}Variant{1} extends {0} {{", - base, name - )?; - variant_base_name = format!("{0}Variant", base); - } else { - self.output_comment(name)?; - writeln!(self.out, "export class {} {{", name)?; - } - if !fields.is_empty() { - writeln!(self.out)?; - } - // Constructor. - writeln!( - self.out, - "constructor ({}) {{", - fields - .iter() - .map(|f| { format!("public {}: {}", &f.name, self.quote_type(&f.value)) }) - .collect::>() - .join(", ") - )?; - if let Some(_base) = variant_base { - self.out.indent(); - writeln!(self.out, "super();")?; - self.out.unindent(); - } - writeln!(self.out, "}}\n")?; - // Serialize - if self.generator.config.serialization { - writeln!( - self.out, - "public serialize(serializer: Serializer): void {{", - )?; - self.out.indent(); - if let Some(index) = variant_index { - writeln!(self.out, "serializer.serializeVariantIndex({});", index)?; - } - for field in fields { - writeln!( - self.out, - "{}", - self.quote_serialize_value(&field.name, &field.value, true) - )?; - } - self.out.unindent(); - writeln!(self.out, "}}\n")?; - } - // Deserialize (struct) or Load (variant) - if self.generator.config.serialization { - if variant_index.is_none() { - writeln!( - self.out, - "static deserialize(deserializer: Deserializer): {} {{", - name, - )?; - } else { - writeln!( - self.out, - "static load(deserializer: Deserializer): {}{} {{", - variant_base_name, name, - )?; - } - self.out.indent(); - for field in fields { - writeln!( - self.out, - "const {} = {};", - field.name, - self.quote_deserialize(&field.value) - )?; - } - writeln!( - self.out, - r#"return new {0}{1}({2});"#, - variant_base_name, - name, - fields - .iter() - .map(|f| f.name.to_string()) - .collect::>() - .join(",") - )?; - self.out.unindent(); - writeln!(self.out, "}}\n")?; - } - writeln!(self.out, "}}") - } - - fn output_enum_container( - &mut self, - name: &str, - variants: &BTreeMap>, - ) -> Result<()> { - self.output_comment(name)?; - writeln!(self.out, "export abstract class {} {{", name)?; - if self.generator.config.serialization { - writeln!( - self.out, - "abstract serialize(serializer: Serializer): void;\n" - )?; - write!( - self.out, - "static deserialize(deserializer: Deserializer): {} {{", - name - )?; - self.out.indent(); - writeln!( - self.out, - r#" -const index = deserializer.deserializeVariantIndex(); -switch (index) {{"#, - )?; - self.out.indent(); - for (index, variant) in variants { - writeln!( - self.out, - "case {}: return {}Variant{}.load(deserializer);", - index, name, variant.name, - )?; - } - writeln!( - self.out, - "default: throw new Error(\"Unknown variant index for {}: \" + index);", - name, - )?; - self.out.unindent(); - writeln!(self.out, "}}")?; - self.out.unindent(); - writeln!(self.out, "}}")?; - } - writeln!(self.out, "}}\n")?; - self.output_variants(name, variants)?; - Ok(()) - } - - fn output_container(&mut self, name: &str, format: &ContainerFormat) -> Result<()> { - use ContainerFormat::*; - let fields = match format { - UnitStruct => Vec::new(), - NewTypeStruct(format) => vec![Named { - name: "value".to_string(), - value: format.as_ref().clone(), - }], - TupleStruct(formats) => formats - .iter() - .enumerate() - .map(|(i, f)| Named { - name: format!("field{}", i), - value: f.clone(), - }) - .collect::>(), - Struct(fields) => fields.clone(), - Enum(variants) => { - self.output_enum_container(name, variants)?; - return Ok(()); - } - }; - self.output_struct_or_variant_container(None, None, name, &fields) - } -} - -/// Installer for generated source files in TypeScript. -pub struct Installer { - install_dir: PathBuf, -} - -impl Installer { - pub fn new(install_dir: PathBuf) -> Self { - Installer { install_dir } - } - - fn install_runtime( - &self, - source_dir: include_dir::Dir, - path: &str, - ) -> std::result::Result<(), Box> { - let dir_path = self.install_dir.join(path); - std::fs::create_dir_all(&dir_path)?; - for entry in source_dir.files() { - let mut file = std::fs::File::create(dir_path.join(entry.path()))?; - file.write_all(entry.contents())?; - } - Ok(()) - } -} - -impl crate::SourceInstaller for Installer { - type Error = Box; - - fn install_module( - &self, - config: &CodeGeneratorConfig, - registry: &Registry, - ) -> std::result::Result<(), Self::Error> { - let dir_path = self.install_dir.join(&config.module_name); - std::fs::create_dir_all(&dir_path)?; - let source_path = dir_path.join("mod.ts"); - let mut file = std::fs::File::create(source_path)?; - - let generator = CodeGenerator::new(config); - generator.output(&mut file, registry)?; - Ok(()) - } - - fn install_serde_runtime(&self) -> std::result::Result<(), Self::Error> { - self.install_runtime(include_directory!("runtime/typescript/serde"), "serde") - } - - fn install_bincode_runtime(&self) -> std::result::Result<(), Self::Error> { - self.install_runtime(include_directory!("runtime/typescript/bincode"), "bincode") - } - - fn install_bcs_runtime(&self) -> std::result::Result<(), Self::Error> { - self.install_runtime(include_directory!("runtime/typescript/bcs"), "bcs") - } -} +impl<'a, T: Write> TypeScriptEmitter<'a, T> { + fn output_preamble(&mut self) -> Result<()> { + writeln!(self.out, r#"import type * as $t from "./serde.ts""#)?; + writeln!(self.out, r#"import {{ BincodeReader, BincodeWriter }} from "./bincode.ts""#)?; + for namespace in self.generator.namespaces_to_import.iter() { + writeln!(self.out, "import * as {} from '../{}/mod.ts';\n", namespace.to_camel_case(), namespace)?; + } + Ok(()) + } + + fn generate_container(&mut self, name: &str, container: &ContainerFormat) -> Result<()> { + // ENCODE + writeln!(self.out, "export const {name} = {{")?; + self.out.indent(); + + writeln!(self.out, "encode(value: {name}, writer = new BincodeWriter()) {{")?; + self.out.indent(); + + match container { + ContainerFormat::UnitStruct => { + writeln!(self.out, "{}", self.quote_write_value("null", &Format::Unit))?; + } + ContainerFormat::Struct(fields) => { + for field in fields.iter() { + writeln!(self.out, "{}", self.quote_write_value(&format!("value.{}", field.name), &field.value))?; + } + } + ContainerFormat::NewTypeStruct(inner_type) => { + writeln!(self.out, "{}", self.quote_write_value(&format!("value"), inner_type))?; + } + ContainerFormat::TupleStruct(inner_types) => { + for (i, inner) in inner_types.iter().enumerate() { + writeln!(self.out, "{}", self.quote_write_value(&format!("value[{i}]"), inner))?; + } + } + ContainerFormat::Enum(variants) => { + self.generate_enum_container(name, variants)?; + return Ok(()); + } + } + + writeln!(self.out, "return writer.getBytes()")?; + + self.out.unindent(); + writeln!(self.out, "}},")?; + + + // DECODE + writeln!(self.out, "decode(input: Uint8Array, reader = new BincodeReader(input)) {{")?; + self.out.indent(); + + match container { + ContainerFormat::UnitStruct => { + writeln!(self.out, "const value: $t.unit = {}", self.quote_read_value(&Format::Unit))?; + } + ContainerFormat::NewTypeStruct(inner) => { + writeln!(self.out, "const value: {name} = {}", self.quote_read_value(inner))?; + } + ContainerFormat::TupleStruct(inner_types) => { + writeln!(self.out, "const value: {name} = {}", self.quote_read_value(&Format::Tuple(inner_types.clone())))?; + } + _ => { writeln!(self.out, "const value = {{}} as {name}")?; } + } + + match container { + ContainerFormat::UnitStruct => { /* set at initialization */ } + ContainerFormat::TupleStruct(inner_types) => { /* set at initialization */ } + ContainerFormat::NewTypeStruct(inner_type) => { /* set at initialization */ } + ContainerFormat::Struct(fields) => { + for field in fields.iter() { + writeln!(self.out, "value.{} = {}", field.name, self.quote_read_value(&field.value))?; + } + } + ContainerFormat::Enum(..) => { /* handled before with generate_enum_container() */ } + } + + writeln!(self.out, "return value")?; + + self.out.unindent(); + writeln!(self.out, "}}")?; // decode end + + self.out.unindent(); + writeln!(self.out, "}}")?; // object end + + Ok(()) + } + + fn generate_enum_container(&mut self, name: &str, variants: &BTreeMap>) -> Result<()> { + writeln!(self.out, "switch (value.$) {{")?; + self.out.indent(); + + for (index, variant) in variants { + writeln!(self.out, r#"case "{}": {{"#, variant.name)?; + self.out.indent(); + writeln!(self.out, "writer.writeVariantIndex({index})"); + + match &variant.value { + VariantFormat::Unit => { + writeln!(self.out, "{}", self.quote_write_value(&format!("value.{}", &variant.name), &Format::Unit)); + }, + VariantFormat::NewType(inner) => { + writeln!(self.out, "{}", self.quote_write_value(&format!("value.{}", &variant.name), inner)); + } + VariantFormat::Tuple(members) => { + let tuple = Format::Tuple(members.clone()); + writeln!(self.out, "{}", self.quote_write_value(&format!("value.{}", &variant.name), &tuple)); + } + VariantFormat::Struct(fields) => { + for field in fields { + writeln!(self.out, "{}", self.quote_write_value(&format!("value.{}.{}", variant.name, field.name), &field.value))?; + } + } + VariantFormat::Variable(_) => panic!("not supported") + } + writeln!(self.out, "break")?; + self.out.unindent(); + writeln!(self.out, "}}")?; // case end + } + + self.out.unindent(); + writeln!(self.out, "}}")?; // switch end + + writeln!(self.out, "return writer.getBytes()"); + self.out.unindent(); + writeln!(self.out, "}},")?; // encode end + + writeln!(self.out, "decode(input: Uint8Array, reader = new BincodeReader(input)) {{")?; + self.out.indent(); + + writeln!(self.out, r#"let value: {name}"#); + + writeln!(self.out, "switch (reader.readVariantIndex()) {{")?; + self.out.indent(); + + for (index, variant) in variants { + writeln!(self.out, r#"case {index}: {{"#)?; + self.out.indent(); + + writeln!(self.out, r#"value = {{ $: "{}" }} as $t.WrapperOfCase<{}, "{}">"#, variant.name, name, variant.name); + + match &variant.value { + VariantFormat::Unit => { + writeln!(self.out, "value.{} = {}", variant.name, self.quote_read_value(&Format::Unit)); + }, + VariantFormat::Tuple(members) => { + let tuple = Format::Tuple(members.clone()); + writeln!(self.out, "value.{} = {}", variant.name, self.quote_read_value(&tuple)); + } + VariantFormat::NewType(inner) => { + writeln!(self.out, "value.{} = {}", variant.name, self.quote_read_value(inner)); + } + VariantFormat::Struct(fields) => { + writeln!(self.out, r#"value.{var} = {{}} as $t.WrapperOfCase<{name}, "{var}">["{var}"]"#, var = variant.name); + for field in fields { + writeln!(self.out, "value.{}.{} = {}", variant.name, field.name, self.quote_read_value(&field.value))?; + } + } + VariantFormat::Variable(_) => panic!("not supported") + } + + writeln!(self.out, "break")?; + self.out.unindent(); + writeln!(self.out, "}}")?; // case end + } + + self.out.unindent(); + writeln!(self.out, "}}")?; // switch end + + writeln!(self.out)?; + writeln!(self.out, "return value")?; + + self.out.unindent(); + writeln!(self.out, "}}")?; // decode end + + self.out.unindent(); + writeln!(self.out, "}}")?; // object end + + Ok(()) + } + + fn output_container_typedef(&mut self, name: &str, container: &ContainerFormat) -> Result<()> { + match container { + ContainerFormat::UnitStruct => { + writeln!(self.out, "export type {name} = $t.unit")?; + } + ContainerFormat::TupleStruct(fields) => { + writeln!(self.out, "export type {name} = [{}]", self.quote_types(&fields, ", "))?; + self.out.unindent(); + } + ContainerFormat::Struct(fields) => { + writeln!(self.out, "export type {name} = {{")?; + self.out.indent(); + for field in fields { + writeln!(self.out, "{}: {},", field.name, self.quote_type(&field.value))?; + } + self.out.unindent(); + writeln!(self.out, "}}")?; + } + ContainerFormat::NewTypeStruct(format) => { + writeln!(self.out, "export type {name} = {}", self.quote_type(format))?; + } + ContainerFormat::Enum(variants) => { + // TODO https://github.com/zefchain/serde-reflection/issues/45 + writeln!(self.out, "export type {name} = ")?; + self.out.indent(); + for (_index, variant) in variants { + match &variant.value { + VariantFormat::Unit => { + writeln!(self.out, r#"| {{ $: "{0}", {0}: {1} }}"#, variant.name, self.quote_type(&Format::Unit))?; + } + VariantFormat::Struct(fields) => { + let fields_str = fields.iter().map(|f| format!("{}: {}", f.name, self.quote_type(&f.value))).collect::>().join(", "); + writeln!(self.out, r#"| {{ $: "{0}", {0}: {{ {1} }} }}"#, variant.name, fields_str)?; + } + VariantFormat::NewType(t) => { + writeln!(self.out, r#"| {{ $: "{0}", {0}: {1} }}"#, variant.name, self.quote_type(&t))?; + } + VariantFormat::Tuple(t) => { + writeln!(self.out, r#"| {{ $: "{0}", {0}: {1} }}"#, variant.name, self.quote_type(&Format::Tuple(t.clone())))?; + } + VariantFormat::Variable(v) => panic!("unknown variant format") + } + } + self.out.unindent(); + } + _ => panic!("format not implemented") + } + + Ok(()) + } + + fn quote_qualified_name(&self, name: &str) -> String { + self.generator.external_qualified_names.get(name).cloned().unwrap_or_else(|| name.to_string()) + } + + fn output_comment(&mut self, name: &str) -> std::io::Result<()> { + let path = vec![name.to_string()]; + if let Some(doc) = self.generator.config.comments.get(&path) { + let text = textwrap::indent(doc, " * ").replace("\n\n", "\n *\n"); + writeln!(self.out, "/**\n{} */", text)?; + } + Ok(()) + } + + fn quote_type(&self, format: &Format) -> String { + use Format::*; + let str = match format { + Unit => "$t.unit", + Bool => "$t.bool", + I8 => "$t.i8", + I16 => "$t.i16", + I32 => "$t.i32", + I64 => "$t.i64", + I128 => "$t.i128", + U8 => "$t.u8", + U16 => "$t.u16", + U32 => "$t.u32", + U64 => "$t.u64", + U128 => "$t.u128", + F32 => "$t.f32", + F64 => "$t.f64", + Char => "$t.char", + Str => "$t.str", + Bytes => "$t.bytes", + + Option(format) => &format!("$t.Optional<{}>", self.quote_type(format)), + Seq(format) => &format!("$t.Seq<{}>", self.quote_type(format)), + Map { key, value } => &format!("$t.Map<{}, {}>", self.quote_type(key), self.quote_type(value)), + Tuple(formats) => &format!("$t.Tuple<[{}]>", self.quote_types(formats, ", ")), + TupleArray { content, .. } => &format!("$t.ListTuple<[{}]>", self.quote_type(content)), + + TypeName(x) => &self.quote_qualified_name(x), + + Variable(_) => panic!("unexpected value"), + }; + str.to_string() + } + + fn quote_types(&self, formats: &[Format], sep: &str) -> String { + formats.iter().map(|f| self.quote_type(f)).collect::>().join(sep) + } + + fn quote_write_value(&self, value: &str, format: &Format) -> String { + use Format::*; + match format { + TypeName(typename) => format!("{typename}.encode({value}, writer)"), + Unit => format!("writer.writeUnit({value})"), + Bool => format!("writer.writeBool({value})"), + I8 => format!("writer.writeI8({value})"), + I16 => format!("writer.writeI16({value})"), + I32 => format!("writer.writeI32({value})"), + I64 => format!("writer.writeI64({value})"), + I128 => format!("writer.writeI128({value})"), + U8 => format!("writer.writeU8({value})"), + U16 => format!("writer.writeU16({value})"), + U32 => format!("writer.writeU32({value})"), + U64 => format!("writer.writeU64({value})"), + U128 => format!("writer.writeU128({value})"), + F32 => format!("writer.writeF32({value})"), + F64 => format!("writer.writeF64({value})"), + Char => format!("writer.writeChar({value})"), + Str => format!("writer.writeString({value})"), + Bytes => format!("writer.writeBytes({value})"), + Option(inner) => { + formatdoc! { + " + if ({value}) {{ + writer.writeOptionTag(true) + {} + }} + else writer.writeOptionTag(false) + ", + self.quote_write_value(value, inner) + } + }, + Seq(format) => { + format!("writer.writeLength({value}.length); {value}.forEach((item) => {}) ", self.quote_write_value("item", format)) + } + Map { key: map_key, value: map_value } => { + format! { + "writer.writeMap({value}, k => {}, v => {})", + self.quote_write_value("k", map_key), + self.quote_write_value("v", map_value) + } + } + Tuple(formats) => { + use std::fmt::Write; + let mut lines = Vec::new(); + for (index, format) in formats.iter().enumerate() { + let expr = format!("{value}[{}]", index); + lines.push(self.quote_write_value(&expr, format)); + } + lines.join("\n") + } + TupleArray { content, .. } => { + format!("{value}.forEach((item) => {})", self.quote_write_value("item[0]", content)) + } + _ => panic!("unexpected case"), + } + } + + fn quote_read_value(&self, format: &Format) -> String { + use Format::*; + let str = match format { + TypeName(name) => &format!("{}.decode(input, reader)", self.quote_qualified_name(name)), + Unit => "reader.readUnit()", + Bool => "reader.readBool()", + I8 => "reader.readI8()", + I16 => "reader.readI16()", + I32 => "reader.readI32()", + I64 => "reader.readI64()", + I128 => "reader.readI128()", + U8 => "reader.readU8()", + U16 => "reader.readU16()", + U32 => "reader.readU32()", + U64 => "reader.readU64()", + U128 => "reader.readU128()", + F32 => "reader.readF32()", + F64 => "reader.readF64()", + Char => "reader.readChar()", + Str => "reader.readString()", + Bytes => "reader.readBytes()", + Option(format) => { + &formatdoc!( + r#"function () {{ + const tag = reader.readOptionTag() + return tag ? {} : null + }}()"#, + self.quote_read_value(format), + ) + } + Seq(format) => { + &formatdoc!( + r#"function () {{ + const length = reader.readLength() + const list: {}[] = [] + for (let i = 0; i < length; i++) list.push({}) + return list + }}()"#, + self.quote_type(format), + self.quote_read_value(format) + ) + } + Map { key, value } => { + &format!( + "reader.readMap<{}, {}>(() => {}, () => {})", + self.quote_type(key), + self.quote_type(value), + self.quote_read_value(key), + self.quote_read_value(value), + ) + } + Tuple(formats) => { + &format!( + "[{}]",formats.iter() + .map(|f| format!("{}", self.quote_read_value(f))) + .collect::>() + .join(", ") + ) + } + TupleArray { content, size } => { + &formatdoc!( + r#"function() {{ + const list: {} = [] + for (let i = 0; i < {}; i++) list.push([{}]) + return list + }}() + "#, + self.quote_type(format), size, self.quote_read_value(content) + ) + } + Variable(_) => panic!("unsupported value") + }; + str.to_string() + } + +} \ No newline at end of file diff --git a/suite/typescript/.gitignore b/suite/typescript/.gitignore new file mode 100644 index 000000000..fc0bac6ee --- /dev/null +++ b/suite/typescript/.gitignore @@ -0,0 +1,3 @@ +node_modules +target +.devenv \ No newline at end of file diff --git a/suite/typescript/Cargo.lock b/suite/typescript/Cargo.lock new file mode 100644 index 000000000..a759fa7d1 --- /dev/null +++ b/suite/typescript/Cargo.lock @@ -0,0 +1,366 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anyhow" +version = "1.0.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10f00e1f6e58a40e807377c75c6a7f97bf9044fab57816f2414e6f5f4499d7b8" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "include_dir" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24b56e147e6187d61e9d0f039f10e070d0c0a887e24fe0bb9ca3f29bfde62cab" +dependencies = [ + "glob", + "include_dir_impl", + "proc-macro-hack", +] + +[[package]] +name = "include_dir_impl" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a0c890c85da4bab7bce4204c707396bbd3c6c8a681716a51c8814cfc2b682df" +dependencies = [ + "anyhow", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "indoc" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" + +[[package]] +name = "libc" +version = "0.2.158" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "phf" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +dependencies = [ + "phf_macros", + "phf_shared", + "proc-macro-hack", +] + +[[package]] +name = "phf_generator" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "phf_shared" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" +dependencies = [ + "siphasher", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.20+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "serde" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-generate" +version = "0.26.0" +dependencies = [ + "heck", + "include_dir", + "indoc", + "phf", + "serde", + "serde-reflection", + "textwrap", +] + +[[package]] +name = "serde-reflection" +version = "0.4.0" +dependencies = [ + "once_cell", + "serde", + "thiserror", +] + +[[package]] +name = "serde_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "serdegen-bincode" +version = "0.1.0" +dependencies = [ + "bincode", + "serde", + "serde-generate", + "serde-reflection", +] + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "smawk" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "textwrap" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd05616119e612a8041ef58f2b578906cc2531a6069047ae092cfb86a325d835" +dependencies = [ + "smawk", + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + +[[package]] +name = "unicode-width" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] diff --git a/suite/typescript/Cargo.toml b/suite/typescript/Cargo.toml new file mode 100644 index 000000000..f85fe644f --- /dev/null +++ b/suite/typescript/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "ts-generator" +version = "0.1.0" +edition = "2021" + +[[bin]] +name = "generator" +path = "./rs/generator.rs" + +[dependencies] +bincode = { version = "1.3.3", features = ["i128"] } +serde = "1" +serde-generate = { path = "../../serde-generate" } +serde-reflection = { path = "../../serde-reflection" } diff --git a/suite/typescript/flake.lock b/suite/typescript/flake.lock new file mode 100644 index 000000000..b7486745a --- /dev/null +++ b/suite/typescript/flake.lock @@ -0,0 +1,458 @@ +{ + "nodes": { + "cachix": { + "inputs": { + "devenv": "devenv_2", + "flake-compat": [ + "devenv", + "flake-compat" + ], + "nixpkgs": [ + "devenv", + "nixpkgs" + ], + "pre-commit-hooks": [ + "devenv", + "pre-commit-hooks" + ] + }, + "locked": { + "lastModified": 1712055811, + "narHash": "sha256-7FcfMm5A/f02yyzuavJe06zLa9hcMHsagE28ADcmQvk=", + "owner": "cachix", + "repo": "cachix", + "rev": "02e38da89851ec7fec3356a5c04bc8349cae0e30", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "cachix", + "type": "github" + } + }, + "devenv": { + "inputs": { + "cachix": "cachix", + "flake-compat": "flake-compat_2", + "nix": "nix_2", + "nixpkgs": [ + "nixpkgs" + ], + "pre-commit-hooks": "pre-commit-hooks" + }, + "locked": { + "lastModified": 1725964132, + "narHash": "sha256-+IW4z7tXTgkEA677hbT+qsfNxaduCZY+1rAKFgVVmLI=", + "owner": "cachix", + "repo": "devenv", + "rev": "98c7c131e3fa30eb00e9bfe44c1a180c7f94102f", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "devenv_2": { + "inputs": { + "flake-compat": [ + "devenv", + "cachix", + "flake-compat" + ], + "nix": "nix", + "nixpkgs": "nixpkgs", + "poetry2nix": "poetry2nix", + "pre-commit-hooks": [ + "devenv", + "cachix", + "pre-commit-hooks" + ] + }, + "locked": { + "lastModified": 1708704632, + "narHash": "sha256-w+dOIW60FKMaHI1q5714CSibk99JfYxm0CzTinYWr+Q=", + "owner": "cachix", + "repo": "devenv", + "rev": "2ee4450b0f4b95a1b90f2eb5ffea98b90e48c196", + "type": "github" + }, + "original": { + "owner": "cachix", + "ref": "python-rewrite", + "repo": "devenv", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_2": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1725234343, + "narHash": "sha256-+ebgonl3NbiKD2UD0x4BszCZQ6sTfL4xioaM49o5B3Y=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "567b938d64d4b4112ee253b9274472dc3a346eb6", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1689068808, + "narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "devenv", + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "nix": { + "inputs": { + "flake-compat": "flake-compat", + "nixpkgs": [ + "devenv", + "cachix", + "devenv", + "nixpkgs" + ], + "nixpkgs-regression": "nixpkgs-regression" + }, + "locked": { + "lastModified": 1712911606, + "narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=", + "owner": "domenkozar", + "repo": "nix", + "rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12", + "type": "github" + }, + "original": { + "owner": "domenkozar", + "ref": "devenv-2.21", + "repo": "nix", + "type": "github" + } + }, + "nix-github-actions": { + "inputs": { + "nixpkgs": [ + "devenv", + "cachix", + "devenv", + "poetry2nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1688870561, + "narHash": "sha256-4UYkifnPEw1nAzqqPOTL2MvWtm3sNGw1UTYTalkTcGY=", + "owner": "nix-community", + "repo": "nix-github-actions", + "rev": "165b1650b753316aa7f1787f3005a8d2da0f5301", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nix-github-actions", + "type": "github" + } + }, + "nix_2": { + "inputs": { + "flake-compat": [ + "devenv", + "flake-compat" + ], + "nixpkgs": [ + "devenv", + "nixpkgs" + ], + "nixpkgs-regression": "nixpkgs-regression_2" + }, + "locked": { + "lastModified": 1712911606, + "narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=", + "owner": "domenkozar", + "repo": "nix", + "rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12", + "type": "github" + }, + "original": { + "owner": "domenkozar", + "ref": "devenv-2.21", + "repo": "nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1692808169, + "narHash": "sha256-x9Opq06rIiwdwGeK2Ykj69dNc2IvUH1fY55Wm7atwrE=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "9201b5ff357e781bf014d0330d18555695df7ba8", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1725233747, + "narHash": "sha256-Ss8QWLXdr2JCBPcYChJhz4xJm+h/xjl4G0c0XlP6a74=", + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/356624c12086a18f2ea2825fed34523d60ccc4e3.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/356624c12086a18f2ea2825fed34523d60ccc4e3.tar.gz" + } + }, + "nixpkgs-regression": { + "locked": { + "lastModified": 1643052045, + "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + } + }, + "nixpkgs-regression_2": { + "locked": { + "lastModified": 1643052045, + "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1710695816, + "narHash": "sha256-3Eh7fhEID17pv9ZxrPwCLfqXnYP006RKzSs0JptsN84=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "614b4613980a522ba49f0d194531beddbb7220d3", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1725910328, + "narHash": "sha256-n9pCtzGZ0httmTwMuEbi5E78UQ4ZbQMr1pzi5N0LAG8=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "5775c2583f1801df7b790bf7f7d710a19bac66f4", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "poetry2nix": { + "inputs": { + "flake-utils": "flake-utils", + "nix-github-actions": "nix-github-actions", + "nixpkgs": [ + "devenv", + "cachix", + "devenv", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1692876271, + "narHash": "sha256-IXfZEkI0Mal5y1jr6IRWMqK8GW2/f28xJenZIPQqkY0=", + "owner": "nix-community", + "repo": "poetry2nix", + "rev": "d5006be9c2c2417dafb2e2e5034d83fabd207ee3", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "poetry2nix", + "type": "github" + } + }, + "pre-commit-hooks": { + "inputs": { + "flake-compat": [ + "devenv", + "flake-compat" + ], + "flake-utils": "flake-utils_2", + "gitignore": "gitignore", + "nixpkgs": [ + "devenv", + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1713775815, + "narHash": "sha256-Wu9cdYTnGQQwtT20QQMg7jzkANKQjwBD9iccfGKkfls=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "2ac4dcbf55ed43f3be0bae15e181f08a57af24a4", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "root": { + "inputs": { + "devenv": "devenv", + "flake-parts": "flake-parts", + "nixpkgs": "nixpkgs_2" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/suite/typescript/flake.nix b/suite/typescript/flake.nix new file mode 100644 index 000000000..8f922f485 --- /dev/null +++ b/suite/typescript/flake.nix @@ -0,0 +1,71 @@ +{ + inputs = { + # + nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; + + flake-parts.url = "github:hercules-ci/flake-parts"; + + devenv.url = "github:cachix/devenv"; + devenv.inputs.nixpkgs.follows = "nixpkgs"; + }; + + outputs = {self, ...} @ inputs: + with builtins; let + lib = inputs.nixpkgs.lib; + in + with lib; + inputs.flake-parts.lib.mkFlake { + inherit inputs; + specialArgs = {inherit lib;}; + } + ({moduleWithSystem, ...}: { + imports = with inputs; [devenv.flakeModule]; + systems = ["x86_64-linux"]; + perSystem = { + config, + system, + self', + inputs', + ... + }: let + pkgs = import inputs.nixpkgs { + inherit system; + config.allowUnfree = true; + }; + in { + _module.args = {inherit pkgs;}; + devenv.shells.default = {config, ...} @ devenvArgs: let + inherit (config.devenv) root state profile; + in { + packages = with pkgs; [nodejs_22 nodePackages.pnpm protobuf_28]; + scripts."generate:proto".exec = concatStringsSep " \\\n" [ + "protoc" + "--plugin ${root}/node_modules/.bin/protoc-gen-ts_proto" + "--ts_proto_out ts/proto" + "--ts_proto_opt esModuleInterop=true,snakeToCamel=false,forceLong=number,oneof=unions,outputJsonMethods=false,env=browser" + + "--proto_path schema-proto" + "--proto_path ${pkgs.protobuf}/include/google/protobuf/" + ''$(find schema-proto -iname "*.proto")'' + ]; + + scripts."generate:bincode".exec = '' + pushd ${root} + cargo run + popd + ''; + + scripts."test".exec = '' + pushd ${root} + node_modules/.bin/tsx ts/test.ts + popd + ''; + scripts."benchmark".exec = '' + pushd ${root} + node_modules/.bin/tsx ts/bench.ts + popd + ''; + }; + }; + }); +} diff --git a/suite/typescript/package.json b/suite/typescript/package.json new file mode 100644 index 000000000..da72aa4cf --- /dev/null +++ b/suite/typescript/package.json @@ -0,0 +1,15 @@ +{ + "name": "js-bin-benchmark", + "type": "module", + "version": "0.0.0", + "dependencies": { + "long": "^5.2.3", + "protobufjs": "^7.4.0", + "tinybench": "^2.9.0", + "ts-proto": "1", + "tsx": "^4.19.0" + }, + "devDependencies": { + "@types/node": "^22.5.4" + } +} diff --git a/suite/typescript/pnpm-lock.yaml b/suite/typescript/pnpm-lock.yaml new file mode 100644 index 000000000..3d23bd4f6 --- /dev/null +++ b/suite/typescript/pnpm-lock.yaml @@ -0,0 +1,453 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + long: + specifier: ^5.2.3 + version: 5.2.3 + protobufjs: + specifier: ^7.4.0 + version: 7.4.0 + tinybench: + specifier: ^2.9.0 + version: 2.9.0 + ts-proto: + specifier: '1' + version: 1.181.2 + tsx: + specifier: ^4.19.0 + version: 4.19.0 + devDependencies: + '@types/node': + specifier: ^22.5.4 + version: 22.5.4 + +packages: + + '@esbuild/aix-ppc64@0.23.1': + resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.23.1': + resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.23.1': + resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.23.1': + resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.23.1': + resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.23.1': + resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.23.1': + resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.23.1': + resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.23.1': + resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.23.1': + resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.23.1': + resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.23.1': + resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.23.1': + resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.23.1': + resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.23.1': + resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.23.1': + resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.23.1': + resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.23.1': + resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.23.1': + resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.23.1': + resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.23.1': + resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.23.1': + resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.23.1': + resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.23.1': + resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@protobufjs/aspromise@1.1.2': + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + + '@protobufjs/base64@1.1.2': + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + + '@protobufjs/codegen@2.0.4': + resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} + + '@protobufjs/eventemitter@1.1.0': + resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} + + '@protobufjs/fetch@1.1.0': + resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} + + '@protobufjs/float@1.0.2': + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} + + '@protobufjs/inquire@1.1.0': + resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} + + '@protobufjs/path@1.1.2': + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + + '@protobufjs/pool@1.1.0': + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + + '@protobufjs/utf8@1.1.0': + resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + + '@types/node@22.5.4': + resolution: {integrity: sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==} + + case-anything@2.1.13: + resolution: {integrity: sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng==} + engines: {node: '>=12.13'} + + detect-libc@1.0.3: + resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} + engines: {node: '>=0.10'} + hasBin: true + + dprint-node@1.0.8: + resolution: {integrity: sha512-iVKnUtYfGrYcW1ZAlfR/F59cUVL8QIhWoBJoSjkkdua/dkWIgjZfiLMeTjiB06X0ZLkQ0M2C1VbUj/CxkIf1zg==} + + esbuild@0.23.1: + resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} + engines: {node: '>=18'} + hasBin: true + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + get-tsconfig@4.8.0: + resolution: {integrity: sha512-Pgba6TExTZ0FJAn1qkJAjIeKoDJ3CsI2ChuLohJnZl/tTU8MVrq3b+2t5UOPfRa4RMsorClBjJALkJUMjG1PAw==} + + long@5.2.3: + resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} + + protobufjs@7.4.0: + resolution: {integrity: sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==} + engines: {node: '>=12.0.0'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + ts-poet@6.9.0: + resolution: {integrity: sha512-roe6W6MeZmCjRmppyfOURklO5tQFQ6Sg7swURKkwYJvV7dbGCrK28um5+51iW3twdPRKtwarqFAVMU6G1mvnuQ==} + + ts-proto-descriptors@1.16.0: + resolution: {integrity: sha512-3yKuzMLpltdpcyQji1PJZRfoo4OJjNieKTYkQY8pF7xGKsYz/RHe3aEe4KiRxcinoBmnEhmuI+yJTxLb922ULA==} + + ts-proto@1.181.2: + resolution: {integrity: sha512-knJ8dtjn2Pd0c5ZGZG8z9DMiD4PUY8iGI9T9tb8DvGdWRMkLpf0WcPO7G+7cmbZyxvNTAG6ci3fybEaFgMZIvg==} + hasBin: true + + tsx@4.19.0: + resolution: {integrity: sha512-bV30kM7bsLZKZIOCHeMNVMJ32/LuJzLVajkQI/qf92J2Qr08ueLQvW00PUZGiuLPP760UINwupgUj8qrSCPUKg==} + engines: {node: '>=18.0.0'} + hasBin: true + + undici-types@6.19.8: + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + +snapshots: + + '@esbuild/aix-ppc64@0.23.1': + optional: true + + '@esbuild/android-arm64@0.23.1': + optional: true + + '@esbuild/android-arm@0.23.1': + optional: true + + '@esbuild/android-x64@0.23.1': + optional: true + + '@esbuild/darwin-arm64@0.23.1': + optional: true + + '@esbuild/darwin-x64@0.23.1': + optional: true + + '@esbuild/freebsd-arm64@0.23.1': + optional: true + + '@esbuild/freebsd-x64@0.23.1': + optional: true + + '@esbuild/linux-arm64@0.23.1': + optional: true + + '@esbuild/linux-arm@0.23.1': + optional: true + + '@esbuild/linux-ia32@0.23.1': + optional: true + + '@esbuild/linux-loong64@0.23.1': + optional: true + + '@esbuild/linux-mips64el@0.23.1': + optional: true + + '@esbuild/linux-ppc64@0.23.1': + optional: true + + '@esbuild/linux-riscv64@0.23.1': + optional: true + + '@esbuild/linux-s390x@0.23.1': + optional: true + + '@esbuild/linux-x64@0.23.1': + optional: true + + '@esbuild/netbsd-x64@0.23.1': + optional: true + + '@esbuild/openbsd-arm64@0.23.1': + optional: true + + '@esbuild/openbsd-x64@0.23.1': + optional: true + + '@esbuild/sunos-x64@0.23.1': + optional: true + + '@esbuild/win32-arm64@0.23.1': + optional: true + + '@esbuild/win32-ia32@0.23.1': + optional: true + + '@esbuild/win32-x64@0.23.1': + optional: true + + '@protobufjs/aspromise@1.1.2': {} + + '@protobufjs/base64@1.1.2': {} + + '@protobufjs/codegen@2.0.4': {} + + '@protobufjs/eventemitter@1.1.0': {} + + '@protobufjs/fetch@1.1.0': + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/inquire': 1.1.0 + + '@protobufjs/float@1.0.2': {} + + '@protobufjs/inquire@1.1.0': {} + + '@protobufjs/path@1.1.2': {} + + '@protobufjs/pool@1.1.0': {} + + '@protobufjs/utf8@1.1.0': {} + + '@types/node@22.5.4': + dependencies: + undici-types: 6.19.8 + + case-anything@2.1.13: {} + + detect-libc@1.0.3: {} + + dprint-node@1.0.8: + dependencies: + detect-libc: 1.0.3 + + esbuild@0.23.1: + optionalDependencies: + '@esbuild/aix-ppc64': 0.23.1 + '@esbuild/android-arm': 0.23.1 + '@esbuild/android-arm64': 0.23.1 + '@esbuild/android-x64': 0.23.1 + '@esbuild/darwin-arm64': 0.23.1 + '@esbuild/darwin-x64': 0.23.1 + '@esbuild/freebsd-arm64': 0.23.1 + '@esbuild/freebsd-x64': 0.23.1 + '@esbuild/linux-arm': 0.23.1 + '@esbuild/linux-arm64': 0.23.1 + '@esbuild/linux-ia32': 0.23.1 + '@esbuild/linux-loong64': 0.23.1 + '@esbuild/linux-mips64el': 0.23.1 + '@esbuild/linux-ppc64': 0.23.1 + '@esbuild/linux-riscv64': 0.23.1 + '@esbuild/linux-s390x': 0.23.1 + '@esbuild/linux-x64': 0.23.1 + '@esbuild/netbsd-x64': 0.23.1 + '@esbuild/openbsd-arm64': 0.23.1 + '@esbuild/openbsd-x64': 0.23.1 + '@esbuild/sunos-x64': 0.23.1 + '@esbuild/win32-arm64': 0.23.1 + '@esbuild/win32-ia32': 0.23.1 + '@esbuild/win32-x64': 0.23.1 + + fsevents@2.3.3: + optional: true + + get-tsconfig@4.8.0: + dependencies: + resolve-pkg-maps: 1.0.0 + + long@5.2.3: {} + + protobufjs@7.4.0: + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.4 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.0 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.0 + '@types/node': 22.5.4 + long: 5.2.3 + + resolve-pkg-maps@1.0.0: {} + + tinybench@2.9.0: {} + + ts-poet@6.9.0: + dependencies: + dprint-node: 1.0.8 + + ts-proto-descriptors@1.16.0: + dependencies: + long: 5.2.3 + protobufjs: 7.4.0 + + ts-proto@1.181.2: + dependencies: + case-anything: 2.1.13 + protobufjs: 7.4.0 + ts-poet: 6.9.0 + ts-proto-descriptors: 1.16.0 + + tsx@4.19.0: + dependencies: + esbuild: 0.23.1 + get-tsconfig: 4.8.0 + optionalDependencies: + fsevents: 2.3.3 + + undici-types@6.19.8: {} diff --git a/suite/typescript/readme.md b/suite/typescript/readme.md new file mode 100644 index 000000000..fc5d30d0f --- /dev/null +++ b/suite/typescript/readme.md @@ -0,0 +1,30 @@ +## JavaScript benchamrks. + +Run: +```sh +nix develop . --impure +pnpm i +generate:proto && generate:bincode && test && benchmark +``` + +## Results +### Encode +``` +┌─────────┬───────────────────────────────┬─────────────┬────────────────────┬──────────┬─────────┐ +│ (index) │ Task Name │ ops/sec │ Average Time (ns) │ Margin │ Samples │ +├─────────┼───────────────────────────────┼─────────────┼────────────────────┼──────────┼─────────┤ +│ 0 │ 'JSON:encode' │ '705,603' │ 1417.2268751310808 │ '±0.31%' │ 705604 │ +│ 1 │ 'protobuf-js-ts-proto:encode' │ '1,175,217' │ 850.9063892825211 │ '±0.77%' │ 1175218 │ +│ 2 │ 'serdegen-bincode:encode' │ '115,288' │ 8673.892392162355 │ '±1.06%' │ 115289 │ +└─────────┴───────────────────────────────┴─────────────┴────────────────────┴──────────┴─────────┘ +``` +### Decode +``` +┌─────────┬───────────────────────────────┬─────────────┬────────────────────┬──────────┬─────────┐ +│ (index) │ Task Name │ ops/sec │ Average Time (ns) │ Margin │ Samples │ +├─────────┼───────────────────────────────┼─────────────┼────────────────────┼──────────┼─────────┤ +│ 0 │ 'JSON:decode' │ '422,401' │ 2367.417069520852 │ '±1.83%' │ 422402 │ +│ 1 │ 'protobuf-js-ts-proto:decode' │ '1,243,009' │ 804.4992534249864 │ '±0.51%' │ 1243010 │ +│ 2 │ 'serdegen-bincode:decode' │ '48,529' │ 20605.876138467553 │ '±0.32%' │ 48530 │ +└─────────┴───────────────────────────────┴─────────────┴────────────────────┴──────────┴─────────┘ +``` \ No newline at end of file diff --git a/suite/typescript/rs/generator.rs b/suite/typescript/rs/generator.rs new file mode 100644 index 000000000..db771a7ad --- /dev/null +++ b/suite/typescript/rs/generator.rs @@ -0,0 +1,74 @@ +use std::{collections::HashMap, error::Error}; +use serde::{Deserialize, Serialize}; + +fn main() -> Result<(), Box> { + use serde_reflection::{Registry, Tracer, TracerConfig}; + let mut tracer = Tracer::new(TracerConfig::default()); + + #[derive(Clone, Debug, Serialize, Deserialize)] + struct SimpleStruct { + a: u32, + b: String, + } + + #[derive(Serialize, Deserialize, Debug)] + enum MultiEnum { + VariantA(i32), + VariantB(String), + VariantC { x: u8, y: f64 }, + UnitVariant, // Unit variant added + } + + #[derive(Serialize, Deserialize, Debug)] + struct UnitStruct; + + #[derive(Serialize, Deserialize, Debug)] + struct NewtypeStruct(i32); + + #[derive(Serialize, Deserialize, Debug)] + struct TupleStruct(i32, f64, String); + + #[derive(Serialize, Deserialize, Debug)] + struct ComplexStruct { + inner: SimpleStruct, + flag: bool, + items: Vec, + unit: UnitStruct, + newtype: NewtypeStruct, + tuple: TupleStruct, + } + + tracer.trace_simple_type::()?; + tracer.trace_simple_type::()?; + tracer.trace_simple_type::()?; + tracer.trace_simple_type::()?; + tracer.trace_simple_type::()?; + tracer.trace_simple_type::()?; + + let simple_instance = SimpleStruct { a: 42, b: "Hello".to_string() }; + let enum_instance = MultiEnum::VariantC { x: 5, y: 3.14 }; + let unit_variant = MultiEnum::UnitVariant; + let complex_instance = ComplexStruct { + inner: simple_instance.clone(), + flag: true, + items: vec![MultiEnum::VariantA(10), MultiEnum::VariantB("World".to_string())], + unit: UnitStruct, + newtype: NewtypeStruct(99), + tuple: TupleStruct(123, 45.67, "Test".to_string()), + }; + + println!("simple_instance: {:?}", bincode::serialize(&simple_instance)?); + println!("enum_instance: {:?}", bincode::serialize(&enum_instance)?); + println!("unit_variant: {:?}", bincode::serialize(&unit_variant)?); + println!("complex_instance: {:?}", bincode::serialize(&complex_instance)?); + + let registry = tracer.registry()?; + + use serde_generate::{typescript, CodeGeneratorConfig, Encoding}; + let mut source = Vec::new(); + let config = CodeGeneratorConfig::new("bincode".to_string()).with_encodings(vec![Encoding::Bincode]); + typescript::CodeGenerator::new(&config).output(&mut source, ®istry)?; + std::fs::write(format!("{}/ts/bincode/registry.ts", env!("CARGO_MANIFEST_DIR")), source)?; + + Ok(()) +} diff --git a/suite/typescript/schema-proto/main.proto b/suite/typescript/schema-proto/main.proto new file mode 100644 index 000000000..b592f0e1c --- /dev/null +++ b/suite/typescript/schema-proto/main.proto @@ -0,0 +1,52 @@ +syntax = "proto3"; + +message UnitStruct {} + +message NewtypeStruct { + int32 value = 1; +} + +message TupleStruct { + int32 first = 1; + double second = 2; + string third = 3; +} + +message SimpleStruct { + int32 a = 1; + string b = 2; +} + +message ComplexStruct { + SimpleStruct inner = 1; + bool flag = 2; + repeated MultiEnum items = 3; + UnitStruct unit = 4; + int32 newtype = 5; + TupleStruct tuple = 6; +} + +message MultiEnum { + oneof variant { + VariantA variant_a = 1; + VariantB variant_b = 2; + VariantC variant_c = 3; + UnitVariant unit_variant = 4; + } +} + +message VariantA { + int32 value = 1; +} + +message VariantB { + string value = 1; +} + +message VariantC { + int32 x = 1; + double y = 2; +} + +// Unit variant (empty) +message UnitVariant {} \ No newline at end of file diff --git a/suite/typescript/ts/bench.ts b/suite/typescript/ts/bench.ts new file mode 100644 index 000000000..b308ebce2 --- /dev/null +++ b/suite/typescript/ts/bench.ts @@ -0,0 +1,60 @@ +import { Bench } from 'tinybench' +import * as ProtobufRegistry from './proto/main.ts' +import * as BincodeRegistry from './bincode/registry.ts' +import * as Data from './data.ts' + +const ComplexStruct_pb_obj: ProtobufRegistry.ComplexStruct = { + inner: { a: 42, b: "Hello" }, + flag: true, + items: [ + { variant: { $case: "variant_a", variant_a: { value: 10 } } }, + { variant: { $case: "variant_b", variant_b: { value: "World" } } }, + ], + unit: {}, + newtype: 99, + tuple: { first: 123, second: 45.67, third: "Test" } +} + +await async function bench_encode() { + const b = new Bench({ time: 1_000 }) + + b.add('JSON:encode', () => { + JSON.stringify(Data.ComplexStruct_obj) + }) + b.add('protobuf-js-ts-proto:encode', () => { + ProtobufRegistry.ComplexStruct.encode(ComplexStruct_pb_obj) + }) + b.add('serdegen-bincode:encode', () => { + BincodeRegistry.ComplexStruct.encode(Data.ComplexStruct_obj) + }) + await b.warmup() + await b.run() + console.table(b.table()) +}() + + + +await async function bench_decode() { + + const b = new Bench({ time: 1_000 }) + + const json_encoded = JSON.stringify(Data.ComplexStruct_obj) + b.add('JSON:decode', () => { + JSON.parse(json_encoded) + }) + + const pb_encoded = ProtobufRegistry.ComplexStruct.encode(ComplexStruct_pb_obj).finish() + b.add('protobuf-js-ts-proto:decode', () => { + ProtobufRegistry.ComplexStruct.decode(pb_encoded) + }) + + const bincodec_encoded = BincodeRegistry.ComplexStruct.encode(Data.ComplexStruct_obj) + b.add('serdegen-bincode:decode', () => { + BincodeRegistry.ComplexStruct.decode(bincodec_encoded) + }) + + await b.warmup() + await b.run() + console.table(b.table()) +}() + diff --git a/suite/typescript/ts/bincode/bincode.ts b/suite/typescript/ts/bincode/bincode.ts new file mode 120000 index 000000000..6e360e71e --- /dev/null +++ b/suite/typescript/ts/bincode/bincode.ts @@ -0,0 +1 @@ +/home/foxpro/misc/sources/serde-reflection/serde-generate/runtime/typescript/bincode.ts \ No newline at end of file diff --git a/suite/typescript/ts/bincode/registry.ts b/suite/typescript/ts/bincode/registry.ts new file mode 100644 index 000000000..decc970a8 --- /dev/null +++ b/suite/typescript/ts/bincode/registry.ts @@ -0,0 +1,162 @@ +import type * as $t from "./serde.ts" +import { BincodeReader, BincodeWriter } from "./bincode.ts" + +export type ComplexStruct = { + inner: SimpleStruct, + flag: $t.bool, + items: $t.Seq, + unit: UnitStruct, + newtype: NewtypeStruct, + tuple: TupleStruct, +} + +export type MultiEnum = + | { $: "VariantA", VariantA: $t.i32 } + | { $: "VariantB", VariantB: $t.str } + | { $: "VariantC", VariantC: { x: $t.u8, y: $t.f64 } } + | { $: "UnitVariant", UnitVariant: $t.unit } + +export type NewtypeStruct = $t.i32 + +export type SimpleStruct = { + a: $t.u32, + b: $t.str, +} + +export type TupleStruct = [$t.i32, $t.f64, $t.str] + +export type UnitStruct = $t.unit + +export const ComplexStruct = { + encode(value: ComplexStruct, writer = new BincodeWriter()) { + SimpleStruct.encode(value.inner, writer) + writer.writeBool(value.flag) + writer.writeLength(value.items.length); value.items.forEach((item) => MultiEnum.encode(item, writer)) + UnitStruct.encode(value.unit, writer) + NewtypeStruct.encode(value.newtype, writer) + TupleStruct.encode(value.tuple, writer) + return writer.getBytes() + }, + decode(input: Uint8Array, reader = new BincodeReader(input)) { + const value = {} as ComplexStruct + value.inner = SimpleStruct.decode(input, reader) + value.flag = reader.readBool() + value.items = function () { + const length = reader.readLength() + const list: MultiEnum[] = [] + for (let i = 0; i < length; i++) list.push(MultiEnum.decode(input, reader)) + return list + }() + value.unit = UnitStruct.decode(input, reader) + value.newtype = NewtypeStruct.decode(input, reader) + value.tuple = TupleStruct.decode(input, reader) + return value + } +} + +export const MultiEnum = { + encode(value: MultiEnum, writer = new BincodeWriter()) { + switch (value.$) { + case "VariantA": { + writer.writeVariantIndex(0) + writer.writeI32(value.VariantA) + break + } + case "VariantB": { + writer.writeVariantIndex(1) + writer.writeString(value.VariantB) + break + } + case "VariantC": { + writer.writeVariantIndex(2) + writer.writeU8(value.VariantC.x) + writer.writeF64(value.VariantC.y) + break + } + case "UnitVariant": { + writer.writeVariantIndex(3) + writer.writeUnit(value.UnitVariant) + break + } + } + return writer.getBytes() + }, + decode(input: Uint8Array, reader = new BincodeReader(input)) { + let value: MultiEnum + switch (reader.readVariantIndex()) { + case 0: { + value = { $: "VariantA" } as $t.WrapperOfCase + value.VariantA = reader.readI32() + break + } + case 1: { + value = { $: "VariantB" } as $t.WrapperOfCase + value.VariantB = reader.readString() + break + } + case 2: { + value = { $: "VariantC" } as $t.WrapperOfCase + value.VariantC = {} as $t.WrapperOfCase["VariantC"] + value.VariantC.x = reader.readU8() + value.VariantC.y = reader.readF64() + break + } + case 3: { + value = { $: "UnitVariant" } as $t.WrapperOfCase + value.UnitVariant = reader.readUnit() + break + } + } + + return value + } +} + +export const NewtypeStruct = { + encode(value: NewtypeStruct, writer = new BincodeWriter()) { + writer.writeI32(value) + return writer.getBytes() + }, + decode(input: Uint8Array, reader = new BincodeReader(input)) { + const value: NewtypeStruct = reader.readI32() + return value + } +} + +export const SimpleStruct = { + encode(value: SimpleStruct, writer = new BincodeWriter()) { + writer.writeU32(value.a) + writer.writeString(value.b) + return writer.getBytes() + }, + decode(input: Uint8Array, reader = new BincodeReader(input)) { + const value = {} as SimpleStruct + value.a = reader.readU32() + value.b = reader.readString() + return value + } +} + +export const TupleStruct = { + encode(value: TupleStruct, writer = new BincodeWriter()) { + writer.writeI32(value[0]) + writer.writeF64(value[1]) + writer.writeString(value[2]) + return writer.getBytes() + }, + decode(input: Uint8Array, reader = new BincodeReader(input)) { + const value: TupleStruct = [reader.readI32(), reader.readF64(), reader.readString()] + return value + } +} + +export const UnitStruct = { + encode(value: UnitStruct, writer = new BincodeWriter()) { + writer.writeUnit(null) + return writer.getBytes() + }, + decode(input: Uint8Array, reader = new BincodeReader(input)) { + const value: $t.unit = reader.readUnit() + return value + } +} diff --git a/suite/typescript/ts/bincode/serde.ts b/suite/typescript/ts/bincode/serde.ts new file mode 120000 index 000000000..726bbb790 --- /dev/null +++ b/suite/typescript/ts/bincode/serde.ts @@ -0,0 +1 @@ +/home/foxpro/misc/sources/serde-reflection/serde-generate/runtime/typescript/serde.ts \ No newline at end of file diff --git a/suite/typescript/ts/data.ts b/suite/typescript/ts/data.ts new file mode 100644 index 000000000..fe76568a8 --- /dev/null +++ b/suite/typescript/ts/data.ts @@ -0,0 +1,31 @@ +import * as Registry from "./bincode/registry.ts" + +export const SimpleStruct_obj: Registry.SimpleStruct = { a: 42, b: "Hello" } +export const SimpleStruct_bin = Uint8Array.from([42, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 72, 101, 108, 108, 111]) + +export const MultiEnum_VariantC_obj: Registry.MultiEnum = { + $: "VariantC", + VariantC: { x: 5, y: 3.14 } +} +export const MultiEnum_VariantC_bin = Uint8Array.from([2, 0, 0, 0, 5, 31, 133, 235, 81, 184, 30, 9, 64]) + +export const MultiEnum_Unit_obj: Registry.MultiEnum = { $: "UnitVariant", UnitVariant: null } +export const MultiEnum_Unit_bin = Uint8Array.from([3, 0, 0, 0]) + +export const ComplexStruct_obj: Registry.ComplexStruct = { + inner: { a: 42, b: "Hello" }, + flag: true, + items: [ + { $: "VariantA", VariantA: 10 }, + { $: "VariantB", VariantB: "World" } + ], + unit: null, + newtype: 99, + tuple: [123, 45.67, "Test"] +} +export const ComplexStruct_bin = Uint8Array.from([ + 42, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 72, 101, 108, 108, 111, 1, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 87, + 111, 114, 108, 100, 99, 0, 0, 0, 123, 0, 0, 0, 246, 40, 92, 143, 194, 213, 70, + 64, 4, 0, 0, 0, 0, 0, 0, 0, 84, 101, 115, 116 +]) \ No newline at end of file diff --git a/suite/typescript/ts/proto/main.ts b/suite/typescript/ts/proto/main.ts new file mode 100644 index 000000000..329a88d5f --- /dev/null +++ b/suite/typescript/ts/proto/main.ts @@ -0,0 +1,668 @@ +// Code generated by protoc-gen-ts_proto. DO NOT EDIT. +// versions: +// protoc-gen-ts_proto v1.181.2 +// protoc v5.28.0 +// source: main.proto + +/* eslint-disable */ +import _m0 from "protobufjs/minimal"; + +export const protobufPackage = ""; + +export interface UnitStruct { +} + +export interface NewtypeStruct { + value: number; +} + +export interface TupleStruct { + first: number; + second: number; + third: string; +} + +export interface SimpleStruct { + a: number; + b: string; +} + +export interface ComplexStruct { + inner: SimpleStruct | undefined; + flag: boolean; + items: MultiEnum[]; + unit: UnitStruct | undefined; + newtype: number; + tuple: TupleStruct | undefined; +} + +export interface MultiEnum { + variant?: + | { $case: "variant_a"; variant_a: VariantA } + | { $case: "variant_b"; variant_b: VariantB } + | { $case: "variant_c"; variant_c: VariantC } + | { $case: "unit_variant"; unit_variant: UnitVariant } + | undefined; +} + +export interface VariantA { + value: number; +} + +export interface VariantB { + value: string; +} + +export interface VariantC { + x: number; + y: number; +} + +/** Unit variant (empty) */ +export interface UnitVariant { +} + +function createBaseUnitStruct(): UnitStruct { + return {}; +} + +export const UnitStruct = { + encode(_: UnitStruct, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): UnitStruct { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseUnitStruct(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + create, I>>(base?: I): UnitStruct { + return UnitStruct.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(_: I): UnitStruct { + const message = createBaseUnitStruct(); + return message; + }, +}; + +function createBaseNewtypeStruct(): NewtypeStruct { + return { value: 0 }; +} + +export const NewtypeStruct = { + encode(message: NewtypeStruct, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.value !== 0) { + writer.uint32(8).int32(message.value); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): NewtypeStruct { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseNewtypeStruct(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.value = reader.int32(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + create, I>>(base?: I): NewtypeStruct { + return NewtypeStruct.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): NewtypeStruct { + const message = createBaseNewtypeStruct(); + message.value = object.value ?? 0; + return message; + }, +}; + +function createBaseTupleStruct(): TupleStruct { + return { first: 0, second: 0, third: "" }; +} + +export const TupleStruct = { + encode(message: TupleStruct, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.first !== 0) { + writer.uint32(8).int32(message.first); + } + if (message.second !== 0) { + writer.uint32(17).double(message.second); + } + if (message.third !== "") { + writer.uint32(26).string(message.third); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): TupleStruct { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseTupleStruct(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.first = reader.int32(); + continue; + case 2: + if (tag !== 17) { + break; + } + + message.second = reader.double(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.third = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + create, I>>(base?: I): TupleStruct { + return TupleStruct.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): TupleStruct { + const message = createBaseTupleStruct(); + message.first = object.first ?? 0; + message.second = object.second ?? 0; + message.third = object.third ?? ""; + return message; + }, +}; + +function createBaseSimpleStruct(): SimpleStruct { + return { a: 0, b: "" }; +} + +export const SimpleStruct = { + encode(message: SimpleStruct, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.a !== 0) { + writer.uint32(8).int32(message.a); + } + if (message.b !== "") { + writer.uint32(18).string(message.b); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): SimpleStruct { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseSimpleStruct(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.a = reader.int32(); + continue; + case 2: + if (tag !== 18) { + break; + } + + message.b = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + create, I>>(base?: I): SimpleStruct { + return SimpleStruct.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): SimpleStruct { + const message = createBaseSimpleStruct(); + message.a = object.a ?? 0; + message.b = object.b ?? ""; + return message; + }, +}; + +function createBaseComplexStruct(): ComplexStruct { + return { inner: undefined, flag: false, items: [], unit: undefined, newtype: 0, tuple: undefined }; +} + +export const ComplexStruct = { + encode(message: ComplexStruct, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.inner !== undefined) { + SimpleStruct.encode(message.inner, writer.uint32(10).fork()).ldelim(); + } + if (message.flag !== false) { + writer.uint32(16).bool(message.flag); + } + for (const v of message.items) { + MultiEnum.encode(v!, writer.uint32(26).fork()).ldelim(); + } + if (message.unit !== undefined) { + UnitStruct.encode(message.unit, writer.uint32(34).fork()).ldelim(); + } + if (message.newtype !== 0) { + writer.uint32(40).int32(message.newtype); + } + if (message.tuple !== undefined) { + TupleStruct.encode(message.tuple, writer.uint32(50).fork()).ldelim(); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): ComplexStruct { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseComplexStruct(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.inner = SimpleStruct.decode(reader, reader.uint32()); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.flag = reader.bool(); + continue; + case 3: + if (tag !== 26) { + break; + } + + message.items.push(MultiEnum.decode(reader, reader.uint32())); + continue; + case 4: + if (tag !== 34) { + break; + } + + message.unit = UnitStruct.decode(reader, reader.uint32()); + continue; + case 5: + if (tag !== 40) { + break; + } + + message.newtype = reader.int32(); + continue; + case 6: + if (tag !== 50) { + break; + } + + message.tuple = TupleStruct.decode(reader, reader.uint32()); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + create, I>>(base?: I): ComplexStruct { + return ComplexStruct.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): ComplexStruct { + const message = createBaseComplexStruct(); + message.inner = (object.inner !== undefined && object.inner !== null) + ? SimpleStruct.fromPartial(object.inner) + : undefined; + message.flag = object.flag ?? false; + message.items = object.items?.map((e) => MultiEnum.fromPartial(e)) || []; + message.unit = (object.unit !== undefined && object.unit !== null) + ? UnitStruct.fromPartial(object.unit) + : undefined; + message.newtype = object.newtype ?? 0; + message.tuple = (object.tuple !== undefined && object.tuple !== null) + ? TupleStruct.fromPartial(object.tuple) + : undefined; + return message; + }, +}; + +function createBaseMultiEnum(): MultiEnum { + return { variant: undefined }; +} + +export const MultiEnum = { + encode(message: MultiEnum, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + switch (message.variant?.$case) { + case "variant_a": + VariantA.encode(message.variant.variant_a, writer.uint32(10).fork()).ldelim(); + break; + case "variant_b": + VariantB.encode(message.variant.variant_b, writer.uint32(18).fork()).ldelim(); + break; + case "variant_c": + VariantC.encode(message.variant.variant_c, writer.uint32(26).fork()).ldelim(); + break; + case "unit_variant": + UnitVariant.encode(message.variant.unit_variant, writer.uint32(34).fork()).ldelim(); + break; + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): MultiEnum { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMultiEnum(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.variant = { $case: "variant_a", variant_a: VariantA.decode(reader, reader.uint32()) }; + continue; + case 2: + if (tag !== 18) { + break; + } + + message.variant = { $case: "variant_b", variant_b: VariantB.decode(reader, reader.uint32()) }; + continue; + case 3: + if (tag !== 26) { + break; + } + + message.variant = { $case: "variant_c", variant_c: VariantC.decode(reader, reader.uint32()) }; + continue; + case 4: + if (tag !== 34) { + break; + } + + message.variant = { $case: "unit_variant", unit_variant: UnitVariant.decode(reader, reader.uint32()) }; + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + create, I>>(base?: I): MultiEnum { + return MultiEnum.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): MultiEnum { + const message = createBaseMultiEnum(); + if ( + object.variant?.$case === "variant_a" && + object.variant?.variant_a !== undefined && + object.variant?.variant_a !== null + ) { + message.variant = { $case: "variant_a", variant_a: VariantA.fromPartial(object.variant.variant_a) }; + } + if ( + object.variant?.$case === "variant_b" && + object.variant?.variant_b !== undefined && + object.variant?.variant_b !== null + ) { + message.variant = { $case: "variant_b", variant_b: VariantB.fromPartial(object.variant.variant_b) }; + } + if ( + object.variant?.$case === "variant_c" && + object.variant?.variant_c !== undefined && + object.variant?.variant_c !== null + ) { + message.variant = { $case: "variant_c", variant_c: VariantC.fromPartial(object.variant.variant_c) }; + } + if ( + object.variant?.$case === "unit_variant" && + object.variant?.unit_variant !== undefined && + object.variant?.unit_variant !== null + ) { + message.variant = { $case: "unit_variant", unit_variant: UnitVariant.fromPartial(object.variant.unit_variant) }; + } + return message; + }, +}; + +function createBaseVariantA(): VariantA { + return { value: 0 }; +} + +export const VariantA = { + encode(message: VariantA, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.value !== 0) { + writer.uint32(8).int32(message.value); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): VariantA { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseVariantA(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.value = reader.int32(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + create, I>>(base?: I): VariantA { + return VariantA.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): VariantA { + const message = createBaseVariantA(); + message.value = object.value ?? 0; + return message; + }, +}; + +function createBaseVariantB(): VariantB { + return { value: "" }; +} + +export const VariantB = { + encode(message: VariantB, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.value !== "") { + writer.uint32(10).string(message.value); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): VariantB { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseVariantB(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 10) { + break; + } + + message.value = reader.string(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + create, I>>(base?: I): VariantB { + return VariantB.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): VariantB { + const message = createBaseVariantB(); + message.value = object.value ?? ""; + return message; + }, +}; + +function createBaseVariantC(): VariantC { + return { x: 0, y: 0 }; +} + +export const VariantC = { + encode(message: VariantC, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.x !== 0) { + writer.uint32(8).int32(message.x); + } + if (message.y !== 0) { + writer.uint32(17).double(message.y); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): VariantC { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseVariantC(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.x = reader.int32(); + continue; + case 2: + if (tag !== 17) { + break; + } + + message.y = reader.double(); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + create, I>>(base?: I): VariantC { + return VariantC.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): VariantC { + const message = createBaseVariantC(); + message.x = object.x ?? 0; + message.y = object.y ?? 0; + return message; + }, +}; + +function createBaseUnitVariant(): UnitVariant { + return {}; +} + +export const UnitVariant = { + encode(_: UnitVariant, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): UnitVariant { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseUnitVariant(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + create, I>>(base?: I): UnitVariant { + return UnitVariant.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(_: I): UnitVariant { + const message = createBaseUnitVariant(); + return message; + }, +}; + +type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined; + +export type DeepPartial = T extends Builtin ? T + : T extends globalThis.Array ? globalThis.Array> + : T extends ReadonlyArray ? ReadonlyArray> + : T extends { $case: string } ? { [K in keyof Omit]?: DeepPartial } & { $case: T["$case"] } + : T extends {} ? { [K in keyof T]?: DeepPartial } + : Partial; + +type KeysOfUnion = T extends T ? keyof T : never; +export type Exact = P extends Builtin ? P + : P & { [K in keyof P]: Exact } & { [K in Exclude>]: never }; diff --git a/suite/typescript/ts/test.ts b/suite/typescript/ts/test.ts new file mode 100644 index 000000000..9274a5b9c --- /dev/null +++ b/suite/typescript/ts/test.ts @@ -0,0 +1,67 @@ +import { test, suite } from "node:test" +import * as assert from "node:assert/strict" + +import * as Registry from "./bincode/registry.ts" +import * as Data from "./data.ts" + +suite("encode", (ctx) => { + test("SimpleStruct", () => { + const simple_instance = Registry.SimpleStruct.encode(Data.SimpleStruct_obj) + assert.deepEqual(simple_instance, Data.SimpleStruct_bin) + }) + test("MultiEnum_VariantC", () => { + const enum_instance = Registry.MultiEnum.encode(Data.MultiEnum_VariantC_obj) + assert.deepEqual(enum_instance, Data.MultiEnum_VariantC_bin) + }) + test("MultiEnum unit variant", () => { + const unit_variant = Registry.MultiEnum.encode(Data.MultiEnum_Unit_obj) + assert.deepEqual(unit_variant, Data.MultiEnum_Unit_bin) + }) + test("ComplexStruct", () => { + const complex_instance = Registry.ComplexStruct.encode(Data.ComplexStruct_obj) + assert.deepEqual(complex_instance, Data.ComplexStruct_bin) + }) + + test("ComplexStruct", () => { + const complex_instance = Registry.ComplexStruct.encode(Data.ComplexStruct_obj) + assert.deepEqual(complex_instance, Data.ComplexStruct_bin) + }) + + test("MultiEnum unit variant", () => { + const bin = Registry.MultiEnum.encode({ $: "UnitVariant", UnitVariant: null }) + assert.deepEqual(bin, Data.MultiEnum_Unit_bin) + }) +}) + +suite("decode", () => { + test("MultiEnum", () => { + const enum_instance = Registry.MultiEnum.decode(Data.MultiEnum_VariantC_bin) + assert.deepEqual(enum_instance, { + $: "VariantC", + VariantC: { x: 5, y: 3.14 } + } satisfies Registry.MultiEnum) + }) + + test("MultiEnum unit variant", () => { + const unit_variant = Registry.MultiEnum.decode(Data.MultiEnum_Unit_bin) + assert.deepEqual(unit_variant, { $: "UnitVariant", UnitVariant: null } satisfies Registry.MultiEnum) + }) + + test("ComplexStruct", () => { + const complex_instance = Registry.ComplexStruct.decode(Data.ComplexStruct_bin) + assert.deepEqual(complex_instance, { + inner: { a: 42, b: "Hello" }, + flag: true, + items: [ + { $: "VariantA", VariantA: 10 }, + { $: "VariantB", VariantB: "World" } + ], + unit: null, + newtype: 99, + tuple: [123, 45.67, "Test"] + } satisfies Registry.ComplexStruct) + }) +}) + + + diff --git a/suite/typescript/tsconfig.json b/suite/typescript/tsconfig.json new file mode 100644 index 000000000..e47bef4d6 --- /dev/null +++ b/suite/typescript/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "noEmit": true, + "module": "ESNext", + "moduleResolution": "Bundler", + "target": "ESNext", + "allowImportingTsExtensions": true, + "types": ["@types/node"] + } +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 000000000..f6c8e4829 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "noEmit": true, + "target": "ESNext", + "module": "Preserve", + "downlevelIteration": true, + "allowImportingTsExtensions": true, + "lib": ["DOM", "ES2023"] + }, + "exclude": [ + "node_modules" + ] +} \ No newline at end of file From 8a172cd0fea74aefbccfa304262888af1372d80c Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Fri, 13 Sep 2024 07:49:46 +0500 Subject: [PATCH 02/30] fix tests --- suite/typescript/ts/test.ts | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/suite/typescript/ts/test.ts b/suite/typescript/ts/test.ts index 9274a5b9c..e945817ba 100644 --- a/suite/typescript/ts/test.ts +++ b/suite/typescript/ts/test.ts @@ -4,7 +4,7 @@ import * as assert from "node:assert/strict" import * as Registry from "./bincode/registry.ts" import * as Data from "./data.ts" -suite("encode", (ctx) => { +suite("encode", () => { test("SimpleStruct", () => { const simple_instance = Registry.SimpleStruct.encode(Data.SimpleStruct_obj) assert.deepEqual(simple_instance, Data.SimpleStruct_bin) @@ -36,30 +36,17 @@ suite("encode", (ctx) => { suite("decode", () => { test("MultiEnum", () => { const enum_instance = Registry.MultiEnum.decode(Data.MultiEnum_VariantC_bin) - assert.deepEqual(enum_instance, { - $: "VariantC", - VariantC: { x: 5, y: 3.14 } - } satisfies Registry.MultiEnum) + assert.deepEqual(enum_instance, Data.MultiEnum_VariantC_obj) }) test("MultiEnum unit variant", () => { const unit_variant = Registry.MultiEnum.decode(Data.MultiEnum_Unit_bin) - assert.deepEqual(unit_variant, { $: "UnitVariant", UnitVariant: null } satisfies Registry.MultiEnum) + assert.deepEqual(unit_variant, Data.MultiEnum_Unit_obj) }) test("ComplexStruct", () => { const complex_instance = Registry.ComplexStruct.decode(Data.ComplexStruct_bin) - assert.deepEqual(complex_instance, { - inner: { a: 42, b: "Hello" }, - flag: true, - items: [ - { $: "VariantA", VariantA: 10 }, - { $: "VariantB", VariantB: "World" } - ], - unit: null, - newtype: 99, - tuple: [123, 45.67, "Test"] - } satisfies Registry.ComplexStruct) + assert.deepEqual(complex_instance, Data.ComplexStruct_obj) }) }) From fbc01ec9e8ab36094ca227cabc08b70fa43faea6 Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Fri, 13 Sep 2024 08:13:46 +0500 Subject: [PATCH 03/30] fix scripts --- suite/typescript/flake.nix | 4 ++-- suite/typescript/readme.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/suite/typescript/flake.nix b/suite/typescript/flake.nix index 8f922f485..8ef2628a3 100644 --- a/suite/typescript/flake.nix +++ b/suite/typescript/flake.nix @@ -55,12 +55,12 @@ popd ''; - scripts."test".exec = '' + scripts."run:test".exec = '' pushd ${root} node_modules/.bin/tsx ts/test.ts popd ''; - scripts."benchmark".exec = '' + scripts."run:benchmark".exec = '' pushd ${root} node_modules/.bin/tsx ts/bench.ts popd diff --git a/suite/typescript/readme.md b/suite/typescript/readme.md index fc5d30d0f..039ffdd03 100644 --- a/suite/typescript/readme.md +++ b/suite/typescript/readme.md @@ -4,7 +4,7 @@ Run: ```sh nix develop . --impure pnpm i -generate:proto && generate:bincode && test && benchmark +generate:proto && generate:bincode && run:test && run:benchmark ``` ## Results From 3ed4de959c2718d98abd4113109a45df813401ce Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Fri, 13 Sep 2024 08:56:50 +0500 Subject: [PATCH 04/30] numerous improvements --- serde-generate/runtime/typescript/serde.ts | 10 ++- serde-generate/src/typescript.rs | 55 ++++++------ suite/typescript/flake.nix | 6 +- suite/typescript/readme.md | 2 +- suite/typescript/rs/generator.rs | 2 + suite/typescript/schema-proto/main.proto | 1 + suite/typescript/ts/bench.ts | 3 +- suite/typescript/ts/bincode/registry.ts | 15 ++-- suite/typescript/ts/data.ts | 8 +- suite/typescript/ts/proto/main.ts | 99 +++++++++++++++++++++- 10 files changed, 152 insertions(+), 49 deletions(-) diff --git a/serde-generate/runtime/typescript/serde.ts b/serde-generate/runtime/typescript/serde.ts index 3345e6397..aa5465ad1 100644 --- a/serde-generate/runtime/typescript/serde.ts +++ b/serde-generate/runtime/typescript/serde.ts @@ -45,7 +45,8 @@ export interface Reader { readLength(): number readVariantIndex(): number readOptionTag(): boolean - readMap(readKey: () => K, readValue: () => V): Map, + readList(readFn: () => T, length?: number): T[] + readMap(readKey: () => K, readValue: () => V): Map checkThatKeySlicesAreIncreasing(key1: [number, number], key2: [number, number]): void } @@ -368,6 +369,13 @@ export abstract class BinaryReader implements Reader { return this.readBool() } + public readList(readFn: () => T, listLength?: number) { + const length = listLength ?? this.readLength() + const list = new Array(length) + for (let i = 0; i < length; i++) list[i] = readFn() + return list + } + public readMap(readKey: () => K, readValue: () => V) { const length = this.readLength(), obj = new Map() let previousKeyStart = 0, previousKeyEnd = 0 diff --git a/serde-generate/src/typescript.rs b/serde-generate/src/typescript.rs index d4d520f21..60c4e2bdb 100644 --- a/serde-generate/src/typescript.rs +++ b/serde-generate/src/typescript.rs @@ -394,13 +394,19 @@ impl<'a, T: Write> TypeScriptEmitter<'a, T> { } }, Seq(format) => { - format!("writer.writeLength({value}.length); {value}.forEach((item) => {}) ", self.quote_write_value("item", format)) + formatdoc!(" + writer.writeLength({value}.length) + for (const item of {value}) {{ + {} + }}", + self.quote_write_value("item", format) + ) } Map { key: map_key, value: map_value } => { format! { - "writer.writeMap({value}, k => {}, v => {})", - self.quote_write_value("k", map_key), - self.quote_write_value("v", map_value) + "writer.writeMap({value}, {}, {})", + self.quote_write_value("", map_key).replace("()", ".bind(writer)"), + self.quote_write_value("", map_value).replace("()", ".bind(writer)") } } Tuple(formats) => { @@ -413,7 +419,12 @@ impl<'a, T: Write> TypeScriptEmitter<'a, T> { lines.join("\n") } TupleArray { content, .. } => { - format!("{value}.forEach((item) => {})", self.quote_write_value("item[0]", content)) + formatdoc!(" + for (const item of {value}) {{ + {} + }}", + self.quote_write_value("item[0]", content) + ) } _ => panic!("unexpected case"), } @@ -441,33 +452,22 @@ impl<'a, T: Write> TypeScriptEmitter<'a, T> { Str => "reader.readString()", Bytes => "reader.readBytes()", Option(format) => { - &formatdoc!( - r#"function () {{ - const tag = reader.readOptionTag() - return tag ? {} : null - }}()"#, - self.quote_read_value(format), - ) + &format!("reader.readOptionTag() ? {} : null", self.quote_read_value(format)) } Seq(format) => { - &formatdoc!( - r#"function () {{ - const length = reader.readLength() - const list: {}[] = [] - for (let i = 0; i < length; i++) list.push({}) - return list - }}()"#, + &format!( + "reader.readList<{}>(() => {})", self.quote_type(format), self.quote_read_value(format) ) } Map { key, value } => { &format!( - "reader.readMap<{}, {}>(() => {}, () => {})", + "reader.readMap<{}, {}>({}, {})", self.quote_type(key), self.quote_type(value), - self.quote_read_value(key), - self.quote_read_value(value), + self.quote_read_value(key).replace("()", ".bind(reader)"), + self.quote_read_value(value).replace("()", ".bind(reader)"), ) } Tuple(formats) => { @@ -479,14 +479,9 @@ impl<'a, T: Write> TypeScriptEmitter<'a, T> { ) } TupleArray { content, size } => { - &formatdoc!( - r#"function() {{ - const list: {} = [] - for (let i = 0; i < {}; i++) list.push([{}]) - return list - }}() - "#, - self.quote_type(format), size, self.quote_read_value(content) + &format!( + "reader.readList<{}>(() => {}, {})", + self.quote_type(format), self.quote_read_value(content), size, ) } Variable(_) => panic!("unsupported value") diff --git a/suite/typescript/flake.nix b/suite/typescript/flake.nix index 8ef2628a3..fc83d7ede 100644 --- a/suite/typescript/flake.nix +++ b/suite/typescript/flake.nix @@ -38,7 +38,7 @@ inherit (config.devenv) root state profile; in { packages = with pkgs; [nodejs_22 nodePackages.pnpm protobuf_28]; - scripts."generate:proto".exec = concatStringsSep " \\\n" [ + scripts."gen:proto".exec = concatStringsSep " \\\n" [ "protoc" "--plugin ${root}/node_modules/.bin/protoc-gen-ts_proto" "--ts_proto_out ts/proto" @@ -49,7 +49,7 @@ ''$(find schema-proto -iname "*.proto")'' ]; - scripts."generate:bincode".exec = '' + scripts."gen:bincode".exec = '' pushd ${root} cargo run popd @@ -60,7 +60,7 @@ node_modules/.bin/tsx ts/test.ts popd ''; - scripts."run:benchmark".exec = '' + scripts."run:benchmarks".exec = '' pushd ${root} node_modules/.bin/tsx ts/bench.ts popd diff --git a/suite/typescript/readme.md b/suite/typescript/readme.md index 039ffdd03..aeae6c13c 100644 --- a/suite/typescript/readme.md +++ b/suite/typescript/readme.md @@ -4,7 +4,7 @@ Run: ```sh nix develop . --impure pnpm i -generate:proto && generate:bincode && run:test && run:benchmark +gen:proto && gen:bincode && run:test && run:benchmarks ``` ## Results diff --git a/suite/typescript/rs/generator.rs b/suite/typescript/rs/generator.rs index db771a7ad..e59b73d40 100644 --- a/suite/typescript/rs/generator.rs +++ b/suite/typescript/rs/generator.rs @@ -36,6 +36,7 @@ fn main() -> Result<(), Box> { unit: UnitStruct, newtype: NewtypeStruct, tuple: TupleStruct, + map: HashMap } tracer.trace_simple_type::()?; @@ -55,6 +56,7 @@ fn main() -> Result<(), Box> { unit: UnitStruct, newtype: NewtypeStruct(99), tuple: TupleStruct(123, 45.67, "Test".to_string()), + map: HashMap::from_iter([(3, 7)]) }; println!("simple_instance: {:?}", bincode::serialize(&simple_instance)?); diff --git a/suite/typescript/schema-proto/main.proto b/suite/typescript/schema-proto/main.proto index b592f0e1c..23a4c2794 100644 --- a/suite/typescript/schema-proto/main.proto +++ b/suite/typescript/schema-proto/main.proto @@ -24,6 +24,7 @@ message ComplexStruct { UnitStruct unit = 4; int32 newtype = 5; TupleStruct tuple = 6; + map map = 7; } message MultiEnum { diff --git a/suite/typescript/ts/bench.ts b/suite/typescript/ts/bench.ts index b308ebce2..6ef73d6bf 100644 --- a/suite/typescript/ts/bench.ts +++ b/suite/typescript/ts/bench.ts @@ -12,7 +12,8 @@ const ComplexStruct_pb_obj: ProtobufRegistry.ComplexStruct = { ], unit: {}, newtype: 99, - tuple: { first: 123, second: 45.67, third: "Test" } + tuple: { first: 123, second: 45.67, third: "Test" }, + map: { 3: 7n } } await async function bench_encode() { diff --git a/suite/typescript/ts/bincode/registry.ts b/suite/typescript/ts/bincode/registry.ts index decc970a8..eef4e367c 100644 --- a/suite/typescript/ts/bincode/registry.ts +++ b/suite/typescript/ts/bincode/registry.ts @@ -8,6 +8,7 @@ export type ComplexStruct = { unit: UnitStruct, newtype: NewtypeStruct, tuple: TupleStruct, + map: $t.Map<$t.i32, $t.i64>, } export type MultiEnum = @@ -31,25 +32,25 @@ export const ComplexStruct = { encode(value: ComplexStruct, writer = new BincodeWriter()) { SimpleStruct.encode(value.inner, writer) writer.writeBool(value.flag) - writer.writeLength(value.items.length); value.items.forEach((item) => MultiEnum.encode(item, writer)) + writer.writeLength(value.items.length) + for (const item of value.items) { + MultiEnum.encode(item, writer) + } UnitStruct.encode(value.unit, writer) NewtypeStruct.encode(value.newtype, writer) TupleStruct.encode(value.tuple, writer) + writer.writeMap(value.map, writer.writeI32.bind(writer), writer.writeI64.bind(writer)) return writer.getBytes() }, decode(input: Uint8Array, reader = new BincodeReader(input)) { const value = {} as ComplexStruct value.inner = SimpleStruct.decode(input, reader) value.flag = reader.readBool() - value.items = function () { - const length = reader.readLength() - const list: MultiEnum[] = [] - for (let i = 0; i < length; i++) list.push(MultiEnum.decode(input, reader)) - return list - }() + value.items = reader.readList(() => MultiEnum.decode(input, reader)) value.unit = UnitStruct.decode(input, reader) value.newtype = NewtypeStruct.decode(input, reader) value.tuple = TupleStruct.decode(input, reader) + value.map = reader.readMap<$t.i32, $t.i64>(reader.readI32.bind(reader), reader.readI64.bind(reader)) return value } } diff --git a/suite/typescript/ts/data.ts b/suite/typescript/ts/data.ts index fe76568a8..e756b4ad2 100644 --- a/suite/typescript/ts/data.ts +++ b/suite/typescript/ts/data.ts @@ -21,11 +21,9 @@ export const ComplexStruct_obj: Registry.ComplexStruct = { ], unit: null, newtype: 99, - tuple: [123, 45.67, "Test"] + tuple: [123, 45.67, "Test"], + map: new Map().set(3, 7n) } export const ComplexStruct_bin = Uint8Array.from([ - 42, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 72, 101, 108, 108, 111, 1, 2, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 87, - 111, 114, 108, 100, 99, 0, 0, 0, 123, 0, 0, 0, 246, 40, 92, 143, 194, 213, 70, - 64, 4, 0, 0, 0, 0, 0, 0, 0, 84, 101, 115, 116 + 42, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 72, 101, 108, 108, 111, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 87, 111, 114, 108, 100, 99, 0, 0, 0, 123, 0, 0, 0, 246, 40, 92, 143, 194, 213, 70, 64, 4, 0, 0, 0, 0, 0, 0, 0, 84, 101, 115, 116, 1, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0 ]) \ No newline at end of file diff --git a/suite/typescript/ts/proto/main.ts b/suite/typescript/ts/proto/main.ts index 329a88d5f..4ebea816c 100644 --- a/suite/typescript/ts/proto/main.ts +++ b/suite/typescript/ts/proto/main.ts @@ -5,6 +5,7 @@ // source: main.proto /* eslint-disable */ +import Long from "long"; import _m0 from "protobufjs/minimal"; export const protobufPackage = ""; @@ -34,6 +35,12 @@ export interface ComplexStruct { unit: UnitStruct | undefined; newtype: number; tuple: TupleStruct | undefined; + map: { [key: number]: number }; +} + +export interface ComplexStruct_MapEntry { + key: number; + value: number; } export interface MultiEnum { @@ -265,7 +272,7 @@ export const SimpleStruct = { }; function createBaseComplexStruct(): ComplexStruct { - return { inner: undefined, flag: false, items: [], unit: undefined, newtype: 0, tuple: undefined }; + return { inner: undefined, flag: false, items: [], unit: undefined, newtype: 0, tuple: undefined, map: {} }; } export const ComplexStruct = { @@ -288,6 +295,9 @@ export const ComplexStruct = { if (message.tuple !== undefined) { TupleStruct.encode(message.tuple, writer.uint32(50).fork()).ldelim(); } + Object.entries(message.map).forEach(([key, value]) => { + ComplexStruct_MapEntry.encode({ key: key as any, value }, writer.uint32(58).fork()).ldelim(); + }); return writer; }, @@ -340,6 +350,16 @@ export const ComplexStruct = { message.tuple = TupleStruct.decode(reader, reader.uint32()); continue; + case 7: + if (tag !== 58) { + break; + } + + const entry7 = ComplexStruct_MapEntry.decode(reader, reader.uint32()); + if (entry7.value !== undefined) { + message.map[entry7.key] = entry7.value; + } + continue; } if ((tag & 7) === 4 || tag === 0) { break; @@ -366,6 +386,68 @@ export const ComplexStruct = { message.tuple = (object.tuple !== undefined && object.tuple !== null) ? TupleStruct.fromPartial(object.tuple) : undefined; + message.map = Object.entries(object.map ?? {}).reduce<{ [key: number]: number }>((acc, [key, value]) => { + if (value !== undefined) { + acc[globalThis.Number(key)] = globalThis.Number(value); + } + return acc; + }, {}); + return message; + }, +}; + +function createBaseComplexStruct_MapEntry(): ComplexStruct_MapEntry { + return { key: 0, value: 0 }; +} + +export const ComplexStruct_MapEntry = { + encode(message: ComplexStruct_MapEntry, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.key !== 0) { + writer.uint32(8).int32(message.key); + } + if (message.value !== 0) { + writer.uint32(16).int64(message.value); + } + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): ComplexStruct_MapEntry { + const reader = input instanceof _m0.Reader ? input : _m0.Reader.create(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseComplexStruct_MapEntry(); + while (reader.pos < end) { + const tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + if (tag !== 8) { + break; + } + + message.key = reader.int32(); + continue; + case 2: + if (tag !== 16) { + break; + } + + message.value = longToNumber(reader.int64() as Long); + continue; + } + if ((tag & 7) === 4 || tag === 0) { + break; + } + reader.skipType(tag & 7); + } + return message; + }, + + create, I>>(base?: I): ComplexStruct_MapEntry { + return ComplexStruct_MapEntry.fromPartial(base ?? ({} as any)); + }, + fromPartial, I>>(object: I): ComplexStruct_MapEntry { + const message = createBaseComplexStruct_MapEntry(); + message.key = object.key ?? 0; + message.value = object.value ?? 0; return message; }, }; @@ -666,3 +748,18 @@ export type DeepPartial = T extends Builtin ? T type KeysOfUnion = T extends T ? keyof T : never; export type Exact = P extends Builtin ? P : P & { [K in keyof P]: Exact } & { [K in Exclude>]: never }; + +function longToNumber(long: Long): number { + if (long.gt(globalThis.Number.MAX_SAFE_INTEGER)) { + throw new globalThis.Error("Value is larger than Number.MAX_SAFE_INTEGER"); + } + if (long.lt(globalThis.Number.MIN_SAFE_INTEGER)) { + throw new globalThis.Error("Value is smaller than Number.MIN_SAFE_INTEGER"); + } + return long.toNumber(); +} + +if (_m0.util.Long !== Long) { + _m0.util.Long = Long as any; + _m0.configure(); +} From 6cf4b0583fc467e9407abcba2dbbd16a05aecd91 Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Fri, 13 Sep 2024 08:58:55 +0500 Subject: [PATCH 05/30] use bigint prefix instead of constructor --- serde-generate/runtime/typescript/serde.ts | 8 ++++---- suite/typescript/ts/bench.ts | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/serde-generate/runtime/typescript/serde.ts b/serde-generate/runtime/typescript/serde.ts index aa5465ad1..4125f4130 100644 --- a/serde-generate/runtime/typescript/serde.ts +++ b/serde-generate/runtime/typescript/serde.ts @@ -78,10 +78,10 @@ export interface Writer { export abstract class BinaryWriter implements Writer { - public static readonly BIG_32: bigint = BigInt(32) - public static readonly BIG_64: bigint = BigInt(64) - public static readonly BIG_32Fs: bigint = BigInt("429967295") - public static readonly BIG_64Fs: bigint = BigInt("18446744073709551615") + public static readonly BIG_32: bigint = 32n + public static readonly BIG_64: bigint = 64n + public static readonly BIG_32Fs: bigint = 429967295n + public static readonly BIG_64Fs: bigint = 18446744073709551615n public static readonly textEncoder = new TextEncoder() public buffer: ArrayBuffer diff --git a/suite/typescript/ts/bench.ts b/suite/typescript/ts/bench.ts index 6ef73d6bf..3031b1de6 100644 --- a/suite/typescript/ts/bench.ts +++ b/suite/typescript/ts/bench.ts @@ -13,6 +13,7 @@ const ComplexStruct_pb_obj: ProtobufRegistry.ComplexStruct = { unit: {}, newtype: 99, tuple: { first: 123, second: 45.67, third: "Test" }, + // @ts-ignore map: { 3: 7n } } From b2ae174ce5a36337bc59fb3856d0c205370d8e41 Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Fri, 13 Sep 2024 09:03:36 +0500 Subject: [PATCH 06/30] remove useless constructor --- serde-generate/runtime/typescript/serde.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/serde-generate/runtime/typescript/serde.ts b/serde-generate/runtime/typescript/serde.ts index 4125f4130..a3278f385 100644 --- a/serde-generate/runtime/typescript/serde.ts +++ b/serde-generate/runtime/typescript/serde.ts @@ -84,13 +84,9 @@ export abstract class BinaryWriter implements Writer { public static readonly BIG_64Fs: bigint = 18446744073709551615n public static readonly textEncoder = new TextEncoder() - public buffer: ArrayBuffer - public offset: number + public buffer = new ArrayBuffer(64) + public offset = 0 - constructor() { - this.buffer = new ArrayBuffer(64) - this.offset = 0 - } private ensureBufferWillHandleSize(bytes: number) { const wishSize = this.offset + bytes @@ -99,6 +95,7 @@ export abstract class BinaryWriter implements Writer { while (newBufferLength < wishSize) newBufferLength *= 2 newBufferLength = Math.max(wishSize, newBufferLength) + // TODO: there is new API for resizing buffer, but in Node it seems to be slower then allocating new // this.buffer.resize(newBufferLength) const newBuffer = new ArrayBuffer(newBufferLength) new Uint8Array(newBuffer).set(new Uint8Array(this.buffer)) @@ -119,6 +116,7 @@ export abstract class BinaryWriter implements Writer { public writeString(value: string) { const bytes = value.length * 3 + 8 this.ensureBufferWillHandleSize(bytes) + // TODO: check this for correctness const { written } = BinaryWriter.textEncoder.encodeInto(value, new Uint8Array(this.buffer, this.offset + 8)) this.writeU64(written) this.offset += written From 61483f1e455ecc387ec97854bba37cb0e43c26e3 Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Fri, 13 Sep 2024 09:07:09 +0500 Subject: [PATCH 07/30] remove excessive bigint transforms & update benchmarks --- serde-generate/runtime/typescript/serde.ts | 16 ++++--------- suite/typescript/readme.md | 28 +++++++++++----------- 2 files changed, 18 insertions(+), 26 deletions(-) diff --git a/serde-generate/runtime/typescript/serde.ts b/serde-generate/runtime/typescript/serde.ts index a3278f385..5e2013b3f 100644 --- a/serde-generate/runtime/typescript/serde.ts +++ b/serde-generate/runtime/typescript/serde.ts @@ -316,10 +316,7 @@ export abstract class BinaryReader implements Reader { const high = this.readU32() // combine the two 32-bit values and return (little endian) - return BigInt( - (BigInt(high.toString()) << BinaryReader.BIG_32) | - BigInt(low.toString()), - ) + return (BigInt(high) << BinaryReader.BIG_32) | BigInt(low) } public readU128() { @@ -327,10 +324,7 @@ export abstract class BinaryReader implements Reader { const high = this.readU64() // combine the two 64-bit values and return (little endian) - return BigInt( - (BigInt(high.toString()) << BinaryReader.BIG_64) | - BigInt(low.toString()), - ) + return (high << BinaryReader.BIG_64) | low } public readI8() { @@ -350,8 +344,7 @@ export abstract class BinaryReader implements Reader { const high = this.readI32() // combine the two 32-bit values and return (little endian) - return (BigInt(high.toString()) << BinaryReader.BIG_32) | - BigInt(low.toString()) + return (BigInt(high) << BinaryReader.BIG_32) | BigInt(low) } public readI128() { @@ -359,8 +352,7 @@ export abstract class BinaryReader implements Reader { const high = this.readI64() // combine the two 64-bit values and return (little endian) - return (BigInt(high.toString()) << BinaryReader.BIG_64) | - BigInt(low.toString()) + return (BigInt(high) << BinaryReader.BIG_64) | BigInt(low) } public readOptionTag() { diff --git a/suite/typescript/readme.md b/suite/typescript/readme.md index aeae6c13c..a9970f58c 100644 --- a/suite/typescript/readme.md +++ b/suite/typescript/readme.md @@ -10,21 +10,21 @@ gen:proto && gen:bincode && run:test && run:benchmarks ## Results ### Encode ``` -┌─────────┬───────────────────────────────┬─────────────┬────────────────────┬──────────┬─────────┐ -│ (index) │ Task Name │ ops/sec │ Average Time (ns) │ Margin │ Samples │ -├─────────┼───────────────────────────────┼─────────────┼────────────────────┼──────────┼─────────┤ -│ 0 │ 'JSON:encode' │ '705,603' │ 1417.2268751310808 │ '±0.31%' │ 705604 │ -│ 1 │ 'protobuf-js-ts-proto:encode' │ '1,175,217' │ 850.9063892825211 │ '±0.77%' │ 1175218 │ -│ 2 │ 'serdegen-bincode:encode' │ '115,288' │ 8673.892392162355 │ '±1.06%' │ 115289 │ -└─────────┴───────────────────────────────┴─────────────┴────────────────────┴──────────┴─────────┘ +┌─────────┬───────────────────────────────┬─────────────┬───────────────────┬──────────┬─────────┐ +│ (index) │ Task Name │ ops/sec │ Average Time (ns) │ Margin │ Samples │ +├─────────┼───────────────────────────────┼─────────────┼───────────────────┼──────────┼─────────┤ +│ 0 │ 'JSON:encode' │ '416,024' │ 2403.70173186696 │ '±0.43%' │ 416025 │ +│ 1 │ 'protobuf-js-ts-proto:encode' │ '1,032,930' │ 968.1190244070607 │ '±0.57%' │ 1032931 │ +│ 2 │ 'serdegen-bincode:encode' │ '163,409' │ 6119.603714582189 │ '±0.15%' │ 163410 │ +└─────────┴───────────────────────────────┴─────────────┴───────────────────┴──────────┴─────────┘ ``` ### Decode ``` -┌─────────┬───────────────────────────────┬─────────────┬────────────────────┬──────────┬─────────┐ -│ (index) │ Task Name │ ops/sec │ Average Time (ns) │ Margin │ Samples │ -├─────────┼───────────────────────────────┼─────────────┼────────────────────┼──────────┼─────────┤ -│ 0 │ 'JSON:decode' │ '422,401' │ 2367.417069520852 │ '±1.83%' │ 422402 │ -│ 1 │ 'protobuf-js-ts-proto:decode' │ '1,243,009' │ 804.4992534249864 │ '±0.51%' │ 1243010 │ -│ 2 │ 'serdegen-bincode:decode' │ '48,529' │ 20605.876138467553 │ '±0.32%' │ 48530 │ -└─────────┴───────────────────────────────┴─────────────┴────────────────────┴──────────┴─────────┘ +┌─────────┬───────────────────────────────┬───────────┬────────────────────┬──────────┬─────────┐ +│ (index) │ Task Name │ ops/sec │ Average Time (ns) │ Margin │ Samples │ +├─────────┼───────────────────────────────┼───────────┼────────────────────┼──────────┼─────────┤ +│ 0 │ 'JSON:decode' │ '533,956' │ 1872.8128856825654 │ '±1.30%' │ 533957 │ +│ 1 │ 'protobuf-js-ts-proto:decode' │ '817,807' │ 1222.7815758713145 │ '±0.24%' │ 817808 │ +│ 2 │ 'serdegen-bincode:decode' │ '38,061' │ 26273.294650832962 │ '±0.58%' │ 38062 │ +└─────────┴───────────────────────────────┴───────────┴────────────────────┴──────────┴─────────┘ ``` \ No newline at end of file From bf4dcb8137d25628f51536cfa12c7bb89aac26bc Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Fri, 13 Sep 2024 09:11:16 +0500 Subject: [PATCH 08/30] simplify reader --- serde-generate/runtime/typescript/serde.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/serde-generate/runtime/typescript/serde.ts b/serde-generate/runtime/typescript/serde.ts index 5e2013b3f..d350ceb9e 100644 --- a/serde-generate/runtime/typescript/serde.ts +++ b/serde-generate/runtime/typescript/serde.ts @@ -252,29 +252,27 @@ export abstract class BinaryWriter implements Writer { } export abstract class BinaryReader implements Reader { - private static readonly BIG_32: bigint = BigInt(32) - private static readonly BIG_64: bigint = BigInt(64) + private static readonly BIG_32: bigint = 32n + private static readonly BIG_64: bigint = 64n private static readonly textDecoder = new TextDecoder() + public buffer: ArrayBuffer - public offset: number + public offset = 0 constructor(data: Uint8Array) { // copies data to prevent outside mutation of buffer. this.buffer = new ArrayBuffer(data.length) new Uint8Array(this.buffer).set(data, 0) - this.offset = 0 } - private read(length): ArrayBuffer { + private read(length: number) { const bytes = this.buffer.slice(this.offset, this.offset + length) this.offset += length return bytes } abstract readLength(): number - abstract readVariantIndex(): number - abstract checkThatKeySlicesAreIncreasing(key1: [number, number], key2: [number, number]): void public readString() { From 6c51c7db1ab6c3c76886c1e0e4ec26eeda3b4ac5 Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Fri, 13 Sep 2024 09:12:48 +0500 Subject: [PATCH 09/30] simplify syntax --- serde-generate/runtime/typescript/serde.ts | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/serde-generate/runtime/typescript/serde.ts b/serde-generate/runtime/typescript/serde.ts index d350ceb9e..29f8e2fbf 100644 --- a/serde-generate/runtime/typescript/serde.ts +++ b/serde-generate/runtime/typescript/serde.ts @@ -76,18 +76,16 @@ export interface Writer { sortMapEntries(offsets: number[]): void } - export abstract class BinaryWriter implements Writer { - public static readonly BIG_32: bigint = 32n - public static readonly BIG_64: bigint = 64n - public static readonly BIG_32Fs: bigint = 429967295n - public static readonly BIG_64Fs: bigint = 18446744073709551615n - public static readonly textEncoder = new TextEncoder() + public static readonly BIG_32 = 32n + public static readonly BIG_64 = 64n + public static readonly BIG_32Fs = 429967295n + public static readonly BIG_64Fs = 18446744073709551615n + public static readonly TEXT_ENCODER = new TextEncoder() public buffer = new ArrayBuffer(64) public offset = 0 - private ensureBufferWillHandleSize(bytes: number) { const wishSize = this.offset + bytes if (wishSize > this.buffer.byteLength) { @@ -117,7 +115,7 @@ export abstract class BinaryWriter implements Writer { const bytes = value.length * 3 + 8 this.ensureBufferWillHandleSize(bytes) // TODO: check this for correctness - const { written } = BinaryWriter.textEncoder.encodeInto(value, new Uint8Array(this.buffer, this.offset + 8)) + const { written } = BinaryWriter.TEXT_ENCODER.encodeInto(value, new Uint8Array(this.buffer, this.offset + 8)) this.writeU64(written) this.offset += written } @@ -252,9 +250,9 @@ export abstract class BinaryWriter implements Writer { } export abstract class BinaryReader implements Reader { - private static readonly BIG_32: bigint = 32n - private static readonly BIG_64: bigint = 64n - private static readonly textDecoder = new TextDecoder() + private static readonly BIG_32 = 32n + private static readonly BIG_64 = 64n + private static readonly TEXT_DECODER = new TextDecoder() public buffer: ArrayBuffer public offset = 0 @@ -277,7 +275,7 @@ export abstract class BinaryReader implements Reader { public readString() { const value = this.readBytes() - return BinaryReader.textDecoder.decode(value) + return BinaryReader.TEXT_DECODER.decode(value) } public readBytes() { From 9b58eba14da5efcadd370c8d8114da01e4f7b1a8 Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Fri, 13 Sep 2024 09:24:11 +0500 Subject: [PATCH 10/30] remvoe extra BigInt constructor --- serde-generate/runtime/typescript/serde.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/serde-generate/runtime/typescript/serde.ts b/serde-generate/runtime/typescript/serde.ts index 29f8e2fbf..72d3eb05e 100644 --- a/serde-generate/runtime/typescript/serde.ts +++ b/serde-generate/runtime/typescript/serde.ts @@ -348,7 +348,7 @@ export abstract class BinaryReader implements Reader { const high = this.readI64() // combine the two 64-bit values and return (little endian) - return (BigInt(high) << BinaryReader.BIG_64) | BigInt(low) + return (high << BinaryReader.BIG_64) | low } public readOptionTag() { From 37d5ac1d315eb5da5fdeef4256a46bbc48363846 Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Fri, 13 Sep 2024 09:26:19 +0500 Subject: [PATCH 11/30] fix formatting --- suite/typescript/ts/bench.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/suite/typescript/ts/bench.ts b/suite/typescript/ts/bench.ts index 3031b1de6..5d680e295 100644 --- a/suite/typescript/ts/bench.ts +++ b/suite/typescript/ts/bench.ts @@ -37,7 +37,6 @@ await async function bench_encode() { await async function bench_decode() { - const b = new Bench({ time: 1_000 }) const json_encoded = JSON.stringify(Data.ComplexStruct_obj) From dd809f438537f4d404aedcc5ecd1701fd983792c Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Fri, 13 Sep 2024 10:02:38 +0500 Subject: [PATCH 12/30] micro-optimisations --- serde-generate/runtime/typescript/serde.ts | 65 ++++++++-------------- suite/typescript/ts/bench.ts | 26 +++++---- 2 files changed, 40 insertions(+), 51 deletions(-) diff --git a/serde-generate/runtime/typescript/serde.ts b/serde-generate/runtime/typescript/serde.ts index 72d3eb05e..964cea3b1 100644 --- a/serde-generate/runtime/typescript/serde.ts +++ b/serde-generate/runtime/typescript/serde.ts @@ -83,7 +83,7 @@ export abstract class BinaryWriter implements Writer { public static readonly BIG_64Fs = 18446744073709551615n public static readonly TEXT_ENCODER = new TextEncoder() - public buffer = new ArrayBuffer(64) + public buffer = new ArrayBuffer(8) public offset = 0 private ensureBufferWillHandleSize(bytes: number) { @@ -91,13 +91,13 @@ export abstract class BinaryWriter implements Writer { if (wishSize > this.buffer.byteLength) { let newBufferLength = this.buffer.byteLength while (newBufferLength < wishSize) newBufferLength *= 2 - newBufferLength = Math.max(wishSize, newBufferLength) // TODO: there is new API for resizing buffer, but in Node it seems to be slower then allocating new // this.buffer.resize(newBufferLength) - const newBuffer = new ArrayBuffer(newBufferLength) - new Uint8Array(newBuffer).set(new Uint8Array(this.buffer)) - this.buffer = newBuffer + + const newBuffer = new Uint8Array(newBufferLength) + newBuffer.set(new Uint8Array(this.buffer)) + this.buffer = newBuffer.buffer } } @@ -155,8 +155,8 @@ export abstract class BinaryWriter implements Writer { } public writeU64(value: bigint | number) { - const low = BigInt(value) & BinaryWriter.BIG_32Fs - const high = BigInt(value) >> BinaryWriter.BIG_32 + const low = BigInt(value) & BinaryWriter.BIG_32Fs, + high = BigInt(value) >> BinaryWriter.BIG_32 // write little endian number this.writeU32(Number(low)) @@ -164,8 +164,8 @@ export abstract class BinaryWriter implements Writer { } public writeU128(value: bigint | number) { - const low = BigInt(value) & BinaryWriter.BIG_64Fs - const high = BigInt(value) >> BinaryWriter.BIG_64 + const low = BigInt(value) & BinaryWriter.BIG_64Fs, + high = BigInt(value) >> BinaryWriter.BIG_64 // write little endian number this.writeU64(low) @@ -194,8 +194,8 @@ export abstract class BinaryWriter implements Writer { } public writeI64(value: bigint | number) { - const low = BigInt(value) & BinaryWriter.BIG_32Fs - const high = BigInt(value) >> BinaryWriter.BIG_32 + const low = BigInt(value) & BinaryWriter.BIG_32Fs, + high = BigInt(value) >> BinaryWriter.BIG_32 // write little endian number this.writeI32(Number(low)) @@ -203,8 +203,8 @@ export abstract class BinaryWriter implements Writer { } public writeI128(value: bigint | number) { - const low = BigInt(value) & BinaryWriter.BIG_64Fs - const high = BigInt(value) >> BinaryWriter.BIG_64 + const low = BigInt(value) & BinaryWriter.BIG_64Fs, + high = BigInt(value) >> BinaryWriter.BIG_64 // write little endian number this.writeI64(low) @@ -245,7 +245,7 @@ export abstract class BinaryWriter implements Writer { } public getBytes() { - return new Uint8Array(this.buffer).slice(0, this.offset) + return new Uint8Array(this.buffer.slice(0, this.offset)) } } @@ -264,9 +264,7 @@ export abstract class BinaryReader implements Reader { } private read(length: number) { - const bytes = this.buffer.slice(this.offset, this.offset + length) - this.offset += length - return bytes + return this.buffer.slice(this.offset, (this.offset = this.offset + length)) } abstract readLength(): number @@ -274,8 +272,7 @@ export abstract class BinaryReader implements Reader { abstract checkThatKeySlicesAreIncreasing(key1: [number, number], key2: [number, number]): void public readString() { - const value = this.readBytes() - return BinaryReader.TEXT_DECODER.decode(value) + return BinaryReader.TEXT_DECODER.decode(this.readBytes()) } public readBytes() { @@ -287,8 +284,7 @@ export abstract class BinaryReader implements Reader { } public readBool() { - const bool = new Uint8Array(this.read(1))[0] - return bool == 1 + return new Uint8Array(this.read(1))[0] === 1 } public readUnit() { @@ -308,17 +304,13 @@ export abstract class BinaryReader implements Reader { } public readU64() { - const low = this.readU32() - const high = this.readU32() - + const low = this.readU32(), high = this.readU32() // combine the two 32-bit values and return (little endian) return (BigInt(high) << BinaryReader.BIG_32) | BigInt(low) } public readU128() { - const low = this.readU64() - const high = this.readU64() - + const low = this.readU64(), high = this.readU64() // combine the two 64-bit values and return (little endian) return (high << BinaryReader.BIG_64) | low } @@ -336,28 +328,21 @@ export abstract class BinaryReader implements Reader { } public readI64() { - const low = this.readI32() - const high = this.readI32() - + const low = this.readI32(), high = this.readI32() // combine the two 32-bit values and return (little endian) return (BigInt(high) << BinaryReader.BIG_32) | BigInt(low) } public readI128() { - const low = this.readI64() - const high = this.readI64() - + const low = this.readI64(), high = this.readI64() // combine the two 64-bit values and return (little endian) return (high << BinaryReader.BIG_64) | low } - public readOptionTag() { - return this.readBool() - } + public readOptionTag = this.readBool public readList(readFn: () => T, listLength?: number) { - const length = listLength ?? this.readLength() - const list = new Array(length) + const length = listLength ?? this.readLength(), list = new Array(length) for (let i = 0; i < length; i++) list[i] = readFn() return list } @@ -366,9 +351,7 @@ export abstract class BinaryReader implements Reader { const length = this.readLength(), obj = new Map() let previousKeyStart = 0, previousKeyEnd = 0 for (let i = 0; i < length; i++) { - const keyStart = this.offset, - key = readKey(), - keyEnd = this.offset + const keyStart = this.offset, key = readKey(), keyEnd = this.offset if (i > 0) { this.checkThatKeySlicesAreIncreasing([previousKeyStart, previousKeyEnd], [keyStart, keyEnd]) } diff --git a/suite/typescript/ts/bench.ts b/suite/typescript/ts/bench.ts index 5d680e295..b4eda4a99 100644 --- a/suite/typescript/ts/bench.ts +++ b/suite/typescript/ts/bench.ts @@ -18,7 +18,11 @@ const ComplexStruct_pb_obj: ProtobufRegistry.ComplexStruct = { } await async function bench_encode() { - const b = new Bench({ time: 1_000 }) + const b = new Bench({ time: 1_500 }) + + b.add('serdegen-bincode:encode', () => { + BincodeRegistry.ComplexStruct.encode(Data.ComplexStruct_obj) + }) b.add('JSON:encode', () => { JSON.stringify(Data.ComplexStruct_obj) @@ -26,18 +30,23 @@ await async function bench_encode() { b.add('protobuf-js-ts-proto:encode', () => { ProtobufRegistry.ComplexStruct.encode(ComplexStruct_pb_obj) }) - b.add('serdegen-bincode:encode', () => { - BincodeRegistry.ComplexStruct.encode(Data.ComplexStruct_obj) - }) + await b.warmup() await b.run() + b.results.sort((x, y) => x.hz - y.hz) + console.table(b.table()) }() await async function bench_decode() { - const b = new Bench({ time: 1_000 }) + const b = new Bench({ time: 1_500 }) + + const bincodec_encoded = BincodeRegistry.ComplexStruct.encode(Data.ComplexStruct_obj) + b.add('serdegen-bincode:decode', () => { + BincodeRegistry.ComplexStruct.decode(bincodec_encoded) + }) const json_encoded = JSON.stringify(Data.ComplexStruct_obj) b.add('JSON:decode', () => { @@ -49,13 +58,10 @@ await async function bench_decode() { ProtobufRegistry.ComplexStruct.decode(pb_encoded) }) - const bincodec_encoded = BincodeRegistry.ComplexStruct.encode(Data.ComplexStruct_obj) - b.add('serdegen-bincode:decode', () => { - BincodeRegistry.ComplexStruct.decode(bincodec_encoded) - }) - await b.warmup() await b.run() + b.results.sort((x, y) => x.hz - y.hz) + console.table(b.table()) }() From 759143fba7969032f5dfe81130b7861c27991158 Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Fri, 13 Sep 2024 21:51:48 +0500 Subject: [PATCH 13/30] cache DataView to improve performance --- serde-generate/runtime/typescript/serde.ts | 184 ++++++++++----------- suite/typescript/readme.md | 20 +-- 2 files changed, 95 insertions(+), 109 deletions(-) diff --git a/serde-generate/runtime/typescript/serde.ts b/serde-generate/runtime/typescript/serde.ts index 964cea3b1..fd67d7f29 100644 --- a/serde-generate/runtime/typescript/serde.ts +++ b/serde-generate/runtime/typescript/serde.ts @@ -26,7 +26,6 @@ export type WrapperOfCase = T extends { $: export interface Reader { readString(): string - readBytes(): Uint8Array readBool(): boolean readUnit(): null readChar(): string @@ -52,7 +51,6 @@ export interface Reader { export interface Writer { writeString(value: string): void - writeBytes(value: Uint8Array): void writeBool(value: boolean): void writeUnit(value: null): void writeChar(value: string): void @@ -76,58 +74,50 @@ export interface Writer { sortMapEntries(offsets: number[]): void } +const BIG_32 = 32n +const BIG_64 = 64n +const BIG_32Fs = 429967295n +const BIG_64Fs = 18446744073709551615n + export abstract class BinaryWriter implements Writer { - public static readonly BIG_32 = 32n - public static readonly BIG_64 = 64n - public static readonly BIG_32Fs = 429967295n - public static readonly BIG_64Fs = 18446744073709551615n public static readonly TEXT_ENCODER = new TextEncoder() - public buffer = new ArrayBuffer(8) + public view = new DataView(new ArrayBuffer(128)) public offset = 0 - private ensureBufferWillHandleSize(bytes: number) { - const wishSize = this.offset + bytes - if (wishSize > this.buffer.byteLength) { - let newBufferLength = this.buffer.byteLength - while (newBufferLength < wishSize) newBufferLength *= 2 + private alloc(allocLength: number) { + const wishSize = this.offset + allocLength + + const currentLength = this.view.buffer.byteLength + if (wishSize > currentLength) { + let newBufferLength = currentLength + while (newBufferLength <= wishSize) newBufferLength = newBufferLength << 1 // TODO: there is new API for resizing buffer, but in Node it seems to be slower then allocating new // this.buffer.resize(newBufferLength) const newBuffer = new Uint8Array(newBufferLength) - newBuffer.set(new Uint8Array(this.buffer)) - this.buffer = newBuffer.buffer + newBuffer.set(new Uint8Array(this.view.buffer)) + + this.view = new DataView(newBuffer.buffer) } } - protected write(values: Uint8Array) { - this.ensureBufferWillHandleSize(values.length) - new Uint8Array(this.buffer, this.offset).set(values) - this.offset += values.length - } - abstract writeLength(value: number): void abstract writeVariantIndex(value: number): void abstract sortMapEntries(offsets: number[]): void public writeString(value: string) { const bytes = value.length * 3 + 8 - this.ensureBufferWillHandleSize(bytes) + this.alloc(bytes) // TODO: check this for correctness - const { written } = BinaryWriter.TEXT_ENCODER.encodeInto(value, new Uint8Array(this.buffer, this.offset + 8)) + const { written } = BinaryWriter.TEXT_ENCODER.encodeInto(value, new Uint8Array(this.view.buffer, this.offset + 8)) this.writeU64(written) this.offset += written } - public writeBytes(value: Uint8Array) { - this.writeLength(value.length) - this.write(value) - } - public writeBool(value: boolean) { - const byteValue = value ? 1 : 0 - this.write(new Uint8Array([byteValue])) + this.writeU8(value ? 1 : 0) } // eslint-disable-next-line @typescript-eslint/no-unused-vars,@typescript-eslint/explicit-module-boundary-types @@ -135,28 +125,26 @@ export abstract class BinaryWriter implements Writer { return } - private writeWithFunction(fn: (byteOffset: number, value: number, littleEndian: boolean) => void, bytesLength: number, value: number) { - this.ensureBufferWillHandleSize(bytesLength) - const dv = new DataView(this.buffer, this.offset) - fn.apply(dv, [0, value, true]) - this.offset += bytesLength - } - public writeU8(value: number) { - this.write(new Uint8Array([value])) + this.alloc(1) + this.view.setUint8(this.offset, value) + this.offset += 1 } public writeU16(value: number) { - this.writeWithFunction(DataView.prototype.setUint16, 2, value) + this.alloc(2) + this.view.setUint16(this.offset, value, true) + this.offset += 2 } public writeU32(value: number) { - this.writeWithFunction(DataView.prototype.setUint32, 4, value) + this.alloc(4) + this.view.setUint32(this.offset, value, true) + this.offset += 4 } public writeU64(value: bigint | number) { - const low = BigInt(value) & BinaryWriter.BIG_32Fs, - high = BigInt(value) >> BinaryWriter.BIG_32 + const low = BigInt(value) & BIG_32Fs, high = BigInt(value) >> BIG_32 // write little endian number this.writeU32(Number(low)) @@ -164,8 +152,7 @@ export abstract class BinaryWriter implements Writer { } public writeU128(value: bigint | number) { - const low = BigInt(value) & BinaryWriter.BIG_64Fs, - high = BigInt(value) >> BinaryWriter.BIG_64 + const low = BigInt(value) & BIG_64Fs, high = BigInt(value) >> BIG_64 // write little endian number this.writeU64(low) @@ -173,29 +160,25 @@ export abstract class BinaryWriter implements Writer { } public writeI8(value: number) { - const bytes = 1 - this.ensureBufferWillHandleSize(bytes) - new DataView(this.buffer, this.offset).setInt8(0, value) - this.offset += bytes + this.alloc(1) + this.view.setInt8(this.offset, value) + this.offset += 1 } public writeI16(value: number) { - const bytes = 2 - this.ensureBufferWillHandleSize(bytes) - new DataView(this.buffer, this.offset).setInt16(0, value, true) - this.offset += bytes + this.alloc(2) + this.view.setInt16(this.offset, value, true) + this.offset += 2 } public writeI32(value: number) { - const bytes = 4 - this.ensureBufferWillHandleSize(bytes) - new DataView(this.buffer, this.offset).setInt32(0, value, true) - this.offset += bytes + this.alloc(4) + this.view.setInt32(this.offset, value, true) + this.offset += 4 } public writeI64(value: bigint | number) { - const low = BigInt(value) & BinaryWriter.BIG_32Fs, - high = BigInt(value) >> BinaryWriter.BIG_32 + const low = BigInt(value) & BIG_32Fs, high = BigInt(value) >> BIG_32 // write little endian number this.writeI32(Number(low)) @@ -203,8 +186,7 @@ export abstract class BinaryWriter implements Writer { } public writeI128(value: bigint | number) { - const low = BigInt(value) & BinaryWriter.BIG_64Fs, - high = BigInt(value) >> BinaryWriter.BIG_64 + const low = BigInt(value) & BIG_64Fs, high = BigInt(value) >> BIG_64 // write little endian number this.writeI64(low) @@ -227,17 +209,15 @@ export abstract class BinaryWriter implements Writer { } public writeF32(value: number) { - const bytes = 4 - this.ensureBufferWillHandleSize(bytes) - new DataView(this.buffer, this.offset).setFloat32(0, value, true) - this.offset += bytes + this.alloc(4) + this.view.setFloat32(this.offset, value, true) + this.offset += 4 } public writeF64(value: number) { - const bytes = 8 - this.ensureBufferWillHandleSize(bytes) - new DataView(this.buffer, this.offset).setFloat64(0, value, true) - this.offset += bytes + this.alloc(8) + this.view.setFloat64(this.offset, value, true) + this.offset += 8 } public writeChar(_value: string) { @@ -245,26 +225,21 @@ export abstract class BinaryWriter implements Writer { } public getBytes() { - return new Uint8Array(this.buffer.slice(0, this.offset)) + return new Uint8Array(this.view.buffer).slice(0, this.offset) } } export abstract class BinaryReader implements Reader { - private static readonly BIG_32 = 32n - private static readonly BIG_64 = 64n private static readonly TEXT_DECODER = new TextDecoder() - public buffer: ArrayBuffer public offset = 0 + public view: DataView constructor(data: Uint8Array) { // copies data to prevent outside mutation of buffer. - this.buffer = new ArrayBuffer(data.length) - new Uint8Array(this.buffer).set(data, 0) - } - - private read(length: number) { - return this.buffer.slice(this.offset, (this.offset = this.offset + length)) + const buffer = new ArrayBuffer(data.length) + new Uint8Array(buffer).set(data, 0) + this.view = new DataView(buffer) } abstract readLength(): number @@ -272,19 +247,14 @@ export abstract class BinaryReader implements Reader { abstract checkThatKeySlicesAreIncreasing(key1: [number, number], key2: [number, number]): void public readString() { - return BinaryReader.TEXT_DECODER.decode(this.readBytes()) - } - - public readBytes() { - const len = this.readLength() - if (len < 0) { - throw new Error("Length of a bytes array can't be negative") - } - return new Uint8Array(this.read(len)) + const length = this.readLength() + const decoded = BinaryReader.TEXT_DECODER.decode(new Uint8Array(this.view.buffer, this.offset, length)) + this.offset += length + return decoded } public readBool() { - return new Uint8Array(this.read(1))[0] === 1 + return this.readU8() === 1 } public readUnit() { @@ -292,51 +262,63 @@ export abstract class BinaryReader implements Reader { } public readU8() { - return new DataView(this.read(1)).getUint8(0) + const value = this.view.getUint8(this.offset) + this.offset += 1 + return value } public readU16() { - return new DataView(this.read(2)).getUint16(0, true) + const value = this.view.getUint16(this.offset, true) + this.offset += 2 + return value } public readU32() { - return new DataView(this.read(4)).getUint32(0, true) + const value = this.view.getUint32(this.offset, true) + this.offset += 4 + return value } public readU64() { const low = this.readU32(), high = this.readU32() // combine the two 32-bit values and return (little endian) - return (BigInt(high) << BinaryReader.BIG_32) | BigInt(low) + return (BigInt(high) << BIG_32) | BigInt(low) } public readU128() { const low = this.readU64(), high = this.readU64() // combine the two 64-bit values and return (little endian) - return (high << BinaryReader.BIG_64) | low + return (high << BIG_64) | low } public readI8() { - return new DataView(this.read(1)).getInt8(0) + const value = this.view.getInt8(this.offset) + this.offset += 1 + return value } public readI16() { - return new DataView(this.read(2)).getInt16(0, true) + const value = this.view.getInt16(this.offset, true) + this.offset += 2 + return value } public readI32() { - return new DataView(this.read(4)).getInt32(0, true) + const value = this.view.getInt32(this.offset, true) + this.offset += 4 + return value } public readI64() { const low = this.readI32(), high = this.readI32() // combine the two 32-bit values and return (little endian) - return (BigInt(high) << BinaryReader.BIG_32) | BigInt(low) + return (BigInt(high) << BIG_32) | BigInt(low) } public readI128() { const low = this.readI64(), high = this.readI64() // combine the two 64-bit values and return (little endian) - return (high << BinaryReader.BIG_64) | low + return (high << BIG_64) | low } public readOptionTag = this.readBool @@ -367,10 +349,14 @@ export abstract class BinaryReader implements Reader { } public readF32() { - return new DataView(this.read(4)).getFloat32(0, true) + const value = this.view.getFloat32(this.offset, true) + this.offset += 4 + return value } public readF64() { - return new DataView(this.read(8)).getFloat64(0, true) + const value = this.view.getFloat64(this.offset, true) + this.offset += 8 + return value } } diff --git a/suite/typescript/readme.md b/suite/typescript/readme.md index a9970f58c..6d594dd1a 100644 --- a/suite/typescript/readme.md +++ b/suite/typescript/readme.md @@ -10,21 +10,21 @@ gen:proto && gen:bincode && run:test && run:benchmarks ## Results ### Encode ``` -┌─────────┬───────────────────────────────┬─────────────┬───────────────────┬──────────┬─────────┐ -│ (index) │ Task Name │ ops/sec │ Average Time (ns) │ Margin │ Samples │ -├─────────┼───────────────────────────────┼─────────────┼───────────────────┼──────────┼─────────┤ -│ 0 │ 'JSON:encode' │ '416,024' │ 2403.70173186696 │ '±0.43%' │ 416025 │ -│ 1 │ 'protobuf-js-ts-proto:encode' │ '1,032,930' │ 968.1190244070607 │ '±0.57%' │ 1032931 │ -│ 2 │ 'serdegen-bincode:encode' │ '163,409' │ 6119.603714582189 │ '±0.15%' │ 163410 │ -└─────────┴───────────────────────────────┴─────────────┴───────────────────┴──────────┴─────────┘ +┌─────────┬───────────────────────────────┬─────────────┬────────────────────┬──────────┬─────────┐ +│ (index) │ Task Name │ ops/sec │ Average Time (ns) │ Margin │ Samples │ +├─────────┼───────────────────────────────┼─────────────┼────────────────────┼──────────┼─────────┤ +│ 0 │ 'serdegen-bincode:encode' │ '303,315' │ 3296.893675242794 │ '±0.35%' │ 454974 │ +│ 1 │ 'JSON:encode' │ '457,023' │ 2188.0700444621666 │ '±0.12%' │ 685536 │ +│ 2 │ 'protobuf-js-ts-proto:encode' │ '1,022,729' │ 977.7752381694245 │ '±0.43%' │ 1534095 │ +└─────────┴───────────────────────────────┴─────────────┴────────────────────┴──────────┴─────────┘ ``` ### Decode ``` ┌─────────┬───────────────────────────────┬───────────┬────────────────────┬──────────┬─────────┐ │ (index) │ Task Name │ ops/sec │ Average Time (ns) │ Margin │ Samples │ ├─────────┼───────────────────────────────┼───────────┼────────────────────┼──────────┼─────────┤ -│ 0 │ 'JSON:decode' │ '533,956' │ 1872.8128856825654 │ '±1.30%' │ 533957 │ -│ 1 │ 'protobuf-js-ts-proto:decode' │ '817,807' │ 1222.7815758713145 │ '±0.24%' │ 817808 │ -│ 2 │ 'serdegen-bincode:decode' │ '38,061' │ 26273.294650832962 │ '±0.58%' │ 38062 │ +│ 0 │ 'serdegen-bincode:decode' │ '717,551' │ 1393.628269449653 │ '±0.42%' │ 1076328 │ +│ 1 │ 'JSON:decode' │ '539,225' │ 1854.5121130414666 │ '±0.33%' │ 808839 │ +│ 2 │ 'protobuf-js-ts-proto:decode' │ '795,607' │ 1256.900927759333 │ '±0.19%' │ 1193412 │ └─────────┴───────────────────────────────┴───────────┴────────────────────┴──────────┴─────────┘ ``` \ No newline at end of file From faff9682cdc61636d7a11a95b30ee4b0a0609ad2 Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Fri, 13 Sep 2024 22:37:38 +0500 Subject: [PATCH 14/30] microoptimisations --- serde-generate/runtime/typescript/serde.ts | 37 +++++++++++++++------- 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/serde-generate/runtime/typescript/serde.ts b/serde-generate/runtime/typescript/serde.ts index fd67d7f29..22310d9ae 100644 --- a/serde-generate/runtime/typescript/serde.ts +++ b/serde-generate/runtime/typescript/serde.ts @@ -87,7 +87,7 @@ export abstract class BinaryWriter implements Writer { private alloc(allocLength: number) { const wishSize = this.offset + allocLength - + const currentLength = this.view.buffer.byteLength if (wishSize > currentLength) { let newBufferLength = currentLength @@ -98,7 +98,7 @@ export abstract class BinaryWriter implements Writer { const newBuffer = new Uint8Array(newBufferLength) newBuffer.set(new Uint8Array(this.view.buffer)) - + this.view = new DataView(newBuffer.buffer) } } @@ -108,12 +108,17 @@ export abstract class BinaryWriter implements Writer { abstract sortMapEntries(offsets: number[]): void public writeString(value: string) { - const bytes = value.length * 3 + 8 - this.alloc(bytes) - // TODO: check this for correctness - const { written } = BinaryWriter.TEXT_ENCODER.encodeInto(value, new Uint8Array(this.view.buffer, this.offset + 8)) - this.writeU64(written) - this.offset += written + const length = value.length + // char and U64 for length + this.alloc(8 + length) + + // encode into buffer with space for string length (u64) + BinaryWriter.TEXT_ENCODER.encodeInto(value, new Uint8Array(this.view.buffer, this.offset + 8)) + + const bLength = BigInt(length) + this.view.setUint32(this.offset, Number(bLength & BIG_32Fs), true) + this.view.setUint32(this.offset + 4, Number(bLength >> BIG_32), true) + this.offset += (8 + length) } public writeBool(value: boolean) { @@ -146,9 +151,13 @@ export abstract class BinaryWriter implements Writer { public writeU64(value: bigint | number) { const low = BigInt(value) & BIG_32Fs, high = BigInt(value) >> BIG_32 + this.alloc(8) + // write little endian number - this.writeU32(Number(low)) - this.writeU32(Number(high)) + this.view.setUint32(this.offset, Number(low), true) + this.view.setUint32(this.offset + 4, Number(high), true) + + this.offset += 8 } public writeU128(value: bigint | number) { @@ -180,9 +189,13 @@ export abstract class BinaryWriter implements Writer { public writeI64(value: bigint | number) { const low = BigInt(value) & BIG_32Fs, high = BigInt(value) >> BIG_32 + this.alloc(8) + // write little endian number - this.writeI32(Number(low)) - this.writeI32(Number(high)) + this.view.setInt32(this.offset, Number(low), true) + this.view.setInt32(this.offset + 4, Number(high), true) + + this.offset += 8 } public writeI128(value: bigint | number) { From 98521731bb3e136ecdd661f1b3a7518417c89538 Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Fri, 13 Sep 2024 23:13:33 +0500 Subject: [PATCH 15/30] improve performance by using subarray and global buffer for encoding --- serde-generate/runtime/typescript/bincode.ts | 2 +- serde-generate/runtime/typescript/serde.ts | 14 +++++++++++--- suite/typescript/readme.md | 12 ++++++------ suite/typescript/ts/bench.ts | 5 ++--- 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/serde-generate/runtime/typescript/bincode.ts b/serde-generate/runtime/typescript/bincode.ts index 5ff8aeefc..5c172f253 100644 --- a/serde-generate/runtime/typescript/bincode.ts +++ b/serde-generate/runtime/typescript/bincode.ts @@ -1,4 +1,4 @@ -import { BinaryReader, BinaryWriter } from "./serde"; +import { BinaryReader, BinaryWriter } from "./serde.ts"; export class BincodeReader extends BinaryReader { readLength() { diff --git a/serde-generate/runtime/typescript/serde.ts b/serde-generate/runtime/typescript/serde.ts index 22310d9ae..bf2792b94 100644 --- a/serde-generate/runtime/typescript/serde.ts +++ b/serde-generate/runtime/typescript/serde.ts @@ -79,12 +79,20 @@ const BIG_64 = 64n const BIG_32Fs = 429967295n const BIG_64Fs = 18446744073709551615n +let WRITE_HEAP = new DataView(new ArrayBuffer(128)) + export abstract class BinaryWriter implements Writer { public static readonly TEXT_ENCODER = new TextEncoder() - public view = new DataView(new ArrayBuffer(128)) + public view = WRITE_HEAP public offset = 0 + constructor() { + if (WRITE_HEAP.byteLength > 1024) { + this.view = WRITE_HEAP = new DataView(new ArrayBuffer(128)) + } + } + private alloc(allocLength: number) { const wishSize = this.offset + allocLength @@ -99,7 +107,7 @@ export abstract class BinaryWriter implements Writer { const newBuffer = new Uint8Array(newBufferLength) newBuffer.set(new Uint8Array(this.view.buffer)) - this.view = new DataView(newBuffer.buffer) + this.view = WRITE_HEAP = new DataView(newBuffer.buffer) } } @@ -238,7 +246,7 @@ export abstract class BinaryWriter implements Writer { } public getBytes() { - return new Uint8Array(this.view.buffer).slice(0, this.offset) + return new Uint8Array(this.view.buffer).subarray(0, this.offset) } } diff --git a/suite/typescript/readme.md b/suite/typescript/readme.md index 6d594dd1a..13bdc2bca 100644 --- a/suite/typescript/readme.md +++ b/suite/typescript/readme.md @@ -13,9 +13,9 @@ gen:proto && gen:bincode && run:test && run:benchmarks ┌─────────┬───────────────────────────────┬─────────────┬────────────────────┬──────────┬─────────┐ │ (index) │ Task Name │ ops/sec │ Average Time (ns) │ Margin │ Samples │ ├─────────┼───────────────────────────────┼─────────────┼────────────────────┼──────────┼─────────┤ -│ 0 │ 'serdegen-bincode:encode' │ '303,315' │ 3296.893675242794 │ '±0.35%' │ 454974 │ -│ 1 │ 'JSON:encode' │ '457,023' │ 2188.0700444621666 │ '±0.12%' │ 685536 │ -│ 2 │ 'protobuf-js-ts-proto:encode' │ '1,022,729' │ 977.7752381694245 │ '±0.43%' │ 1534095 │ +│ 0 │ 'serdegen-bincode:encode' │ '511,820' │ 1953.8108308771214 │ '±0.30%' │ 767731 │ +│ 1 │ 'JSON:encode' │ '465,955' │ 2146.1257915628617 │ '±0.16%' │ 698934 │ +│ 2 │ 'protobuf-js-ts-proto:encode' │ '1,027,594' │ 973.1467887466059 │ '±0.35%' │ 1541392 │ └─────────┴───────────────────────────────┴─────────────┴────────────────────┴──────────┴─────────┘ ``` ### Decode @@ -23,8 +23,8 @@ gen:proto && gen:bincode && run:test && run:benchmarks ┌─────────┬───────────────────────────────┬───────────┬────────────────────┬──────────┬─────────┐ │ (index) │ Task Name │ ops/sec │ Average Time (ns) │ Margin │ Samples │ ├─────────┼───────────────────────────────┼───────────┼────────────────────┼──────────┼─────────┤ -│ 0 │ 'serdegen-bincode:decode' │ '717,551' │ 1393.628269449653 │ '±0.42%' │ 1076328 │ -│ 1 │ 'JSON:decode' │ '539,225' │ 1854.5121130414666 │ '±0.33%' │ 808839 │ -│ 2 │ 'protobuf-js-ts-proto:decode' │ '795,607' │ 1256.900927759333 │ '±0.19%' │ 1193412 │ +│ 0 │ 'serdegen-bincode:decode' │ '578,700' │ 1728.0100086284194 │ '±0.33%' │ 868051 │ +│ 1 │ 'JSON:decode' │ '507,000' │ 1972.3851724058761 │ '±0.19%' │ 760501 │ +│ 2 │ 'protobuf-js-ts-proto:decode' │ '748,869' │ 1335.345884706478 │ '±0.33%' │ 1123383 │ └─────────┴───────────────────────────────┴───────────┴────────────────────┴──────────┴─────────┘ ``` \ No newline at end of file diff --git a/suite/typescript/ts/bench.ts b/suite/typescript/ts/bench.ts index b4eda4a99..2aa97fa9c 100644 --- a/suite/typescript/ts/bench.ts +++ b/suite/typescript/ts/bench.ts @@ -27,13 +27,13 @@ await async function bench_encode() { b.add('JSON:encode', () => { JSON.stringify(Data.ComplexStruct_obj) }) + b.add('protobuf-js-ts-proto:encode', () => { ProtobufRegistry.ComplexStruct.encode(ComplexStruct_pb_obj) }) await b.warmup() await b.run() - b.results.sort((x, y) => x.hz - y.hz) console.table(b.table()) }() @@ -60,8 +60,7 @@ await async function bench_decode() { await b.warmup() await b.run() - b.results.sort((x, y) => x.hz - y.hz) - + console.table(b.table()) }() From 5966e6402da09e22f8102c4d2904f7d53670a3d1 Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Fri, 13 Sep 2024 23:21:17 +0500 Subject: [PATCH 16/30] remove unnescessary copying in BinaryReader --- serde-generate/runtime/typescript/serde.ts | 5 +---- suite/typescript/readme.md | 14 +++++++------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/serde-generate/runtime/typescript/serde.ts b/serde-generate/runtime/typescript/serde.ts index bf2792b94..811734f4d 100644 --- a/serde-generate/runtime/typescript/serde.ts +++ b/serde-generate/runtime/typescript/serde.ts @@ -257,10 +257,7 @@ export abstract class BinaryReader implements Reader { public view: DataView constructor(data: Uint8Array) { - // copies data to prevent outside mutation of buffer. - const buffer = new ArrayBuffer(data.length) - new Uint8Array(buffer).set(data, 0) - this.view = new DataView(buffer) + this.view = new DataView(data.buffer) } abstract readLength(): number diff --git a/suite/typescript/readme.md b/suite/typescript/readme.md index 13bdc2bca..92fa57787 100644 --- a/suite/typescript/readme.md +++ b/suite/typescript/readme.md @@ -20,11 +20,11 @@ gen:proto && gen:bincode && run:test && run:benchmarks ``` ### Decode ``` -┌─────────┬───────────────────────────────┬───────────┬────────────────────┬──────────┬─────────┐ -│ (index) │ Task Name │ ops/sec │ Average Time (ns) │ Margin │ Samples │ -├─────────┼───────────────────────────────┼───────────┼────────────────────┼──────────┼─────────┤ -│ 0 │ 'serdegen-bincode:decode' │ '578,700' │ 1728.0100086284194 │ '±0.33%' │ 868051 │ -│ 1 │ 'JSON:decode' │ '507,000' │ 1972.3851724058761 │ '±0.19%' │ 760501 │ -│ 2 │ 'protobuf-js-ts-proto:decode' │ '748,869' │ 1335.345884706478 │ '±0.33%' │ 1123383 │ -└─────────┴───────────────────────────────┴───────────┴────────────────────┴──────────┴─────────┘ +┌─────────┬───────────────────────────────┬─────────────┬────────────────────┬──────────┬─────────┐ +│ (index) │ Task Name │ ops/sec │ Average Time (ns) │ Margin │ Samples │ +├─────────┼───────────────────────────────┼─────────────┼────────────────────┼──────────┼─────────┤ +│ 0 │ 'serdegen-bincode:decode' │ '1,066,364' │ 937.7653036984581 │ '±0.05%' │ 1599548 │ +│ 1 │ 'JSON:decode' │ '549,795' │ 1818.8571833913045 │ '±0.15%' │ 824694 │ +│ 2 │ 'protobuf-js-ts-proto:decode' │ '837,735' │ 1193.694625111962 │ '±0.26%' │ 1256603 │ +└─────────┴───────────────────────────────┴─────────────┴────────────────────┴──────────┴─────────┘ ``` \ No newline at end of file From 50158391e0710c104d2977f393a2151f2dcace20 Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Sat, 14 Sep 2024 02:25:12 +0500 Subject: [PATCH 17/30] make unit values optional --- serde-generate/src/typescript.rs | 9 +++++++-- suite/typescript/ts/bincode/registry.ts | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/serde-generate/src/typescript.rs b/serde-generate/src/typescript.rs index 60c4e2bdb..004c6dc65 100644 --- a/serde-generate/src/typescript.rs +++ b/serde-generate/src/typescript.rs @@ -271,7 +271,12 @@ impl<'a, T: Write> TypeScriptEmitter<'a, T> { writeln!(self.out, "export type {name} = {{")?; self.out.indent(); for field in fields { - writeln!(self.out, "{}: {},", field.name, self.quote_type(&field.value))?; + match field.value { + Format::Unit => { + writeln!(self.out, "{}?: {},", field.name, self.quote_type(&field.value))?; + } + _ => { writeln!(self.out, "{}: {},", field.name, self.quote_type(&field.value))?; } + } } self.out.unindent(); writeln!(self.out, "}}")?; @@ -286,7 +291,7 @@ impl<'a, T: Write> TypeScriptEmitter<'a, T> { for (_index, variant) in variants { match &variant.value { VariantFormat::Unit => { - writeln!(self.out, r#"| {{ $: "{0}", {0}: {1} }}"#, variant.name, self.quote_type(&Format::Unit))?; + writeln!(self.out, r#"| {{ $: "{0}", {0}?: {1} }}"#, variant.name, self.quote_type(&Format::Unit))?; } VariantFormat::Struct(fields) => { let fields_str = fields.iter().map(|f| format!("{}: {}", f.name, self.quote_type(&f.value))).collect::>().join(", "); diff --git a/suite/typescript/ts/bincode/registry.ts b/suite/typescript/ts/bincode/registry.ts index eef4e367c..dcff7a363 100644 --- a/suite/typescript/ts/bincode/registry.ts +++ b/suite/typescript/ts/bincode/registry.ts @@ -15,7 +15,7 @@ export type MultiEnum = | { $: "VariantA", VariantA: $t.i32 } | { $: "VariantB", VariantB: $t.str } | { $: "VariantC", VariantC: { x: $t.u8, y: $t.f64 } } - | { $: "UnitVariant", UnitVariant: $t.unit } + | { $: "UnitVariant", UnitVariant?: $t.unit } export type NewtypeStruct = $t.i32 From 83f238cd7989bc9173fbcf97eedd1b5b3c1443a6 Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Sat, 14 Sep 2024 03:16:48 +0500 Subject: [PATCH 18/30] make Option values optional --- serde-generate/src/typescript.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/serde-generate/src/typescript.rs b/serde-generate/src/typescript.rs index 004c6dc65..7c2e2b3ad 100644 --- a/serde-generate/src/typescript.rs +++ b/serde-generate/src/typescript.rs @@ -272,7 +272,7 @@ impl<'a, T: Write> TypeScriptEmitter<'a, T> { self.out.indent(); for field in fields { match field.value { - Format::Unit => { + Format::Unit | Format::Option {..} => { writeln!(self.out, "{}?: {},", field.name, self.quote_type(&field.value))?; } _ => { writeln!(self.out, "{}: {},", field.name, self.quote_type(&field.value))?; } From ed1fbf1735bbdf79f10d79b089b8b8f5b5574ee8 Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Sat, 28 Sep 2024 20:08:18 +0500 Subject: [PATCH 19/30] remove .js extension --- serde-generate/src/typescript.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/serde-generate/src/typescript.rs b/serde-generate/src/typescript.rs index 7c2e2b3ad..050ac4bfe 100644 --- a/serde-generate/src/typescript.rs +++ b/serde-generate/src/typescript.rs @@ -80,10 +80,10 @@ impl<'a> CodeGenerator<'a> { impl<'a, T: Write> TypeScriptEmitter<'a, T> { fn output_preamble(&mut self) -> Result<()> { - writeln!(self.out, r#"import type * as $t from "./serde.ts""#)?; - writeln!(self.out, r#"import {{ BincodeReader, BincodeWriter }} from "./bincode.ts""#)?; + writeln!(self.out, r#"import type * as $t from "./serde""#)?; + writeln!(self.out, r#"import {{ BincodeReader, BincodeWriter }} from "./bincode""#)?; for namespace in self.generator.namespaces_to_import.iter() { - writeln!(self.out, "import * as {} from '../{}/mod.ts';\n", namespace.to_camel_case(), namespace)?; + writeln!(self.out, "import * as {} from '../{}/mod';\n", namespace.to_camel_case(), namespace)?; } Ok(()) } From 84bc2db6bb156c6b24b4d223258ef51a282d6aff Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Tue, 1 Oct 2024 14:28:40 +0500 Subject: [PATCH 20/30] inline variants, represent tuples as objects, convert to snake_case --- serde-generate/runtime/typescript/bincode.ts | 22 +- serde-generate/runtime/typescript/serde.ts | 226 ++++++++++--------- serde-generate/src/typescript.rs | 173 +++++++------- suite/typescript/rs/generator.rs | 12 +- suite/typescript/ts/bincode/registry.ts | 128 ++++++----- suite/typescript/ts/data.ts | 17 +- suite/typescript/ts/test.ts | 5 +- 7 files changed, 308 insertions(+), 275 deletions(-) diff --git a/serde-generate/runtime/typescript/bincode.ts b/serde-generate/runtime/typescript/bincode.ts index 5c172f253..57dc17b1a 100644 --- a/serde-generate/runtime/typescript/bincode.ts +++ b/serde-generate/runtime/typescript/bincode.ts @@ -1,29 +1,29 @@ -import { BinaryReader, BinaryWriter } from "./serde.ts"; +import { BinaryReader, BinaryWriter } from "./serde"; export class BincodeReader extends BinaryReader { - readLength() { - return Number(this.readU64()) + read_length() { + return Number(this.read_u64()) } - public readVariantIndex() { - return this.readU32() + public read_variant_index() { + return this.read_u32() } - checkThatKeySlicesAreIncreasing(key1: [number, number], key2: [number, number]) { + check_that_key_slices_are_increasing(key1: [number, number], key2: [number, number]) { return } } export class BincodeWriter extends BinaryWriter { - writeLength(value: number) { - this.writeU64(value) + write_length(value: number) { + this.write_u64(value) } - public writeVariantIndex(value: number) { - this.writeU32(value) + public write_variant_index(value: number) { + this.write_u32(value) } - public sortMapEntries(offsets: number[]) { + public sort_map_entries(offsets: number[]) { return } } diff --git a/serde-generate/runtime/typescript/serde.ts b/serde-generate/runtime/typescript/serde.ts index 811734f4d..fae6feef8 100644 --- a/serde-generate/runtime/typescript/serde.ts +++ b/serde-generate/runtime/typescript/serde.ts @@ -1,6 +1,8 @@ export type Optional = T | null export type Seq = T[] -export type Tuple = T +export type Tuple = { + [K in keyof T as `$${Exclude extends string ? Exclude : never}` ]: T[K] +} export type ListTuple = Tuple[] export type Map = globalThis.Map @@ -25,53 +27,53 @@ export type bytes = Uint8Array export type WrapperOfCase = T extends { $: infer _U extends K } ? T : never export interface Reader { - readString(): string - readBool(): boolean - readUnit(): null - readChar(): string - readF32(): number - readF64(): number - readU8(): number - readU16(): number - readU32(): number - readU64(): bigint - readU128(): bigint - readI8(): number - readI16(): number - readI32(): number - readI64(): bigint - readI128(): bigint - readLength(): number - readVariantIndex(): number - readOptionTag(): boolean - readList(readFn: () => T, length?: number): T[] - readMap(readKey: () => K, readValue: () => V): Map - checkThatKeySlicesAreIncreasing(key1: [number, number], key2: [number, number]): void + read_string(): string + read_bool(): boolean + read_unit(): null + read_char(): string + read_f32(): number + read_f64(): number + read_u8(): number + read_u16(): number + read_u32(): number + read_u64(): bigint + read_u128(): bigint + read_i8(): number + read_i16(): number + read_i32(): number + read_i64(): bigint + read_i128(): bigint + read_length(): number + read_variant_index(): number + read_option_tag(): boolean + read_list(read_fn: () => T, length?: number): T[] + read_map(read_key: () => K, read_value: () => V): Map + check_that_key_slices_are_increasing(key1: [number, number], key2: [number, number]): void } export interface Writer { - writeString(value: string): void - writeBool(value: boolean): void - writeUnit(value: null): void - writeChar(value: string): void - writeF32(value: number): void - writeF64(value: number): void - writeU8(value: number): void - writeU16(value: number): void - writeU32(value: number): void - writeU64(value: bigint | number): void - writeU128(value: bigint | number): void - writeI8(value: number): void - writeI16(value: number): void - writeI32(value: number): void - writeI64(value: bigint | number): void - writeI128(value: bigint | number): void - writeLength(value: number): void - writeVariantIndex(value: number): void - writeOptionTag(value: boolean): void - writeMap(value: Map, writeKey: (key: K) => void, writeValue: (value: V) => void): void - getBytes(): Uint8Array - sortMapEntries(offsets: number[]): void + write_string(value: string): void + write_bool(value: boolean): void + write_unit(value: null): void + write_char(value: string): void + write_f32(value: number): void + write_f64(value: number): void + write_u8(value: number): void + write_u16(value: number): void + write_u32(value: number): void + write_u64(value: bigint | number): void + write_u128(value: bigint | number): void + write_i8(value: number): void + write_i16(value: number): void + write_i32(value: number): void + write_i64(value: bigint | number): void + write_i128(value: bigint | number): void + write_length(value: number): void + write_variant_index(value: number): void + write_option_tag(value: boolean): void + write_map(value: Map, write_key: (key: K) => void, write_value: (value: V) => void): void + get_bytes(): Uint8Array + sort_map_entries(offsets: number[]): void } const BIG_32 = 32n @@ -111,11 +113,11 @@ export abstract class BinaryWriter implements Writer { } } - abstract writeLength(value: number): void - abstract writeVariantIndex(value: number): void - abstract sortMapEntries(offsets: number[]): void + abstract write_length(value: number): void + abstract write_variant_index(value: number): void + abstract sort_map_entries(offsets: number[]): void - public writeString(value: string) { + public write_string(value: string) { const length = value.length // char and U64 for length this.alloc(8 + length) @@ -129,34 +131,34 @@ export abstract class BinaryWriter implements Writer { this.offset += (8 + length) } - public writeBool(value: boolean) { - this.writeU8(value ? 1 : 0) + public write_bool(value: boolean) { + this.write_u8(value ? 1 : 0) } // eslint-disable-next-line @typescript-eslint/no-unused-vars,@typescript-eslint/explicit-module-boundary-types - public writeUnit(_value: null) { + public write_unit(_value: null) { return } - public writeU8(value: number) { + public write_u8(value: number) { this.alloc(1) this.view.setUint8(this.offset, value) this.offset += 1 } - public writeU16(value: number) { + public write_u16(value: number) { this.alloc(2) this.view.setUint16(this.offset, value, true) this.offset += 2 } - public writeU32(value: number) { + public write_u32(value: number) { this.alloc(4) this.view.setUint32(this.offset, value, true) this.offset += 4 } - public writeU64(value: bigint | number) { + public write_u64(value: bigint | number) { const low = BigInt(value) & BIG_32Fs, high = BigInt(value) >> BIG_32 this.alloc(8) @@ -168,33 +170,33 @@ export abstract class BinaryWriter implements Writer { this.offset += 8 } - public writeU128(value: bigint | number) { + public write_u128(value: bigint | number) { const low = BigInt(value) & BIG_64Fs, high = BigInt(value) >> BIG_64 // write little endian number - this.writeU64(low) - this.writeU64(high) + this.write_u64(low) + this.write_u64(high) } - public writeI8(value: number) { + public write_i8(value: number) { this.alloc(1) this.view.setInt8(this.offset, value) this.offset += 1 } - public writeI16(value: number) { + public write_i16(value: number) { this.alloc(2) this.view.setInt16(this.offset, value, true) this.offset += 2 } - public writeI32(value: number) { + public write_i32(value: number) { this.alloc(4) this.view.setInt32(this.offset, value, true) this.offset += 4 } - public writeI64(value: bigint | number) { + public write_i64(value: bigint | number) { const low = BigInt(value) & BIG_32Fs, high = BigInt(value) >> BIG_32 this.alloc(8) @@ -206,46 +208,46 @@ export abstract class BinaryWriter implements Writer { this.offset += 8 } - public writeI128(value: bigint | number) { + public write_i128(value: bigint | number) { const low = BigInt(value) & BIG_64Fs, high = BigInt(value) >> BIG_64 // write little endian number - this.writeI64(low) - this.writeI64(high) + this.write_i64(low) + this.write_i64(high) } - public writeOptionTag(value: boolean) { - this.writeBool(value) + public write_option_tag(value: boolean) { + this.write_bool(value) } - public writeMap(map: Map, writeKey: (key: T) => void, writeValue: (value: V) => void): void { - this.writeLength(map.size) + public write_map(map: Map, write_key: (key: T) => void, write_value: (value: V) => void): void { + this.write_length(map.size) const offsets: number[] = [] for (const [k, v] of map.entries()) { offsets.push(this.offset) - writeKey(k) - writeValue(v) + write_key(k) + write_value(v) } - this.sortMapEntries(offsets) + this.sort_map_entries(offsets) } - public writeF32(value: number) { + public write_f32(value: number) { this.alloc(4) this.view.setFloat32(this.offset, value, true) this.offset += 4 } - public writeF64(value: number) { + public write_f64(value: number) { this.alloc(8) this.view.setFloat64(this.offset, value, true) this.offset += 8 } - public writeChar(_value: string) { + public write_char(_value: string) { throw new Error("Method serializeChar not implemented.") } - public getBytes() { + public get_bytes() { return new Uint8Array(this.view.buffer).subarray(0, this.offset) } } @@ -260,119 +262,119 @@ export abstract class BinaryReader implements Reader { this.view = new DataView(data.buffer) } - abstract readLength(): number - abstract readVariantIndex(): number - abstract checkThatKeySlicesAreIncreasing(key1: [number, number], key2: [number, number]): void + abstract read_length(): number + abstract read_variant_index(): number + abstract check_that_key_slices_are_increasing(key1: [number, number], key2: [number, number]): void - public readString() { - const length = this.readLength() + public read_string() { + const length = this.read_length() const decoded = BinaryReader.TEXT_DECODER.decode(new Uint8Array(this.view.buffer, this.offset, length)) this.offset += length return decoded } - public readBool() { - return this.readU8() === 1 + public read_bool() { + return this.read_u8() === 1 } - public readUnit() { + public read_unit() { return null } - public readU8() { + public read_u8() { const value = this.view.getUint8(this.offset) this.offset += 1 return value } - public readU16() { + public read_u16() { const value = this.view.getUint16(this.offset, true) this.offset += 2 return value } - public readU32() { + public read_u32() { const value = this.view.getUint32(this.offset, true) this.offset += 4 return value } - public readU64() { - const low = this.readU32(), high = this.readU32() + public read_u64() { + const low = this.read_u32(), high = this.read_u32() // combine the two 32-bit values and return (little endian) return (BigInt(high) << BIG_32) | BigInt(low) } - public readU128() { - const low = this.readU64(), high = this.readU64() + public read_u128() { + const low = this.read_u64(), high = this.read_u64() // combine the two 64-bit values and return (little endian) return (high << BIG_64) | low } - public readI8() { + public read_i8() { const value = this.view.getInt8(this.offset) this.offset += 1 return value } - public readI16() { + public read_i16() { const value = this.view.getInt16(this.offset, true) this.offset += 2 return value } - public readI32() { + public read_i32() { const value = this.view.getInt32(this.offset, true) this.offset += 4 return value } - public readI64() { - const low = this.readI32(), high = this.readI32() + public read_i64() { + const low = this.read_i32(), high = this.read_i32() // combine the two 32-bit values and return (little endian) return (BigInt(high) << BIG_32) | BigInt(low) } - public readI128() { - const low = this.readI64(), high = this.readI64() + public read_i128() { + const low = this.read_i64(), high = this.read_i64() // combine the two 64-bit values and return (little endian) return (high << BIG_64) | low } - public readOptionTag = this.readBool + public read_option_tag = this.read_bool - public readList(readFn: () => T, listLength?: number) { - const length = listLength ?? this.readLength(), list = new Array(length) - for (let i = 0; i < length; i++) list[i] = readFn() + public read_list(read_fn: () => T, listLength?: number) { + const length = listLength ?? this.read_length(), list = new Array(length) + for (let i = 0; i < length; i++) list[i] = read_fn() return list } - public readMap(readKey: () => K, readValue: () => V) { - const length = this.readLength(), obj = new Map() + public read_map(read_key: () => K, read_value: () => V) { + const length = this.read_length(), obj = new Map() let previousKeyStart = 0, previousKeyEnd = 0 for (let i = 0; i < length; i++) { - const keyStart = this.offset, key = readKey(), keyEnd = this.offset + const keyStart = this.offset, key = read_key(), keyEnd = this.offset if (i > 0) { - this.checkThatKeySlicesAreIncreasing([previousKeyStart, previousKeyEnd], [keyStart, keyEnd]) + this.check_that_key_slices_are_increasing([previousKeyStart, previousKeyEnd], [keyStart, keyEnd]) } previousKeyStart = keyStart previousKeyEnd = keyEnd - obj.set(key, readValue()) + obj.set(key, read_value()) } return obj } - public readChar(): string { - throw new Error("Method readChar not implemented.") + public read_char(): string { + throw new Error("Method read_char not implemented.") } - public readF32() { + public read_f32() { const value = this.view.getFloat32(this.offset, true) this.offset += 4 return value } - public readF64() { + public read_f64() { const value = this.view.getFloat64(this.offset, true) this.offset += 8 return value diff --git a/serde-generate/src/typescript.rs b/serde-generate/src/typescript.rs index 050ac4bfe..a88ac5edc 100644 --- a/serde-generate/src/typescript.rs +++ b/serde-generate/src/typescript.rs @@ -4,7 +4,7 @@ use crate::{ indent::{IndentConfig, IndentedWriter}, CodeGeneratorConfig, }; -use heck::CamelCase; +use heck::{CamelCase, SnakeCase}; use include_dir::include_dir as include_directory; use indoc::{formatdoc, indoc, writedoc}; use serde_reflection::{ContainerFormat, Format, FormatHolder, Named, Registry, VariantFormat}; @@ -14,8 +14,7 @@ use std::{ path::PathBuf, }; -/// Main configuration object for code-generation in TypeScript, powered by -/// the Deno runtime. +/// Main configuration object for code-generation in TypeScript pub struct CodeGenerator<'a> { /// Language-independent configuration. config: &'a CodeGeneratorConfig, @@ -67,7 +66,7 @@ impl<'a> CodeGenerator<'a> { for (name, format) in registry { writeln!(emitter.out)?; - emitter.output_container_typedef(name, format)?; + emitter.generate_container_typedef(name, format)?; } for (name, format) in registry { writeln!(emitter.out)?; @@ -89,7 +88,7 @@ impl<'a, T: Write> TypeScriptEmitter<'a, T> { } fn generate_container(&mut self, name: &str, container: &ContainerFormat) -> Result<()> { - // ENCODE + // Encode writeln!(self.out, "export const {name} = {{")?; self.out.indent(); @@ -110,7 +109,7 @@ impl<'a, T: Write> TypeScriptEmitter<'a, T> { } ContainerFormat::TupleStruct(inner_types) => { for (i, inner) in inner_types.iter().enumerate() { - writeln!(self.out, "{}", self.quote_write_value(&format!("value[{i}]"), inner))?; + writeln!(self.out, "{}", self.quote_write_value(&format!("value.${i}"), inner))?; } } ContainerFormat::Enum(variants) => { @@ -119,13 +118,13 @@ impl<'a, T: Write> TypeScriptEmitter<'a, T> { } } - writeln!(self.out, "return writer.getBytes()")?; + writeln!(self.out, "return writer.get_bytes()")?; self.out.unindent(); writeln!(self.out, "}},")?; - // DECODE + // Decode writeln!(self.out, "decode(input: Uint8Array, reader = new BincodeReader(input)) {{")?; self.out.indent(); @@ -137,7 +136,13 @@ impl<'a, T: Write> TypeScriptEmitter<'a, T> { writeln!(self.out, "const value: {name} = {}", self.quote_read_value(inner))?; } ContainerFormat::TupleStruct(inner_types) => { - writeln!(self.out, "const value: {name} = {}", self.quote_read_value(&Format::Tuple(inner_types.clone())))?; + writeln!(self.out, "const value: {name} = {{")?; + self.out.indent(); + for (i, inner) in inner_types.iter().enumerate() { + writeln!(self.out, "${i}: {},", self.quote_read_value(&inner))?; + } + self.out.unindent(); + writeln!(self.out, "}}")?; } _ => { writeln!(self.out, "const value = {{}} as {name}")?; } } @@ -170,24 +175,24 @@ impl<'a, T: Write> TypeScriptEmitter<'a, T> { self.out.indent(); for (index, variant) in variants { - writeln!(self.out, r#"case "{}": {{"#, variant.name)?; + writeln!(self.out, r#"case "{}": {{"#, variant.name.to_snake_case())?; self.out.indent(); - writeln!(self.out, "writer.writeVariantIndex({index})"); + writeln!(self.out, "writer.write_variant_index({index})"); match &variant.value { VariantFormat::Unit => { - writeln!(self.out, "{}", self.quote_write_value(&format!("value.{}", &variant.name), &Format::Unit)); + writeln!(self.out, "{}", self.quote_write_value("value.$0", &Format::Unit)); }, VariantFormat::NewType(inner) => { - writeln!(self.out, "{}", self.quote_write_value(&format!("value.{}", &variant.name), inner)); + writeln!(self.out, "{}", self.quote_write_value("value.$0", inner)); } VariantFormat::Tuple(members) => { let tuple = Format::Tuple(members.clone()); - writeln!(self.out, "{}", self.quote_write_value(&format!("value.{}", &variant.name), &tuple)); + writeln!(self.out, "{}", self.quote_write_value("value", &tuple)); } VariantFormat::Struct(fields) => { for field in fields { - writeln!(self.out, "{}", self.quote_write_value(&format!("value.{}.{}", variant.name, field.name), &field.value))?; + writeln!(self.out, "{}", self.quote_write_value(&format!("value.{}", field.name), &field.value))?; } } VariantFormat::Variable(_) => panic!("not supported") @@ -200,7 +205,7 @@ impl<'a, T: Write> TypeScriptEmitter<'a, T> { self.out.unindent(); writeln!(self.out, "}}")?; // switch end - writeln!(self.out, "return writer.getBytes()"); + writeln!(self.out, "return writer.get_bytes()"); self.out.unindent(); writeln!(self.out, "}},")?; // encode end @@ -209,35 +214,41 @@ impl<'a, T: Write> TypeScriptEmitter<'a, T> { writeln!(self.out, r#"let value: {name}"#); - writeln!(self.out, "switch (reader.readVariantIndex()) {{")?; + writeln!(self.out, "switch (reader.read_variant_index()) {{")?; self.out.indent(); for (index, variant) in variants { writeln!(self.out, r#"case {index}: {{"#)?; self.out.indent(); - writeln!(self.out, r#"value = {{ $: "{}" }} as $t.WrapperOfCase<{}, "{}">"#, variant.name, name, variant.name); + writeln!(self.out, r#"value = {{"#); + self.out.indent(); + writeln!(self.out, r#"$: "{0}","#, variant.name.to_snake_case()); match &variant.value { VariantFormat::Unit => { - writeln!(self.out, "value.{} = {}", variant.name, self.quote_read_value(&Format::Unit)); + writeln!(self.out, "$0: {}", self.quote_read_value(&Format::Unit)); }, VariantFormat::Tuple(members) => { let tuple = Format::Tuple(members.clone()); - writeln!(self.out, "value.{} = {}", variant.name, self.quote_read_value(&tuple)); + for (i, member) in members.iter().enumerate() { + writeln!(self.out, "${i}: {},", self.quote_read_value(&member)); + } } VariantFormat::NewType(inner) => { - writeln!(self.out, "value.{} = {}", variant.name, self.quote_read_value(inner)); + writeln!(self.out, "$0: {},", self.quote_read_value(inner)); } VariantFormat::Struct(fields) => { - writeln!(self.out, r#"value.{var} = {{}} as $t.WrapperOfCase<{name}, "{var}">["{var}"]"#, var = variant.name); for field in fields { - writeln!(self.out, "value.{}.{} = {}", variant.name, field.name, self.quote_read_value(&field.value))?; + writeln!(self.out, "{}: {},", field.name, self.quote_read_value(&field.value))?; } } VariantFormat::Variable(_) => panic!("not supported") } + self.out.unindent(); + writeln!(self.out, r#"}} satisfies $t.WrapperOfCase<{0}, "{1}">"#, name, variant.name.to_snake_case())?; + writeln!(self.out, "break")?; self.out.unindent(); writeln!(self.out, "}}")?; // case end @@ -258,13 +269,13 @@ impl<'a, T: Write> TypeScriptEmitter<'a, T> { Ok(()) } - fn output_container_typedef(&mut self, name: &str, container: &ContainerFormat) -> Result<()> { + fn generate_container_typedef(&mut self, name: &str, container: &ContainerFormat) -> Result<()> { match container { ContainerFormat::UnitStruct => { writeln!(self.out, "export type {name} = $t.unit")?; } ContainerFormat::TupleStruct(fields) => { - writeln!(self.out, "export type {name} = [{}]", self.quote_types(&fields, ", "))?; + writeln!(self.out, "export type {name} = $t.Tuple<[{}]>", self.quote_types(&fields, ", "))?; self.out.unindent(); } ContainerFormat::Struct(fields) => { @@ -289,19 +300,20 @@ impl<'a, T: Write> TypeScriptEmitter<'a, T> { writeln!(self.out, "export type {name} = ")?; self.out.indent(); for (_index, variant) in variants { + let variant_name_snake = variant.name.to_snake_case(); match &variant.value { VariantFormat::Unit => { - writeln!(self.out, r#"| {{ $: "{0}", {0}?: {1} }}"#, variant.name, self.quote_type(&Format::Unit))?; + writeln!(self.out, r#"| {{ $: "{0}", $0?: {1} }}"#, variant_name_snake, self.quote_type(&Format::Unit))?; } VariantFormat::Struct(fields) => { let fields_str = fields.iter().map(|f| format!("{}: {}", f.name, self.quote_type(&f.value))).collect::>().join(", "); - writeln!(self.out, r#"| {{ $: "{0}", {0}: {{ {1} }} }}"#, variant.name, fields_str)?; + writeln!(self.out, r#"| {{ $: "{0}", {1} }}"#, variant_name_snake, fields_str)?; } VariantFormat::NewType(t) => { - writeln!(self.out, r#"| {{ $: "{0}", {0}: {1} }}"#, variant.name, self.quote_type(&t))?; + writeln!(self.out, r#"| {{ $: "{0}", $0: {1} }}"#, variant_name_snake, self.quote_type(&t))?; } VariantFormat::Tuple(t) => { - writeln!(self.out, r#"| {{ $: "{0}", {0}: {1} }}"#, variant.name, self.quote_type(&Format::Tuple(t.clone())))?; + writeln!(self.out, r#"| {{ $: "{0}" }} & {1}"#, variant_name_snake, self.quote_type(&Format::Tuple(t.clone())))?; } VariantFormat::Variable(v) => panic!("unknown variant format") } @@ -369,38 +381,37 @@ impl<'a, T: Write> TypeScriptEmitter<'a, T> { use Format::*; match format { TypeName(typename) => format!("{typename}.encode({value}, writer)"), - Unit => format!("writer.writeUnit({value})"), - Bool => format!("writer.writeBool({value})"), - I8 => format!("writer.writeI8({value})"), - I16 => format!("writer.writeI16({value})"), - I32 => format!("writer.writeI32({value})"), - I64 => format!("writer.writeI64({value})"), - I128 => format!("writer.writeI128({value})"), - U8 => format!("writer.writeU8({value})"), - U16 => format!("writer.writeU16({value})"), - U32 => format!("writer.writeU32({value})"), - U64 => format!("writer.writeU64({value})"), - U128 => format!("writer.writeU128({value})"), - F32 => format!("writer.writeF32({value})"), - F64 => format!("writer.writeF64({value})"), - Char => format!("writer.writeChar({value})"), - Str => format!("writer.writeString({value})"), - Bytes => format!("writer.writeBytes({value})"), - Option(inner) => { + Unit => format!("writer.write_unit({value})"), + Bool => format!("writer.write_bool({value})"), + I8 => format!("writer.write_i8({value})"), + I16 => format!("writer.write_i16({value})"), + I32 => format!("writer.write_i32({value})"), + I64 => format!("writer.write_i64({value})"), + I128 => format!("writer.write_i128({value})"), + U8 => format!("writer.write_u8({value})"), + U16 => format!("writer.write_u16({value})"), + U32 => format!("writer.write_u32({value})"), + U64 => format!("writer.write_u64({value})"), + U128 => format!("writer.write_u128({value})"), + F32 => format!("writer.write_f32({value})"), + F64 => format!("writer.write_f64({value})"), + Char => format!("writer.write_char({value})"), + Str => format!("writer.write_string({value})"), + Bytes => format!("writer.write_bytes({value})"), Option(inner) => { formatdoc! { " if ({value}) {{ - writer.writeOptionTag(true) + writer.write_option_tag(true) {} }} - else writer.writeOptionTag(false) + else writer.write_option_tag(false) ", self.quote_write_value(value, inner) } }, Seq(format) => { formatdoc!(" - writer.writeLength({value}.length) + writer.write_length({value}.length) for (const item of {value}) {{ {} }}", @@ -409,7 +420,7 @@ impl<'a, T: Write> TypeScriptEmitter<'a, T> { } Map { key: map_key, value: map_value } => { format! { - "writer.writeMap({value}, {}, {})", + "writer.write_map({value}, {}, {})", self.quote_write_value("", map_key).replace("()", ".bind(writer)"), self.quote_write_value("", map_value).replace("()", ".bind(writer)") } @@ -418,7 +429,7 @@ impl<'a, T: Write> TypeScriptEmitter<'a, T> { use std::fmt::Write; let mut lines = Vec::new(); for (index, format) in formats.iter().enumerate() { - let expr = format!("{value}[{}]", index); + let expr = format!("{value}.${}", index); lines.push(self.quote_write_value(&expr, format)); } lines.join("\n") @@ -439,36 +450,36 @@ impl<'a, T: Write> TypeScriptEmitter<'a, T> { use Format::*; let str = match format { TypeName(name) => &format!("{}.decode(input, reader)", self.quote_qualified_name(name)), - Unit => "reader.readUnit()", - Bool => "reader.readBool()", - I8 => "reader.readI8()", - I16 => "reader.readI16()", - I32 => "reader.readI32()", - I64 => "reader.readI64()", - I128 => "reader.readI128()", - U8 => "reader.readU8()", - U16 => "reader.readU16()", - U32 => "reader.readU32()", - U64 => "reader.readU64()", - U128 => "reader.readU128()", - F32 => "reader.readF32()", - F64 => "reader.readF64()", - Char => "reader.readChar()", - Str => "reader.readString()", - Bytes => "reader.readBytes()", + Unit => "reader.read_unit()", + Bool => "reader.read_bool()", + I8 => "reader.read_i8()", + I16 => "reader.read_i16()", + I32 => "reader.read_i32()", + I64 => "reader.read_i64()", + I128 => "reader.read_i128()", + U8 => "reader.read_u8()", + U16 => "reader.read_u16()", + U32 => "reader.read_u32()", + U64 => "reader.read_u64()", + U128 => "reader.read_u128()", + F32 => "reader.read_f32()", + F64 => "reader.read_f64()", + Char => "reader.read_char()", + Str => "reader.read_string()", + Bytes => "reader.read_bytes()", Option(format) => { - &format!("reader.readOptionTag() ? {} : null", self.quote_read_value(format)) + &format!("reader.read_option_tag() ? {} : null", self.quote_read_value(format)) } Seq(format) => { &format!( - "reader.readList<{}>(() => {})", + "reader.read_list<{}>(() => {})", self.quote_type(format), self.quote_read_value(format) ) } Map { key, value } => { &format!( - "reader.readMap<{}, {}>({}, {})", + "reader.read_map<{}, {}>({}, {})", self.quote_type(key), self.quote_type(value), self.quote_read_value(key).replace("()", ".bind(reader)"), @@ -476,16 +487,20 @@ impl<'a, T: Write> TypeScriptEmitter<'a, T> { ) } Tuple(formats) => { - &format!( - "[{}]",formats.iter() - .map(|f| format!("{}", self.quote_read_value(f))) - .collect::>() - .join(", ") - ) + let mut buf = Vec::new(); + let mut writer = IndentedWriter::new(&mut buf, IndentConfig::Tab); + writeln!(writer, "{{"); + writer.indent(); + for (i, f) in formats.iter().enumerate() { + writeln!(writer, "${i}: {},", self.quote_read_value(f)); + } + writer.unindent(); + write!(writer, "}}"); + Box::leak(String::from_utf8(buf).unwrap().into_boxed_str()) } TupleArray { content, size } => { &format!( - "reader.readList<{}>(() => {}, {})", + "reader.read_list<{}>(() => {}, {})", self.quote_type(format), self.quote_read_value(content), size, ) } diff --git a/suite/typescript/rs/generator.rs b/suite/typescript/rs/generator.rs index e59b73d40..a6ef1b728 100644 --- a/suite/typescript/rs/generator.rs +++ b/suite/typescript/rs/generator.rs @@ -16,7 +16,7 @@ fn main() -> Result<(), Box> { VariantA(i32), VariantB(String), VariantC { x: u8, y: f64 }, - UnitVariant, // Unit variant added + UnitVariant, } #[derive(Serialize, Deserialize, Debug)] @@ -36,6 +36,7 @@ fn main() -> Result<(), Box> { unit: UnitStruct, newtype: NewtypeStruct, tuple: TupleStruct, + tupple_inline: (String, i32), map: HashMap } @@ -46,16 +47,17 @@ fn main() -> Result<(), Box> { tracer.trace_simple_type::()?; tracer.trace_simple_type::()?; - let simple_instance = SimpleStruct { a: 42, b: "Hello".to_string() }; + let simple_instance = SimpleStruct { a: 42, b: "Hello".into() }; let enum_instance = MultiEnum::VariantC { x: 5, y: 3.14 }; let unit_variant = MultiEnum::UnitVariant; let complex_instance = ComplexStruct { inner: simple_instance.clone(), flag: true, - items: vec![MultiEnum::VariantA(10), MultiEnum::VariantB("World".to_string())], + items: vec![MultiEnum::VariantA(10), MultiEnum::VariantB("World".into())], unit: UnitStruct, newtype: NewtypeStruct(99), - tuple: TupleStruct(123, 45.67, "Test".to_string()), + tuple: TupleStruct(123, 45.67, "Test".into()), + tupple_inline: ("SomeString".into(), 777), map: HashMap::from_iter([(3, 7)]) }; @@ -68,7 +70,7 @@ fn main() -> Result<(), Box> { use serde_generate::{typescript, CodeGeneratorConfig, Encoding}; let mut source = Vec::new(); - let config = CodeGeneratorConfig::new("bincode".to_string()).with_encodings(vec![Encoding::Bincode]); + let config = CodeGeneratorConfig::new("bincode".into()).with_encodings(vec![Encoding::Bincode]); typescript::CodeGenerator::new(&config).output(&mut source, ®istry)?; std::fs::write(format!("{}/ts/bincode/registry.ts", env!("CARGO_MANIFEST_DIR")), source)?; diff --git a/suite/typescript/ts/bincode/registry.ts b/suite/typescript/ts/bincode/registry.ts index dcff7a363..20f5e3292 100644 --- a/suite/typescript/ts/bincode/registry.ts +++ b/suite/typescript/ts/bincode/registry.ts @@ -1,5 +1,5 @@ -import type * as $t from "./serde.ts" -import { BincodeReader, BincodeWriter } from "./bincode.ts" +import type * as $t from "./serde" +import { BincodeReader, BincodeWriter } from "./bincode" export type ComplexStruct = { inner: SimpleStruct, @@ -8,14 +8,15 @@ export type ComplexStruct = { unit: UnitStruct, newtype: NewtypeStruct, tuple: TupleStruct, + tupple_inline: $t.Tuple<[$t.str, $t.i32]>, map: $t.Map<$t.i32, $t.i64>, } export type MultiEnum = - | { $: "VariantA", VariantA: $t.i32 } - | { $: "VariantB", VariantB: $t.str } - | { $: "VariantC", VariantC: { x: $t.u8, y: $t.f64 } } - | { $: "UnitVariant", UnitVariant?: $t.unit } + | { $: "variant_a", $0: $t.i32 } + | { $: "variant_b", $0: $t.str } + | { $: "variant_c", x: $t.u8, y: $t.f64 } + | { $: "unit_variant", $0?: $t.unit } export type NewtypeStruct = $t.i32 @@ -24,33 +25,39 @@ export type SimpleStruct = { b: $t.str, } -export type TupleStruct = [$t.i32, $t.f64, $t.str] +export type TupleStruct = $t.Tuple<[$t.i32, $t.f64, $t.str]> export type UnitStruct = $t.unit export const ComplexStruct = { encode(value: ComplexStruct, writer = new BincodeWriter()) { SimpleStruct.encode(value.inner, writer) - writer.writeBool(value.flag) - writer.writeLength(value.items.length) + writer.write_bool(value.flag) + writer.write_length(value.items.length) for (const item of value.items) { MultiEnum.encode(item, writer) } UnitStruct.encode(value.unit, writer) NewtypeStruct.encode(value.newtype, writer) TupleStruct.encode(value.tuple, writer) - writer.writeMap(value.map, writer.writeI32.bind(writer), writer.writeI64.bind(writer)) - return writer.getBytes() + writer.write_string(value.tupple_inline.$0) + writer.write_i32(value.tupple_inline.$1) + writer.write_map(value.map, writer.write_i32.bind(writer), writer.write_i64.bind(writer)) + return writer.get_bytes() }, decode(input: Uint8Array, reader = new BincodeReader(input)) { const value = {} as ComplexStruct value.inner = SimpleStruct.decode(input, reader) - value.flag = reader.readBool() - value.items = reader.readList(() => MultiEnum.decode(input, reader)) + value.flag = reader.read_bool() + value.items = reader.read_list(() => MultiEnum.decode(input, reader)) value.unit = UnitStruct.decode(input, reader) value.newtype = NewtypeStruct.decode(input, reader) value.tuple = TupleStruct.decode(input, reader) - value.map = reader.readMap<$t.i32, $t.i64>(reader.readI32.bind(reader), reader.readI64.bind(reader)) + value.tupple_inline = { + $0: reader.read_string(), + $1: reader.read_i32(), + } + value.map = reader.read_map<$t.i32, $t.i64>(reader.read_i32.bind(reader), reader.read_i64.bind(reader)) return value } } @@ -58,53 +65,60 @@ export const ComplexStruct = { export const MultiEnum = { encode(value: MultiEnum, writer = new BincodeWriter()) { switch (value.$) { - case "VariantA": { - writer.writeVariantIndex(0) - writer.writeI32(value.VariantA) + case "variant_a": { + writer.write_variant_index(0) + writer.write_i32(value.$0) break } - case "VariantB": { - writer.writeVariantIndex(1) - writer.writeString(value.VariantB) + case "variant_b": { + writer.write_variant_index(1) + writer.write_string(value.$0) break } - case "VariantC": { - writer.writeVariantIndex(2) - writer.writeU8(value.VariantC.x) - writer.writeF64(value.VariantC.y) + case "variant_c": { + writer.write_variant_index(2) + writer.write_u8(value.x) + writer.write_f64(value.y) break } - case "UnitVariant": { - writer.writeVariantIndex(3) - writer.writeUnit(value.UnitVariant) + case "unit_variant": { + writer.write_variant_index(3) + writer.write_unit(value.$0) break } } - return writer.getBytes() + return writer.get_bytes() }, decode(input: Uint8Array, reader = new BincodeReader(input)) { let value: MultiEnum - switch (reader.readVariantIndex()) { + switch (reader.read_variant_index()) { case 0: { - value = { $: "VariantA" } as $t.WrapperOfCase - value.VariantA = reader.readI32() + value = { + $: "variant_a", + $0: reader.read_i32(), + } satisfies $t.WrapperOfCase break } case 1: { - value = { $: "VariantB" } as $t.WrapperOfCase - value.VariantB = reader.readString() + value = { + $: "variant_b", + $0: reader.read_string(), + } satisfies $t.WrapperOfCase break } case 2: { - value = { $: "VariantC" } as $t.WrapperOfCase - value.VariantC = {} as $t.WrapperOfCase["VariantC"] - value.VariantC.x = reader.readU8() - value.VariantC.y = reader.readF64() + value = { + $: "variant_c", + x: reader.read_u8(), + y: reader.read_f64(), + } satisfies $t.WrapperOfCase break } case 3: { - value = { $: "UnitVariant" } as $t.WrapperOfCase - value.UnitVariant = reader.readUnit() + value = { + $: "unit_variant", + $0: reader.read_unit() + } satisfies $t.WrapperOfCase break } } @@ -115,49 +129,53 @@ export const MultiEnum = { export const NewtypeStruct = { encode(value: NewtypeStruct, writer = new BincodeWriter()) { - writer.writeI32(value) - return writer.getBytes() + writer.write_i32(value) + return writer.get_bytes() }, decode(input: Uint8Array, reader = new BincodeReader(input)) { - const value: NewtypeStruct = reader.readI32() + const value: NewtypeStruct = reader.read_i32() return value } } export const SimpleStruct = { encode(value: SimpleStruct, writer = new BincodeWriter()) { - writer.writeU32(value.a) - writer.writeString(value.b) - return writer.getBytes() + writer.write_u32(value.a) + writer.write_string(value.b) + return writer.get_bytes() }, decode(input: Uint8Array, reader = new BincodeReader(input)) { const value = {} as SimpleStruct - value.a = reader.readU32() - value.b = reader.readString() + value.a = reader.read_u32() + value.b = reader.read_string() return value } } export const TupleStruct = { encode(value: TupleStruct, writer = new BincodeWriter()) { - writer.writeI32(value[0]) - writer.writeF64(value[1]) - writer.writeString(value[2]) - return writer.getBytes() + writer.write_i32(value.$0) + writer.write_f64(value.$1) + writer.write_string(value.$2) + return writer.get_bytes() }, decode(input: Uint8Array, reader = new BincodeReader(input)) { - const value: TupleStruct = [reader.readI32(), reader.readF64(), reader.readString()] + const value: TupleStruct = { + $0: reader.read_i32(), + $1: reader.read_f64(), + $2: reader.read_string(), + } return value } } export const UnitStruct = { encode(value: UnitStruct, writer = new BincodeWriter()) { - writer.writeUnit(null) - return writer.getBytes() + writer.write_unit(null) + return writer.get_bytes() }, decode(input: Uint8Array, reader = new BincodeReader(input)) { - const value: $t.unit = reader.readUnit() + const value: $t.unit = reader.read_unit() return value } } diff --git a/suite/typescript/ts/data.ts b/suite/typescript/ts/data.ts index e756b4ad2..9d67c2650 100644 --- a/suite/typescript/ts/data.ts +++ b/suite/typescript/ts/data.ts @@ -4,26 +4,25 @@ export const SimpleStruct_obj: Registry.SimpleStruct = { a: 42, b: "Hello" } export const SimpleStruct_bin = Uint8Array.from([42, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 72, 101, 108, 108, 111]) export const MultiEnum_VariantC_obj: Registry.MultiEnum = { - $: "VariantC", - VariantC: { x: 5, y: 3.14 } + $: "variant_c", + x: 5, y: 3.14 } export const MultiEnum_VariantC_bin = Uint8Array.from([2, 0, 0, 0, 5, 31, 133, 235, 81, 184, 30, 9, 64]) -export const MultiEnum_Unit_obj: Registry.MultiEnum = { $: "UnitVariant", UnitVariant: null } +export const MultiEnum_Unit_obj: Registry.MultiEnum = { $: "unit_variant", $0: null } export const MultiEnum_Unit_bin = Uint8Array.from([3, 0, 0, 0]) export const ComplexStruct_obj: Registry.ComplexStruct = { inner: { a: 42, b: "Hello" }, flag: true, items: [ - { $: "VariantA", VariantA: 10 }, - { $: "VariantB", VariantB: "World" } + { $: "variant_a", $0: 10 }, + { $: "variant_b", $0: "World" } ], unit: null, newtype: 99, - tuple: [123, 45.67, "Test"], + tuple: { $0: 123, $1: 45.67, $2: "Test" }, + tupple_inline: { $0: "SomeString", $1: 777 }, map: new Map().set(3, 7n) } -export const ComplexStruct_bin = Uint8Array.from([ - 42, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 72, 101, 108, 108, 111, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 87, 111, 114, 108, 100, 99, 0, 0, 0, 123, 0, 0, 0, 246, 40, 92, 143, 194, 213, 70, 64, 4, 0, 0, 0, 0, 0, 0, 0, 84, 101, 115, 116, 1, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0 -]) \ No newline at end of file +export const ComplexStruct_bin = Uint8Array.from([42, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 72, 101, 108, 108, 111, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 87, 111, 114, 108, 100, 99, 0, 0, 0, 123, 0, 0, 0, 246, 40, 92, 143, 194, 213, 70, 64, 4, 0, 0, 0, 0, 0, 0, 0, 84, 101, 115, 116, 10, 0, 0, 0, 0, 0, 0, 0, 83, 111, 109, 101, 83, 116, 114, 105, 110, 103, 9, 3, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0]) diff --git a/suite/typescript/ts/test.ts b/suite/typescript/ts/test.ts index e945817ba..282d74a30 100644 --- a/suite/typescript/ts/test.ts +++ b/suite/typescript/ts/test.ts @@ -28,7 +28,7 @@ suite("encode", () => { }) test("MultiEnum unit variant", () => { - const bin = Registry.MultiEnum.encode({ $: "UnitVariant", UnitVariant: null }) + const bin = Registry.MultiEnum.encode({ $: "unit_variant" }) assert.deepEqual(bin, Data.MultiEnum_Unit_bin) }) }) @@ -49,6 +49,3 @@ suite("decode", () => { assert.deepEqual(complex_instance, Data.ComplexStruct_obj) }) }) - - - From ee0401d49f9deb0b3cf69fbb1fe74a0e7978c0c1 Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Wed, 2 Oct 2024 22:07:12 +0500 Subject: [PATCH 21/30] respect config.serialization --- serde-generate/src/typescript.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/serde-generate/src/typescript.rs b/serde-generate/src/typescript.rs index a88ac5edc..89dc17706 100644 --- a/serde-generate/src/typescript.rs +++ b/serde-generate/src/typescript.rs @@ -68,9 +68,11 @@ impl<'a> CodeGenerator<'a> { writeln!(emitter.out)?; emitter.generate_container_typedef(name, format)?; } - for (name, format) in registry { - writeln!(emitter.out)?; - emitter.generate_container(name, format)?; + if self.config.serialization { + for (name, format) in registry { + writeln!(emitter.out)?; + emitter.generate_container(name, format)?; + } } Ok(()) From c3304530e31f34d768acd7616abce7080017a3f6 Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Fri, 4 Oct 2024 15:49:19 +0500 Subject: [PATCH 22/30] replace WrapperOfCase with TypeScript bult-in `Extract` --- serde-generate/runtime/typescript/serde.ts | 2 -- serde-generate/src/typescript.rs | 2 +- suite/typescript/ts/bincode/registry.ts | 8 ++++---- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/serde-generate/runtime/typescript/serde.ts b/serde-generate/runtime/typescript/serde.ts index fae6feef8..3ae084ac4 100644 --- a/serde-generate/runtime/typescript/serde.ts +++ b/serde-generate/runtime/typescript/serde.ts @@ -24,8 +24,6 @@ export type char = string export type str = string export type bytes = Uint8Array -export type WrapperOfCase = T extends { $: infer _U extends K } ? T : never - export interface Reader { read_string(): string read_bool(): boolean diff --git a/serde-generate/src/typescript.rs b/serde-generate/src/typescript.rs index 89dc17706..7da683035 100644 --- a/serde-generate/src/typescript.rs +++ b/serde-generate/src/typescript.rs @@ -249,7 +249,7 @@ impl<'a, T: Write> TypeScriptEmitter<'a, T> { } self.out.unindent(); - writeln!(self.out, r#"}} satisfies $t.WrapperOfCase<{0}, "{1}">"#, name, variant.name.to_snake_case())?; + writeln!(self.out, r#"}} satisfies Extract<{0}, {{ $: "{1}" }}>"#, name, variant.name.to_snake_case())?; writeln!(self.out, "break")?; self.out.unindent(); diff --git a/suite/typescript/ts/bincode/registry.ts b/suite/typescript/ts/bincode/registry.ts index 20f5e3292..def180d27 100644 --- a/suite/typescript/ts/bincode/registry.ts +++ b/suite/typescript/ts/bincode/registry.ts @@ -96,14 +96,14 @@ export const MultiEnum = { value = { $: "variant_a", $0: reader.read_i32(), - } satisfies $t.WrapperOfCase + } satisfies Extract break } case 1: { value = { $: "variant_b", $0: reader.read_string(), - } satisfies $t.WrapperOfCase + } satisfies Extract break } case 2: { @@ -111,14 +111,14 @@ export const MultiEnum = { $: "variant_c", x: reader.read_u8(), y: reader.read_f64(), - } satisfies $t.WrapperOfCase + } satisfies Extract break } case 3: { value = { $: "unit_variant", $0: reader.read_unit() - } satisfies $t.WrapperOfCase + } satisfies Extract break } } From 38316d7608d4ac53427be17c435e9aebbfc94689 Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Fri, 4 Oct 2024 16:10:00 +0500 Subject: [PATCH 23/30] simplify decoding initialization --- serde-generate/runtime/typescript/serde.ts | 4 +-- serde-generate/src/typescript.rs | 17 +++++------- suite/typescript/ts/bincode/registry.ts | 30 ++++++++++++---------- 3 files changed, 25 insertions(+), 26 deletions(-) diff --git a/serde-generate/runtime/typescript/serde.ts b/serde-generate/runtime/typescript/serde.ts index 3ae084ac4..fbe635452 100644 --- a/serde-generate/runtime/typescript/serde.ts +++ b/serde-generate/runtime/typescript/serde.ts @@ -1,7 +1,7 @@ export type Optional = T | null export type Seq = T[] -export type Tuple = { - [K in keyof T as `$${Exclude extends string ? Exclude : never}` ]: T[K] +export type Tuple = { + [K in keyof T as `$${Exclude extends string ? Exclude : never}` ]: T[K] } export type ListTuple = Tuple[] export type Map = globalThis.Map diff --git a/serde-generate/src/typescript.rs b/serde-generate/src/typescript.rs index 7da683035..d28ebe4de 100644 --- a/serde-generate/src/typescript.rs +++ b/serde-generate/src/typescript.rs @@ -146,19 +146,16 @@ impl<'a, T: Write> TypeScriptEmitter<'a, T> { self.out.unindent(); writeln!(self.out, "}}")?; } - _ => { writeln!(self.out, "const value = {{}} as {name}")?; } - } - - match container { - ContainerFormat::UnitStruct => { /* set at initialization */ } - ContainerFormat::TupleStruct(inner_types) => { /* set at initialization */ } - ContainerFormat::NewTypeStruct(inner_type) => { /* set at initialization */ } ContainerFormat::Struct(fields) => { + writeln!(self.out, "const value: {name} = {{")?; + self.out.indent(); for field in fields.iter() { - writeln!(self.out, "value.{} = {}", field.name, self.quote_read_value(&field.value))?; + writeln!(self.out, "{}: {},", field.name, self.quote_read_value(&field.value))?; } - } - ContainerFormat::Enum(..) => { /* handled before with generate_enum_container() */ } + self.out.unindent(); + writeln!(self.out, "}}")?; + }, + _ => unreachable!(), } writeln!(self.out, "return value")?; diff --git a/suite/typescript/ts/bincode/registry.ts b/suite/typescript/ts/bincode/registry.ts index def180d27..afc189f1b 100644 --- a/suite/typescript/ts/bincode/registry.ts +++ b/suite/typescript/ts/bincode/registry.ts @@ -46,18 +46,19 @@ export const ComplexStruct = { return writer.get_bytes() }, decode(input: Uint8Array, reader = new BincodeReader(input)) { - const value = {} as ComplexStruct - value.inner = SimpleStruct.decode(input, reader) - value.flag = reader.read_bool() - value.items = reader.read_list(() => MultiEnum.decode(input, reader)) - value.unit = UnitStruct.decode(input, reader) - value.newtype = NewtypeStruct.decode(input, reader) - value.tuple = TupleStruct.decode(input, reader) - value.tupple_inline = { - $0: reader.read_string(), - $1: reader.read_i32(), + const value: ComplexStruct = { + inner: SimpleStruct.decode(input, reader), + flag: reader.read_bool(), + items: reader.read_list(() => MultiEnum.decode(input, reader)), + unit: UnitStruct.decode(input, reader), + newtype: NewtypeStruct.decode(input, reader), + tuple: TupleStruct.decode(input, reader), + tupple_inline: { + $0: reader.read_string(), + $1: reader.read_i32(), + }, + map: reader.read_map<$t.i32, $t.i64>(reader.read_i32.bind(reader), reader.read_i64.bind(reader)), } - value.map = reader.read_map<$t.i32, $t.i64>(reader.read_i32.bind(reader), reader.read_i64.bind(reader)) return value } } @@ -145,9 +146,10 @@ export const SimpleStruct = { return writer.get_bytes() }, decode(input: Uint8Array, reader = new BincodeReader(input)) { - const value = {} as SimpleStruct - value.a = reader.read_u32() - value.b = reader.read_string() + const value: SimpleStruct = { + a: reader.read_u32(), + b: reader.read_string(), + } return value } } From 5e9bc72cea86a4504b75eef9cc07785542510b50 Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Sat, 5 Oct 2024 02:13:07 +0500 Subject: [PATCH 24/30] fix string encoding --- serde-generate/runtime/typescript/serde.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/serde-generate/runtime/typescript/serde.ts b/serde-generate/runtime/typescript/serde.ts index fbe635452..2a99751af 100644 --- a/serde-generate/runtime/typescript/serde.ts +++ b/serde-generate/runtime/typescript/serde.ts @@ -1,7 +1,7 @@ export type Optional = T | null export type Seq = T[] export type Tuple = { - [K in keyof T as `$${Exclude extends string ? Exclude : never}` ]: T[K] + [K in keyof T as `$${Exclude extends string ? Exclude : never}`]: T[K] } export type ListTuple = Tuple[] export type Map = globalThis.Map @@ -116,12 +116,12 @@ export abstract class BinaryWriter implements Writer { abstract sort_map_entries(offsets: number[]): void public write_string(value: string) { - const length = value.length + // https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder/encodeInto#buffer_sizing // char and U64 for length - this.alloc(8 + length) + this.alloc(8 + value.length * 3) // encode into buffer with space for string length (u64) - BinaryWriter.TEXT_ENCODER.encodeInto(value, new Uint8Array(this.view.buffer, this.offset + 8)) + let { written: length } = BinaryWriter.TEXT_ENCODER.encodeInto(value, new Uint8Array(this.view.buffer, this.offset + 8)) const bLength = BigInt(length) this.view.setUint32(this.offset, Number(bLength & BIG_32Fs), true) From 06f897538468603bbe8d843d18007508cdd95d4e Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Sat, 5 Oct 2024 12:02:47 +0500 Subject: [PATCH 25/30] fix string encoding again & use native `setBigUint64` --- serde-generate/runtime/typescript/serde.ts | 49 ++++++++-------------- 1 file changed, 18 insertions(+), 31 deletions(-) diff --git a/serde-generate/runtime/typescript/serde.ts b/serde-generate/runtime/typescript/serde.ts index 2a99751af..8336201b6 100644 --- a/serde-generate/runtime/typescript/serde.ts +++ b/serde-generate/runtime/typescript/serde.ts @@ -93,39 +93,38 @@ export abstract class BinaryWriter implements Writer { } } - private alloc(allocLength: number) { - const wishSize = this.offset + allocLength +private alloc(allocLength: number) { + const wish_size = this.offset + allocLength - const currentLength = this.view.buffer.byteLength - if (wishSize > currentLength) { - let newBufferLength = currentLength - while (newBufferLength <= wishSize) newBufferLength = newBufferLength << 1 + const current_length = this.view.buffer.byteLength + if (wish_size > current_length) { + let new_buffer_length = current_length + while (new_buffer_length < wish_size) new_buffer_length = new_buffer_length << 1 - // TODO: there is new API for resizing buffer, but in Node it seems to be slower then allocating new - // this.buffer.resize(newBufferLength) + // TODO: there is new API for resizing buffer, but in Node it seems to be slower then allocating new + // this.buffer.resize(newBufferLength) - const newBuffer = new Uint8Array(newBufferLength) - newBuffer.set(new Uint8Array(this.view.buffer)) + const newBuffer = new Uint8Array(new_buffer_length) + newBuffer.set(new Uint8Array(this.view.buffer)) - this.view = WRITE_HEAP = new DataView(newBuffer.buffer) - } + this.view = WRITE_HEAP = new DataView(newBuffer.buffer) } +} abstract write_length(value: number): void abstract write_variant_index(value: number): void abstract sort_map_entries(offsets: number[]): void + // https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder/encodeInto#buffer_sizing public write_string(value: string) { - // https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder/encodeInto#buffer_sizing - // char and U64 for length + // allocate space for string length marker and whole string this.alloc(8 + value.length * 3) // encode into buffer with space for string length (u64) let { written: length } = BinaryWriter.TEXT_ENCODER.encodeInto(value, new Uint8Array(this.view.buffer, this.offset + 8)) - const bLength = BigInt(length) - this.view.setUint32(this.offset, Number(bLength & BIG_32Fs), true) - this.view.setUint32(this.offset + 4, Number(bLength >> BIG_32), true) + const b_length = BigInt(length) + this.view.setBigUint64(this.offset, b_length, true) this.offset += (8 + length) } @@ -157,14 +156,8 @@ export abstract class BinaryWriter implements Writer { } public write_u64(value: bigint | number) { - const low = BigInt(value) & BIG_32Fs, high = BigInt(value) >> BIG_32 - this.alloc(8) - - // write little endian number - this.view.setUint32(this.offset, Number(low), true) - this.view.setUint32(this.offset + 4, Number(high), true) - + this.view.setBigUint64(this.offset, BigInt(value), true) this.offset += 8 } @@ -195,14 +188,8 @@ export abstract class BinaryWriter implements Writer { } public write_i64(value: bigint | number) { - const low = BigInt(value) & BIG_32Fs, high = BigInt(value) >> BIG_32 - this.alloc(8) - - // write little endian number - this.view.setInt32(this.offset, Number(low), true) - this.view.setInt32(this.offset + 4, Number(high), true) - + this.view.setBigInt64(this.offset, BigInt(value), true) this.offset += 8 } From b7996e6d3832aaad54660653845eebe165077186 Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Sat, 5 Oct 2024 12:05:08 +0500 Subject: [PATCH 26/30] update benchmarks table --- suite/typescript/readme.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/suite/typescript/readme.md b/suite/typescript/readme.md index 92fa57787..7f8c7ecbe 100644 --- a/suite/typescript/readme.md +++ b/suite/typescript/readme.md @@ -13,18 +13,18 @@ gen:proto && gen:bincode && run:test && run:benchmarks ┌─────────┬───────────────────────────────┬─────────────┬────────────────────┬──────────┬─────────┐ │ (index) │ Task Name │ ops/sec │ Average Time (ns) │ Margin │ Samples │ ├─────────┼───────────────────────────────┼─────────────┼────────────────────┼──────────┼─────────┤ -│ 0 │ 'serdegen-bincode:encode' │ '511,820' │ 1953.8108308771214 │ '±0.30%' │ 767731 │ -│ 1 │ 'JSON:encode' │ '465,955' │ 2146.1257915628617 │ '±0.16%' │ 698934 │ -│ 2 │ 'protobuf-js-ts-proto:encode' │ '1,027,594' │ 973.1467887466059 │ '±0.35%' │ 1541392 │ +│ 0 │ 'serdegen-bincode:encode' │ '717,309' │ 1394.0992784144942 │ '±0.31%' │ 1075964 │ +│ 1 │ 'JSON:encode' │ '384,683' │ 2599.5422867295706 │ '±0.05%' │ 577025 │ +│ 2 │ 'protobuf-js-ts-proto:encode' │ '1,049,439' │ 952.8891828025206 │ '±0.50%' │ 1574160 │ └─────────┴───────────────────────────────┴─────────────┴────────────────────┴──────────┴─────────┘ ``` ### Decode ``` -┌─────────┬───────────────────────────────┬─────────────┬────────────────────┬──────────┬─────────┐ -│ (index) │ Task Name │ ops/sec │ Average Time (ns) │ Margin │ Samples │ -├─────────┼───────────────────────────────┼─────────────┼────────────────────┼──────────┼─────────┤ -│ 0 │ 'serdegen-bincode:decode' │ '1,066,364' │ 937.7653036984581 │ '±0.05%' │ 1599548 │ -│ 1 │ 'JSON:decode' │ '549,795' │ 1818.8571833913045 │ '±0.15%' │ 824694 │ -│ 2 │ 'protobuf-js-ts-proto:decode' │ '837,735' │ 1193.694625111962 │ '±0.26%' │ 1256603 │ -└─────────┴───────────────────────────────┴─────────────┴────────────────────┴──────────┴─────────┘ +┌─────────┬───────────────────────────────┬───────────┬────────────────────┬──────────┬─────────┐ +│ (index) │ Task Name │ ops/sec │ Average Time (ns) │ Margin │ Samples │ +├─────────┼───────────────────────────────┼───────────┼────────────────────┼──────────┼─────────┤ +│ 0 │ 'serdegen-bincode:decode' │ '894,259' │ 1118.2439247680838 │ '±0.24%' │ 1341389 │ +│ 1 │ 'JSON:decode' │ '460,448' │ 2171.7932150326965 │ '±0.14%' │ 690674 │ +│ 2 │ 'protobuf-js-ts-proto:decode' │ '837,149' │ 1194.5301794026077 │ '±0.18%' │ 1255724 │ +└─────────┴───────────────────────────────┴───────────┴────────────────────┴──────────┴─────────┘ ``` \ No newline at end of file From c9a70cc89767163cc35a0fd4e3e02e7e726d7f02 Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Wed, 9 Oct 2024 04:29:21 +0500 Subject: [PATCH 27/30] use `getBigUint64`, `getBigInt64` --- serde-generate/runtime/typescript/serde.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/serde-generate/runtime/typescript/serde.ts b/serde-generate/runtime/typescript/serde.ts index 8336201b6..217818b19 100644 --- a/serde-generate/runtime/typescript/serde.ts +++ b/serde-generate/runtime/typescript/serde.ts @@ -285,9 +285,9 @@ export abstract class BinaryReader implements Reader { } public read_u64() { - const low = this.read_u32(), high = this.read_u32() - // combine the two 32-bit values and return (little endian) - return (BigInt(high) << BIG_32) | BigInt(low) + const value = this.view.getBigUint64(this.offset, true) + this.offset += 8 + return value } public read_u128() { @@ -315,9 +315,9 @@ export abstract class BinaryReader implements Reader { } public read_i64() { - const low = this.read_i32(), high = this.read_i32() - // combine the two 32-bit values and return (little endian) - return (BigInt(high) << BIG_32) | BigInt(low) + const value = this.view.getBigInt64(this.offset, true) + this.offset += 8 + return value } public read_i128() { From 02e746ca9774f6c9a0a458642db318d937c39545 Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Sun, 9 Mar 2025 12:59:51 +0500 Subject: [PATCH 28/30] switch to bincode 2 switch to node --experimental-strip-types --- Cargo.lock | 5 +- serde-generate/runtime/typescript/bincode.ts | 6 +- serde-generate/src/typescript.rs | 18 +- suite/typescript/.gitignore | 3 +- suite/typescript/Cargo.toml | 5 +- suite/typescript/flake.lock | 212 +++++++++++-- suite/typescript/flake.nix | 83 +++-- suite/typescript/package.json | 7 +- suite/typescript/pnpm-lock.yaml | 317 +------------------ suite/typescript/rs/generator.rs | 21 +- suite/typescript/ts/bench.ts | 32 +- suite/typescript/ts/bincode/registry.ts | 16 +- suite/typescript/ts/proto/main.ts | 4 +- suite/typescript/tsconfig.json | 3 +- tsconfig.json | 13 - 15 files changed, 313 insertions(+), 432 deletions(-) delete mode 100644 tsconfig.json diff --git a/Cargo.lock b/Cargo.lock index a4f930d99..83daffe3a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1777,12 +1777,13 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" dependencies = [ "indexmap 2.7.0", "itoa", + "memchr", "ryu", "serde", ] diff --git a/serde-generate/runtime/typescript/bincode.ts b/serde-generate/runtime/typescript/bincode.ts index 57dc17b1a..d3194edbc 100644 --- a/serde-generate/runtime/typescript/bincode.ts +++ b/serde-generate/runtime/typescript/bincode.ts @@ -1,14 +1,12 @@ -import { BinaryReader, BinaryWriter } from "./serde"; +import { BinaryReader, BinaryWriter } from "./serde.ts"; export class BincodeReader extends BinaryReader { read_length() { return Number(this.read_u64()) } - public read_variant_index() { return this.read_u32() } - check_that_key_slices_are_increasing(key1: [number, number], key2: [number, number]) { return } @@ -18,11 +16,9 @@ export class BincodeWriter extends BinaryWriter { write_length(value: number) { this.write_u64(value) } - public write_variant_index(value: number) { this.write_u32(value) } - public sort_map_entries(offsets: number[]) { return } diff --git a/serde-generate/src/typescript.rs b/serde-generate/src/typescript.rs index d28ebe4de..d40834167 100644 --- a/serde-generate/src/typescript.rs +++ b/serde-generate/src/typescript.rs @@ -81,10 +81,10 @@ impl<'a> CodeGenerator<'a> { impl<'a, T: Write> TypeScriptEmitter<'a, T> { fn output_preamble(&mut self) -> Result<()> { - writeln!(self.out, r#"import type * as $t from "./serde""#)?; - writeln!(self.out, r#"import {{ BincodeReader, BincodeWriter }} from "./bincode""#)?; + writeln!(self.out, r#"import type * as $t from "./serde.ts""#)?; + writeln!(self.out, r#"import {{ BincodeReader, BincodeWriter }} from "./bincode.ts""#)?; for namespace in self.generator.namespaces_to_import.iter() { - writeln!(self.out, "import * as {} from '../{}/mod';\n", namespace.to_camel_case(), namespace)?; + writeln!(self.out, "import * as {} from '../{}/mod.ts';\n", namespace.to_camel_case(), namespace)?; } Ok(()) } @@ -132,13 +132,13 @@ impl<'a, T: Write> TypeScriptEmitter<'a, T> { match container { ContainerFormat::UnitStruct => { - writeln!(self.out, "const value: $t.unit = {}", self.quote_read_value(&Format::Unit))?; + writeln!(self.out, "let value: $t.unit = {}", self.quote_read_value(&Format::Unit))?; } ContainerFormat::NewTypeStruct(inner) => { - writeln!(self.out, "const value: {name} = {}", self.quote_read_value(inner))?; + writeln!(self.out, "let value: {name} = {}", self.quote_read_value(inner))?; } ContainerFormat::TupleStruct(inner_types) => { - writeln!(self.out, "const value: {name} = {{")?; + writeln!(self.out, "let value: {name} = {{")?; self.out.indent(); for (i, inner) in inner_types.iter().enumerate() { writeln!(self.out, "${i}: {},", self.quote_read_value(&inner))?; @@ -147,7 +147,7 @@ impl<'a, T: Write> TypeScriptEmitter<'a, T> { writeln!(self.out, "}}")?; } ContainerFormat::Struct(fields) => { - writeln!(self.out, "const value: {name} = {{")?; + writeln!(self.out, "let value: {name} = {{")?; self.out.indent(); for field in fields.iter() { writeln!(self.out, "{}: {},", field.name, self.quote_read_value(&field.value))?; @@ -411,7 +411,7 @@ impl<'a, T: Write> TypeScriptEmitter<'a, T> { Seq(format) => { formatdoc!(" writer.write_length({value}.length) - for (const item of {value}) {{ + for (let item of {value}) {{ {} }}", self.quote_write_value("item", format) @@ -435,7 +435,7 @@ impl<'a, T: Write> TypeScriptEmitter<'a, T> { } TupleArray { content, .. } => { formatdoc!(" - for (const item of {value}) {{ + for (let item of {value}) {{ {} }}", self.quote_write_value("item[0]", content) diff --git a/suite/typescript/.gitignore b/suite/typescript/.gitignore index fc0bac6ee..7600b1b2c 100644 --- a/suite/typescript/.gitignore +++ b/suite/typescript/.gitignore @@ -1,3 +1,4 @@ node_modules target -.devenv \ No newline at end of file +.devenv +.envrc \ No newline at end of file diff --git a/suite/typescript/Cargo.toml b/suite/typescript/Cargo.toml index f85fe644f..0d6367eee 100644 --- a/suite/typescript/Cargo.toml +++ b/suite/typescript/Cargo.toml @@ -8,7 +8,8 @@ name = "generator" path = "./rs/generator.rs" [dependencies] -bincode = { version = "1.3.3", features = ["i128"] } -serde = "1" +bincode = { version = "2", features = ["serde"] } +serde = { version = "1" } serde-generate = { path = "../../serde-generate" } serde-reflection = { path = "../../serde-reflection" } +serde_json = { version = "1" } diff --git a/suite/typescript/flake.lock b/suite/typescript/flake.lock index b7486745a..f027e9e34 100644 --- a/suite/typescript/flake.lock +++ b/suite/typescript/flake.lock @@ -11,10 +11,7 @@ "devenv", "nixpkgs" ], - "pre-commit-hooks": [ - "devenv", - "pre-commit-hooks" - ] + "pre-commit-hooks": "pre-commit-hooks" }, "locked": { "lastModified": 1712055811, @@ -33,24 +30,25 @@ "devenv": { "inputs": { "cachix": "cachix", - "flake-compat": "flake-compat_2", + "flake-compat": "flake-compat_3", "nix": "nix_2", "nixpkgs": [ "nixpkgs" ], - "pre-commit-hooks": "pre-commit-hooks" + "pre-commit-hooks": "pre-commit-hooks_3" }, "locked": { - "lastModified": 1725964132, - "narHash": "sha256-+IW4z7tXTgkEA677hbT+qsfNxaduCZY+1rAKFgVVmLI=", + "lastModified": 1727098005, + "narHash": "sha256-aKRUBfaDCJxdM8f/9RNmGJa82oZXDbOl6hxc/s2nLss=", "owner": "cachix", "repo": "devenv", - "rev": "98c7c131e3fa30eb00e9bfe44c1a180c7f94102f", + "rev": "f318d27a4637aff765a378106d82dfded124c3b3", "type": "github" }, "original": { "owner": "cachix", "repo": "devenv", + "rev": "f318d27a4637aff765a378106d82dfded124c3b3", "type": "github" } }, @@ -117,7 +115,45 @@ "type": "github" } }, + "flake-compat_3": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, "flake-parts": { + "inputs": { + "nixpkgs-lib": [ + "devenv", + "nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1741352980, + "narHash": "sha256-+u2UunDA4Cl5Fci3m7S643HzKmIDAe+fiXrLqYsR2fs=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "f4330d22f1c5d2ba72d3d22df5597d123fdb60a9", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-parts_2": { "inputs": { "nixpkgs-lib": "nixpkgs-lib" }, @@ -172,6 +208,29 @@ } }, "gitignore": { + "inputs": { + "nixpkgs": [ + "devenv", + "cachix", + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "gitignore_2": { "inputs": { "nixpkgs": [ "devenv", @@ -193,6 +252,22 @@ "type": "github" } }, + "libgit2": { + "flake": false, + "locked": { + "lastModified": 1740952128, + "narHash": "sha256-lmG/Vg3hxb/dRVPFhkQRB1dp8XT2YJzCvkPjm7yXP/A=", + "owner": "libgit2", + "repo": "libgit2", + "rev": "21a351b0ed207d0871cb23e09c027d1ee42eae98", + "type": "github" + }, + "original": { + "owner": "libgit2", + "repo": "libgit2", + "type": "github" + } + }, "nix": { "inputs": { "flake-compat": "flake-compat", @@ -249,23 +324,24 @@ "devenv", "flake-compat" ], - "nixpkgs": [ - "devenv", - "nixpkgs" - ], - "nixpkgs-regression": "nixpkgs-regression_2" + "flake-parts": "flake-parts", + "libgit2": "libgit2", + "nixpkgs": "nixpkgs_2", + "nixpkgs-23-11": "nixpkgs-23-11", + "nixpkgs-regression": "nixpkgs-regression_2", + "pre-commit-hooks": "pre-commit-hooks_2" }, "locked": { - "lastModified": 1712911606, - "narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=", + "lastModified": 1734114420, + "narHash": "sha256-n52PUzub5jZWc8nI/sR7UICOheU8rNA+YZ73YaHeCBg=", "owner": "domenkozar", "repo": "nix", - "rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12", + "rev": "bde6a1a0d1f2af86caa4d20d23eca019f3d57eee", "type": "github" }, "original": { "owner": "domenkozar", - "ref": "devenv-2.21", + "ref": "devenv-2.24", "repo": "nix", "type": "github" } @@ -286,6 +362,22 @@ "type": "github" } }, + "nixpkgs-23-11": { + "locked": { + "lastModified": 1717159533, + "narHash": "sha256-oamiKNfr2MS6yH64rUn99mIZjc45nGJlj9eGth/3Xuw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "a62e6edd6d5e1fa0329b8653c801147986f8d446", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "a62e6edd6d5e1fa0329b8653c801147986f8d446", + "type": "github" + } + }, "nixpkgs-lib": { "locked": { "lastModified": 1725233747, @@ -348,17 +440,33 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1725910328, - "narHash": "sha256-n9pCtzGZ0httmTwMuEbi5E78UQ4ZbQMr1pzi5N0LAG8=", + "lastModified": 1735651292, + "narHash": "sha256-YLbzcBtYo1/FEzFsB3AnM16qFc6fWPMIoOuSoDwvg9g=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "0da3c44a9460a26d2025ec3ed2ec60a895eb1114", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "release-24.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_3": { + "locked": { + "lastModified": 1740019556, + "narHash": "sha256-vn285HxnnlHLWnv59Og7muqECNMS33mWLM14soFIv2g=", "owner": "nixos", "repo": "nixpkgs", - "rev": "5775c2583f1801df7b790bf7f7d710a19bac66f4", + "rev": "dad564433178067be1fbdfcce23b546254b6d641", "type": "github" }, "original": { "owner": "nixos", - "ref": "nixpkgs-unstable", "repo": "nixpkgs", + "rev": "dad564433178067be1fbdfcce23b546254b6d641", "type": "github" } }, @@ -388,13 +496,67 @@ } }, "pre-commit-hooks": { + "inputs": { + "flake-compat": "flake-compat_2", + "gitignore": "gitignore", + "nixpkgs": [ + "devenv", + "cachix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1741379162, + "narHash": "sha256-srpAbmJapkaqGRE3ytf3bj4XshspVR5964OX5LfjDWc=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "b5a62751225b2f62ff3147d0a334055ebadcd5cc", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "pre-commit-hooks_2": { + "inputs": { + "flake-compat": [ + "devenv", + "nix" + ], + "gitignore": [ + "devenv", + "nix" + ], + "nixpkgs": [ + "devenv", + "nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1741379162, + "narHash": "sha256-srpAbmJapkaqGRE3ytf3bj4XshspVR5964OX5LfjDWc=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "b5a62751225b2f62ff3147d0a334055ebadcd5cc", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "pre-commit-hooks_3": { "inputs": { "flake-compat": [ "devenv", "flake-compat" ], "flake-utils": "flake-utils_2", - "gitignore": "gitignore", + "gitignore": "gitignore_2", "nixpkgs": [ "devenv", "nixpkgs" @@ -418,8 +580,8 @@ "root": { "inputs": { "devenv": "devenv", - "flake-parts": "flake-parts", - "nixpkgs": "nixpkgs_2" + "flake-parts": "flake-parts_2", + "nixpkgs": "nixpkgs_3" } }, "systems": { diff --git a/suite/typescript/flake.nix b/suite/typescript/flake.nix index fc83d7ede..e67e4bc8b 100644 --- a/suite/typescript/flake.nix +++ b/suite/typescript/flake.nix @@ -1,11 +1,10 @@ { inputs = { - # - nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable"; - + nixpkgs.url = "github:nixos/nixpkgs?rev=dad564433178067be1fbdfcce23b546254b6d641"; + flake-parts.url = "github:hercules-ci/flake-parts"; - devenv.url = "github:cachix/devenv"; + devenv.url = "github:cachix/devenv?rev=f318d27a4637aff765a378106d82dfded124c3b3"; # https://github.com/cachix/devenv/issues/1513 devenv.inputs.nixpkgs.follows = "nixpkgs"; }; @@ -14,57 +13,75 @@ lib = inputs.nixpkgs.lib; in with lib; - inputs.flake-parts.lib.mkFlake { - inherit inputs; - specialArgs = {inherit lib;}; - } + inputs.flake-parts.lib.mkFlake { inherit inputs; specialArgs = {inherit lib;}; } ({moduleWithSystem, ...}: { imports = with inputs; [devenv.flakeModule]; systems = ["x86_64-linux"]; - perSystem = { - config, - system, - self', - inputs', - ... - }: let - pkgs = import inputs.nixpkgs { - inherit system; - config.allowUnfree = true; - }; + perSystem = { config, system, self', inputs', ... }: let + pkgs = import inputs.nixpkgs { inherit system; config.allowUnfree = true; }; in { _module.args = {inherit pkgs;}; devenv.shells.default = {config, ...} @ devenvArgs: let inherit (config.devenv) root state profile; in { - packages = with pkgs; [nodejs_22 nodePackages.pnpm protobuf_28]; - scripts."gen:proto".exec = concatStringsSep " \\\n" [ - "protoc" - "--plugin ${root}/node_modules/.bin/protoc-gen-ts_proto" - "--ts_proto_out ts/proto" - "--ts_proto_opt esModuleInterop=true,snakeToCamel=false,forceLong=number,oneof=unions,outputJsonMethods=false,env=browser" - - "--proto_path schema-proto" - "--proto_path ${pkgs.protobuf}/include/google/protobuf/" - ''$(find schema-proto -iname "*.proto")'' - ]; + enterShell = let + # ANSI colors: https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit + commands = pipe devenvArgs.config.scripts [ + attrNames + (groupBy (cmd: elemAt (splitString ":" cmd) 0)) + (mapAttrsToList (group: commands: let + splitted = pipe commands [ + (sortOn stringLength) + (map (removePrefix group)) + (concatStringsSep "|") + ]; + in "$(tput setaf 226)${group}$(tput sgr0)|${splitted}")) + (intersperse "\n") + concatStrings + ]; + in '' + echo "$(tput setaf 118)Welcome to serde-reflection typescript suite.$(tput sgr0)" + echo "${commands}" | ${pkgs.unixtools.column}/bin/column --table -W 1 -T 1 -t -s "|" + ''; + packages = with pkgs; [nodejs_23 nodePackages.pnpm protobuf_28]; + scripts."gen:proto".exec = '' + pushd ${root} + rm -rf ts/proto/* + mkdir -p ts/proto + ${concatStringsSep " \\\n" [ + "protoc" + "--plugin ${root}/node_modules/.bin/protoc-gen-ts_proto" + "--ts_proto_out ts/proto" + "--ts_proto_opt esModuleInterop=true,snakeToCamel=false,forceLong=number,oneof=unions,outputJsonMethods=false,env=both,importSuffix=.js" + "--proto_path schema-proto" + "--proto_path ${pkgs.protobuf}/include/google/protobuf/" + ''$(find schema-proto -iname "*.proto")'' + ]} + popd + ''; scripts."gen:bincode".exec = '' pushd ${root} + rm ts/bincode/registry.ts cargo run popd ''; - scripts."run:test".exec = '' pushd ${root} - node_modules/.bin/tsx ts/test.ts + node --experimental-strip-types --no-warnings ts/test.ts popd ''; scripts."run:benchmarks".exec = '' pushd ${root} - node_modules/.bin/tsx ts/bench.ts + node --experimental-strip-types --no-warnings ts/bench.ts popd ''; + scripts."run:suite".exec = '' + gen:bincode + gen:proto + run:test + run:benchmarks + ''; }; }; }); diff --git a/suite/typescript/package.json b/suite/typescript/package.json index da72aa4cf..7daeb082e 100644 --- a/suite/typescript/package.json +++ b/suite/typescript/package.json @@ -3,13 +3,12 @@ "type": "module", "version": "0.0.0", "dependencies": { - "long": "^5.2.3", + "long": "^5.3.1", "protobufjs": "^7.4.0", "tinybench": "^2.9.0", - "ts-proto": "1", - "tsx": "^4.19.0" + "ts-proto": "1" }, "devDependencies": { - "@types/node": "^22.5.4" + "@types/node": "^22.13.10" } } diff --git a/suite/typescript/pnpm-lock.yaml b/suite/typescript/pnpm-lock.yaml index 3d23bd4f6..a4c75daaa 100644 --- a/suite/typescript/pnpm-lock.yaml +++ b/suite/typescript/pnpm-lock.yaml @@ -9,8 +9,8 @@ importers: .: dependencies: long: - specifier: ^5.2.3 - version: 5.2.3 + specifier: ^5.3.1 + version: 5.3.1 protobufjs: specifier: ^7.4.0 version: 7.4.0 @@ -20,160 +20,13 @@ importers: ts-proto: specifier: '1' version: 1.181.2 - tsx: - specifier: ^4.19.0 - version: 4.19.0 devDependencies: '@types/node': - specifier: ^22.5.4 - version: 22.5.4 + specifier: ^22.13.10 + version: 22.13.10 packages: - '@esbuild/aix-ppc64@0.23.1': - resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - - '@esbuild/android-arm64@0.23.1': - resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm@0.23.1': - resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - - '@esbuild/android-x64@0.23.1': - resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - - '@esbuild/darwin-arm64@0.23.1': - resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-x64@0.23.1': - resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - - '@esbuild/freebsd-arm64@0.23.1': - resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.23.1': - resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - - '@esbuild/linux-arm64@0.23.1': - resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm@0.23.1': - resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-ia32@0.23.1': - resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-loong64@0.23.1': - resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-mips64el@0.23.1': - resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-ppc64@0.23.1': - resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-riscv64@0.23.1': - resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-s390x@0.23.1': - resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-x64@0.23.1': - resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - - '@esbuild/netbsd-x64@0.23.1': - resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - - '@esbuild/openbsd-arm64@0.23.1': - resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.23.1': - resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - - '@esbuild/sunos-x64@0.23.1': - resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - - '@esbuild/win32-arm64@0.23.1': - resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-ia32@0.23.1': - resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-x64@0.23.1': - resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - '@protobufjs/aspromise@1.1.2': resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} @@ -204,8 +57,8 @@ packages: '@protobufjs/utf8@1.1.0': resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} - '@types/node@22.5.4': - resolution: {integrity: sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==} + '@types/node@22.13.10': + resolution: {integrity: sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==} case-anything@2.1.13: resolution: {integrity: sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng==} @@ -219,29 +72,13 @@ packages: dprint-node@1.0.8: resolution: {integrity: sha512-iVKnUtYfGrYcW1ZAlfR/F59cUVL8QIhWoBJoSjkkdua/dkWIgjZfiLMeTjiB06X0ZLkQ0M2C1VbUj/CxkIf1zg==} - esbuild@0.23.1: - resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} - engines: {node: '>=18'} - hasBin: true - - fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - - get-tsconfig@4.8.0: - resolution: {integrity: sha512-Pgba6TExTZ0FJAn1qkJAjIeKoDJ3CsI2ChuLohJnZl/tTU8MVrq3b+2t5UOPfRa4RMsorClBjJALkJUMjG1PAw==} - - long@5.2.3: - resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} + long@5.3.1: + resolution: {integrity: sha512-ka87Jz3gcx/I7Hal94xaN2tZEOPoUOEVftkQqZx2EeQRN7LGdfLlI3FvZ+7WDplm+vK2Urx9ULrvSowtdCieng==} protobufjs@7.4.0: resolution: {integrity: sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==} engines: {node: '>=12.0.0'} - resolve-pkg-maps@1.0.0: - resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} @@ -255,88 +92,11 @@ packages: resolution: {integrity: sha512-knJ8dtjn2Pd0c5ZGZG8z9DMiD4PUY8iGI9T9tb8DvGdWRMkLpf0WcPO7G+7cmbZyxvNTAG6ci3fybEaFgMZIvg==} hasBin: true - tsx@4.19.0: - resolution: {integrity: sha512-bV30kM7bsLZKZIOCHeMNVMJ32/LuJzLVajkQI/qf92J2Qr08ueLQvW00PUZGiuLPP760UINwupgUj8qrSCPUKg==} - engines: {node: '>=18.0.0'} - hasBin: true - - undici-types@6.19.8: - resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + undici-types@6.20.0: + resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} snapshots: - '@esbuild/aix-ppc64@0.23.1': - optional: true - - '@esbuild/android-arm64@0.23.1': - optional: true - - '@esbuild/android-arm@0.23.1': - optional: true - - '@esbuild/android-x64@0.23.1': - optional: true - - '@esbuild/darwin-arm64@0.23.1': - optional: true - - '@esbuild/darwin-x64@0.23.1': - optional: true - - '@esbuild/freebsd-arm64@0.23.1': - optional: true - - '@esbuild/freebsd-x64@0.23.1': - optional: true - - '@esbuild/linux-arm64@0.23.1': - optional: true - - '@esbuild/linux-arm@0.23.1': - optional: true - - '@esbuild/linux-ia32@0.23.1': - optional: true - - '@esbuild/linux-loong64@0.23.1': - optional: true - - '@esbuild/linux-mips64el@0.23.1': - optional: true - - '@esbuild/linux-ppc64@0.23.1': - optional: true - - '@esbuild/linux-riscv64@0.23.1': - optional: true - - '@esbuild/linux-s390x@0.23.1': - optional: true - - '@esbuild/linux-x64@0.23.1': - optional: true - - '@esbuild/netbsd-x64@0.23.1': - optional: true - - '@esbuild/openbsd-arm64@0.23.1': - optional: true - - '@esbuild/openbsd-x64@0.23.1': - optional: true - - '@esbuild/sunos-x64@0.23.1': - optional: true - - '@esbuild/win32-arm64@0.23.1': - optional: true - - '@esbuild/win32-ia32@0.23.1': - optional: true - - '@esbuild/win32-x64@0.23.1': - optional: true - '@protobufjs/aspromise@1.1.2': {} '@protobufjs/base64@1.1.2': {} @@ -360,9 +120,9 @@ snapshots: '@protobufjs/utf8@1.1.0': {} - '@types/node@22.5.4': + '@types/node@22.13.10': dependencies: - undici-types: 6.19.8 + undici-types: 6.20.0 case-anything@2.1.13: {} @@ -372,41 +132,7 @@ snapshots: dependencies: detect-libc: 1.0.3 - esbuild@0.23.1: - optionalDependencies: - '@esbuild/aix-ppc64': 0.23.1 - '@esbuild/android-arm': 0.23.1 - '@esbuild/android-arm64': 0.23.1 - '@esbuild/android-x64': 0.23.1 - '@esbuild/darwin-arm64': 0.23.1 - '@esbuild/darwin-x64': 0.23.1 - '@esbuild/freebsd-arm64': 0.23.1 - '@esbuild/freebsd-x64': 0.23.1 - '@esbuild/linux-arm': 0.23.1 - '@esbuild/linux-arm64': 0.23.1 - '@esbuild/linux-ia32': 0.23.1 - '@esbuild/linux-loong64': 0.23.1 - '@esbuild/linux-mips64el': 0.23.1 - '@esbuild/linux-ppc64': 0.23.1 - '@esbuild/linux-riscv64': 0.23.1 - '@esbuild/linux-s390x': 0.23.1 - '@esbuild/linux-x64': 0.23.1 - '@esbuild/netbsd-x64': 0.23.1 - '@esbuild/openbsd-arm64': 0.23.1 - '@esbuild/openbsd-x64': 0.23.1 - '@esbuild/sunos-x64': 0.23.1 - '@esbuild/win32-arm64': 0.23.1 - '@esbuild/win32-ia32': 0.23.1 - '@esbuild/win32-x64': 0.23.1 - - fsevents@2.3.3: - optional: true - - get-tsconfig@4.8.0: - dependencies: - resolve-pkg-maps: 1.0.0 - - long@5.2.3: {} + long@5.3.1: {} protobufjs@7.4.0: dependencies: @@ -420,10 +146,8 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 22.5.4 - long: 5.2.3 - - resolve-pkg-maps@1.0.0: {} + '@types/node': 22.13.10 + long: 5.3.1 tinybench@2.9.0: {} @@ -433,7 +157,7 @@ snapshots: ts-proto-descriptors@1.16.0: dependencies: - long: 5.2.3 + long: 5.3.1 protobufjs: 7.4.0 ts-proto@1.181.2: @@ -443,11 +167,4 @@ snapshots: ts-poet: 6.9.0 ts-proto-descriptors: 1.16.0 - tsx@4.19.0: - dependencies: - esbuild: 0.23.1 - get-tsconfig: 4.8.0 - optionalDependencies: - fsevents: 2.3.3 - - undici-types@6.19.8: {} + undici-types@6.20.0: {} diff --git a/suite/typescript/rs/generator.rs b/suite/typescript/rs/generator.rs index a6ef1b728..4fc0fc9e3 100644 --- a/suite/typescript/rs/generator.rs +++ b/suite/typescript/rs/generator.rs @@ -1,17 +1,18 @@ use std::{collections::HashMap, error::Error}; use serde::{Deserialize, Serialize}; +use bincode::{Encode, Decode}; fn main() -> Result<(), Box> { use serde_reflection::{Registry, Tracer, TracerConfig}; let mut tracer = Tracer::new(TracerConfig::default()); - #[derive(Clone, Debug, Serialize, Deserialize)] + #[derive(Clone, Debug, Serialize, Deserialize, Encode, Decode)] struct SimpleStruct { a: u32, b: String, } - #[derive(Serialize, Deserialize, Debug)] + #[derive(Debug, Serialize, Deserialize, Encode, Decode)] enum MultiEnum { VariantA(i32), VariantB(String), @@ -19,16 +20,16 @@ fn main() -> Result<(), Box> { UnitVariant, } - #[derive(Serialize, Deserialize, Debug)] + #[derive(Debug, Serialize, Deserialize, Encode, Decode)] struct UnitStruct; - #[derive(Serialize, Deserialize, Debug)] + #[derive(Debug, Serialize, Deserialize, Encode, Decode)] struct NewtypeStruct(i32); - #[derive(Serialize, Deserialize, Debug)] + #[derive(Debug, Serialize, Deserialize, Encode, Decode)] struct TupleStruct(i32, f64, String); - #[derive(Serialize, Deserialize, Debug)] + #[derive(Debug, Serialize, Deserialize, Encode, Decode)] struct ComplexStruct { inner: SimpleStruct, flag: bool, @@ -61,10 +62,10 @@ fn main() -> Result<(), Box> { map: HashMap::from_iter([(3, 7)]) }; - println!("simple_instance: {:?}", bincode::serialize(&simple_instance)?); - println!("enum_instance: {:?}", bincode::serialize(&enum_instance)?); - println!("unit_variant: {:?}", bincode::serialize(&unit_variant)?); - println!("complex_instance: {:?}", bincode::serialize(&complex_instance)?); + println!("simple_instance: {:?}", bincode::encode_to_vec(&simple_instance, bincode::config::standard())?); + println!("enum_instance: {:?}", bincode::encode_to_vec(&enum_instance, bincode::config::standard())?); + println!("unit_variant: {:?}", bincode::encode_to_vec(&unit_variant, bincode::config::standard())?); + println!("complex_instance: {:?}", bincode::encode_to_vec(&complex_instance, bincode::config::standard())?); let registry = tracer.registry()?; diff --git a/suite/typescript/ts/bench.ts b/suite/typescript/ts/bench.ts index 2aa97fa9c..46cedef69 100644 --- a/suite/typescript/ts/bench.ts +++ b/suite/typescript/ts/bench.ts @@ -1,7 +1,7 @@ -import { Bench } from 'tinybench' -import * as ProtobufRegistry from './proto/main.ts' -import * as BincodeRegistry from './bincode/registry.ts' -import * as Data from './data.ts' +import { Bench } from "tinybench" +import * as ProtobufRegistry from "./proto/main.ts" +import * as BincodeRegistry from "./bincode/registry.ts" +import * as Data from "./data.ts" const ComplexStruct_pb_obj: ProtobufRegistry.ComplexStruct = { inner: { a: 42, b: "Hello" }, @@ -18,17 +18,17 @@ const ComplexStruct_pb_obj: ProtobufRegistry.ComplexStruct = { } await async function bench_encode() { - const b = new Bench({ time: 1_500 }) + let b = new Bench({ time: 1_500 }) - b.add('serdegen-bincode:encode', () => { + b.add("serdegen-bincode:encode", () => { BincodeRegistry.ComplexStruct.encode(Data.ComplexStruct_obj) }) - b.add('JSON:encode', () => { + b.add("JSON:encode", () => { JSON.stringify(Data.ComplexStruct_obj) }) - b.add('protobuf-js-ts-proto:encode', () => { + b.add("protobuf-js-ts-proto:encode", () => { ProtobufRegistry.ComplexStruct.encode(ComplexStruct_pb_obj) }) @@ -38,23 +38,21 @@ await async function bench_encode() { console.table(b.table()) }() - - await async function bench_decode() { - const b = new Bench({ time: 1_500 }) + let b = new Bench({ time: 1_500 }) - const bincodec_encoded = BincodeRegistry.ComplexStruct.encode(Data.ComplexStruct_obj) - b.add('serdegen-bincode:decode', () => { + let bincodec_encoded = BincodeRegistry.ComplexStruct.encode(Data.ComplexStruct_obj) + b.add("serdegen-bincode:decode", () => { BincodeRegistry.ComplexStruct.decode(bincodec_encoded) }) - const json_encoded = JSON.stringify(Data.ComplexStruct_obj) - b.add('JSON:decode', () => { + let json_encoded = JSON.stringify(Data.ComplexStruct_obj) + b.add("JSON:decode", () => { JSON.parse(json_encoded) }) - const pb_encoded = ProtobufRegistry.ComplexStruct.encode(ComplexStruct_pb_obj).finish() - b.add('protobuf-js-ts-proto:decode', () => { + let pb_encoded = ProtobufRegistry.ComplexStruct.encode(ComplexStruct_pb_obj).finish() + b.add("protobuf-js-ts-proto:decode", () => { ProtobufRegistry.ComplexStruct.decode(pb_encoded) }) diff --git a/suite/typescript/ts/bincode/registry.ts b/suite/typescript/ts/bincode/registry.ts index afc189f1b..a545c617c 100644 --- a/suite/typescript/ts/bincode/registry.ts +++ b/suite/typescript/ts/bincode/registry.ts @@ -1,5 +1,5 @@ -import type * as $t from "./serde" -import { BincodeReader, BincodeWriter } from "./bincode" +import type * as $t from "./serde.ts" +import { BincodeReader, BincodeWriter } from "./bincode.ts" export type ComplexStruct = { inner: SimpleStruct, @@ -34,7 +34,7 @@ export const ComplexStruct = { SimpleStruct.encode(value.inner, writer) writer.write_bool(value.flag) writer.write_length(value.items.length) - for (const item of value.items) { + for (let item of value.items) { MultiEnum.encode(item, writer) } UnitStruct.encode(value.unit, writer) @@ -46,7 +46,7 @@ export const ComplexStruct = { return writer.get_bytes() }, decode(input: Uint8Array, reader = new BincodeReader(input)) { - const value: ComplexStruct = { + let value: ComplexStruct = { inner: SimpleStruct.decode(input, reader), flag: reader.read_bool(), items: reader.read_list(() => MultiEnum.decode(input, reader)), @@ -134,7 +134,7 @@ export const NewtypeStruct = { return writer.get_bytes() }, decode(input: Uint8Array, reader = new BincodeReader(input)) { - const value: NewtypeStruct = reader.read_i32() + let value: NewtypeStruct = reader.read_i32() return value } } @@ -146,7 +146,7 @@ export const SimpleStruct = { return writer.get_bytes() }, decode(input: Uint8Array, reader = new BincodeReader(input)) { - const value: SimpleStruct = { + let value: SimpleStruct = { a: reader.read_u32(), b: reader.read_string(), } @@ -162,7 +162,7 @@ export const TupleStruct = { return writer.get_bytes() }, decode(input: Uint8Array, reader = new BincodeReader(input)) { - const value: TupleStruct = { + let value: TupleStruct = { $0: reader.read_i32(), $1: reader.read_f64(), $2: reader.read_string(), @@ -177,7 +177,7 @@ export const UnitStruct = { return writer.get_bytes() }, decode(input: Uint8Array, reader = new BincodeReader(input)) { - const value: $t.unit = reader.read_unit() + let value: $t.unit = reader.read_unit() return value } } diff --git a/suite/typescript/ts/proto/main.ts b/suite/typescript/ts/proto/main.ts index 4ebea816c..e302b8412 100644 --- a/suite/typescript/ts/proto/main.ts +++ b/suite/typescript/ts/proto/main.ts @@ -1,12 +1,12 @@ // Code generated by protoc-gen-ts_proto. DO NOT EDIT. // versions: // protoc-gen-ts_proto v1.181.2 -// protoc v5.28.0 +// protoc v5.28.3 // source: main.proto /* eslint-disable */ import Long from "long"; -import _m0 from "protobufjs/minimal"; +import _m0 from "protobufjs/minimal.js"; export const protobufPackage = ""; diff --git a/suite/typescript/tsconfig.json b/suite/typescript/tsconfig.json index e47bef4d6..b5d6fb7a3 100644 --- a/suite/typescript/tsconfig.json +++ b/suite/typescript/tsconfig.json @@ -1,10 +1,11 @@ { "compilerOptions": { + "erasableSyntaxOnly": true, "noEmit": true, "module": "ESNext", "moduleResolution": "Bundler", "target": "ESNext", "allowImportingTsExtensions": true, - "types": ["@types/node"] + "types": ["@types/node"], } } \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index f6c8e4829..000000000 --- a/tsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "compilerOptions": { - "noEmit": true, - "target": "ESNext", - "module": "Preserve", - "downlevelIteration": true, - "allowImportingTsExtensions": true, - "lib": ["DOM", "ES2023"] - }, - "exclude": [ - "node_modules" - ] -} \ No newline at end of file From 145392ee6ef1b30dd8b153e1143832237c6dc37f Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Sun, 9 Mar 2025 13:11:19 +0500 Subject: [PATCH 29/30] minor fixes --- Cargo.lock | 59 ++++++++++++++++++++++++++++++++------ serde-generate/Cargo.toml | 1 + suite/typescript/flake.nix | 2 +- 3 files changed, 53 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 83daffe3a..148215565 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -380,6 +380,26 @@ dependencies = [ "serde", ] +[[package]] +name = "bincode" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ad1fa75f77bbd06f187540aa1d70ca50b80b27ce85e7f41c0ce7ff42b34ed3b" +dependencies = [ + "bincode_derive", + "serde", + "unty", +] + +[[package]] +name = "bincode_derive" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1cef5dd4a4457dd11529e743d18ba4fabbd5f20b6895f4c865cb257337dcf9f" +dependencies = [ + "virtue", +] + [[package]] name = "bit-set" version = "0.5.3" @@ -957,6 +977,12 @@ dependencies = [ "serde", ] +[[package]] +name = "indoc" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" + [[package]] name = "itertools" version = "0.10.5" @@ -966,12 +992,6 @@ dependencies = [ "either", ] -[[package]] -name = "indoc" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" - [[package]] name = "itoa" version = "1.0.11" @@ -1698,7 +1718,7 @@ dependencies = [ "alloy-sol-types", "anyhow", "bcs", - "bincode", + "bincode 1.3.3", "heck 0.3.3", "hex", "include_dir", @@ -1744,7 +1764,7 @@ dependencies = [ name = "serde-reflection" version = "0.5.0" dependencies = [ - "bincode", + "bincode 1.3.3", "erased-discriminant", "once_cell", "serde", @@ -2056,6 +2076,17 @@ dependencies = [ "winnow", ] +[[package]] +name = "ts-generator" +version = "0.1.0" +dependencies = [ + "bincode 2.0.0", + "serde", + "serde-generate", + "serde-reflection", + "serde_json", +] + [[package]] name = "typeid" version = "1.0.2" @@ -2116,6 +2147,12 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "unty" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a88342087869553c259588a3ec9ca73ce9b2d538b7051ba5789ff236b6c129" + [[package]] name = "valuable" version = "0.1.0" @@ -2134,6 +2171,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "virtue" +version = "0.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1" + [[package]] name = "wait-timeout" version = "0.2.0" diff --git a/serde-generate/Cargo.toml b/serde-generate/Cargo.toml index 9c2509fbc..910d347ee 100644 --- a/serde-generate/Cargo.toml +++ b/serde-generate/Cargo.toml @@ -25,6 +25,7 @@ serde = { version = "1.0.126", features = ["derive"] } textwrap = "0.13.4" phf = { version = "0.10", features = ["macros"], optional = true } serde-reflection = { path = "../serde-reflection", version = "0.5.0" } +indoc = { version = "2" } [dev-dependencies] alloy-sol-types = "0.8.18" diff --git a/suite/typescript/flake.nix b/suite/typescript/flake.nix index e67e4bc8b..bbf9be67f 100644 --- a/suite/typescript/flake.nix +++ b/suite/typescript/flake.nix @@ -62,7 +62,7 @@ ''; scripts."gen:bincode".exec = '' pushd ${root} - rm ts/bincode/registry.ts + rm -f ts/bincode/registry.ts cargo run popd ''; From 0844a9e1807b6b1f6c0da48298e550b10fe0cb28 Mon Sep 17 00:00:00 2001 From: mrfoxpro Date: Sun, 9 Mar 2025 13:15:48 +0500 Subject: [PATCH 30/30] remove unused rule --- serde-generate/src/typescript.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/serde-generate/src/typescript.rs b/serde-generate/src/typescript.rs index d40834167..e69bd3f56 100644 --- a/serde-generate/src/typescript.rs +++ b/serde-generate/src/typescript.rs @@ -1,4 +1,3 @@ -#![allow(unused)] use crate::{ common, indent::{IndentConfig, IndentedWriter},