Skip to content

Commit a27913a

Browse files
authored
Merge pull request #19 from msgpack/int64
refine int64 handling
2 parents de7487c + 7f2d982 commit a27913a

File tree

5 files changed

+52
-74
lines changed

5 files changed

+52
-74
lines changed

src/Decoder.ts

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { prettyByte } from "./utils/prettyByte";
22
import { ExtensionCodec } from "./ExtensionCodec";
3-
import { decodeInt64 } from "./utils/int";
3+
import { getInt64, getUint64 } from "./utils/int";
44
import { utf8Decode } from "./utils/utf8";
55
import { createDataView, ensureUint8Array } from "./utils/typedArrays";
66

@@ -427,23 +427,15 @@ export class Decoder {
427427
}
428428

429429
readU64(): number {
430-
const high = this.view.getUint32(this.pos);
431-
const low = this.view.getUint32(this.pos + 4);
430+
const value = getUint64(this.view, this.pos);
432431
this.pos += 8;
433-
return high * 0x100000000 + low;
432+
return value;
434433
}
435434

436435
readI64(): number {
437-
const b1 = this.view.getUint8(this.pos);
438-
const b2 = this.view.getUint8(this.pos + 1);
439-
const b3 = this.view.getUint8(this.pos + 2);
440-
const b4 = this.view.getUint8(this.pos + 3);
441-
const b5 = this.view.getUint8(this.pos + 4);
442-
const b6 = this.view.getUint8(this.pos + 5);
443-
const b7 = this.view.getUint8(this.pos + 6);
444-
const b8 = this.view.getUint8(this.pos + 7);
436+
const value = getInt64(this.view, this.pos);
445437
this.pos += 8;
446-
return decodeInt64(b1, b2, b3, b4, b5, b6, b7, b8);
438+
return value;
447439
}
448440

449441
readF32() {

src/Encoder.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { utf8Encode, utf8Count } from "./utils/utf8";
22
import { ExtensionCodec } from "./ExtensionCodec";
3-
import { encodeInt64, encodeUint64 } from "./utils/int";
3+
import { setInt64, setUint64 } from "./utils/int";
44
import { ensureUint8Array } from "./utils/typedArrays";
55
import { ExtData } from "./ExtData";
66

@@ -323,14 +323,14 @@ export class Encoder {
323323
writeU64(value: number) {
324324
this.ensureBufferSizeToWrite(8);
325325

326-
encodeUint64(value, this.view, this.pos);
326+
setUint64(this.view, this.pos, value);
327327
this.pos += 8;
328328
}
329329

330330
writeI64(value: number) {
331331
this.ensureBufferSizeToWrite(8);
332332

333-
encodeInt64(value, this.view, this.pos);
333+
setInt64(this.view, this.pos, value);
334334
this.pos += 8;
335335
}
336336
}

src/ExtensionCodec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { decodeInt64, encodeInt64 } from "./utils/int";
1+
import { getInt64, setInt64 } from "./utils/int";
22
import { ExtData } from "./ExtData";
33

44
export const EXT_TIMESTAMP = -1;
@@ -41,7 +41,7 @@ export function encodeTimestampFromTimeSpec({ sec, nsec }: TimeSpec): Uint8Array
4141
const rv = new Uint8Array(12);
4242
const view = new DataView(rv.buffer);
4343
view.setUint32(0, nsec);
44-
encodeInt64(sec, view, 4);
44+
setInt64(view, 4, sec);
4545
return rv;
4646
}
4747
}
@@ -93,7 +93,7 @@ export const decodeTimestampExtension: ExtensionDecoderType = (data: Uint8Array)
9393
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
9494

9595
const nsec = view.getUint32(0);
96-
const sec = decodeInt64(data[4], data[5], data[6], data[7], data[8], data[9], data[10], data[11]);
96+
const sec = getInt64(view, 4);
9797

9898
return new Date(sec * 1000 + nsec / 1e6);
9999
}

src/utils/int.ts

