diff --git a/src/libAtomVM/bif.c b/src/libAtomVM/bif.c index 2ac303aab..0dd4ae0ab 100644 --- a/src/libAtomVM/bif.c +++ b/src/libAtomVM/bif.c @@ -79,16 +79,10 @@ #define INT64_MAX_AS_AVM_FLOAT 9223372036854775295.0 // 0x43DFFFFFFFFFFFFF = 2^62 * 1.1...1b #endif -// intn.h and term.h headers are decoupled. We check here that sign enum values are matching. -_Static_assert( - (int) TermPositiveInteger == (int) IntNPositiveInteger, "term/intn definition mismatch"); -_Static_assert( - (int) TermNegativeInteger == (int) IntNNegativeInteger, "term/intn definition mismatch"); - static term make_bigint(Context *ctx, uint32_t fail_label, uint32_t live, const intn_digit_t bigres[], size_t bigres_len, intn_integer_sign_t sign); -static void term_to_bigint(term arg1, intn_digit_t *tmp_buf1, const intn_digit_t **b1, +static void conv_term_to_bigint(term arg1, intn_digit_t *tmp_buf1, const intn_digit_t **b1, size_t *b1_len, intn_integer_sign_t *b1_sign); const struct ExportedFunction *bif_registry_get_handler(const char *mfa) @@ -555,11 +549,11 @@ static term add_maybe_bigint(Context *ctx, uint32_t fail_label, uint32_t live, t const intn_digit_t *bn1; size_t bn1_len; intn_integer_sign_t bn1_sign; - term_to_bigint(arg1, tmp_buf1, &bn1, &bn1_len, &bn1_sign); + conv_term_to_bigint(arg1, tmp_buf1, &bn1, &bn1_len, &bn1_sign); const intn_digit_t *bn2; size_t bn2_len; intn_integer_sign_t bn2_sign; - term_to_bigint(arg2, tmp_buf2, &bn2, &bn2_len, &bn2_sign); + conv_term_to_bigint(arg2, tmp_buf2, &bn2, &bn2_len, &bn2_sign); size_t bigres_len = INTN_ADD_OUT_LEN(bn1_len, bn2_len); if (bigres_len > INTN_MAX_RES_LEN) { @@ -693,11 +687,11 @@ static term sub_maybe_bigint(Context *ctx, uint32_t fail_label, uint32_t live, t const intn_digit_t *bn1; size_t bn1_len; intn_integer_sign_t bn1_sign; - term_to_bigint(arg1, tmp_buf1, &bn1, &bn1_len, &bn1_sign); + conv_term_to_bigint(arg1, tmp_buf1, &bn1, &bn1_len, &bn1_sign); const intn_digit_t *bn2; size_t bn2_len; intn_integer_sign_t bn2_sign; - term_to_bigint(arg2, tmp_buf2, &bn2, &bn2_len, &bn2_sign); + conv_term_to_bigint(arg2, tmp_buf2, &bn2, &bn2_len, &bn2_sign); size_t bigres_len = INTN_SUB_OUT_LEN(bn1_len, bn2_len); if (bigres_len > INTN_MAX_RES_LEN) { @@ -815,18 +809,17 @@ static term make_bigint(Context *ctx, uint32_t fail_label, uint32_t live, if (!intn_fits_int64(bigres, count, sign)) { size_t intn_data_size; size_t rounded_res_len; - term_intn_to_term_size(count, &intn_data_size, &rounded_res_len); + term_bigint_size_requirements(count, &intn_data_size, &rounded_res_len); if (UNLIKELY(memory_ensure_free_with_roots( - ctx, BOXED_INTN_SIZE(intn_data_size), live, ctx->x, MEMORY_CAN_SHRINK) + ctx, BOXED_BIGINT_HEAP_SIZE(intn_data_size), live, ctx->x, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { RAISE_ERROR_BIF(fail_label, OUT_OF_MEMORY_ATOM); } - term bigres_term = term_create_uninitialized_intn( + term bigres_term = term_create_uninitialized_bigint( intn_data_size, (term_integer_sign_t) sign, &ctx->heap); - intn_digit_t *dest_buf = (void *) term_intn_data(bigres_term); - intn_copy(bigres, count, dest_buf, rounded_res_len); + term_initialize_bigint(bigres_term, bigres, count, rounded_res_len); return bigres_term; } else { @@ -839,14 +832,12 @@ static term make_bigint(Context *ctx, uint32_t fail_label, uint32_t live, } } -static void term_to_bigint(term arg1, intn_digit_t *tmp_buf1, const intn_digit_t **b1, +static void conv_term_to_bigint(term arg1, intn_digit_t *tmp_buf1, const intn_digit_t **b1, size_t *b1_len, intn_integer_sign_t *b1_sign) { - if (term_is_boxed_integer(arg1) - && (term_boxed_size(arg1) > (INTN_INT64_LEN * sizeof(intn_digit_t)) / sizeof(term))) { - *b1 = term_intn_data(arg1); - *b1_len = term_intn_size(arg1) * (sizeof(term) / sizeof(intn_digit_t)); - *b1_sign = (intn_integer_sign_t) term_boxed_integer_sign(arg1); + if (term_is_bigint(arg1)) { + term_to_bigint(arg1, b1, b1_len, b1_sign); + } else { avm_int64_t i64 = term_maybe_unbox_int64(arg1); intn_from_int64(i64, tmp_buf1, b1_sign); @@ -874,11 +865,11 @@ static term mul_maybe_bigint(Context *ctx, uint32_t fail_label, uint32_t live, t const intn_digit_t *bn1; size_t bn1_len; intn_integer_sign_t bn1_sign; - term_to_bigint(arg1, tmp_buf1, &bn1, &bn1_len, &bn1_sign); + conv_term_to_bigint(arg1, tmp_buf1, &bn1, &bn1_len, &bn1_sign); const intn_digit_t *bn2; size_t bn2_len; intn_integer_sign_t bn2_sign; - term_to_bigint(arg2, tmp_buf2, &bn2, &bn2_len, &bn2_sign); + conv_term_to_bigint(arg2, tmp_buf2, &bn2, &bn2_len, &bn2_sign); size_t bigres_len = INTN_MUL_OUT_LEN(bn1_len, bn2_len); if (bigres_len > INTN_MAX_RES_LEN) { @@ -1038,11 +1029,11 @@ static term div_maybe_bigint(Context *ctx, uint32_t fail_label, uint32_t live, t const intn_digit_t *bn1; size_t bn1_len; intn_integer_sign_t bn1_sign; - term_to_bigint(arg1, tmp_buf1, &bn1, &bn1_len, &bn1_sign); + conv_term_to_bigint(arg1, tmp_buf1, &bn1, &bn1_len, &bn1_sign); const intn_digit_t *bn2; size_t bn2_len; intn_integer_sign_t bn2_sign; - term_to_bigint(arg2, tmp_buf2, &bn2, &bn2_len, &bn2_sign); + conv_term_to_bigint(arg2, tmp_buf2, &bn2, &bn2_len, &bn2_sign); int cmp_result = intn_cmp(bn1, bn1_len, bn2, bn2_len); if (cmp_result < 0) { @@ -1146,10 +1137,10 @@ term bif_erlang_div_2(Context *ctx, uint32_t fail_label, int live, term arg1, te // that just copies the given term but changes the sign static term neg_bigint(Context *ctx, uint32_t fail_label, uint32_t live, term arg1) { - // update when updating term_to_bigint - intn_digit_t *m = term_intn_data(arg1); - size_t m_len = term_intn_size(arg1) * (sizeof(term) / sizeof(intn_digit_t)); - intn_integer_sign_t m_sign = (intn_integer_sign_t) term_boxed_integer_sign(arg1); + const intn_digit_t *m; + size_t m_len; + intn_integer_sign_t m_sign; + term_to_bigint(arg1, &m, &m_len, &m_sign); intn_digit_t tmp_copy[INTN_MAX_RES_LEN]; memcpy(tmp_copy, m, m_len * sizeof(intn_digit_t)); @@ -1242,9 +1233,10 @@ term bif_erlang_neg_1(Context *ctx, uint32_t fail_label, int live, term arg1) // that just copies the given term but changes the sign static term abs_bigint(Context *ctx, uint32_t fail_label, uint32_t live, term arg1) { - // update when updating term_to_bigint - intn_digit_t *m = term_intn_data(arg1); - size_t m_len = term_intn_size(arg1) * (sizeof(term) / sizeof(intn_digit_t)); + const intn_digit_t *m; + size_t m_len; + intn_integer_sign_t discarded_sign; + term_to_bigint(arg1, &m, &m_len, &discarded_sign); intn_digit_t tmp_copy[INTN_MAX_RES_LEN]; memcpy(tmp_copy, m, m_len * sizeof(intn_digit_t)); @@ -1359,11 +1351,11 @@ static term rem_maybe_bigint(Context *ctx, uint32_t fail_label, uint32_t live, t const intn_digit_t *bn1; size_t bn1_len; intn_integer_sign_t bn1_sign; - term_to_bigint(arg1, tmp_buf1, &bn1, &bn1_len, &bn1_sign); + conv_term_to_bigint(arg1, tmp_buf1, &bn1, &bn1_len, &bn1_sign); const intn_digit_t *bn2; size_t bn2_len; intn_integer_sign_t bn2_sign; - term_to_bigint(arg2, tmp_buf2, &bn2, &bn2_len, &bn2_sign); + conv_term_to_bigint(arg2, tmp_buf2, &bn2, &bn2_len, &bn2_sign); int cmp_result = intn_cmp(bn1, bn1_len, bn2, bn2_len); if (cmp_result < 0) { @@ -1622,11 +1614,11 @@ static inline term bitwise_helper( const intn_digit_t *m; size_t m_len; intn_integer_sign_t m_sign; - term_to_bigint(arg1, tmp_buf1, &m, &m_len, &m_sign); + conv_term_to_bigint(arg1, tmp_buf1, &m, &m_len, &m_sign); const intn_digit_t *n; size_t n_len; intn_integer_sign_t n_sign; - term_to_bigint(arg2, tmp_buf2, &n, &n_len, &n_sign); + conv_term_to_bigint(arg2, tmp_buf2, &n, &n_len, &n_sign); intn_digit_t bigres[INTN_MAX_RES_LEN]; intn_integer_sign_t bigres_sign; @@ -1702,7 +1694,7 @@ term bif_erlang_bsl_2(Context *ctx, uint32_t fail_label, int live, term arg1, te const intn_digit_t *m; size_t m_len; intn_integer_sign_t m_sign; - term_to_bigint(arg1, tmp_buf1, &m, &m_len, &m_sign); + conv_term_to_bigint(arg1, tmp_buf1, &m, &m_len, &m_sign); intn_digit_t bigres[INTN_MAX_RES_LEN]; size_t bigres_len = intn_bsl(m, m_len, b, bigres); @@ -1762,7 +1754,7 @@ term bif_erlang_bsr_2(Context *ctx, uint32_t fail_label, int live, term arg1, te const intn_digit_t *m; size_t m_len; intn_integer_sign_t m_sign; - term_to_bigint(arg1, tmp_buf1, &m, &m_len, &m_sign); + conv_term_to_bigint(arg1, tmp_buf1, &m, &m_len, &m_sign); intn_digit_t bigres[INTN_MAX_RES_LEN]; size_t bigres_len = intn_bsr(m, m_len, m_sign, b, bigres); @@ -1823,7 +1815,7 @@ static term bnot_boxed_helper(Context *ctx, uint32_t fail_label, uint32_t live, const intn_digit_t *m; size_t m_len; intn_integer_sign_t m_sign; - term_to_bigint(arg1, tmp_buf1, &m, &m_len, &m_sign); + conv_term_to_bigint(arg1, tmp_buf1, &m, &m_len, &m_sign); intn_digit_t bigres[INTN_MAX_RES_LEN]; intn_integer_sign_t bigres_sign; diff --git a/src/libAtomVM/externalterm.c b/src/libAtomVM/externalterm.c index cbbf2b30a..cf34de81b 100644 --- a/src/libAtomVM/externalterm.c +++ b/src/libAtomVM/externalterm.c @@ -234,14 +234,12 @@ static int serialize_term(uint8_t *buf, term t, GlobalContext *glb) return SMALL_BIG_EXT_BASE_SIZE + num_bytes; } } else { - size_t intn_size = term_intn_size(t); - size_t digits_per_term = sizeof(term) / sizeof(intn_digit_t); - size_t bigint_len = intn_size * digits_per_term; - const intn_digit_t *bigint = (const intn_digit_t *) term_intn_data(t); + const intn_digit_t *bigint; + size_t bigint_len; + intn_integer_sign_t sign; + term_to_bigint(t, &bigint, &bigint_len, &sign); size_t num_bytes = intn_required_unsigned_integer_bytes(bigint, bigint_len); if (buf != NULL) { - intn_integer_sign_t sign = (intn_integer_sign_t) term_boxed_integer_sign(t); - buf[0] = SMALL_BIG_EXT; buf[1] = num_bytes; buf[2] = sign == IntNNegativeInteger ? 0x01 : 0x00; @@ -594,13 +592,12 @@ static term parse_external_terms(const uint8_t *external_term_buf, size_t *eterm size_t intn_data_size; size_t rounded_res_len; - term_intn_to_term_size(count, &intn_data_size, &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_intn(intn_data_size, (term_integer_sign_t) sign, heap); - intn_digit_t *dest_buf = (void *) term_intn_data(bigint_term); - intn_copy(bigint, count, dest_buf, rounded_res_len); + = term_create_uninitialized_bigint(intn_data_size, (term_integer_sign_t) sign, heap); + term_initialize_bigint(bigint_term, bigint, count, rounded_res_len); return bigint_term; } @@ -1014,8 +1011,8 @@ static int calculate_heap_usage(const uint8_t *external_term_buf, size_t remaini size_t required_digits = intn_required_digits_for_unsigned_integer(num_bytes); size_t data_size; size_t unused_rounded_len; - term_intn_to_term_size(required_digits, &data_size, &unused_rounded_len); - return BOXED_INTN_SIZE(data_size); + term_bigint_size_requirements(required_digits, &data_size, &unused_rounded_len); + return BOXED_BIGINT_HEAP_SIZE(data_size); } case ATOM_UTF8_EXT: diff --git a/src/libAtomVM/jit.c b/src/libAtomVM/jit.c index eb72e5e61..775631217 100644 --- a/src/libAtomVM/jit.c +++ b/src/libAtomVM/jit.c @@ -627,17 +627,19 @@ static term jit_alloc_big_integer_fragment( size_t intn_data_size; size_t rounded_res_len; - term_intn_to_term_size(digits_len, &intn_data_size, &rounded_res_len); + term_bigint_size_requirements(digits_len, &intn_data_size, &rounded_res_len); - if (UNLIKELY(memory_init_heap(&heap, BOXED_INTN_SIZE(intn_data_size)) != MEMORY_GC_OK)) { + if (UNLIKELY(memory_init_heap(&heap, BOXED_BIGINT_HEAP_SIZE(intn_data_size)) != MEMORY_GC_OK)) { ctx->x[0] = ERROR_ATOM; ctx->x[1] = OUT_OF_MEMORY_ATOM; return term_invalid_term(); } term bigint_term - = term_create_uninitialized_intn(intn_data_size, (term_integer_sign_t) sign, &heap); - void *digits_mem = term_intn_data(bigint_term); + = term_create_uninitialized_bigint(intn_data_size, (term_integer_sign_t) sign, &heap); + // Assumption: here we assume that bigints have standard boxed term layout + // This code might need to be updated when changing bigint memory layout + void *digits_mem = (void *) (term_to_const_term_ptr(bigint_term) + 1); // TODO: optimize: just initialize space that will not be used memset(digits_mem, 0, intn_data_size * sizeof(term)); diff --git a/src/libAtomVM/nifs.c b/src/libAtomVM/nifs.c index dc546da7f..21e889585 100644 --- a/src/libAtomVM/nifs.c +++ b/src/libAtomVM/nifs.c @@ -2057,16 +2057,15 @@ static term make_bigint(Context *ctx, const intn_digit_t bigres[], size_t bigres { size_t intn_data_size; size_t rounded_res_len; - term_intn_to_term_size(bigres_len, &intn_data_size, &rounded_res_len); + term_bigint_size_requirements(bigres_len, &intn_data_size, &rounded_res_len); - if (UNLIKELY(memory_ensure_free(ctx, BOXED_INTN_SIZE(intn_data_size)) != MEMORY_GC_OK)) { + if (UNLIKELY(memory_ensure_free(ctx, BOXED_BIGINT_HEAP_SIZE(intn_data_size)) != MEMORY_GC_OK)) { RAISE_ERROR(OUT_OF_MEMORY_ATOM); } term bigres_term - = term_create_uninitialized_intn(intn_data_size, (term_integer_sign_t) sign, &ctx->heap); - intn_digit_t *dest_buf = (void *) term_intn_data(bigres_term); - intn_copy(bigres, bigres_len, dest_buf, rounded_res_len); + = term_create_uninitialized_bigint(intn_data_size, (term_integer_sign_t) sign, &ctx->heap); + term_initialize_bigint(bigres_term, bigres, bigres_len, rounded_res_len); return bigres_term; } @@ -2396,12 +2395,11 @@ static term integer_to_buf(Context *ctx, int argc, term argv[], char *tmp_buf, s } #endif default: { - size_t boxed_size = term_intn_size(value); - size_t digits_per_term = sizeof(term) / sizeof(intn_digit_t); - const intn_digit_t *intn_buf = (const intn_digit_t *) term_intn_data(value); - intn_integer_sign_t sign = (intn_integer_sign_t) term_boxed_integer_sign(value); - *int_buf - = intn_to_string(intn_buf, boxed_size * digits_per_term, sign, base, int_len); + const intn_digit_t *intn_buf; + size_t intn_buf_len; + intn_integer_sign_t sign; + term_to_bigint(value, &intn_buf, &intn_buf_len, &sign); + *int_buf = intn_to_string(intn_buf, intn_buf_len, sign, base, int_len); *needs_cleanup = true; } } diff --git a/src/libAtomVM/opcodesswitch.h b/src/libAtomVM/opcodesswitch.h index d3cb15d32..3fa56031e 100644 --- a/src/libAtomVM/opcodesswitch.h +++ b/src/libAtomVM/opcodesswitch.h @@ -1840,11 +1840,11 @@ static bool maybe_call_native(Context *ctx, atom_index_t module_name, atom_index size_t intn_data_size; size_t rounded_res_len; - term_intn_to_term_size(count, &intn_data_size, &rounded_res_len); + term_bigint_size_requirements(count, &intn_data_size, &rounded_res_len); Heap heap; if (UNLIKELY( - memory_init_heap(&heap, BOXED_INTN_SIZE(intn_data_size)) != MEMORY_GC_OK)) { + memory_init_heap(&heap, BOXED_BIGINT_HEAP_SIZE(intn_data_size)) != MEMORY_GC_OK)) { ctx->x[0] = ERROR_ATOM; ctx->x[1] = OUT_OF_MEMORY_ATOM; *out_term = term_invalid_term(); @@ -1852,9 +1852,8 @@ static bool maybe_call_native(Context *ctx, atom_index_t module_name, atom_index } term bigint_term - = term_create_uninitialized_intn(intn_data_size, (term_integer_sign_t) sign, &heap); - intn_digit_t *dest_buf = (void *) term_intn_data(bigint_term); - intn_copy(bigint, count, dest_buf, rounded_res_len); + = term_create_uninitialized_bigint(intn_data_size, (term_integer_sign_t) sign, &heap); + term_initialize_bigint(bigint_term, bigint, count, rounded_res_len); memory_heap_append_heap(&ctx->heap, &heap); diff --git a/src/libAtomVM/term.c b/src/libAtomVM/term.c index cbd73d0bf..e00ea1d1b 100644 --- a/src/libAtomVM/term.c +++ b/src/libAtomVM/term.c @@ -420,13 +420,12 @@ int term_funprint(PrinterFun *fun, term t, const GlobalContext *global) return fun->print(fun, AVM_INT64_FMT, term_unbox_int64(t)); #endif default: { - size_t digits_per_term = sizeof(term) / sizeof(intn_digit_t); - size_t boxed_size = term_intn_size(t); - const intn_digit_t *intn_data = (const intn_digit_t *) term_intn_data(t); - intn_integer_sign_t sign = (intn_integer_sign_t) term_boxed_integer_sign(t); + const intn_digit_t *intn_data; + size_t intn_data_len; + intn_integer_sign_t sign; + term_to_bigint(t, &intn_data, &intn_data_len, &sign); size_t unused_s_len; - char *s = intn_to_string( - intn_data, boxed_size * digits_per_term, sign, 10, &unused_s_len); + char *s = intn_to_string(intn_data, intn_data_len, sign, 10, &unused_s_len); if (IS_NULL_PTR(s)) { return -1; } @@ -1087,12 +1086,12 @@ avm_float_t term_conv_to_float(term t) return term_unbox_int64(t); #endif default: { - const intn_digit_t *num = (intn_digit_t *) term_intn_data(t); - size_t digits_per_term = (sizeof(term) / sizeof(intn_digit_t)); - size_t len = boxed_size * digits_per_term; - term_integer_sign_t t_sign = term_boxed_integer_sign(t); + const intn_digit_t *num; + size_t num_len; + intn_integer_sign_t num_sign; + term_to_bigint(t, &num, &num_len, &num_sign); - return intn_to_double(num, len, (intn_integer_sign_t) t_sign); + return intn_to_double(num, num_len, num_sign); } } } else { diff --git a/src/libAtomVM/term.h b/src/libAtomVM/term.h index e5d7d7c39..6e94d22b5 100644 --- a/src/libAtomVM/term.h +++ b/src/libAtomVM/term.h @@ -145,7 +145,6 @@ extern "C" { #define FUNCTION_REFERENCE_SIZE 4 #define BOXED_INT_SIZE (BOXED_TERMS_REQUIRED_FOR_INT + 1) #define BOXED_INT64_SIZE (BOXED_TERMS_REQUIRED_FOR_INT64 + 1) -#define BOXED_INTN_SIZE(term_size) ((term_size) + 1) #define BOXED_FUN_SIZE 3 #define FLOAT_SIZE (sizeof(float_term_t) / sizeof(term) + 1) #define REF_SIZE ((int) ((sizeof(uint64_t) / sizeof(term)) + 1)) @@ -188,6 +187,17 @@ extern "C" { #define TERM_BINARY_HEAP_SIZE(size) \ (TERM_BINARY_DATA_SIZE_IN_TERMS(size) + BINARY_HEADER_SIZE) +/** + * @def BOXED_BIGINT_HEAP_SIZE(term_size) + * @brief Calculate total heap allocation size for a bigint including header + * + * @param term_size Data size in terms (from \c term_bigint_size_requirements()) + * @return Total heap size needed including 1 term for boxed header + * + * @see term_bigint_size_requirements() which provides the term_size parameter + */ +#define BOXED_BIGINT_HEAP_SIZE(term_size) ((term_size) + 1) + #define TERM_DEBUG_ASSERT(...) #define TERM_FROM_ATOM_INDEX(atom_index) ((atom_index << TERM_IMMED2_TAG_SIZE) | TERM_IMMED2_ATOM) @@ -1301,7 +1311,36 @@ static inline size_t term_boxed_integer_size(avm_int64_t value) } } -static inline term term_create_uninitialized_intn(size_t n, term_integer_sign_t sign, Heap *heap) +/** + * @brief Create an uninitialized bigint term with allocated storage + * + * Allocates heap space for a multi-precision integer term and sets up the + * boxed header with size and sign information. The digit data area is left + * uninitialized and must be filled using \c term_initialize_bigint(). + * + * @param n Size of data area in terms (not digits), from \c term_bigint_size_requirements() + * @param sign Sign of the integer (\c TERM_INTEGER_POSITIVE or \c TERM_INTEGER_NEGATIVE) + * @param heap Heap to allocate from + * @return Newly created uninitialized bigint term + * + * @pre n must be > \c BOXED_TERMS_REQUIRED_FOR_INT64 to ensure bigint distinction + * @pre heap must have at least (1 + n) terms of free space + * + * @post Allocates 1 header term + n data terms on the heap + * @post Header contains size and sign information + * @post Data area is uninitialized and must be filled before use + * + * @warning When ensuring heap space, use \c BOXED_BIGINT_HEAP_SIZE(n) to include + * the header term in the allocation size + * + * @note The size n is in terms, not \c intn_digit_t digits + * @note Use \c term_bigint_size_requirements() to calculate appropriate n value + * + * @see term_initialize_bigint() to fill the allocated data area + * @see term_bigint_size_requirements() to calculate required size + * @see BOXED_BIGINT_HEAP_SIZE() to calculate total heap allocation including header + */ +static inline term term_create_uninitialized_bigint(size_t n, term_integer_sign_t sign, Heap *heap) { term *boxed_int = memory_heap_alloc(heap, 1 + n); boxed_int[0] = (n << 6) | TERM_BOXED_POSITIVE_INTEGER | sign; @@ -1309,30 +1348,94 @@ static inline term term_create_uninitialized_intn(size_t n, term_integer_sign_t return ((term) boxed_int) | TERM_PRIMARY_BOXED; } -static inline void *term_intn_data(term t) -{ - const term *boxed_value = term_to_const_term_ptr(t); - return (void *) (boxed_value + 1); -} - -static inline size_t term_intn_size(term t) +/** + * @brief Initialize multi-precision integer data in a pre-allocated term + * + * Copies multi-precision integer digits into an already allocated boxed term, + * zero-extending to fill the entire allocated space. This function is used + * after creating an uninitialized bigint term to populate it with actual data. + * + * @param t Uninitialized bigint term (created with \c term_create_uninitialized_bigint()) + * @param bigint Source digit array to copy + * @param bigint_len Number of digits in source array + * @param uninitialized_size Total size of destination buffer in digits + * + * @pre t must be a valid uninitialized bigint term + * @pre bigint != NULL + * @pre uninitialized_size must match the size allocated for the term + * + * @post Copies bigint_len digits from source to term + * @post Zero-fills remaining space from bigint_len to uninitialized_size + * + * @note This function does not set the sign - that should be done when creating the term + * @note The destination buffer size (uninitialized_size) is typically rounded up for alignment + * + * @see term_create_uninitialized_bigint() to allocate the term before initialization + * @see intn_copy() which performs the actual copy and zero-extension + */ +static inline void term_initialize_bigint( + term t, const intn_digit_t *bigint, size_t bigint_len, size_t uninitialized_size) { const term *boxed_value = term_to_const_term_ptr(t); - return term_get_size_from_boxed_header(boxed_value[0]); + intn_digit_t *dest_buf = (intn_digit_t *) (boxed_value + 1); + intn_copy(bigint, bigint_len, dest_buf, uninitialized_size); } -static inline void term_intn_to_term_size(size_t n, size_t *intn_data_size, size_t *rounded_num_len) +/** + * @brief Calculate term allocation size for multi-precision integer + * + * Converts the number of \c intn_digit_t digits needed for a bigint into the + * corresponding term allocation size and rounded digit count. Handles platform + * differences between 32-bit systems (where term = intn_digit_t) and 64-bit + * systems (where term = 2 × intn_digit_t), including alignment requirements. + * + * @param n Number of non-zero \c intn_digit_t digits in the integer + * @param[out] intn_data_size Number of terms needed for storage (excludes header) + * @param[out] rounded_num_len Rounded number of digits for zero-padding + * + * @pre n > 0 + * @pre intn_data_size != NULL + * @pre rounded_num_len != NULL + * + * @post *intn_data_size > \c BOXED_TERMS_REQUIRED_FOR_INT64 (ensures bigint distinction) + * @post *rounded_num_len >= n (includes padding for alignment) + * + * @warning The returned intn_data_size does NOT include the boxed header term. + * Use \c BOXED_BIGINT_HEAP_SIZE(intn_data_size) when allocating heap space. + * + * @note Forces minimum size > \c BOXED_TERMS_REQUIRED_FOR_INT64 to distinguish + * bigints from regular boxed int64 values (which use two's complement) + * @note Rounds up to 8-byte boundaries for alignment + * @note On 64-bit systems, 2 digits fit per term; on 32-bit systems, 1 digit per term + * + * @code + * // Example usage: + * size_t count = intn_count_digits(bigint, bigint_len); + * size_t intn_data_size, rounded_res_len; + * term_bigint_size_requirements(count, &intn_data_size, &rounded_res_len); + * + * // Ensure heap has space for data + header + * memory_ensure_free(ctx, BOXED_BIGINT_HEAP_SIZE(intn_data_size)); + * + * term t = term_create_uninitialized_bigint(intn_data_size, sign, heap); + * term_initialize_bigint(t, bigint, count, rounded_res_len); + * @endcode + * + * @see BOXED_BIGINT_HEAP_SIZE() to include header in heap allocation + */ +static inline void term_bigint_size_requirements( + size_t n, size_t *intn_data_size, size_t *rounded_num_len) { size_t bytes = n * sizeof(intn_digit_t); size_t rounded = ((bytes + 7) >> 3) << 3; *intn_data_size = rounded / sizeof(term); if (*intn_data_size == BOXED_TERMS_REQUIRED_FOR_INT64) { - // we need to distinguish between "small" boxed integers, that are integers - // up to int64, and bigger integers. - // The real difference is that "small" boxed integers use 2-complement, - // real bigints not (and also endianess might differ). - // So we force real bigints to be > BOXED_TERMS_REQUIRED_FOR_INT64 terms + // We need to distinguish between "small" boxed integers (up to int64) + // and true bigints. Small boxed integers use two's complement + // representation, while bigints use sign-magnitude (and endianness + // might also differ). To ensure this distinction, we force bigints + // to use > BOXED_TERMS_REQUIRED_FOR_INT64 terms. *intn_data_size = BOXED_TERMS_REQUIRED_FOR_INT64 + 1; rounded = *intn_data_size * sizeof(term); } @@ -1340,6 +1443,85 @@ static inline void term_intn_to_term_size(size_t n, size_t *intn_data_size, size *rounded_num_len = rounded / sizeof(intn_digit_t); } +/** + * @brief Check if term is a multi-precision integer larger than \c int64_t + * + * Tests whether a term represents a boxed integer that requires multi-precision + * representation (i.e., larger than can fit in \c int64_t). These are integers + * that need more than \c INTN_INT64_LEN digits for their representation. + * + * In the current implementation, a bigint is defined as a boxed integer with + * size greater than: + * - \c BOXED_TERMS_REQUIRED_FOR_INT64 on 32-bit systems + * - \c BOXED_TERMS_REQUIRED_FOR_INT on 64-bit systems + * + * This effectively identifies integers that cannot be represented in the + * platform's native integer types and require multi-precision arithmetic, + * while avoiding confusion with regular boxed \c int64_t values that still + * fit within standard integer ranges. + * + * @param t Term to check + * @return true if term is a multi-precision integer, false otherwise + * + * @note Returns false for integers that fit in \c int64_t, even if boxed + * @note This is the correct check before calling \c term_to_bigint() + * + * @see term_to_bigint() to extract the multi-precision integer data + * @see term_is_boxed_integer() for checking any boxed integer + * @see term_is_any_integer() for checking all integer representations + */ +static inline bool term_is_bigint(term t) +{ + return term_is_boxed_integer(t) + && (term_boxed_size(t) > (INTN_INT64_LEN * sizeof(intn_digit_t)) / sizeof(term)); +} + +// intn doesn't depend on term +_Static_assert( + (int) TermPositiveInteger == (int) IntNPositiveInteger, "term/intn definition mismatch"); +_Static_assert( + (int) TermNegativeInteger == (int) IntNNegativeInteger, "term/intn definition mismatch"); + +/** + * @brief Extract multi-precision integer data from boxed term + * + * Extracts the raw multi-precision integer representation from a boxed + * integer term. This function provides direct access to the internal + * digit array without copying, returning a pointer to the data within + * the term structure. + * + * @param t Boxed integer term to extract from + * @param[out] bigint Pointer to the digit array within the term (borrowed reference) + * @param[out] bigint_len Number of digits in the integer + * @param[out] bigint_sign Sign of the integer + * + * @pre \c term_is_bigint(t) must be true + * @pre bigint != NULL + * @pre bigint_len != NULL + * @pre bigint_sign != NULL + * + * @warning Returned pointer is a borrowed reference into the term structure + * @warning Data becomes invalid if term is garbage collected or modified + * @warning Caller must not free the returned pointer + * + * @note The digit array may not be normalized (may have leading zeros) + * @note Length is calculated as boxed_size * (sizeof(term) / sizeof(intn_digit_t)) + * + * @see term_is_bigint() to check if term is a multi-precision integer + * @see term_boxed_integer_sign() to get the sign + */ +static inline void term_to_bigint( + term t, const intn_digit_t *bigint[], size_t *bigint_len, intn_integer_sign_t *bigint_sign) +{ + const term *boxed_value = term_to_const_term_ptr(t); + *bigint = (const intn_digit_t *) (boxed_value + 1); + + size_t boxed_size = term_get_size_from_boxed_header(boxed_value[0]); + *bigint_len = boxed_size * (sizeof(term) / sizeof(intn_digit_t)); + + *bigint_sign = (intn_integer_sign_t) term_boxed_integer_sign(t); +} + static inline term term_from_catch_label(unsigned int module_index, unsigned int label) { return (term) ((module_index << 24) | (label << 6) | TERM_IMMED2_CATCH);