Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions packages/protons-runtime/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,13 @@
"dep-check": "aegir dep-check",
"doc-check": "aegir doc-check",
"build": "aegir build",
"test": "aegir test",
"test:chrome": "aegir test -t browser --cov",
"test:chrome-webworker": "aegir test -t webworker",
"test:firefox": "aegir test -t browser -- --browser firefox",
"test:firefox-webworker": "aegir test -t webworker -- --browser firefox",
"test:node": "aegir test -t node --cov",
"test:electron-main": "aegir test -t electron-main",
"release": "aegir release"
},
"dependencies": {
Expand Down
24 changes: 3 additions & 21 deletions packages/protons-runtime/src/utils/longbits.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,26 +122,10 @@ export class LongBits {
return this.fromNumber(Number(value))
}

const negative = value < 0n
// two's complement in one step, so a zero low word carries into the high word
const bits = BigInt.asUintN(64, value)

if (negative) {
value = -value
}

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

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

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

return new LongBits(Number(lo), Number(hi))
return new LongBits(Number(bits & 0xffffffffn), Number(bits >> 32n))
}

/**
Expand Down Expand Up @@ -185,5 +169,3 @@ const zero = new LongBits(0, 0)
zero.toBigInt = function () { return 0n }
zero.zzEncode = zero.zzDecode = function () { return this }
zero.length = function () { return 1 }

const TWO_32 = 4294967296n
51 changes: 51 additions & 0 deletions packages/protons-runtime/test/longbits.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { expect } from 'aegir/chai'
import { reader, writer } from '../src/index.ts'

// negative values below -(2^53 - 1) whose low 32 bits are zero used to lose
// the two's-complement carry into the high word and encode as value - 2^32
describe('negative 64-bit values', () => {
const cases: Record<'int64' | 'sint64' | 'sfixed64', Array<[bigint, number[]]>> = {
int64: [
[-(2n ** 53n), [0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xf0, 0xff, 0x01]],
[-(2n ** 53n + 2n ** 32n), [0x80, 0x80, 0x80, 0x80, 0xf0, 0xff, 0xff, 0xef, 0xff, 0x01]],
[-(2n ** 60n), [0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xf0, 0x01]],
[-(2n ** 63n - 2n ** 32n), [0x80, 0x80, 0x80, 0x80, 0x90, 0x80, 0x80, 0x80, 0x80, 0x01]],
[-(2n ** 63n), [0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x01]],
[-(2n ** 53n + 1n), [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0x01]]
],
sint64: [
[-(2n ** 53n), [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f]],
[-(2n ** 53n + 2n ** 32n), [0xff, 0xff, 0xff, 0xff, 0x9f, 0x80, 0x80, 0x20]],
[-(2n ** 60n), [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f]],
[-(2n ** 63n - 2n ** 32n), [0xff, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xff, 0x01]],
[-(2n ** 63n), [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01]],
[-(2n ** 53n + 1n), [0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x20]]
],
sfixed64: [
[-(2n ** 53n), [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff]],
[-(2n ** 53n + 2n ** 32n), [0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xdf, 0xff]],
[-(2n ** 60n), [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0]],
[-(2n ** 63n - 2n ** 32n), [0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x80]],
[-(2n ** 63n), [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80]],
[-(2n ** 53n + 1n), [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdf, 0xff]]
]
}

for (const method of ['int64', 'sint64', 'sfixed64'] as const) {
for (const [value, bytes] of cases[method]) {
it(`should encode ${method} ${value}`, () => {
const w = writer()
w[method](value)

expect(w.finish()).to.equalBytes(bytes)
})

it(`should round trip ${method} ${value}`, () => {
const w = writer()
w[method](value)

expect(reader(w.finish())[method]()).to.equal(value)
})
}
}
})
Loading