-
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
implement i128/u128 types (credit goes to @Jabolol)
- Loading branch information
1 parent
68d2479
commit 1ebb18a
Showing
4 changed files
with
196 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
import { type Options, SizedType } from "../types/mod.ts"; | ||
import { isLittleEndian } from "../util.ts"; | ||
|
||
export class U128 extends SizedType<bigint> { | ||
constructor( | ||
readonly littleEndian: boolean = isLittleEndian, | ||
/** | ||
* Clang, GCC and rust 1.78 (with LLVM 18) say 16 byte alignment. | ||
* | ||
* Older rust versions (or rust with older LLVM versions) say 8 byte alignment. | ||
* | ||
* Due to compatiblity reason we allow 8 byte alignment with these older versions. | ||
* | ||
* But by default it uses the now correct 16 byte alignment. | ||
* | ||
* See [Rust's blogpost](https://blog.rust-lang.org/2024/03/30/i128-layout-update.html) for more details | ||
*/ | ||
alignment: 8 | 16 = 16, | ||
) { | ||
super(16, alignment); | ||
} | ||
|
||
override readPacked( | ||
dt: DataView, | ||
options: Options = { byteOffset: 0 }, | ||
): bigint { | ||
const partOne = dt.getBigUint64(options.byteOffset, this.littleEndian); | ||
const partTwo = dt.getBigUint64(options.byteOffset + 8, this.littleEndian); | ||
super.incrementOffset(options); | ||
// deno-fmt-ignore | ||
return this.littleEndian | ||
? (partTwo << 64n) | partOne | ||
: (partOne << 64n) | partTwo; | ||
} | ||
|
||
override writePacked( | ||
value: bigint, | ||
dt: DataView, | ||
options: Options = { byteOffset: 0 }, | ||
): void { | ||
const hi = value >> 64n; | ||
dt.setBigUint64( | ||
options.byteOffset, | ||
this.littleEndian ? hi : value, | ||
this.littleEndian, | ||
); | ||
dt.setBigUint64( | ||
options.byteOffset + 8, | ||
this.littleEndian ? value : hi, | ||
this.littleEndian, | ||
); | ||
|
||
super.incrementOffset(options); | ||
} | ||
} | ||
|
||
export class I128 extends SizedType<bigint> { | ||
constructor( | ||
readonly littleEndian: boolean = isLittleEndian, | ||
/** | ||
* Clang, GCC and rust 1.78 (with LLVM 18) say 16 byte alignment. | ||
* | ||
* Older rust versions (or rust with older LLVM versions) say 8 byte alignment. | ||
* | ||
* Due to compatiblity reason we allow 8 byte alignment with these older versions. | ||
* | ||
* But by default it uses the now correct 16 byte alignment. | ||
* | ||
* See [Rust's blogpost](https://blog.rust-lang.org/2024/03/30/i128-layout-update.html) for more details | ||
*/ | ||
alignment: 8 | 16 = 16, | ||
) { | ||
super(16, alignment); | ||
} | ||
|
||
override readPacked( | ||
dt: DataView, | ||
options: Options = { byteOffset: 0 }, | ||
): bigint { | ||
const partOne = dt.getBigInt64(options.byteOffset, this.littleEndian); | ||
const partTwo = dt.getBigInt64(options.byteOffset + 8, this.littleEndian); | ||
super.incrementOffset(options); | ||
// deno-fmt-ignore | ||
return this.littleEndian | ||
? (partTwo << 64n) | partOne | ||
: (partOne << 64n) | partTwo; | ||
} | ||
|
||
override writePacked( | ||
value: bigint, | ||
dt: DataView, | ||
options: Options = { byteOffset: 0 }, | ||
): void { | ||
const hi = value >> 64n; | ||
dt.setBigInt64( | ||
options.byteOffset, | ||
this.littleEndian ? hi : value, | ||
this.littleEndian, | ||
); | ||
dt.setBigInt64( | ||
options.byteOffset + 8, | ||
this.littleEndian ? value : hi, | ||
this.littleEndian, | ||
); | ||
|
||
super.incrementOffset(options); | ||
} | ||
} | ||
|
||
export const u128le: U128 = new U128(true); | ||
export const u128be: U128 = new U128(false); | ||
export const u128: U128 = new U128(); | ||
|
||
export const i128le: I128 = new I128(true); | ||
export const i128be: I128 = new I128(false); | ||
export const i128: I128 = new I128(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import { assertEquals, assertThrows } from "../../test_deps.ts"; | ||
import { i128be, i128le, u128be, u128le } from "./big_numbers.ts"; | ||
|
||
Deno.test("u128", async (t) => { | ||
const buff = new ArrayBuffer(16); | ||
const dt = new DataView(buff); | ||
const value = 12n; | ||
|
||
await t.step("read", () => { | ||
// Little endian | ||
const lo = value & 0xffffffffffffffffn; | ||
const hi = value >> 64n; | ||
dt.setBigUint64(0, lo, true); | ||
dt.setBigUint64(8, hi, true); | ||
assertEquals(value, u128le.read(dt)); | ||
// Big endian | ||
dt.setBigUint64(0, hi, false); | ||
dt.setBigUint64(8, lo, false); | ||
assertEquals(value, u128be.read(dt)); | ||
}); | ||
|
||
await t.step("write", () => { | ||
// Little endian | ||
u128le.write(value, dt); | ||
let lo = dt.getBigInt64(0, true); | ||
let hi = dt.getBigInt64(8, true); | ||
assertEquals(value, (lo << 64n) | hi); | ||
// Big endian | ||
u128be.write(value, dt); | ||
lo = dt.getBigInt64(8, false); | ||
hi = dt.getBigInt64(0, false); | ||
assertEquals(value, (lo << 64n) | hi); | ||
}); | ||
|
||
await t.step("OOB Read", () => { | ||
assertThrows(() => { | ||
u128le.read(dt, { byteOffset: 9 }); | ||
}, RangeError); | ||
}); | ||
}); | ||
|
||
Deno.test("i128", async (t) => { | ||
const buff = new ArrayBuffer(16); | ||
const dt = new DataView(buff); | ||
const value = 12n; | ||
|
||
await t.step("read", () => { | ||
// Little endian | ||
const lo = value & 0xffffffffffffffffn; | ||
const hi = value >> 64n; | ||
dt.setBigUint64(0, lo, true); | ||
dt.setBigUint64(8, hi, true); | ||
assertEquals(value, i128le.read(dt)); | ||
// Big endian | ||
dt.setBigUint64(0, hi, false); | ||
dt.setBigUint64(8, lo, false); | ||
assertEquals(value, i128be.read(dt)); | ||
}); | ||
|
||
await t.step("write", () => { | ||
// Little endian | ||
i128le.write(value, dt); | ||
let lo = dt.getBigInt64(0, true); | ||
let hi = dt.getBigInt64(8, true); | ||
assertEquals(value, (lo << 64n) | hi); | ||
// Big endian | ||
i128be.write(value, dt); | ||
lo = dt.getBigInt64(8, false); | ||
hi = dt.getBigInt64(0, false); | ||
assertEquals(value, (lo << 64n) | hi); | ||
}); | ||
|
||
await t.step("OOB Read", () => { | ||
assertThrows(() => { | ||
i128le.read(dt, { byteOffset: 9 }); | ||
}, RangeError); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from "./small_numbers.ts"; | ||
export * from "./big_numbers.ts"; |
File renamed without changes.