mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-28 11:49:44 +00:00
LibJS+LibCrypto: Use a bitwise approach for BigInt's as*IntN methods
This speeds up expressions such as `BigInt.asIntN(0x4000000000000, 1n)` (#3615). And those involving very large bigints.
This commit is contained in:
parent
92d0cd3c7c
commit
12cbefbee7
Notes:
github-actions[bot]
2025-03-20 08:45:14 +00:00
Author: https://github.com/ttrssreal
Commit: 12cbefbee7
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3994
Reviewed-by: https://github.com/gmta ✅
9 changed files with 110 additions and 33 deletions
|
@ -131,7 +131,7 @@ FLATTEN void UnsignedBigIntegerAlgorithms::bitwise_xor_without_allocation(
|
||||||
/**
|
/**
|
||||||
* Complexity: O(N) where N is the number of words
|
* Complexity: O(N) where N is the number of words
|
||||||
*/
|
*/
|
||||||
FLATTEN void UnsignedBigIntegerAlgorithms::bitwise_not_fill_to_one_based_index_without_allocation(
|
FLATTEN ErrorOr<void> UnsignedBigIntegerAlgorithms::bitwise_not_fill_to_one_based_index_without_allocation(
|
||||||
UnsignedBigInteger const& right,
|
UnsignedBigInteger const& right,
|
||||||
size_t index,
|
size_t index,
|
||||||
UnsignedBigInteger& output)
|
UnsignedBigInteger& output)
|
||||||
|
@ -139,16 +139,16 @@ FLATTEN void UnsignedBigIntegerAlgorithms::bitwise_not_fill_to_one_based_index_w
|
||||||
// If the value is invalid, the output value is invalid as well.
|
// If the value is invalid, the output value is invalid as well.
|
||||||
if (right.is_invalid()) {
|
if (right.is_invalid()) {
|
||||||
output.invalidate();
|
output.invalidate();
|
||||||
return;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index == 0) {
|
if (index == 0) {
|
||||||
output.set_to_0();
|
output.set_to_0();
|
||||||
return;
|
return {};
|
||||||
}
|
}
|
||||||
size_t size = (index + UnsignedBigInteger::BITS_IN_WORD - 1) / UnsignedBigInteger::BITS_IN_WORD;
|
size_t size = (index + UnsignedBigInteger::BITS_IN_WORD - 1) / UnsignedBigInteger::BITS_IN_WORD;
|
||||||
|
|
||||||
output.m_words.resize_and_keep_capacity(size);
|
TRY(output.m_words.try_resize_and_keep_capacity(size));
|
||||||
VERIFY(size > 0);
|
VERIFY(size > 0);
|
||||||
for (size_t i = 0; i < size - 1; ++i)
|
for (size_t i = 0; i < size - 1; ++i)
|
||||||
output.m_words[i] = ~(i < right.length() ? right.words()[i] : 0);
|
output.m_words[i] = ~(i < right.length() ? right.words()[i] : 0);
|
||||||
|
@ -158,6 +158,8 @@ FLATTEN void UnsignedBigIntegerAlgorithms::bitwise_not_fill_to_one_based_index_w
|
||||||
auto last_word = last_word_index < right.length() ? right.words()[last_word_index] : 0;
|
auto last_word = last_word_index < right.length() ? right.words()[last_word_index] : 0;
|
||||||
|
|
||||||
output.m_words[last_word_index] = (NumericLimits<UnsignedBigInteger::Word>::max() >> (UnsignedBigInteger::BITS_IN_WORD - index)) & ~last_word;
|
output.m_words[last_word_index] = (NumericLimits<UnsignedBigInteger::Word>::max() >> (UnsignedBigInteger::BITS_IN_WORD - index)) & ~last_word;
|
||||||
|
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
FLATTEN void UnsignedBigIntegerAlgorithms::shift_left_without_allocation(
|
FLATTEN void UnsignedBigIntegerAlgorithms::shift_left_without_allocation(
|
||||||
|
|
|
@ -20,13 +20,13 @@ public:
|
||||||
static void bitwise_or_without_allocation(UnsignedBigInteger const& left, UnsignedBigInteger const& right, UnsignedBigInteger& output);
|
static void bitwise_or_without_allocation(UnsignedBigInteger const& left, UnsignedBigInteger const& right, UnsignedBigInteger& output);
|
||||||
static void bitwise_and_without_allocation(UnsignedBigInteger const& left, UnsignedBigInteger const& right, UnsignedBigInteger& output);
|
static void bitwise_and_without_allocation(UnsignedBigInteger const& left, UnsignedBigInteger const& right, UnsignedBigInteger& output);
|
||||||
static void bitwise_xor_without_allocation(UnsignedBigInteger const& left, UnsignedBigInteger const& right, UnsignedBigInteger& output);
|
static void bitwise_xor_without_allocation(UnsignedBigInteger const& left, UnsignedBigInteger const& right, UnsignedBigInteger& output);
|
||||||
static void bitwise_not_fill_to_one_based_index_without_allocation(UnsignedBigInteger const& left, size_t, UnsignedBigInteger& output);
|
|
||||||
static void shift_left_without_allocation(UnsignedBigInteger const& number, size_t bits_to_shift_by, UnsignedBigInteger& temp_result, UnsignedBigInteger& temp_plus, UnsignedBigInteger& output);
|
static void shift_left_without_allocation(UnsignedBigInteger const& number, size_t bits_to_shift_by, UnsignedBigInteger& temp_result, UnsignedBigInteger& temp_plus, UnsignedBigInteger& output);
|
||||||
static void shift_right_without_allocation(UnsignedBigInteger const& number, size_t num_bits, UnsignedBigInteger& output);
|
static void shift_right_without_allocation(UnsignedBigInteger const& number, size_t num_bits, UnsignedBigInteger& output);
|
||||||
static void multiply_without_allocation(UnsignedBigInteger const& left, UnsignedBigInteger const& right, UnsignedBigInteger& temp_shift_result, UnsignedBigInteger& temp_shift_plus, UnsignedBigInteger& temp_shift, UnsignedBigInteger& output);
|
static void multiply_without_allocation(UnsignedBigInteger const& left, UnsignedBigInteger const& right, UnsignedBigInteger& temp_shift_result, UnsignedBigInteger& temp_shift_plus, UnsignedBigInteger& temp_shift, UnsignedBigInteger& output);
|
||||||
static void divide_without_allocation(UnsignedBigInteger const& numerator, UnsignedBigInteger const& denominator, UnsignedBigInteger& quotient, UnsignedBigInteger& remainder);
|
static void divide_without_allocation(UnsignedBigInteger const& numerator, UnsignedBigInteger const& denominator, UnsignedBigInteger& quotient, UnsignedBigInteger& remainder);
|
||||||
static void divide_u16_without_allocation(UnsignedBigInteger const& numerator, UnsignedBigInteger::Word denominator, UnsignedBigInteger& quotient, UnsignedBigInteger& remainder);
|
static void divide_u16_without_allocation(UnsignedBigInteger const& numerator, UnsignedBigInteger::Word denominator, UnsignedBigInteger& quotient, UnsignedBigInteger& remainder);
|
||||||
|
|
||||||
|
static ErrorOr<void> bitwise_not_fill_to_one_based_index_without_allocation(UnsignedBigInteger const& left, size_t, UnsignedBigInteger& output);
|
||||||
static ErrorOr<void> try_shift_left_without_allocation(UnsignedBigInteger const& number, size_t bits_to_shift_by, UnsignedBigInteger& temp_result, UnsignedBigInteger& temp_plus, UnsignedBigInteger& output);
|
static ErrorOr<void> try_shift_left_without_allocation(UnsignedBigInteger const& number, size_t bits_to_shift_by, UnsignedBigInteger& temp_result, UnsignedBigInteger& temp_plus, UnsignedBigInteger& output);
|
||||||
|
|
||||||
static void extended_GCD_without_allocation(UnsignedBigInteger const& a, UnsignedBigInteger const& b, UnsignedBigInteger& x, UnsignedBigInteger& y, UnsignedBigInteger& gcd, UnsignedBigInteger& temp_quotient, UnsignedBigInteger& temp_1, UnsignedBigInteger& temp_2, UnsignedBigInteger& temp_shift_result, UnsignedBigInteger& temp_shift_plus, UnsignedBigInteger& temp_shift, UnsignedBigInteger& temp_r, UnsignedBigInteger& temp_s, UnsignedBigInteger& temp_t);
|
static void extended_GCD_without_allocation(UnsignedBigInteger const& a, UnsignedBigInteger const& b, UnsignedBigInteger& x, UnsignedBigInteger& y, UnsignedBigInteger& gcd, UnsignedBigInteger& temp_quotient, UnsignedBigInteger& temp_1, UnsignedBigInteger& temp_2, UnsignedBigInteger& temp_shift_result, UnsignedBigInteger& temp_shift_plus, UnsignedBigInteger& temp_shift, UnsignedBigInteger& temp_r, UnsignedBigInteger& temp_s, UnsignedBigInteger& temp_t);
|
||||||
|
|
|
@ -291,6 +291,17 @@ FLATTEN SignedBigInteger SignedBigInteger::shift_right(size_t num_bits) const
|
||||||
return SignedBigInteger { m_unsigned_data.shift_right(num_bits), m_sign };
|
return SignedBigInteger { m_unsigned_data.shift_right(num_bits), m_sign };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FLATTEN ErrorOr<SignedBigInteger> SignedBigInteger::mod_power_of_two(size_t power_of_two) const
|
||||||
|
{
|
||||||
|
auto const lower_bits = m_unsigned_data.as_n_bits(power_of_two);
|
||||||
|
|
||||||
|
if (is_positive())
|
||||||
|
return SignedBigInteger(lower_bits);
|
||||||
|
|
||||||
|
// twos encode lower bits
|
||||||
|
return SignedBigInteger(TRY(lower_bits.try_bitwise_not_fill_to_one_based_index(power_of_two)).plus(1).as_n_bits(power_of_two));
|
||||||
|
}
|
||||||
|
|
||||||
FLATTEN SignedBigInteger SignedBigInteger::multiplied_by(SignedBigInteger const& other) const
|
FLATTEN SignedBigInteger SignedBigInteger::multiplied_by(SignedBigInteger const& other) const
|
||||||
{
|
{
|
||||||
bool result_sign = m_sign ^ other.m_sign;
|
bool result_sign = m_sign ^ other.m_sign;
|
||||||
|
|
|
@ -120,6 +120,7 @@ public:
|
||||||
[[nodiscard]] SignedBigInteger multiplied_by(SignedBigInteger const& other) const;
|
[[nodiscard]] SignedBigInteger multiplied_by(SignedBigInteger const& other) const;
|
||||||
[[nodiscard]] SignedDivisionResult divided_by(SignedBigInteger const& divisor) const;
|
[[nodiscard]] SignedDivisionResult divided_by(SignedBigInteger const& divisor) const;
|
||||||
|
|
||||||
|
[[nodiscard]] ErrorOr<SignedBigInteger> mod_power_of_two(size_t power_of_two) const;
|
||||||
[[nodiscard]] ErrorOr<SignedBigInteger> try_shift_left(size_t num_bits) const;
|
[[nodiscard]] ErrorOr<SignedBigInteger> try_shift_left(size_t num_bits) const;
|
||||||
|
|
||||||
[[nodiscard]] SignedBigInteger plus(UnsignedBigInteger const& other) const;
|
[[nodiscard]] SignedBigInteger plus(UnsignedBigInteger const& other) const;
|
||||||
|
|
|
@ -473,10 +473,15 @@ FLATTEN UnsignedBigInteger UnsignedBigInteger::bitwise_xor(UnsignedBigInteger co
|
||||||
}
|
}
|
||||||
|
|
||||||
FLATTEN UnsignedBigInteger UnsignedBigInteger::bitwise_not_fill_to_one_based_index(size_t size) const
|
FLATTEN UnsignedBigInteger UnsignedBigInteger::bitwise_not_fill_to_one_based_index(size_t size) const
|
||||||
|
{
|
||||||
|
return MUST(try_bitwise_not_fill_to_one_based_index(size));
|
||||||
|
}
|
||||||
|
|
||||||
|
FLATTEN ErrorOr<UnsignedBigInteger> UnsignedBigInteger::try_bitwise_not_fill_to_one_based_index(size_t size) const
|
||||||
{
|
{
|
||||||
UnsignedBigInteger result;
|
UnsignedBigInteger result;
|
||||||
|
|
||||||
UnsignedBigIntegerAlgorithms::bitwise_not_fill_to_one_based_index_without_allocation(*this, size, result);
|
TRY(UnsignedBigIntegerAlgorithms::bitwise_not_fill_to_one_based_index_without_allocation(*this, size, result));
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -506,6 +511,33 @@ FLATTEN UnsignedBigInteger UnsignedBigInteger::shift_right(size_t num_bits) cons
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FLATTEN UnsignedBigInteger UnsignedBigInteger::as_n_bits(size_t n) const
|
||||||
|
{
|
||||||
|
if (auto const num_bits = one_based_index_of_highest_set_bit(); n >= num_bits)
|
||||||
|
return *this;
|
||||||
|
|
||||||
|
UnsignedBigInteger output;
|
||||||
|
output.set_to(*this);
|
||||||
|
|
||||||
|
auto const word_index = n / BITS_IN_WORD;
|
||||||
|
|
||||||
|
auto const bits_to_keep = n % BITS_IN_WORD;
|
||||||
|
auto const bits_to_discard = BITS_IN_WORD - bits_to_keep;
|
||||||
|
|
||||||
|
output.m_words.resize(word_index + 1);
|
||||||
|
|
||||||
|
auto const last_word = output.m_words[word_index];
|
||||||
|
Word new_last_word = 0;
|
||||||
|
|
||||||
|
// avoid UB from a 32 bit shift on a u32
|
||||||
|
if (bits_to_keep != 0)
|
||||||
|
new_last_word = last_word << bits_to_discard >> bits_to_discard;
|
||||||
|
|
||||||
|
output.m_words[word_index] = new_last_word;
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
FLATTEN UnsignedBigInteger UnsignedBigInteger::multiplied_by(UnsignedBigInteger const& other) const
|
FLATTEN UnsignedBigInteger UnsignedBigInteger::multiplied_by(UnsignedBigInteger const& other) const
|
||||||
{
|
{
|
||||||
UnsignedBigInteger result;
|
UnsignedBigInteger result;
|
||||||
|
|
|
@ -117,9 +117,11 @@ public:
|
||||||
[[nodiscard]] UnsignedBigInteger bitwise_not_fill_to_one_based_index(size_t) const;
|
[[nodiscard]] UnsignedBigInteger bitwise_not_fill_to_one_based_index(size_t) const;
|
||||||
[[nodiscard]] UnsignedBigInteger shift_left(size_t num_bits) const;
|
[[nodiscard]] UnsignedBigInteger shift_left(size_t num_bits) const;
|
||||||
[[nodiscard]] UnsignedBigInteger shift_right(size_t num_bits) const;
|
[[nodiscard]] UnsignedBigInteger shift_right(size_t num_bits) const;
|
||||||
|
[[nodiscard]] UnsignedBigInteger as_n_bits(size_t n) const;
|
||||||
[[nodiscard]] UnsignedBigInteger multiplied_by(UnsignedBigInteger const& other) const;
|
[[nodiscard]] UnsignedBigInteger multiplied_by(UnsignedBigInteger const& other) const;
|
||||||
[[nodiscard]] UnsignedDivisionResult divided_by(UnsignedBigInteger const& divisor) const;
|
[[nodiscard]] UnsignedDivisionResult divided_by(UnsignedBigInteger const& divisor) const;
|
||||||
|
|
||||||
|
[[nodiscard]] ErrorOr<UnsignedBigInteger> try_bitwise_not_fill_to_one_based_index(size_t) const;
|
||||||
[[nodiscard]] ErrorOr<UnsignedBigInteger> try_shift_left(size_t num_bits) const;
|
[[nodiscard]] ErrorOr<UnsignedBigInteger> try_shift_left(size_t num_bits) const;
|
||||||
|
|
||||||
[[nodiscard]] u32 hash() const;
|
[[nodiscard]] u32 hash() const;
|
||||||
|
|
|
@ -18,6 +18,7 @@ namespace JS {
|
||||||
GC_DEFINE_ALLOCATOR(BigIntConstructor);
|
GC_DEFINE_ALLOCATOR(BigIntConstructor);
|
||||||
|
|
||||||
static Crypto::SignedBigInteger const BIGINT_ONE { 1 };
|
static Crypto::SignedBigInteger const BIGINT_ONE { 1 };
|
||||||
|
static Crypto::SignedBigInteger const BIGINT_ZERO { 0 };
|
||||||
|
|
||||||
BigIntConstructor::BigIntConstructor(Realm& realm)
|
BigIntConstructor::BigIntConstructor(Realm& realm)
|
||||||
: NativeFunction(realm.vm().names.BigInt.as_string(), realm.intrinsics().function_prototype())
|
: NativeFunction(realm.vm().names.BigInt.as_string(), realm.intrinsics().function_prototype())
|
||||||
|
@ -72,20 +73,25 @@ JS_DEFINE_NATIVE_FUNCTION(BigIntConstructor::as_int_n)
|
||||||
// 2. Set bigint to ? ToBigInt(bigint).
|
// 2. Set bigint to ? ToBigInt(bigint).
|
||||||
auto bigint = TRY(vm.argument(1).to_bigint(vm));
|
auto bigint = TRY(vm.argument(1).to_bigint(vm));
|
||||||
|
|
||||||
|
// OPTIMIZATION: mod = bigint (mod 2^0) = 0 < 2^(0-1) = 0.5
|
||||||
|
if (bits == 0)
|
||||||
|
return BigInt::create(vm, BIGINT_ZERO);
|
||||||
|
|
||||||
// 3. Let mod be ℝ(bigint) modulo 2^bits.
|
// 3. Let mod be ℝ(bigint) modulo 2^bits.
|
||||||
// FIXME: For large values of `bits`, this can likely be improved with a SignedBigInteger API to
|
auto const mod = TRY_OR_THROW_OOM(vm, bigint->big_integer().mod_power_of_two(bits));
|
||||||
// drop the most significant bits.
|
|
||||||
auto bits_shift_left = TRY_OR_THROW_OOM(vm, BIGINT_ONE.try_shift_left(bits));
|
|
||||||
auto mod = modulo(bigint->big_integer(), bits_shift_left);
|
|
||||||
|
|
||||||
// 4. If mod ≥ 2^(bits-1), return ℤ(mod - 2^bits); otherwise, return ℤ(mod).
|
// OPTIMIZATION: mod < 2^(bits-1)
|
||||||
// NOTE: Some of the below conditionals are non-standard, but are to protect SignedBigInteger from
|
if (mod.is_zero())
|
||||||
// allocating an absurd amount of memory if `bits - 1` overflows to NumericLimits<size_t>::max.
|
return BigInt::create(vm, BIGINT_ZERO);
|
||||||
if ((bits == 0) && (mod >= BIGINT_ONE))
|
|
||||||
return BigInt::create(vm, mod.minus(bits_shift_left));
|
|
||||||
if ((bits > 0) && (mod >= BIGINT_ONE.shift_left(bits - 1)))
|
|
||||||
return BigInt::create(vm, mod.minus(bits_shift_left));
|
|
||||||
|
|
||||||
|
// 4. If mod ≥ 2^(bits-1), return ℤ(mod - 2^bits); ...
|
||||||
|
if (auto top_bit_index = mod.unsigned_value().one_based_index_of_highest_set_bit(); top_bit_index >= bits) {
|
||||||
|
// twos complement decode
|
||||||
|
auto decoded = TRY_OR_THROW_OOM(vm, mod.unsigned_value().try_bitwise_not_fill_to_one_based_index(bits)).plus(1);
|
||||||
|
return BigInt::create(vm, Crypto::SignedBigInteger { std::move(decoded), true });
|
||||||
|
}
|
||||||
|
|
||||||
|
// ... otherwise, return ℤ(mod).
|
||||||
return BigInt::create(vm, mod);
|
return BigInt::create(vm, mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,10 +104,10 @@ JS_DEFINE_NATIVE_FUNCTION(BigIntConstructor::as_uint_n)
|
||||||
// 2. Set bigint to ? ToBigInt(bigint).
|
// 2. Set bigint to ? ToBigInt(bigint).
|
||||||
auto bigint = TRY(vm.argument(1).to_bigint(vm));
|
auto bigint = TRY(vm.argument(1).to_bigint(vm));
|
||||||
|
|
||||||
// 3. Return the BigInt value that represents ℝ(bigint) modulo 2bits.
|
// 3. Return the BigInt value that represents ℝ(bigint) modulo 2^bits.
|
||||||
// FIXME: For large values of `bits`, this can likely be improved with a SignedBigInteger API to
|
auto const mod = TRY_OR_THROW_OOM(vm, bigint->big_integer().mod_power_of_two(bits));
|
||||||
// drop the most significant bits.
|
|
||||||
return BigInt::create(vm, modulo(bigint->big_integer(), TRY_OR_THROW_OOM(vm, BIGINT_ONE.try_shift_left(bits))));
|
return BigInt::create(vm, mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,12 +22,6 @@ describe("errors", () => {
|
||||||
BigInt.asIntN(1, "foo");
|
BigInt.asIntN(1, "foo");
|
||||||
}).toThrowWithMessage(SyntaxError, "Invalid value for BigInt: foo");
|
}).toThrowWithMessage(SyntaxError, "Invalid value for BigInt: foo");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("large allocation", () => {
|
|
||||||
expect(() => {
|
|
||||||
BigInt.asIntN(0x4000000000000, 1n);
|
|
||||||
}).toThrowWithMessage(InternalError, "Out of memory");
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("correct behavior", () => {
|
describe("correct behavior", () => {
|
||||||
|
@ -82,4 +76,23 @@ describe("correct behavior", () => {
|
||||||
expect(BigInt.asIntN(128, -extremelyBigInt)).toBe(99061374399389259395070030194384019691n);
|
expect(BigInt.asIntN(128, -extremelyBigInt)).toBe(99061374399389259395070030194384019691n);
|
||||||
expect(BigInt.asIntN(256, -extremelyBigInt)).toBe(-extremelyBigInt);
|
expect(BigInt.asIntN(256, -extremelyBigInt)).toBe(-extremelyBigInt);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("large bit values", () => {
|
||||||
|
expect(BigInt.asIntN(0x4000000000000, 1n)).toBe(1n);
|
||||||
|
expect(BigInt.asIntN(0x8ffffffffffff, 1n)).toBe(1n);
|
||||||
|
expect(BigInt.asIntN(2 ** 53 - 1, 2n)).toBe(2n);
|
||||||
|
|
||||||
|
// These incur large intermediate values that 00M. For now, ensure they don't crash
|
||||||
|
expect(() => {
|
||||||
|
BigInt.asIntN(0x4000000000000, -1n);
|
||||||
|
}).toThrowWithMessage(InternalError, "Out of memory");
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
BigInt.asIntN(0x8ffffffffffff, -1n);
|
||||||
|
}).toThrowWithMessage(InternalError, "Out of memory");
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
BigInt.asIntN(2 ** 53 - 1, -2n);
|
||||||
|
}).toThrowWithMessage(InternalError, "Out of memory");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -22,12 +22,6 @@ describe("errors", () => {
|
||||||
BigInt.asUintN(1, "foo");
|
BigInt.asUintN(1, "foo");
|
||||||
}).toThrowWithMessage(SyntaxError, "Invalid value for BigInt: foo");
|
}).toThrowWithMessage(SyntaxError, "Invalid value for BigInt: foo");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("large allocation", () => {
|
|
||||||
expect(() => {
|
|
||||||
BigInt.asUintN(0x4000000000000, 1n);
|
|
||||||
}).toThrowWithMessage(InternalError, "Out of memory");
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("correct behavior", () => {
|
describe("correct behavior", () => {
|
||||||
|
@ -80,4 +74,20 @@ describe("correct behavior", () => {
|
||||||
115792089237316195423570861551898784396480861208851440582668460551124006183147n
|
115792089237316195423570861551898784396480861208851440582668460551124006183147n
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("large bit values", () => {
|
||||||
|
const INDEX_MAX = 2 ** 53 - 1;
|
||||||
|
const LAST_8_DIGITS = 10n ** 8n;
|
||||||
|
|
||||||
|
expect(BigInt.asUintN(0x400000000, 1n)).toBe(1n);
|
||||||
|
expect(BigInt.asUintN(0x4000, -1n) % LAST_8_DIGITS).toBe(64066815n);
|
||||||
|
|
||||||
|
expect(BigInt.asUintN(0x400000000, 2n)).toBe(2n);
|
||||||
|
expect(BigInt.asUintN(0x4000, -2n) % LAST_8_DIGITS).toBe(64066814n);
|
||||||
|
|
||||||
|
expect(BigInt.asUintN(INDEX_MAX, 2n)).toBe(2n);
|
||||||
|
expect(() => {
|
||||||
|
BigInt.asUintN(INDEX_MAX, -2n);
|
||||||
|
}).toThrowWithMessage(InternalError, "Out of memory");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue