Skip to content

Commit ce3bf97

Browse files
committed
NIFs: refactor integer_to_binary/list
Refactor it in order to use new `int*_write_to_ascii_buf` functions, to make it easier supporting big integers and to share code across to_binary and to_list functions. Also remove `lltoa` function that is super slow: it relies on 64 bit division that in most embedded architectures requires a helper function. Signed-off-by: Davide Bettio <davide@uninstall.it>
1 parent 9597fa0 commit ce3bf97

File tree

1 file changed

+71
-56
lines changed

1 file changed

+71
-56
lines changed

src/libAtomVM/nifs.c

Lines changed: 71 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -2184,41 +2184,8 @@ static term nif_erlang_atom_to_list_1(Context *ctx, int argc, term argv[])
21842184
return ret;
21852185
}
21862186

2187-
static size_t lltoa(avm_int64_t int_value, unsigned base, char *integer_string)
2188-
{
2189-
int integer_string_len = 0;
2190-
bool neg = int_value < 0;
2191-
if (neg) {
2192-
integer_string_len++;
2193-
if (integer_string) {
2194-
integer_string[0] = '-';
2195-
}
2196-
}
2197-
avm_int64_t v = int_value;
2198-
do {
2199-
v = v / base;
2200-
integer_string_len++;
2201-
} while (v != 0);
2202-
if (integer_string) {
2203-
int ix = 1;
2204-
do {
2205-
avm_int_t digit = int_value % base;
2206-
if (digit < 0) {
2207-
digit = -digit;
2208-
}
2209-
if (digit < 10) {
2210-
integer_string[integer_string_len - ix] = '0' + digit;
2211-
} else {
2212-
integer_string[integer_string_len - ix] = 'A' + digit - 10;
2213-
}
2214-
int_value = int_value / base;
2215-
ix++;
2216-
} while (int_value != 0);
2217-
}
2218-
return integer_string_len;
2219-
}
2220-
2221-
static term nif_erlang_integer_to_binary_2(Context *ctx, int argc, term argv[])
2187+
static term integer_to_buf(Context *ctx, int argc, term argv[], char *tmp_buf, size_t tmp_buf_size,
2188+
char **int_buf, size_t *int_len)
22222189
{
22232190
term value = argv[0];
22242191
avm_int_t base = 10;
@@ -2231,36 +2198,84 @@ static term nif_erlang_integer_to_binary_2(Context *ctx, int argc, term argv[])
22312198
}
22322199
}
22332200

2234-
avm_int64_t int_value = term_maybe_unbox_int64(value);
2235-
size_t len = lltoa(int_value, base, NULL);
2201+
_Static_assert(sizeof(intptr_t) >= sizeof(avm_int_t), "Cast to intptr_t is not safe");
22362202

2237-
if (UNLIKELY(memory_ensure_free_opt(ctx, term_binary_heap_size(len), MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
2203+
if (term_is_integer(value)) {
2204+
avm_int_t int_val = term_to_int(value);
2205+
size_t wlen = intptr_write_to_ascii_buf(int_val, base, tmp_buf + tmp_buf_size);
2206+
*int_len = wlen;
2207+
*int_buf = tmp_buf + tmp_buf_size - wlen;
2208+
} else {
2209+
switch (term_boxed_size(value)) {
2210+
case 0:
2211+
UNREACHABLE();
2212+
case 1: {
2213+
avm_int_t int_val = term_unbox_int(value);
2214+
size_t wlen = intptr_write_to_ascii_buf(int_val, base, tmp_buf + tmp_buf_size);
2215+
*int_len = wlen;
2216+
*int_buf = tmp_buf + tmp_buf_size - wlen;
2217+
break;
2218+
}
2219+
#if BOXED_TERMS_REQUIRED_FOR_INT64 == 2
2220+
case 2: {
2221+
avm_int64_t int64_val = term_unbox_int64(value);
2222+
size_t wlen = int64_write_to_ascii_buf(int64_val, base, tmp_buf + tmp_buf_size);
2223+
*int_len = wlen;
2224+
*int_buf = tmp_buf + tmp_buf_size - wlen;
2225+
break;
2226+
}
2227+
#endif
2228+
default:
2229+
abort();
2230+
}
2231+
}
2232+
2233+
return term_nil();
2234+
}
2235+
2236+
static term nif_erlang_integer_to_binary_2(Context *ctx, int argc, term argv[])
2237+
{
2238+
#ifdef INT64_TO_A_BUF_LEN
2239+
size_t tmp_buf_size = INT64_WRITE_TO_ASCII_BUF_LEN;
2240+
#else
2241+
size_t tmp_buf_size = INTPTR_WRITE_TO_ASCII_BUF_LEN;
2242+
#endif
2243+
char tmp_buf[tmp_buf_size];
2244+
2245+
char *int_buf;
2246+
size_t int_len;
2247+
term maybe_fail_ret
2248+
= integer_to_buf(ctx, argc, argv, tmp_buf, tmp_buf_size, &int_buf, &int_len);
2249+
if (UNLIKELY(term_is_invalid_term(maybe_fail_ret))) {
2250+
return maybe_fail_ret;
2251+
}
2252+
2253+
if (UNLIKELY(memory_ensure_free_opt(ctx, term_binary_heap_size(int_len), MEMORY_CAN_SHRINK)
2254+
!= MEMORY_GC_OK)) {
22382255
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
22392256
}
2240-
term result = term_create_empty_binary(len, &ctx->heap, ctx->global);
2241-
lltoa(int_value, base, (char *) term_binary_data(result));
2242-
return result;
2257+
2258+
return term_from_literal_binary(int_buf, int_len, &ctx->heap, ctx->global);
22432259
}
22442260

22452261
static term nif_erlang_integer_to_list_2(Context *ctx, int argc, term argv[])
22462262
{
2247-
term value = argv[0];
2248-
unsigned base = 10;
2249-
VALIDATE_VALUE(value, term_is_any_integer);
2250-
if (argc > 1) {
2251-
VALIDATE_VALUE(argv[1], term_is_integer);
2252-
base = term_to_int(argv[1]);
2253-
if (UNLIKELY(base < 2 || base > 36)) {
2254-
RAISE_ERROR(BADARG_ATOM);
2255-
}
2256-
}
2263+
#ifdef INT64_TO_A_BUF_LEN
2264+
size_t tmp_buf_size = INT64_WRITE_TO_ASCII_BUF_LEN;
2265+
#else
2266+
size_t tmp_buf_size = INTPTR_WRITE_TO_ASCII_BUF_LEN;
2267+
#endif
2268+
char tmp_buf[tmp_buf_size];
22572269

2258-
avm_int64_t int_value = term_maybe_unbox_int64(value);
2259-
size_t integer_string_len = lltoa(int_value, base, NULL);
2260-
char integer_string[integer_string_len];
2261-
lltoa(int_value, base, integer_string);
2270+
char *int_buf;
2271+
size_t int_len;
2272+
term maybe_fail_ret
2273+
= integer_to_buf(ctx, argc, argv, tmp_buf, tmp_buf_size, &int_buf, &int_len);
2274+
if (UNLIKELY(term_is_invalid_term(maybe_fail_ret))) {
2275+
return maybe_fail_ret;
2276+
}
22622277

2263-
return make_list_from_ascii_buf((uint8_t *) integer_string, integer_string_len, ctx);
2278+
return make_list_from_ascii_buf((uint8_t *) int_buf, int_len, ctx);
22642279
}
22652280

22662281
static int format_float(term value, int scientific, int decimals, int compact, char *out_buf, int outbuf_len)

0 commit comments

Comments
 (0)