Skip to content

Commit ba35475

Browse files
authored
feat: support jstype custom options (#117)
Allow overriding 64 bit number types with strings or regular js numbers. Closes #112
1 parent d5bf315 commit ba35475

File tree

23 files changed

+853
-44
lines changed

23 files changed

+853
-44
lines changed

packages/protons-benchmark/package.json

+6-6
Original file line numberDiff line numberDiff line change
@@ -32,19 +32,19 @@
3232
"sourceType": "module"
3333
},
3434
"ignorePatterns": [
35-
"src/pbjs/*",
36-
"src/protobuf-ts/*",
37-
"src/protobufjs/*"
35+
"src/implementations/pbjs/*",
36+
"src/implementations/protobuf-ts/*",
37+
"src/implementations/protobufjs/*"
3838
]
3939
},
4040
"scripts": {
4141
"clean": "aegir clean",
4242
"lint": "aegir lint",
4343
"dep-check": "aegir dep-check --ignore @protobuf-ts/plugin pbjs protons",
44-
"build": "aegir build --no-bundle && cp -R src/protobufjs dist/src/protobufjs",
44+
"build": "aegir build --no-bundle && cp -R src/implementations/protobufjs dist/src/implementations/protobufjs",
4545
"prestart": "npm run build",
46-
"start": "node dist/src/index.js",
47-
"start:browser": "npx playwright-test dist/src/index.js --runner benchmark"
46+
"start": "node dist/src/implementations/index.js",
47+
"start:browser": "npx playwright-test dist/src/implementations/index.js --runner benchmark"
4848
},
4949
"dependencies": {
5050
"@protobuf-ts/plugin": "^2.8.1",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/* eslint-disable no-console */
2+
3+
/*
4+
$ node dist/src/numbers/index.js
5+
$ npx playwright-test dist/src/numbers/index.js --runner benchmark
6+
*/
7+
8+
import { readBenchmark } from './read.js'
9+
import { writeBenchmark } from './write.js'
10+
11+
console.info('-- read --')
12+
await readBenchmark()
13+
14+
console.info('-- write --')
15+
await writeBenchmark()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/* eslint-disable no-console */
2+
3+
import Benchmark from 'benchmark'
4+
import { writer, reader } from 'protons-runtime'
5+
6+
const bigint = 100n
7+
8+
const w = writer()
9+
w.uint64(bigint)
10+
11+
const buf = w.finish()
12+
13+
export async function readBenchmark (): Promise<void> {
14+
return new Promise<void>((resolve) => {
15+
new Benchmark.Suite()
16+
.add('uint64 (BigInt)', () => {
17+
const r = reader(buf)
18+
r.uint64()
19+
})
20+
.add('uint64number', () => {
21+
const r = reader(buf)
22+
r.uint64Number()
23+
})
24+
.add('uint64string', () => {
25+
const r = reader(buf)
26+
r.uint64String()
27+
})
28+
.on('error', (err: Error) => {
29+
console.error(err)
30+
})
31+
.on('cycle', (event: any) => {
32+
console.info(String(event.target))
33+
})
34+
.on('complete', function () {
35+
// @ts-expect-error types are wrong
36+
console.info(`Fastest is ${this.filter('fastest').map('name')}`)
37+
resolve()
38+
})
39+
// run async
40+
.run({ async: true })
41+
})
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/* eslint-disable no-console */
2+
3+
import Benchmark from 'benchmark'
4+
import { writer } from 'protons-runtime'
5+
6+
const number = 100
7+
const bigint = 100n
8+
const string = '100'
9+
10+
export async function writeBenchmark (): Promise<void> {
11+
return new Promise<void>((resolve) => {
12+
new Benchmark.Suite()
13+
.add('uint64 (BigInt)', () => {
14+
const w = writer()
15+
w.uint64(bigint)
16+
})
17+
.add('uint64number', () => {
18+
const w = writer()
19+
w.uint64Number(number)
20+
})
21+
.add('uint64string', () => {
22+
const w = writer()
23+
w.uint64String(string)
24+
})
25+
.on('error', (err: Error) => {
26+
console.error(err)
27+
})
28+
.on('cycle', (event: any) => {
29+
console.info(String(event.target))
30+
})
31+
.on('complete', function () {
32+
// @ts-expect-error types are wrong
33+
console.info(`Fastest is ${this.filter('fastest').map('name')}`)
34+
resolve()
35+
})
36+
// run async
37+
.run({ async: true })
38+
})
39+
}

packages/protons-benchmark/tsconfig.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
"test"
1010
],
1111
"exclude": [
12-
"src/protobufjs/bench.js",
13-
"src/protobufjs/rpc.js"
12+
"src/implementations/protobufjs/bench.js",
13+
"src/implementations/protobufjs/rpc.js"
1414
],
1515
"references": [
1616
{

packages/protons-runtime/src/index.ts

+100
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,46 @@ export interface Writer {
4848
*/
4949
uint64(value: bigint): this
5050

51+
/**
52+
* Writes an unsigned 64 bit value as a varint
53+
*/
54+
uint64Number(value: number): this
55+
56+
/**
57+
* Writes an unsigned 64 bit value as a varint
58+
*/
59+
uint64String(value: string): this
60+
5161
/**
5262
* Writes a signed 64 bit value as a varint
5363
*/
5464
int64(value: bigint): this
5565

66+
/**
67+
* Writes a signed 64 bit value as a varint
68+
*/
69+
int64Number(value: number): this
70+
71+
/**
72+
* Writes a signed 64 bit value as a varint
73+
*/
74+
int64String(value: string): this
75+
5676
/**
5777
* Writes a signed 64 bit value as a varint, zig-zag encoded
5878
*/
5979
sint64(value: bigint): this
6080

81+
/**
82+
* Writes a signed 64 bit value as a varint, zig-zag encoded
83+
*/
84+
sint64Number(value: number): this
85+
86+
/**
87+
* Writes a signed 64 bit value as a varint, zig-zag encoded
88+
*/
89+
sint64String(value: string): this
90+
6191
/**
6292
* Writes a boolish value as a varint
6393
*/
@@ -78,11 +108,31 @@ export interface Writer {
78108
*/
79109
fixed64(value: bigint): this
80110

111+
/**
112+
* Writes an unsigned 64 bit value as fixed 64 bits
113+
*/
114+
fixed64Number(value: number): this
115+
116+
/**
117+
* Writes an unsigned 64 bit value as fixed 64 bits
118+
*/
119+
fixed64String(value: string): this
120+
81121
/**
82122
* Writes a signed 64 bit value as fixed 64 bits
83123
*/
84124
sfixed64(value: bigint): this
85125

126+
/**
127+
* Writes a signed 64 bit value as fixed 64 bits
128+
*/
129+
sfixed64Number(value: number): this
130+
131+
/**
132+
* Writes a signed 64 bit value as fixed 64 bits
133+
*/
134+
sfixed64String(value: string): this
135+
86136
/**
87137
* Writes a float (32 bit)
88138
*/
@@ -206,23 +256,73 @@ export interface Reader {
206256
*/
207257
int64(): bigint
208258

259+
/**
260+
* Reads a varint as a signed 64 bit value
261+
*/
262+
int64Number(): number
263+
264+
/**
265+
* Reads a varint as a signed 64 bit value
266+
*/
267+
int64String(): string
268+
209269
/**
210270
* Reads a varint as an unsigned 64 bit value
211271
*/
212272
uint64(): bigint
213273

274+
/**
275+
* Reads a varint as an unsigned 64 bit value
276+
*/
277+
uint64Number(): number
278+
279+
/**
280+
* Reads a varint as an unsigned 64 bit value
281+
*/
282+
uint64String(): string
283+
214284
/**
215285
* Reads a zig-zag encoded varint as a signed 64 bit value
216286
*/
217287
sint64(): bigint
218288

289+
/**
290+
* Reads a zig-zag encoded varint as a signed 64 bit value
291+
*/
292+
sint64Number(): number
293+
294+
/**
295+
* Reads a zig-zag encoded varint as a signed 64 bit value
296+
*/
297+
sint64String(): string
298+
219299
/**
220300
* Reads fixed 64 bits
221301
*/
222302
fixed64(): bigint
223303

304+
/**
305+
* Reads fixed 64 bits
306+
*/
307+
fixed64Number(): number
308+
309+
/**
310+
* Reads fixed 64 bits
311+
*/
312+
fixed64String(): string
313+
224314
/**
225315
* Reads zig-zag encoded fixed 64 bits
226316
*/
227317
sfixed64(): bigint
318+
319+
/**
320+
* Reads zig-zag encoded fixed 64 bits
321+
*/
322+
sfixed64Number(): number
323+
324+
/**
325+
* Reads zig-zag encoded fixed 64 bits
326+
*/
327+
sfixed64String(): string
228328
}

packages/protons-runtime/src/utils/longbits.ts

+45-11
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
// the largest BigInt we can safely downcast to a Number
2+
const MAX_SAFE_NUMBER_INTEGER = BigInt(Number.MAX_SAFE_INTEGER)
3+
const MIN_SAFE_NUMBER_INTEGER = BigInt(Number.MIN_SAFE_INTEGER)
4+
15
/**
26
* Constructs new long bits.
37
*
@@ -29,10 +33,24 @@ export class LongBits {
2933
/**
3034
* Converts this long bits to a possibly unsafe JavaScript number
3135
*/
36+
toNumber (unsigned: boolean = false): number {
37+
if (!unsigned && (this.hi >>> 31) > 0) {
38+
const lo = ~this.lo + 1 >>> 0
39+
let hi = ~this.hi >>> 0
40+
if (lo === 0) {
41+
hi = hi + 1 >>> 0
42+
}
43+
return -(lo + hi * 4294967296)
44+
}
45+
return this.lo + this.hi * 4294967296
46+
}
47+
48+
/**
49+
* Converts this long bits to a bigint
50+
*/
3251
toBigInt (unsigned: boolean = false): bigint {
3352
if (unsigned) {
34-
const result = BigInt(this.lo >>> 0) + (BigInt(this.hi >>> 0) << 32n)
35-
return result
53+
return BigInt(this.lo >>> 0) + (BigInt(this.hi >>> 0) << 32n)
3654
}
3755

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

68+
/**
69+
* Converts this long bits to a string
70+
*/
71+
toString (unsigned: boolean = false): string {
72+
return this.toBigInt(unsigned).toString()
73+
}
74+
5075
/**
5176
* Zig-zag encodes this long bits
5277
*/
@@ -89,25 +114,34 @@ export class LongBits {
89114
* Constructs new long bits from the specified number
90115
*/
91116
static fromBigInt (value: bigint): LongBits {
92-
if (value === 0n) { return zero }
117+
if (value === 0n) {
118+
return zero
119+
}
120+
121+
if (value < MAX_SAFE_NUMBER_INTEGER && value > MIN_SAFE_NUMBER_INTEGER) {
122+
return this.fromNumber(Number(value))
123+
}
124+
125+
const negative = value < 0n
93126

94-
const negative = value < 0
95127
if (negative) {
96128
value = -value
97129
}
98-
let hi = Number(value >> 32n)
99-
let lo = Number(value - (BigInt(hi) << 32n))
130+
131+
let hi = value >> 32n
132+
let lo = value - (hi << 32n)
100133

101134
if (negative) {
102-
hi = ~hi >>> 0
103-
lo = ~lo >>> 0
135+
hi = ~hi | 0n
136+
lo = ~lo | 0n
137+
104138
if (++lo > TWO_32) {
105-
lo = 0
106-
if (++hi > TWO_32) { hi = 0 }
139+
lo = 0n
140+
if (++hi > TWO_32) { hi = 0n }
107141
}
108142
}
109143

110-
return new LongBits(lo, hi)
144+
return new LongBits(Number(lo), Number(hi))
111145
}
112146

113147
/**

0 commit comments

Comments
 (0)