aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/beam/big.c
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/beam/big.c')
-rw-r--r--erts/emulator/beam/big.c153
1 files changed, 153 insertions, 0 deletions
diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c
index 02d37e24df..29e677d2e5 100644
--- a/erts/emulator/beam/big.c
+++ b/erts/emulator/beam/big.c
@@ -2732,3 +2732,156 @@ bytebuf_to_integer_1_done:
return res;
}
+
+int do_list_to_integer(Process *p, Eterm orig_list,
+ Eterm *integer, Eterm *rest)
+{
+ Sint i = 0;
+ Uint ui = 0;
+ int skip = 0;
+ int neg = 0;
+ Sint n = 0;
+ int m;
+ int lg2;
+ Eterm res;
+ Eterm* hp;
+ Eterm *hp_end;
+ Eterm lst = orig_list;
+ Eterm tail = lst;
+ int error_res = LTI_BAD_STRUCTURE;
+
+ if (is_nil(lst)) {
+ error_res = LTI_NO_INTEGER;
+ error:
+ *rest = tail;
+ *integer = make_small(0);
+ return error_res;
+ }
+ if (is_not_list(lst))
+ goto error;
+
+ /* if first char is a '-' then it is a negative integer */
+ if (CAR(list_val(lst)) == make_small('-')) {
+ neg = 1;
+ skip = 1;
+ lst = CDR(list_val(lst));
+ if (is_not_list(lst)) {
+ tail = lst;
+ error_res = LTI_NO_INTEGER;
+ goto error;
+ }
+ } else if (CAR(list_val(lst)) == make_small('+')) {
+ /* ignore plus */
+ skip = 1;
+ lst = CDR(list_val(lst));
+ if (is_not_list(lst)) {
+ tail = lst;
+ error_res = LTI_NO_INTEGER;
+ goto error;
+ }
+ }
+
+ /* Calculate size and do type check */
+
+ while(1) {
+ if (is_not_small(CAR(list_val(lst)))) {
+ break;
+ }
+ if (unsigned_val(CAR(list_val(lst))) < '0' ||
+ unsigned_val(CAR(list_val(lst))) > '9') {
+ break;
+ }
+ ui = ui * 10;
+ ui = ui + unsigned_val(CAR(list_val(lst))) - '0';
+ n++;
+ lst = CDR(list_val(lst));
+ if (is_nil(lst)) {
+ break;
+ }
+ if (is_not_list(lst)) {
+ break;
+ }
+ }
+
+ tail = lst;
+ if (!n) {
+ error_res = LTI_NO_INTEGER;
+ goto error;
+ }
+
+
+ /* If n <= 8 then we know it's a small int
+ ** since 2^27 = 134217728. If n > 8 then we must
+ ** construct a bignum and let that routine do the checking
+ */
+
+ if (n <= SMALL_DIGITS) { /* It must be small */
+ if (neg) i = -(Sint)ui;
+ else i = (Sint)ui;
+ res = make_small(i);
+ } else {
+ /* Convert from log10 to log2 by multiplying with 1/log10(2)=3.3219
+ which we round up to (3 + 1/3) */
+ lg2 = (n+1)*3 + (n+1)/3 + 1;
+ m = (lg2+D_EXP-1)/D_EXP; /* number of digits */
+ m = BIG_NEED_SIZE(m); /* number of words + thing */
+
+ hp = HAlloc(p, m);
+ hp_end = hp + m;
+
+ lst = orig_list;
+ if (skip)
+ lst = CDR(list_val(lst));
+
+ /* load first digits (at least one digit) */
+ if ((i = (n % D_DECIMAL_EXP)) == 0)
+ i = D_DECIMAL_EXP;
+ n -= i;
+ m = 0;
+ while(i--) {
+ m = 10*m + (unsigned_val(CAR(list_val(lst))) - '0');
+ lst = CDR(list_val(lst));
+ }
+ res = small_to_big(m, hp); /* load first digits */
+
+ while(n) {
+ i = D_DECIMAL_EXP;
+ n -= D_DECIMAL_EXP;
+ m = 0;
+ while(i--) {
+ m = 10*m + (unsigned_val(CAR(list_val(lst))) - '0');
+ lst = CDR(list_val(lst));
+ }
+ if (is_small(res))
+ res = small_to_big(signed_val(res), hp);
+ res = big_times_small(res, D_DECIMAL_BASE, hp);
+ if (is_small(res))
+ res = small_to_big(signed_val(res), hp);
+ res = big_plus_small(res, m, hp);
+ }
+
+ if (neg) {
+ if (is_small(res))
+ res = make_small(-signed_val(res));
+ else {
+ Uint *big = big_val(res); /* point to thing */
+ *big = bignum_header_neg(*big);
+ }
+ }
+
+ if (is_not_small(res)) {
+ res = big_plus_small(res, 0, hp); /* includes conversion to small */
+
+ if (is_not_small(res)) {
+ hp += (big_arity(res)+1);
+ }
+ }
+ HRelease(p,hp_end,hp);
+ }
+ *integer = res;
+ *rest = tail;
+ if (tail != NIL) {
+ return LTI_SOME_INTEGER;
+ }
+ return LTI_ALL_INTEGER;
+}