Skip to content

Commit

Permalink
feat: support jstype custom options (#117)
Browse files Browse the repository at this point in the history
Allow overriding 64 bit number types with strings or regular js numbers.

Closes #112
  • Loading branch information
achingbrain authored Oct 23, 2023
1 parent d5bf315 commit ba35475
Show file tree
Hide file tree
Showing 23 changed files with 853 additions and 44 deletions.
12 changes: 6 additions & 6 deletions packages/protons-benchmark/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,19 @@
"sourceType": "module"
},
"ignorePatterns": [
"src/pbjs/*",
"src/protobuf-ts/*",
"src/protobufjs/*"
"src/implementations/pbjs/*",
"src/implementations/protobuf-ts/*",
"src/implementations/protobufjs/*"
]
},
"scripts": {
"clean": "aegir clean",
"lint": "aegir lint",
"dep-check": "aegir dep-check --ignore @protobuf-ts/plugin pbjs protons",
"build": "aegir build --no-bundle && cp -R src/protobufjs dist/src/protobufjs",
"build": "aegir build --no-bundle && cp -R src/implementations/protobufjs dist/src/implementations/protobufjs",
"prestart": "npm run build",
"start": "node dist/src/index.js",
"start:browser": "npx playwright-test dist/src/index.js --runner benchmark"
"start": "node dist/src/implementations/index.js",
"start:browser": "npx playwright-test dist/src/implementations/index.js --runner benchmark"
},
"dependencies": {
"@protobuf-ts/plugin": "^2.8.1",
Expand Down
File renamed without changes.
File renamed without changes.
15 changes: 15 additions & 0 deletions packages/protons-benchmark/src/numbers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/* eslint-disable no-console */

/*
$ node dist/src/numbers/index.js
$ npx playwright-test dist/src/numbers/index.js --runner benchmark
*/

import { readBenchmark } from './read.js'
import { writeBenchmark } from './write.js'

console.info('-- read --')
await readBenchmark()

console.info('-- write --')
await writeBenchmark()
42 changes: 42 additions & 0 deletions packages/protons-benchmark/src/numbers/read.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/* eslint-disable no-console */

import Benchmark from 'benchmark'
import { writer, reader } from 'protons-runtime'

const bigint = 100n

const w = writer()
w.uint64(bigint)

const buf = w.finish()

export async function readBenchmark (): Promise<void> {
return new Promise<void>((resolve) => {
new Benchmark.Suite()
.add('uint64 (BigInt)', () => {
const r = reader(buf)
r.uint64()
})
.add('uint64number', () => {
const r = reader(buf)
r.uint64Number()
})
.add('uint64string', () => {
const r = reader(buf)
r.uint64String()
})
.on('error', (err: Error) => {
console.error(err)
})
.on('cycle', (event: any) => {
console.info(String(event.target))
})
.on('complete', function () {
// @ts-expect-error types are wrong
console.info(`Fastest is ${this.filter('fastest').map('name')}`)
resolve()
})
// run async
.run({ async: true })
})
}
39 changes: 39 additions & 0 deletions packages/protons-benchmark/src/numbers/write.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/* eslint-disable no-console */

import Benchmark from 'benchmark'
import { writer } from 'protons-runtime'

const number = 100
const bigint = 100n
const string = '100'

export async function writeBenchmark (): Promise<void> {
return new Promise<void>((resolve) => {
new Benchmark.Suite()
.add('uint64 (BigInt)', () => {
const w = writer()
w.uint64(bigint)
})
.add('uint64number', () => {
const w = writer()
w.uint64Number(number)
})
.add('uint64string', () => {
const w = writer()
w.uint64String(string)
})
.on('error', (err: Error) => {
console.error(err)
})
.on('cycle', (event: any) => {
console.info(String(event.target))
})
.on('complete', function () {
// @ts-expect-error types are wrong
console.info(`Fastest is ${this.filter('fastest').map('name')}`)
resolve()
})
// run async
.run({ async: true })
})
}
4 changes: 2 additions & 2 deletions packages/protons-benchmark/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
"test"
],
"exclude": [
"src/protobufjs/bench.js",
"src/protobufjs/rpc.js"
"src/implementations/protobufjs/bench.js",
"src/implementations/protobufjs/rpc.js"
],
"references": [
{
Expand Down
100 changes: 100 additions & 0 deletions packages/protons-runtime/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,46 @@ export interface Writer {
*/
uint64(value: bigint): this

/**
* Writes an unsigned 64 bit value as a varint
*/
uint64Number(value: number): this

/**
* Writes an unsigned 64 bit value as a varint
*/
uint64String(value: string): this

/**
* Writes a signed 64 bit value as a varint
*/
int64(value: bigint): this

/**
* Writes a signed 64 bit value as a varint
*/
int64Number(value: number): this

/**
* Writes a signed 64 bit value as a varint
*/
int64String(value: string): this

/**
* Writes a signed 64 bit value as a varint, zig-zag encoded
*/
sint64(value: bigint): this

/**
* Writes a signed 64 bit value as a varint, zig-zag encoded
*/
sint64Number(value: number): this

/**
* Writes a signed 64 bit value as a varint, zig-zag encoded
*/
sint64String(value: string): this

/**
* Writes a boolish value as a varint
*/
Expand All @@ -78,11 +108,31 @@ export interface Writer {
*/
fixed64(value: bigint): this

/**
* Writes an unsigned 64 bit value as fixed 64 bits
*/
fixed64Number(value: number): this

/**
* Writes an unsigned 64 bit value as fixed 64 bits
*/
fixed64String(value: string): this

/**
* Writes a signed 64 bit value as fixed 64 bits
*/
sfixed64(value: bigint): this

/**
* Writes a signed 64 bit value as fixed 64 bits
*/
sfixed64Number(value: number): this

/**
* Writes a signed 64 bit value as fixed 64 bits
*/
sfixed64String(value: string): this

/**
* Writes a float (32 bit)
*/
Expand Down Expand Up @@ -206,23 +256,73 @@ export interface Reader {
*/
int64(): bigint

/**
* Reads a varint as a signed 64 bit value
*/
int64Number(): number

/**
* Reads a varint as a signed 64 bit value
*/
int64String(): string

/**
* Reads a varint as an unsigned 64 bit value
*/
uint64(): bigint

/**
* Reads a varint as an unsigned 64 bit value
*/
uint64Number(): number

/**
* Reads a varint as an unsigned 64 bit value
*/
uint64String(): string

/**
* Reads a zig-zag encoded varint as a signed 64 bit value
*/
sint64(): bigint

/**
* Reads a zig-zag encoded varint as a signed 64 bit value
*/
sint64Number(): number

/**
* Reads a zig-zag encoded varint as a signed 64 bit value
*/
sint64String(): string

/**
* Reads fixed 64 bits
*/
fixed64(): bigint

/**
* Reads fixed 64 bits
*/
fixed64Number(): number

/**
* Reads fixed 64 bits
*/
fixed64String(): string

/**
* Reads zig-zag encoded fixed 64 bits
*/
sfixed64(): bigint

/**
* Reads zig-zag encoded fixed 64 bits
*/
sfixed64Number(): number

/**
* Reads zig-zag encoded fixed 64 bits
*/
sfixed64String(): string
}
56 changes: 45 additions & 11 deletions packages/protons-runtime/src/utils/longbits.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// the largest BigInt we can safely downcast to a Number
const MAX_SAFE_NUMBER_INTEGER = BigInt(Number.MAX_SAFE_INTEGER)
const MIN_SAFE_NUMBER_INTEGER = BigInt(Number.MIN_SAFE_INTEGER)

/**
* Constructs new long bits.
*
Expand Down Expand Up @@ -29,10 +33,24 @@ export class LongBits {
/**
* Converts this long bits to a possibly unsafe JavaScript number
*/
toNumber (unsigned: boolean = false): number {
if (!unsigned && (this.hi >>> 31) > 0) {
const lo = ~this.lo + 1 >>> 0
let hi = ~this.hi >>> 0
if (lo === 0) {
hi = hi + 1 >>> 0
}
return -(lo + hi * 4294967296)
}
return this.lo + this.hi * 4294967296
}

/**
* Converts this long bits to a bigint
*/
toBigInt (unsigned: boolean = false): bigint {
if (unsigned) {
const result = BigInt(this.lo >>> 0) + (BigInt(this.hi >>> 0) << 32n)
return result
return BigInt(this.lo >>> 0) + (BigInt(this.hi >>> 0) << 32n)
}

if ((this.hi >>> 31) !== 0) {
Expand All @@ -47,6 +65,13 @@ export class LongBits {
return BigInt(this.lo >>> 0) + (BigInt(this.hi >>> 0) << 32n)
}

/**
* Converts this long bits to a string
*/
toString (unsigned: boolean = false): string {
return this.toBigInt(unsigned).toString()
}

/**
* Zig-zag encodes this long bits
*/
Expand Down Expand Up @@ -89,25 +114,34 @@ export class LongBits {
* Constructs new long bits from the specified number
*/
static fromBigInt (value: bigint): LongBits {
if (value === 0n) { return zero }
if (value === 0n) {
return zero
}

if (value < MAX_SAFE_NUMBER_INTEGER && value > MIN_SAFE_NUMBER_INTEGER) {
return this.fromNumber(Number(value))
}

const negative = value < 0n

const negative = value < 0
if (negative) {
value = -value
}
let hi = Number(value >> 32n)
let lo = Number(value - (BigInt(hi) << 32n))

let hi = value >> 32n
let lo = value - (hi << 32n)

if (negative) {
hi = ~hi >>> 0
lo = ~lo >>> 0
hi = ~hi | 0n
lo = ~lo | 0n

if (++lo > TWO_32) {
lo = 0
if (++hi > TWO_32) { hi = 0 }
lo = 0n
if (++hi > TWO_32) { hi = 0n }
}
}

return new LongBits(lo, hi)
return new LongBits(Number(lo), Number(hi))
}

/**
Expand Down
Loading

0 comments on commit ba35475

Please sign in to comment.