Skip to content

Commit 06557f6

Browse files
committed
intn: add intn_to_integer_bytes and intn_required_unsigned_integer_bytes
Add functions useful for writing a big integer back to a buffer, as a little/big-endian integer. Signed-off-by: Davide Bettio <davide@uninstall.it>
1 parent e81e035 commit 06557f6

File tree

2 files changed

+102
-3
lines changed

2 files changed

+102
-3
lines changed

src/libAtomVM/intn.c

+97-3
Original file line numberDiff line numberDiff line change
@@ -190,9 +190,9 @@ static size_t count16(const uint16_t *num, size_t num_len)
190190
return count;
191191
}
192192

193-
static inline uint32_t nlz(uint32_t x)
193+
// make sure that x != 0 before calling this function
194+
static inline uint32_t uint32_nlz(uint32_t x)
194195
{
195-
// This function is used only from divmnu, that doesn't allow 32 leading zeros
196196
ASSUME(x != 0);
197197

198198
#ifdef __has_builtin
@@ -272,7 +272,7 @@ static int divmnu16(
272272
// same amount. We may have to append a high-order
273273
// digit on the dividend; we do that unconditionally.
274274

275-
s = nlz(v[n - 1]) - 16; // 0 <= s <= 15.
275+
s = uint32_nlz(v[n - 1]) - 16; // 0 <= s <= 15.
276276
uint16_t vn[INTN_DIVMNU_MAX_IN_LEN * (sizeof(intn_digit_t) / sizeof(uint16_t))];
277277
for (i = n - 1; i > 0; i--)
278278
vn[i] = (v[i] << s) | (v[i - 1] >> (16 - s));
@@ -694,3 +694,97 @@ int intn_from_integer_bytes(const uint8_t in[], size_t in_size, intn_from_intege
694694

695695
return cond_neg_in_place(sign, out);
696696
}
697+
698+
int intn_to_integer_bytes(const intn_digit_t in[], size_t in_len, intn_integer_sign_t in_sign,
699+
intn_from_integer_options_t opts, uint8_t out[], size_t out_len)
700+
{
701+
size_t count = intn_count_digits(in, in_len);
702+
if (UNLIKELY(count == 0)) {
703+
memset(out, 0, out_len);
704+
return out_len;
705+
}
706+
707+
size_t to_copy = (count - 1);
708+
size_t to_copy_bytes = to_copy * sizeof(intn_digit_t);
709+
710+
if (UNLIKELY(to_copy_bytes > out_len)) {
711+
return -1;
712+
}
713+
714+
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
715+
memcpy(out, in, to_copy_bytes);
716+
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
717+
for (size_t i = 0; i < to_copy; i++) {
718+
out[i * 4] = in[i] & 0xFF;
719+
out[i * 4 + 1] = (in[i] >> 8) & 0xFF;
720+
out[i * 4 + 2] = (in[i] >> 16) & 0xFF;
721+
out[i * 4 + 3] = (in[i] >> 24) & 0xFF;
722+
}
723+
#else
724+
#error "Unsupported endianess"
725+
#endif
726+
727+
intn_digit_t last_in = in[to_copy];
728+
size_t k;
729+
for (k = to_copy * 4; k < (to_copy + 1) * 4; k++) {
730+
if (last_in == 0) {
731+
break;
732+
}
733+
if (UNLIKELY(k >= out_len)) {
734+
return -1;
735+
}
736+
out[k] = last_in & 0xFF;
737+
last_in >>= 8;
738+
}
739+
size_t copied_len = k;
740+
741+
bool negate = false;
742+
if ((opts & IntnSigned) && (in_sign == IntNNegativeInteger)) {
743+
negate = true;
744+
}
745+
746+
uint8_t filler = 0x00;
747+
if (negate) {
748+
filler = 0xFF;
749+
unsigned int carry = 1;
750+
for (size_t i = 0; i < copied_len; i++) {
751+
unsigned int temp = ((int) (~out[i])) + carry;
752+
out[i] = temp & 0xFF;
753+
carry = temp >> 8;
754+
}
755+
}
756+
757+
if ((opts & IntnSigned) && (copied_len == out_len)) {
758+
uint8_t last_byte = out[copied_len - 1];
759+
if (UNLIKELY(
760+
(negate && ((last_byte & 0x80) == 0)) || (!negate && ((last_byte & 0x80) != 0)))) {
761+
return -1;
762+
}
763+
}
764+
765+
memset(out + copied_len, filler, out_len - copied_len);
766+
767+
// rotate when big endian
768+
if (!(opts & IntnLittleEndian)) {
769+
for (size_t i = 0; i < out_len / 2; i++) {
770+
uint8_t tmp = out[i];
771+
out[i] = out[out_len - 1 - i];
772+
out[out_len - 1 - i] = tmp;
773+
}
774+
}
775+
776+
return out_len;
777+
}
778+
779+
size_t intn_required_unsigned_integer_bytes(const intn_digit_t in[], size_t in_len)
780+
{
781+
int i;
782+
for (i = in_len - 1; i >= 0; i--) {
783+
uint32_t in_i = in[i];
784+
if (in_i != 0) {
785+
return (i + 1) * sizeof(uint32_t) - (uint32_nlz(in_i) / 8);
786+
}
787+
}
788+
789+
return 0;
790+
}

src/libAtomVM/intn.h

+5
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,11 @@ int intn_from_double(double dnum, intn_digit_t *out, intn_integer_sign_t *out_si
8686
int intn_from_integer_bytes(const uint8_t in[], size_t in_size, intn_from_integer_options_t opts,
8787
intn_digit_t out[], intn_integer_sign_t *out_sign);
8888

89+
int intn_to_integer_bytes(const intn_digit_t in[], size_t in_len, intn_integer_sign_t in_sign,
90+
intn_from_integer_options_t opts, uint8_t out[], size_t out_len);
91+
92+
size_t intn_required_unsigned_integer_bytes(const intn_digit_t in[], size_t in_len);
93+
8994
static inline void intn_copy(
9095
const intn_digit_t *num, size_t num_len, intn_digit_t *out, size_t extend_to)
9196
{

0 commit comments

Comments
 (0)