Skip to content

Commit 8aaf82e

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 06557f6 commit 8aaf82e

File tree

2 files changed

+158
-13
lines changed

2 files changed

+158
-13
lines changed

src/libAtomVM/externalterm.c

+29-12
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

+129-1
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),
@@ -651,6 +652,133 @@ big_literals() ->
651652

652653
0.
653654

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

0 commit comments

Comments
 (0)