Skip to content

Commit

Permalink
Fix bit array encoding of negative Int values wider than 48 bits on J…
Browse files Browse the repository at this point in the history
…avaScript
  • Loading branch information
richard-viney committed Nov 4, 2024
1 parent 771f876 commit 0a02efe
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 8 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,9 @@
annotation of a parameter of an anonymous function would do nothing.
([Surya Rose](https://github.com/GearsDatapacks))
- Fixed a bug where an incorrect bit array would be generated on JavaScript for
negative `Int` values when the segment's `size` was wider than 48 bits.
## v1.5.1 - 2024-09-26
### Bug Fixes
Expand Down
25 changes: 17 additions & 8 deletions compiler-core/templates/prelude.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -200,22 +200,31 @@ export function sizedInt(value, size, isBigEndian) {

const byteArray = new Uint8Array(size / 8);

// Convert negative number to two's complement representation
let byteModulus = 256;

// Convert negative number to two's complement representation. For integer
// sizes larger than 48 bits it is necessary to use the slower BigInt type
// to maintain accuracy.
if (value < 0) {
value = 2 ** size + value;
if (size <= 48) {
value = 2 ** size + value;
} else {
value = 2n ** BigInt(size) + BigInt(value);
byteModulus = BigInt(byteModulus);
}
}

if (isBigEndian) {
for (let i = byteArray.length - 1; i >= 0; i--) {
const byte = value % 256;
byteArray[i] = byte;
value = (value - byte) / 256;
const byte = value % byteModulus;
byteArray[i] = Number(byte);
value = (value - byte) / byteModulus;
}
} else {
for (let i = 0; i < byteArray.length; i++) {
const byte = value % 256;
byteArray[i] = byte;
value = (value - byte) / 256;
const byte = value % byteModulus;
byteArray[i] = Number(byte);
value = (value - byte) / byteModulus;
}
}

Expand Down
30 changes: 30 additions & 0 deletions test/javascript_prelude/main.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
stringBits,
toBitArray,
toList,
sizedInt,
} from "./prelude.mjs";

let failures = 0;
Expand Down Expand Up @@ -394,6 +395,8 @@ assertNotEqual(new HasCustomEquals(1, 1), new HasCustomEquals(2, 1));
assertEqual(hasEqualsField, { ...hasEqualsField });
assertNotEqual(hasEqualsField, hasEqualsField2);

// BitArray

assertEqual(new BitArray(new Uint8Array([1, 2, 3])).byteAt(0), 1);
assertEqual(new BitArray(new Uint8Array([1, 2, 3])).byteAt(2), 3);
assertEqual(new BitArray(new Uint8Array([1, 2, 3])).intFromSlice(0, 1, true, false), 1);
Expand Down Expand Up @@ -424,6 +427,33 @@ assertEqual(
new BitArray(new Uint8Array([2, 3])),
);

// sizedInt()

assertEqual(
sizedInt(0, 32, true),
new Uint8Array([0, 0, 0, 0]),
);
assertEqual(
sizedInt(1, 24, true),
new Uint8Array([0, 0, 1]),
);
assertEqual(
sizedInt(-1, 32, true),
new Uint8Array([255, 255, 255, 255]),
);
assertEqual(
sizedInt(-1, 64, true),
new Uint8Array([255, 255, 255, 255, 255, 255, 255, 255]),
);
assertEqual(
sizedInt(Number.MAX_SAFE_INTEGER, 64, true),
new Uint8Array([0, 31, 255, 255, 255, 255, 255, 255]),
);
assertEqual(
sizedInt(Number.MIN_SAFE_INTEGER, 64, true),
new Uint8Array([255, 224, 0, 0, 0, 0, 0, 1]),
);

// Result.isOk

assertEqual(new Ok(1).isOk(), true);
Expand Down

0 comments on commit 0a02efe

Please sign in to comment.