Skip to content

Commit 2582014

Browse files
committed
externalterm: encode big integers as SMALL_BIG_EXT
Allow calling term_to_binary for serializing big integers. Signed-off-by: Davide Bettio <davide@uninstall.it>
1 parent fcfebff commit 2582014

File tree

3 files changed

+172
-26
lines changed

3 files changed

+172
-26
lines changed

src/libAtomVM/externalterm.c

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -237,23 +237,40 @@ static int serialize_term(uint8_t *buf, term t, GlobalContext *glb)
237237
return 2;
238238

239239
} else if (term_is_any_integer(t)) {
240-
241-
avm_int64_t val = term_maybe_unbox_int64(t);
242-
if (val >= INT32_MIN && val <= INT32_MAX) {
243-
if (buf != NULL) {
244-
buf[0] = INTEGER_EXT;
245-
WRITE_32_UNALIGNED(buf + 1, (int32_t) val);
240+
if (term_is_integer(t) || term_boxed_size(t) <= BOXED_TERMS_REQUIRED_FOR_INT64) {
241+
avm_int64_t val = term_maybe_unbox_int64(t);
242+
if (val >= INT32_MIN && val <= INT32_MAX) {
243+
if (buf != NULL) {
244+
buf[0] = INTEGER_EXT;
245+
WRITE_32_UNALIGNED(buf + 1, (int32_t) val);
246+
}
247+
return INTEGER_EXT_SIZE;
248+
} else {
249+
bool is_negative;
250+
avm_uint64_t unsigned_val = int64_safe_unsigned_abs_set_flag(val, &is_negative);
251+
uint8_t num_bytes = get_num_bytes(unsigned_val);
252+
if (buf != NULL) {
253+
buf[0] = SMALL_BIG_EXT;
254+
buf[1] = num_bytes;
255+
buf[2] = is_negative ? 0x01 : 0x00;
256+
write_bytes(buf + 3, unsigned_val);
257+
}
258+
return SMALL_BIG_EXT_BASE_SIZE + num_bytes;
246259
}
247-
return INTEGER_EXT_SIZE;
248260
} else {
249-
bool is_negative;
250-
avm_uint64_t unsigned_val = int64_safe_unsigned_abs_set_flag(val, &is_negative);
251-
uint8_t num_bytes = get_num_bytes(unsigned_val);
261+
size_t intn_size = term_intn_size(t);
262+
size_t digits_per_term = sizeof(term) / sizeof(intn_digit_t);
263+
size_t bigint_len = intn_size * digits_per_term;
264+
const intn_digit_t *bigint = (const intn_digit_t *) term_intn_data(t);
265+
size_t num_bytes = intn_required_unsigned_integer_bytes(bigint, bigint_len);
252266
if (buf != NULL) {
267+
intn_integer_sign_t sign = (intn_integer_sign_t) term_boxed_integer_sign(t);
268+
253269
buf[0] = SMALL_BIG_EXT;
254270
buf[1] = num_bytes;
255-
buf[2] = is_negative ? 0x01 : 0x00;
256-
write_bytes(buf + 3, unsigned_val);
271+
buf[2] = sign == IntNNegativeInteger ? 0x01 : 0x00;
272+
intn_to_integer_bytes(bigint, bigint_len, IntNPositiveInteger, IntnLittleEndian,
273+
buf + 3, num_bytes);
257274
}
258275
return SMALL_BIG_EXT_BASE_SIZE + num_bytes;
259276
}

tests/erlang_tests/bigint.erl

Lines changed: 131 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ start() ->
5252
test_cmp() +
5353
conv_to_from_float() +
5454
external_term_decode() +
55-
big_literals().
55+
big_literals() +
56+
to_external_term().
5657

5758
test_mul() ->
5859
Expected_INT64_MIN = ?MODULE:pow(-2, 63),
@@ -660,6 +661,135 @@ lit_ovf1() ->
660661
lit_ovf2() ->
661662
?MODULE:id(-16#10000000000000000000000000000000000000000000000000000000000000000).
662663

664+
to_external_term() ->
665+
% maximum
666+
<<131, 110, 32, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
667+
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
668+
255>> = ?MODULE:id(
669+
erlang:term_to_binary(
670+
?MODULE:id(
671+
erlang:binary_to_integer(
672+
?MODULE:id(
673+
<<"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF">>
674+
),
675+
16
676+
)
677+
)
678+
)
679+
),
680+
681+
% minimum
682+
<<131, 110, 32, 1, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
683+
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
684+
255>> = ?MODULE:id(
685+
erlang:term_to_binary(
686+
?MODULE:id(
687+
erlang:binary_to_integer(
688+
?MODULE:id(
689+
<<"-FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF">>
690+
),
691+
16
692+
)
693+
)
694+
)
695+
),
696+
697+
% positive test pattern
698+
<<131, 110, 32, 0, 189, 121, 53, 209, 236, 251, 234, 208, 201, 184, 167, 86, 79, 62, 45, 28, 11,
699+
42, 49, 82, 116, 150, 248, 222, 188, 154, 120, 86, 52, 18, 254, 202>> = ?MODULE:id(
700+
erlang:term_to_binary(
701+
?MODULE:id(
702+
erlang:binary_to_integer(
703+
?MODULE:id(
704+
<<"CAFE123456789ABCDEF8967452312A0B1C2D3E4F56A7B8C9D0EAFBECD13579BD">>
705+
),
706+
16
707+
)
708+
)
709+
)
710+
),
711+
712+
% negative test pattern
713+
<<131, 110, 32, 1, 189, 121, 53, 209, 236, 251, 234, 208, 201, 184, 167, 86, 79, 62, 45, 28, 11,
714+
42, 49, 82, 116, 150, 248, 222, 188, 154, 120, 86, 52, 18, 254, 202>> = ?MODULE:id(
715+
erlang:term_to_binary(
716+
?MODULE:id(
717+
erlang:binary_to_integer(
718+
?MODULE:id(
719+
<<"-CAFE123456789ABCDEF8967452312A0B1C2D3E4F56A7B8C9D0EAFBECD13579BD">>
720+
),
721+
16
722+
)
723+
)
724+
)
725+
),
726+
727+
% test encoding multiple elements
728+
<<131, 108, 0, 0, 0, 2, 110, 32, 0, 189, 121, 53, 209, 236, 251, 234, 208, 201, 184, 167, 86,
729+
79, 62, 45, 28, 11, 42, 49, 82, 116, 150, 248, 222, 188, 154, 120, 86, 52, 18, 254, 202,
730+
109, 0, 0, 0, 3, 116, 115, 116, 106>> = ?MODULE:id(
731+
erlang:term_to_binary([
732+
?MODULE:id(
733+
erlang:binary_to_integer(
734+
?MODULE:id(
735+
<<"CAFE123456789ABCDEF8967452312A0B1C2D3E4F56A7B8C9D0EAFBECD13579BD">>
736+
),
737+
16
738+
)
739+
),
740+
?MODULE:id(<<"tst">>)
741+
])
742+
),
743+
744+
% length is 31 bytes long, not divisible by 4, this might cause buffer overflows
745+
% if not handled correctly
746+
<<131, 110, 31, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
747+
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
748+
255>> = ?MODULE:id(
749+
erlang:term_to_binary(
750+
?MODULE:id(
751+
erlang:binary_to_integer(
752+
?MODULE:id(
753+
<<"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF">>
754+
),
755+
16
756+
)
757+
)
758+
)
759+
),
760+
761+
% length is 27 bytes long, not disible by 4, also on 64 bits system there is a 0 digit once encoded as term
762+
% this might cause issues if not handled correctly
763+
<<131, 110, 27, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
764+
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255>> = ?MODULE:id(
765+
erlang:term_to_binary(
766+
?MODULE:id(
767+
erlang:binary_to_integer(
768+
?MODULE:id(<<"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF">>), 16
769+
)
770+
)
771+
)
772+
),
773+
774+
% test if encoding multiple elements works
775+
<<131, 108, 0, 0, 0, 3, 97, 1, 110, 27, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
776+
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 109, 0,
777+
0, 0, 6, 116, 115, 116, 98, 105, 110, 106>> = ?MODULE:id(
778+
erlang:term_to_binary(
779+
?MODULE:id([
780+
1,
781+
?MODULE:id(
782+
erlang:binary_to_integer(
783+
?MODULE:id(<<"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF">>), 16
784+
)
785+
),
786+
<<"tstbin">>
787+
])
788+
)
789+
),
790+
791+
0.
792+
663793
id(X) ->
664794
X.
665795

tests/erlang_tests/small_big_ext.erl

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -55,19 +55,18 @@ start() ->
5555
true = test_reverse(pow(59) - 1, <<131, 110, 8, 0, 255, 255, 255, 255, 255, 255, 255, 7>>),
5656
true = test_reverse(-pow(59), <<131, 110, 8, 1, 0, 0, 0, 0, 0, 0, 0, 8>>),
5757

58-
% TODO: enable as soon as serialization for big integers is ready
59-
%true = test_reverse(
60-
% erlang:binary_to_integer(?MODULE:id(<<"8000000000000001">>), 16),
61-
% <<131, 110, 8, 0, 1, 0, 0, 0, 0, 0, 0, 128>>
62-
%),
63-
%true = test_reverse(
64-
% erlang:binary_to_integer(?MODULE:id(<<"-8000000000000002">>), 16),
65-
% <<131, 110, 8, 1, 2, 0, 0, 0, 0, 0, 0, 128>>
66-
%),
67-
%true = test_reverse(
68-
% erlang:binary_to_integer(?MODULE:id(<<"100000000000000000000000000000000">>), 16),
69-
% <<131, 110, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1>>
70-
%),
58+
true = test_reverse(
59+
erlang:binary_to_integer(?MODULE:id(<<"8000000000000001">>), 16),
60+
<<131, 110, 8, 0, 1, 0, 0, 0, 0, 0, 0, 128>>
61+
),
62+
true = test_reverse(
63+
erlang:binary_to_integer(?MODULE:id(<<"-8000000000000002">>), 16),
64+
<<131, 110, 8, 1, 2, 0, 0, 0, 0, 0, 0, 128>>
65+
),
66+
true = test_reverse(
67+
erlang:binary_to_integer(?MODULE:id(<<"100000000000000000000000000000000">>), 16),
68+
<<131, 110, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1>>
69+
),
7170

7271
%% missing sign
7372
ok = assert_badarg(

0 commit comments

Comments
 (0)