Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed `term_is_uint32` accepting big integers whose low 64 bits are within range on 32-bit
builds, which made `erlang:crc32/2`, `erlang:crc32_combine/3` and `crypto:pbkdf2_hmac/5`
silently truncate huge integer arguments instead of raising `badarg`
- Fixed a bug where bigints were not normalized, yielding equality errors

## [0.7.0-alpha.1] - 2026-04-06

Expand Down
7 changes: 6 additions & 1 deletion src/libAtomVM/external_term.c
Original file line number Diff line number Diff line change
Expand Up @@ -712,11 +712,16 @@ static term parse_external_terms(const uint8_t *external_term_buf, size_t *eterm
AVM_ABORT();
}

intn_integer_sign_t sign = is_negative ? IntNNegativeInteger : IntNPositiveInteger;
count = (int) intn_count_digits(bigint, count);
if (intn_fits_int64(bigint, count, sign)) {
return term_make_maybe_boxed_int64(intn_to_int64(bigint, count, sign), heap);
}

size_t intn_data_size;
size_t rounded_res_len;
term_bigint_size_requirements(count, &intn_data_size, &rounded_res_len);

intn_integer_sign_t sign = is_negative ? IntNNegativeInteger : IntNPositiveInteger;
term bigint_term
= term_create_uninitialized_bigint(intn_data_size, (term_integer_sign_t) sign, heap);
term_initialize_bigint(bigint_term, bigint, count, rounded_res_len);
Expand Down
10 changes: 10 additions & 0 deletions src/libAtomVM/jit.c
Original file line number Diff line number Diff line change
Expand Up @@ -1446,6 +1446,16 @@ static term jit_term_alloc_bin_match_state(Context *ctx, term src, int slots)
static term make_bigint_from_digits(
Context *ctx, JITState *jit_state, intn_digit_t *bigint, intn_integer_sign_t sign, int count)
{
count = (int) intn_count_digits(bigint, count);
if (intn_fits_int64(bigint, count, sign)) {
term t = maybe_alloc_boxed_integer_fragment(ctx, intn_to_int64(bigint, count, sign));
if (UNLIKELY(term_is_invalid_term(t))) {
set_error(ctx, jit_state, 0, OUT_OF_MEMORY_ATOM);
return FALSE_ATOM;
}
return t;
}

size_t intn_data_size;
size_t rounded_res_len;
term_bigint_size_requirements(count, &intn_data_size, &rounded_res_len);
Expand Down
5 changes: 5 additions & 0 deletions src/libAtomVM/opcodesswitch.h
Original file line number Diff line number Diff line change
Expand Up @@ -1572,6 +1572,11 @@ static bool maybe_call_native(Context *ctx, atom_index_t module_name, atom_index
#ifndef AVM_NO_EMU
static term make_bigint_from_digits(Context *ctx, intn_digit_t *bigint, intn_integer_sign_t sign, size_t count)
{
count = intn_count_digits(bigint, count);
if (intn_fits_int64(bigint, count, sign)) {
return maybe_alloc_boxed_integer_fragment(ctx, intn_to_int64(bigint, count, sign));
}

size_t intn_data_size;
size_t rounded_res_len;
term_bigint_size_requirements(count, &intn_data_size, &rounded_res_len);
Expand Down
2 changes: 2 additions & 0 deletions tests/erlang_tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,7 @@ compile_erlang(test_list_tuple_eq)
compile_erlang(test_ref_eq)
compile_erlang(test_binary_eq)
compile_erlang(test_bigint_eq)
compile_erlang(test_bigint_normalization)

compile_erlang(test_binaries_ordering)
compile_erlang(test_lists_ordering)
Expand Down Expand Up @@ -1003,6 +1004,7 @@ set(erlang_test_beams
test_ref_eq.beam
test_binary_eq.beam
test_bigint_eq.beam
test_bigint_normalization.beam

test_binaries_ordering.beam
test_lists_ordering.beam
Expand Down
93 changes: 93 additions & 0 deletions tests/erlang_tests/test_bigint_normalization.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
%
% This file is part of AtomVM.
%
% Copyright 2026 Paul Guyot <pguyot@kallisys.net>
%
% Licensed under the Apache License, Version 2.0 (the "License");
% you may not use this file except in compliance with the License.
% You may obtain a copy of the License at
%
% http://www.apache.org/licenses/LICENSE-2.0
%
% Unless required by applicable law or agreed to in writing, software
% distributed under the License is distributed on an "AS IS" BASIS,
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
% See the License for the specific language governing permissions and
% limitations under the License.
%
% SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
%

-module(test_bigint_normalization).

-export([start/0, id/1]).

start() ->
ok = test_small_value_from_wide_segment(),
ok = test_negative_small_value_from_wide_segment(),
ok = test_zero_from_wide_segment(),
ok = test_large_value_stays_big(),
ok = test_normalized_value_in_containers(),
ok = test_wide_small_big_ext(),
0.

test_small_value_from_wide_segment() ->
A = u72(id(<<42:72>>)),
true = is_integer(A),
true = A =:= 42,
true = A == 42,
42 = A,
0 = A - 42,
84 = A + 42,
ok.

test_negative_small_value_from_wide_segment() ->
B = s72(id(<<-42:72>>)),
true = B =:= -42,
true = B == -42,
-42 = B,
ok.

test_zero_from_wide_segment() ->
Z = u72(id(<<0:72>>)),
true = Z =:= 0,
0 = Z,
ok.

test_large_value_stays_big() ->
C = u72(id(<<(1 bsl 70):72>>)),
true = C =:= (1 bsl 70),
false = C =:= 42,
%% 1 bsl 62 exceeds the small integer range on every supported word size
D = u72(id(<<(1 bsl 62):72>>)),
true = D =:= (1 bsl 62),
ok.

test_normalized_value_in_containers() ->
A = u72(id(<<42:72>>)),
true = lists:member(A, [41, 42, 43]),
[42] = [A],
{ok, 42} = {ok, A},
true = [A] =:= [42],
ok.

test_wide_small_big_ext() ->
P = binary_to_term(id(<<131, 110, 9, 0, 42, 0, 0, 0, 0, 0, 0, 0, 0>>)),
true = P =:= 42,
42 = P,
N = binary_to_term(id(<<131, 110, 9, 1, 42, 0, 0, 0, 0, 0, 0, 0, 0>>)),
true = N =:= -42,
-42 = N,
Big = binary_to_term(id(<<131, 110, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64>>)),
true = Big =:= (1 bsl 70),
ok.

u72(Bin) ->
<<X:72>> = Bin,
X.

s72(Bin) ->
<<X:72/signed>> = Bin,
X.

id(X) -> X.
1 change: 1 addition & 0 deletions tests/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,7 @@ struct Test tests[] = {
TEST_CASE_EXPECTED(test_ref_eq, 1),
TEST_CASE_EXPECTED(test_binary_eq, 1),
TEST_CASE_EXPECTED(test_bigint_eq, 1),
TEST_CASE(test_bigint_normalization),

TEST_CASE_EXPECTED(test_binaries_ordering, 15),
TEST_CASE_EXPECTED(test_lists_ordering, 7),
Expand Down
Loading