Lines changed: 19 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,28 @@
1-
// the actual range is int53 (a.k.a. safe integer)
2-
export function encodeUint64(value: number, view: DataView, offset: number): void {
3-
const high = value / 0x100000000;
4-
const low = value & 0xffffffff;
1+
// DataView extension to handle int64 / uint64,
2+
// where the actual range is 53-bits integer (a.k.a. safe integer)
53

4+
export function setUint64(view: DataView, offset: number, value: number): void {
5+
const high = value / 0x1_0000_0000;
6+
const low = value; // high bits are truncated by DataView
67
view.setUint32(offset, high);
78
view.setUint32(offset + 4, low);
89
}
910

10-
// the actual range is int53 (a.k.a. safe integer)
11-
export function encodeInt64(value: number, view: DataView, offset: number): void {
12-
if (value < 0) {
13-
const absMinusOne = -value - 1;
14-
const high = absMinusOne / 0x1_0000_0000;
15-
const low = absMinusOne & 0xffff_ffff;
11+
export function setInt64(view: DataView, offset: number, value: number): void {
12+
const high = Math.floor(value / 0x1_0000_0000);
13+
const low = value; // high bits are truncated by DataView
14+
view.setUint32(offset, high);
15+
view.setUint32(offset + 4, low);
16+
}
1617

17-
view.setUint32(offset, (high ^ 0xffff_ffff) | 0x8000_0000);
18-
view.setUint32(offset + 4, low ^ 0xffff_ffff);
19-
} else {
20-
encodeUint64(value, view, offset);
21-
}
18+
export function getInt64(view: DataView, offset: number) {
19+
const high = view.getInt32(offset);
20+
const low = view.getUint32(offset + 4);
21+
return high * 0x1_0000_0000 + low;
2222
}
2323

24-
// the actual range is int53 (a.k.a. safe integer)
25-
export function decodeInt64(
26-
b1: number,
27-
b2: number,
28-
b3: number,
29-
b4: number,
30-
b5: number,
31-
b6: number,
32-
b7: number,
33-
b8: number,
34-
): number {
35-
if (b1 & 0x80) {
36-
// to avoid overflow
37-
return -(
38-
(b1 ^ 0xff) * 0x100000000000000 +
39-
(b2 ^ 0xff) * 0x1000000000000 +
40-
(b3 ^ 0xff) * 0x10000000000 +
41-
(b4 ^ 0xff) * 0x100000000 +
42-
(b5 ^ 0xff) * 0x1000000 +
43-
(b6 ^ 0xff) * 0x10000 +
44-
(b7 ^ 0xff) * 0x100 +
45-
(b8 ^ 0xff) +
46-
1
47-
);
48-
}
49-
return (
50-
b1 * 0x100000000000000 +
51-
b2 * 0x1000000000000 +
52-
b3 * 0x10000000000 +
53-
b4 * 0x100000000 +
54-
b5 * 0x1000000 +
55-
b6 * 0x10000 +
56-
b7 * 0x100 +
57-
b8
58-
);
24+
export function getUint64(view: DataView, offset: number) {
25+
const high = view.getUint32(offset);
26+
const low = view.getUint32(offset + 4);
27+
return high * 0x1_0000_0000 + low;
5928
}

test/codec-int.test.ts

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import assert from "assert";
2-
import { encodeInt64, decodeInt64 } from "../src/utils/int";
2+
import { setInt64, getInt64, getUint64, setUint64 } from "../src/utils/int";
33

44
const INT64SPECS = {
55
ZERO: 0,
@@ -13,16 +13,33 @@ const INT64SPECS = {
1313
MIN_SAFE_INTEGER: Number.MIN_SAFE_INTEGER,
1414
} as Record<string, number>;
1515

16-
describe("codec: encode and decode int 32/64", () => {
16+
describe("codec: int64 / uint64", () => {
1717
context("int 64", () => {
1818
for (const name of Object.keys(INT64SPECS)) {
1919
const value = INT64SPECS[name];
2020

21-
it(`${value} (${value < 0 ? "-" : ""}0x${Math.abs(value).toString(16)})`, () => {
21+
it(`sets and gets ${value} (${value < 0 ? "-" : ""}0x${Math.abs(value).toString(16)})`, () => {
2222
const b = new Uint8Array(8);
23-
encodeInt64(value, new DataView(b.buffer), 0);
24-
assert.deepStrictEqual(decodeInt64(b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]), value);
23+
const view = new DataView(b.buffer);
24+
setInt64(view, 0, value);
25+
assert.deepStrictEqual(getInt64(view, 0), value);
2526
});
2627
}
2728
});
29+
30+
context("uint 64", () => {
31+
it(`sets and gets 0`, () => {
32+
const b = new Uint8Array(8);
33+
const view = new DataView(b.buffer);
34+
setUint64(view, 0, 0);
35+
assert.deepStrictEqual(getUint64(view, 0), 0);
36+
});
37+
38+
it(`sets and gets MAX_SAFE_INTEGER`, () => {
39+
const b = new Uint8Array(8);
40+
const view = new DataView(b.buffer);
41+
setUint64(view, 0, Number.MAX_SAFE_INTEGER);
42+
assert.deepStrictEqual(getUint64(view, 0), Number.MAX_SAFE_INTEGER);
43+
});
44+
});
2845
});

0 commit comments

Comments
 (0)