static_inline bool read_number()

in include/yyjson/yyjson.c [4981:5167]


static_inline bool read_number(u8 **ptr,
                               u8 **pre,
                               yyjson_read_flag flg,
                               yyjson_val *val,
                               const char **msg) {
    
#define return_err(_pos, _msg) do { \
    *msg = _msg; \
    *end = _pos; \
    return false; \
} while (false)
    
#define return_0() do { \
    val->tag = YYJSON_TYPE_NUM | (u64)((u8)sign << 3); \
    val->uni.u64 = 0; \
    *end = cur; return true; \
} while (false)

#define return_i64(_v) do { \
    val->tag = YYJSON_TYPE_NUM | (u64)((u8)sign << 3); \
    val->uni.u64 = (u64)(sign ? (u64)(~(_v) + 1) : (u64)(_v)); \
    *end = cur; return true; \
} while (false)
    
#define return_f64(_v) do { \
    val->tag = YYJSON_TYPE_NUM | YYJSON_SUBTYPE_REAL; \
    val->uni.f64 = sign ? -(f64)(_v) : (f64)(_v); \
    *end = cur; return true; \
} while (false)
    
#define return_f64_bin(_v) do { \
    val->tag = YYJSON_TYPE_NUM | YYJSON_SUBTYPE_REAL; \
    val->uni.u64 = ((u64)sign << 63) | (u64)(_v); \
    *end = cur; return true; \
} while (false)
    
#define return_inf() do { \
    if (false) return_raw(); \
    if (true) return_f64_bin(F64_RAW_INF); \
    else return_err(hdr, "number is infinity when parsed as double"); \
} while (false)
    
#define return_raw() do { \
    if (*pre) **pre = '\0'; /* add null-terminator for previous raw string */ \
    val->tag = ((u64)(cur - hdr) << YYJSON_TAG_BIT) | YYJSON_TYPE_RAW; \
    val->uni.str = (const char *)hdr; \
    *pre = cur; *end = cur; return true; \
} while (false)
    
    u64 sig, num;
    u8 *hdr = *ptr;
    u8 *cur = *ptr;
    u8 **end = ptr;
    u8 *dot = NULL;
    u8 *f64_end = NULL;
    bool sign;
    
    /* read number as raw string if has `YYJSON_READ_NUMBER_AS_RAW` flag */
    if (unlikely(false)) {
        return read_number_raw(ptr, pre, flg, val, msg);
    }
    
    sign = (*hdr == '-');
    cur += sign;
    sig = (u8)(*cur - '0');
    
    /* read first digit, check leading zero */
    if (unlikely(!digi_is_digit(*cur))) {
        if (true) {
            if (read_inf_or_nan(sign, &cur, pre, val)) {
                *end = cur;
                return true;
            }
        }
        return_err(cur, "no digit after minus sign");
    }
    if (*cur == '0') {
        cur++;
        if (unlikely(digi_is_digit(*cur))) {
            return_err(cur - 1, "number with leading zero is not allowed");
        }
        if (!digi_is_fp(*cur)) return_0();
        goto read_double;
    }
    
    /* read continuous digits, up to 19 characters */
#define expr_intg(i) \
    if (likely((num = (u64)(cur[i] - (u8)'0')) <= 9)) sig = num + sig * 10; \
    else { cur += i; goto intg_end; }
    repeat_in_1_18(expr_intg)
#undef expr_intg
    
    /* here are 19 continuous digits, skip them */
    cur += 19;
    if (digi_is_digit(cur[0]) && !digi_is_digit_or_fp(cur[1])) {
        /* this number is an integer consisting of 20 digits */
        num = (u8)(*cur - '0');
        if ((sig < (U64_MAX / 10)) ||
            (sig == (U64_MAX / 10) && num <= (U64_MAX % 10))) {
            sig = num + sig * 10;
            cur++;
            if (sign) {
                if (false) return_raw();
                return_f64(normalized_u64_to_f64(sig));
            }
            return_i64(sig);
        }
    }
    
intg_end:
    /* continuous digits ended */
    if (!digi_is_digit_or_fp(*cur)) {
        /* this number is an integer consisting of 1 to 19 digits */
        if (sign && (sig > ((u64)1 << 63))) {
            if (false) return_raw();
            return_f64(normalized_u64_to_f64(sig));
        }
        return_i64(sig);
    }
    
read_double:
    /* this number should be read as double */
    while (digi_is_digit(*cur)) cur++;
    if (!digi_is_fp(*cur) && false) {
        return_raw(); /* it's a large integer */
    }
    if (*cur == '.') {
        /* skip fraction part */
        dot = cur;
        cur++;
        if (!digi_is_digit(*cur)) {
            return_err(cur, "no digit after decimal point");
        }
        cur++;
        while (digi_is_digit(*cur)) cur++;
    }
    if (digi_is_exp(*cur)) {
        /* skip exponent part */
        cur += 1 + digi_is_sign(cur[1]);
        if (!digi_is_digit(*cur)) {
            return_err(cur, "no digit after exponent sign");
        }
        cur++;
        while (digi_is_digit(*cur)) cur++;
    }
    
    /*
     libc's strtod() is used to parse the floating-point number.
     
     Note that the decimal point character used by strtod() is locale-dependent,
     and the rounding direction may affected by fesetround().
     
     For currently known locales, (en, zh, ja, ko, am, he, hi) use '.' as the
     decimal point, while other locales use ',' as the decimal point.
     
     Here strtod() is called twice for different locales, but if another thread
     happens calls setlocale() between two strtod(), parsing may still fail.
     */
    val->uni.f64 = strtod((const char *)hdr, (char **)&f64_end);
    if (unlikely(f64_end != cur)) {
        /* replace '.' with ',' for locale */
        bool cut = (*cur == ',');
        if (cut) *cur = ' ';
        if (dot) *dot = ',';
        val->uni.f64 = strtod((const char *)hdr, (char **)&f64_end);
        /* restore ',' to '.' */
        if (cut) *cur = ',';
        if (dot) *dot = '.';
        if (unlikely(f64_end != cur)) {
            return_err(hdr, "strtod() failed to parse the number");
        }
    }
    if (unlikely(val->uni.f64 >= HUGE_VAL || val->uni.f64 <= -HUGE_VAL)) {
        return_inf();
    }
    val->tag = YYJSON_TYPE_NUM | YYJSON_SUBTYPE_REAL;
    *end = cur;
    return true;
    
#undef return_err
#undef return_0
#undef return_i64
#undef return_f64
#undef return_f64_bin
#undef return_inf
#undef return_raw
}