static int bf_atof_internal()

in GaiaXAndroidQuickJS/quickjs/gxquickjs/libbf.c [2878:3127]


static int bf_atof_internal(bf_t *r, slimb_t *pexponent,
                            const char *str, const char **pnext, int radix,
                            limb_t prec, bf_flags_t flags, BOOL is_dec)
{
    const char *p, *p_start;
    int is_neg, radix_bits, exp_is_neg, ret, digits_per_limb, shift;
    limb_t cur_limb;
    slimb_t pos, expn, int_len, digit_count;
    BOOL has_decpt, is_bin_exp;
    bf_t a_s, *a;
    
    *pexponent = 0;
    p = str;
    if (!(flags & BF_ATOF_NO_NAN_INF) && radix <= 16 &&
        strcasestart(p, "nan", &p)) {
        bf_set_nan(r);
        ret = 0;
        goto done;
    }
    is_neg = 0;
    
    if (p[0] == '+') {
        p++;
        p_start = p;
    } else if (p[0] == '-') {
        is_neg = 1;
        p++;
        p_start = p;
    } else {
        p_start = p;
    }
    if (p[0] == '0') {
        if ((p[1] == 'x' || p[1] == 'X') &&
            (radix == 0 || radix == 16) &&
            !(flags & BF_ATOF_NO_HEX)) {
            radix = 16;
            p += 2;
        } else if ((p[1] == 'o' || p[1] == 'O') &&
                   radix == 0 && (flags & BF_ATOF_BIN_OCT)) {
            p += 2;
            radix = 8;
        } else if ((p[1] == 'b' || p[1] == 'B') &&
                   radix == 0 && (flags & BF_ATOF_BIN_OCT)) {
            p += 2;
            radix = 2;
        } else {
            goto no_prefix;
        }
        /* there must be a digit after the prefix */
        if (to_digit((uint8_t)*p) >= radix) {
            bf_set_nan(r);
            ret = 0;
            goto done;
        }
    no_prefix: ;
    } else {
        if (!(flags & BF_ATOF_NO_NAN_INF) && radix <= 16 &&
            strcasestart(p, "inf", &p)) {
            bf_set_inf(r, is_neg);
            ret = 0;
            goto done;
        }
    }
    
    if (radix == 0)
        radix = 10;
    if (is_dec) {
        assert(radix == 10);
        radix_bits = 0;
        a = r;
    } else if ((radix & (radix - 1)) != 0) {
        radix_bits = 0; /* base is not a power of two */
        a = &a_s;
        bf_init(r->ctx, a);
    } else {
        radix_bits = ceil_log2(radix);
        a = r;
    }

    /* skip leading zeros */
    /* XXX: could also skip zeros after the decimal point */
    while (*p == '0')
        p++;

    if (radix_bits) {
        shift = digits_per_limb = LIMB_BITS;
    } else {
        radix_bits = 0;
        shift = digits_per_limb = digits_per_limb_table[radix - 2];
    }
    cur_limb = 0;
    bf_resize(a, 1);
    pos = 0;
    has_decpt = FALSE;
    int_len = digit_count = 0;
    for(;;) {
        limb_t c;
        if (*p == '.' && (p > p_start || to_digit(p[1]) < radix)) {
            if (has_decpt)
                break;
            has_decpt = TRUE;
            int_len = digit_count;
            p++;
        }
        c = to_digit(*p);
        if (c >= radix)
            break;
        digit_count++;
        p++;
        if (radix_bits) {
            shift -= radix_bits;
            if (shift <= 0) {
                cur_limb |= c >> (-shift);
                if (bf_add_limb(a, &pos, cur_limb))
                    goto mem_error;
                if (shift < 0)
                    cur_limb = c << (LIMB_BITS + shift);
                else
                    cur_limb = 0;
                shift += LIMB_BITS;
            } else {
                cur_limb |= c << shift;
            }
        } else {
            cur_limb = cur_limb * radix + c;
            shift--;
            if (shift == 0) {
                if (bf_add_limb(a, &pos, cur_limb))
                    goto mem_error;
                shift = digits_per_limb;
                cur_limb = 0;
            }
        }
    }
    if (!has_decpt)
        int_len = digit_count;

    /* add the last limb and pad with zeros */
    if (shift != digits_per_limb) {
        if (radix_bits == 0) {
            while (shift != 0) {
                cur_limb *= radix;
                shift--;
            }
        }
        if (bf_add_limb(a, &pos, cur_limb)) {
        mem_error:
            ret = BF_ST_MEM_ERROR;
            if (!radix_bits)
                bf_delete(a);
            bf_set_nan(r);
            goto done;
        }
    }
            
    /* reset the next limbs to zero (we prefer to reallocate in the
       renormalization) */
    memset(a->tab, 0, (pos + 1) * sizeof(limb_t));

    if (p == p_start) {
        ret = 0;
        if (!radix_bits)
            bf_delete(a);
        bf_set_nan(r);
        goto done;
    }

    /* parse the exponent, if any */
    expn = 0;
    is_bin_exp = FALSE;
    if (((radix == 10 && (*p == 'e' || *p == 'E')) ||
         (radix != 10 && (*p == '@' ||
                          (radix_bits && (*p == 'p' || *p == 'P'))))) &&
        p > p_start) {
        is_bin_exp = (*p == 'p' || *p == 'P');
        p++;
        exp_is_neg = 0;
        if (*p == '+') {
            p++;
        } else if (*p == '-') {
            exp_is_neg = 1;
            p++;
        }
        for(;;) {
            int c;
            c = to_digit(*p);
            if (c >= 10)
                break;
            if (unlikely(expn > ((BF_RAW_EXP_MAX - 2 - 9) / 10))) {
                /* exponent overflow */
                if (exp_is_neg) {
                    bf_set_zero(r, is_neg);
                    ret = BF_ST_UNDERFLOW | BF_ST_INEXACT;
                } else {
                    bf_set_inf(r, is_neg);
                    ret = BF_ST_OVERFLOW | BF_ST_INEXACT;
                }
                goto done;
            }
            p++;
            expn = expn * 10 + c;
        }
        if (exp_is_neg)
            expn = -expn;
    }
    if (is_dec) {
        a->expn = expn + int_len;
        a->sign = is_neg;
        ret = bfdec_normalize_and_round((bfdec_t *)a, prec, flags);
    } else if (radix_bits) {
        /* XXX: may overflow */
        if (!is_bin_exp)
            expn *= radix_bits; 
        a->expn = expn + (int_len * radix_bits);
        a->sign = is_neg;
        ret = bf_normalize_and_round(a, prec, flags);
    } else {
        limb_t l;
        pos++;
        l = a->len - pos; /* number of limbs */
        if (l == 0) {
            bf_set_zero(r, is_neg);
            ret = 0;
        } else {
            bf_t T_s, *T = &T_s;

            expn -= l * digits_per_limb - int_len;
            bf_init(r->ctx, T);
            if (bf_integer_from_radix(T, a->tab + pos, l, radix)) {
                bf_set_nan(r);
                ret = BF_ST_MEM_ERROR;
            } else {
                T->sign = is_neg;
                if (flags & BF_ATOF_EXPONENT) {
                    /* return the exponent */
                    *pexponent = expn;
                    ret = bf_set(r, T);
                } else {
                    ret = bf_mul_pow_radix(r, T, radix, expn, prec, flags);
                }
            }
            bf_delete(T);
        }
        bf_delete(a);
    }
 done:
    if (pnext)
        *pnext = p;
    return ret;
}