Skip to content

Commit 7887ed1

Browse files
authored
feat(i128/u128): implement i128/u128 types again (credit goes to @Jabolol) (#53)
1 parent 68d2479 commit 7887ed1

File tree

4 files changed

+196
-0
lines changed

4 files changed

+196
-0
lines changed
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import { type Options, SizedType } from "../types/mod.ts";
2+
import { isLittleEndian } from "../util.ts";
3+
4+
export class U128 extends SizedType<bigint> {
5+
constructor(
6+
readonly littleEndian: boolean = isLittleEndian,
7+
/**
8+
* Clang, GCC and rust 1.78 (with LLVM 18) say 16 byte alignment.
9+
*
10+
* Older rust versions (or rust with older LLVM versions) say 8 byte alignment.
11+
*
12+
* Due to compatiblity reason we allow 8 byte alignment with these older versions.
13+
*
14+
* But by default it uses the now correct 16 byte alignment.
15+
*
16+
* See [Rust's blogpost](https://blog.rust-lang.org/2024/03/30/i128-layout-update.html) for more details
17+
*/
18+
alignment: 8 | 16 = 16,
19+
) {
20+
super(16, alignment);
21+
}
22+
23+
override readPacked(
24+
dt: DataView,
25+
options: Options = { byteOffset: 0 },
26+
): bigint {
27+
const partOne = dt.getBigUint64(options.byteOffset, this.littleEndian);
28+
const partTwo = dt.getBigUint64(options.byteOffset + 8, this.littleEndian);
29+
super.incrementOffset(options);
30+
// deno-fmt-ignore
31+
return this.littleEndian
32+
? (partTwo << 64n) | partOne
33+
: (partOne << 64n) | partTwo;
34+
}
35+
36+
override writePacked(
37+
value: bigint,
38+
dt: DataView,
39+
options: Options = { byteOffset: 0 },
40+
): void {
41+
const hi = value >> 64n;
42+
dt.setBigUint64(
43+
options.byteOffset,
44+
this.littleEndian ? hi : value,
45+
this.littleEndian,
46+
);
47+
dt.setBigUint64(
48+
options.byteOffset + 8,
49+
this.littleEndian ? value : hi,
50+
this.littleEndian,
51+
);
52+
53+
super.incrementOffset(options);
54+
}
55+
}
56+
57+
export class I128 extends SizedType<bigint> {
58+
constructor(
59+
readonly littleEndian: boolean = isLittleEndian,
60+
/**
61+
* Clang, GCC and rust 1.78 (with LLVM 18) say 16 byte alignment.
62+
*
63+
* Older rust versions (or rust with older LLVM versions) say 8 byte alignment.
64+
*
65+
* Due to compatiblity reason we allow 8 byte alignment with these older versions.
66+
*
67+
* But by default it uses the now correct 16 byte alignment.
68+
*
69+
* See [Rust's blogpost](https://blog.rust-lang.org/2024/03/30/i128-layout-update.html) for more details
70+
*/
71+
alignment: 8 | 16 = 16,
72+
) {
73+
super(16, alignment);
74+
}
75+
76+
override readPacked(
77+
dt: DataView,
78+
options: Options = { byteOffset: 0 },
79+
): bigint {
80+
const partOne = dt.getBigInt64(options.byteOffset, this.littleEndian);
81+
const partTwo = dt.getBigInt64(options.byteOffset + 8, this.littleEndian);
82+
super.incrementOffset(options);
83+
// deno-fmt-ignore
84+
return this.littleEndian
85+
? (partTwo << 64n) | partOne
86+
: (partOne << 64n) | partTwo;
87+
}
88+
89+
override writePacked(
90+
value: bigint,
91+
dt: DataView,
92+
options: Options = { byteOffset: 0 },
93+
): void {
94+
const hi = value >> 64n;
95+
dt.setBigInt64(
96+
options.byteOffset,
97+
this.littleEndian ? hi : value,
98+
this.littleEndian,
99+
);
100+
dt.setBigInt64(
101+
options.byteOffset + 8,
102+
this.littleEndian ? value : hi,
103+
this.littleEndian,
104+
);
105+
106+
super.incrementOffset(options);
107+
}
108+
}
109+
110+
export const u128le: U128 = new U128(true);
111+
export const u128be: U128 = new U128(false);
112+
export const u128: U128 = new U128();
113+
114+
export const i128le: I128 = new I128(true);
115+
export const i128be: I128 = new I128(false);
116+
export const i128: I128 = new I128();
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { assertEquals, assertThrows } from "../../test_deps.ts";
2+
import { i128be, i128le, u128be, u128le } from "./big_numbers.ts";
3+
4+
Deno.test("u128", async (t) => {
5+
const buff = new ArrayBuffer(16);
6+
const dt = new DataView(buff);
7+
const value = 12n;
8+
9+
await t.step("read", () => {
10+
// Little endian
11+
const lo = value & 0xffffffffffffffffn;
12+
const hi = value >> 64n;
13+
dt.setBigUint64(0, lo, true);
14+
dt.setBigUint64(8, hi, true);
15+
assertEquals(value, u128le.read(dt));
16+
// Big endian
17+
dt.setBigUint64(0, hi, false);
18+
dt.setBigUint64(8, lo, false);
19+
assertEquals(value, u128be.read(dt));
20+
});
21+
22+
await t.step("write", () => {
23+
// Little endian
24+
u128le.write(value, dt);
25+
let lo = dt.getBigInt64(0, true);
26+
let hi = dt.getBigInt64(8, true);
27+
assertEquals(value, (lo << 64n) | hi);
28+
// Big endian
29+
u128be.write(value, dt);
30+
lo = dt.getBigInt64(8, false);
31+
hi = dt.getBigInt64(0, false);
32+
assertEquals(value, (lo << 64n) | hi);
33+
});
34+
35+
await t.step("OOB Read", () => {
36+
assertThrows(() => {
37+
u128le.read(dt, { byteOffset: 9 });
38+
}, RangeError);
39+
});
40+
});
41+
42+
Deno.test("i128", async (t) => {
43+
const buff = new ArrayBuffer(16);
44+
const dt = new DataView(buff);
45+
const value = 12n;
46+
47+
await t.step("read", () => {
48+
// Little endian
49+
const lo = value & 0xffffffffffffffffn;
50+
const hi = value >> 64n;
51+
dt.setBigUint64(0, lo, true);
52+
dt.setBigUint64(8, hi, true);
53+
assertEquals(value, i128le.read(dt));
54+
// Big endian
55+
dt.setBigUint64(0, hi, false);
56+
dt.setBigUint64(8, lo, false);
57+
assertEquals(value, i128be.read(dt));
58+
});
59+
60+
await t.step("write", () => {
61+
// Little endian
62+
i128le.write(value, dt);
63+
let lo = dt.getBigInt64(0, true);
64+
let hi = dt.getBigInt64(8, true);
65+
assertEquals(value, (lo << 64n) | hi);
66+
// Big endian
67+
i128be.write(value, dt);
68+
lo = dt.getBigInt64(8, false);
69+
hi = dt.getBigInt64(0, false);
70+
assertEquals(value, (lo << 64n) | hi);
71+
});
72+
73+
await t.step("OOB Read", () => {
74+
assertThrows(() => {
75+
i128le.read(dt, { byteOffset: 9 });
76+
}, RangeError);
77+
});
78+
});

src/non-standard-numbers/mod.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from "./small_numbers.ts";
2+
export * from "./big_numbers.ts";
File renamed without changes.

0 commit comments

Comments
 (0)