/***********************************************************************
 *                                                                      *
 *               This software is part of the ast package               *
 *          Copyright (c) 1985-2011 AT&T Intellectual Property          *
 *                      and is licensed under the                       *
 *                 Eclipse Public License, Version 1.0                  *
 *                    by AT&T Intellectual Property                     *
 *                                                                      *
 *                A copy of the License is available at                 *
 *          http://www.eclipse.org/org/documents/epl-v10.html           *
 *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
 *                                                                      *
 *              Information and Software Systems Research               *
 *                            AT&T Research                             *
 *                           Florham Park NJ                            *
 *                                                                      *
 *               Glenn Fowler <glenn.s.fowler@gmail.com>                *
 *                    David Korn <dgkorn@gmail.com>                     *
 *                     Phong Vo <phongvo@gmail.com>                     *
 *                                                                      *
 ***********************************************************************/
/*
 * AT&T Research
 * Glenn Fowler & Phong Vo
 *
 * common header and implementation for
 *
 *      strtod          _sfdscan
 *      strntof
 *
 * define these macros to instantiate an implementation:
 *
 *      S2F_function    the function name
 *      S2F_static      <0:export =0:extern >0:static
 *      S2F_type        0:float 1:double 2:long.double
 *      S2F_qualifier   1 for optional [fFlL] qualifier suffix
 *      S2F_size        1 for interface with size_t second arg
 *      S2F_scan        1 for alternate interface with these arguments:
 *                              void* handle
 *                              int (*getchar)(void* handle, int flag)
 *                      exactly one extra (*getchar)() is done, i.e.,
 *                      the caller must do the pushback
 *                              flag==0         get next char
 *                              flag==1         no number seen
 *                      return 0 on error or EOF
 */
#ifndef _SFSTRTOF_H
#define _SFSTRTOF_H 1

#include "ast_float.h"
#include "sfhdr.h"

/*
 * the default is _sfdscan for standalone sfio compatibility
 */

#if !defined(S2F_function)
#define S2F_function _sfdscan
#define S2F_static 1
#define S2F_type 2
#define S2F_scan 1
#ifndef elementsof
#define elementsof(a) (sizeof(a) / sizeof(a[0]))
#endif
#endif

#if S2F_type == 2 && _ast_fltmax_double
#undef S2F_type
#define S2F_type 1
#endif

#if S2F_type == 0
#define S2F_number float
#define S2F_ldexp ldexp
#define S2F_pow10 _Sffpow10
#define S2F_inf _Sffinf
#define S2F_nan _Sffnan
#define S2F_min (FLT_MIN)
#define S2F_max (FLT_MAX)
#define S2F_exp_10_max (FLT_MAX_10_EXP)
#define S2F_exp_2_min (FLT_MIN_EXP)
#define S2F_exp_2_max (FLT_MAX_EXP)
#endif
#if S2F_type == 1
#define S2F_number double
#define S2F_ldexp ldexp
#define S2F_pow10 _Sfdpow10
#define S2F_inf _Sfdinf
#define S2F_nan _Sfdnan
#define S2F_min (DBL_MIN)
#define S2F_max (DBL_MAX)
#define S2F_exp_10_max (DBL_MAX_10_EXP)
#define S2F_exp_2_min (DBL_MIN_EXP)
#define S2F_exp_2_max (DBL_MAX_EXP)
#endif
#if S2F_type == 2
#define S2F_number long double
#define S2F_ldexp ldexpl
#define S2F_pow10 _Sflpow10
#define S2F_inf _Sflinf
#define S2F_nan _Sflnan
#define S2F_min (LDBL_MIN)
#define S2F_max (LDBL_MAX)
#define S2F_exp_10_max (LDBL_MAX_10_EXP)
#define S2F_exp_2_min (LDBL_MIN_EXP)
#define S2F_exp_2_max (LDBL_MAX_EXP)
#endif

#define S2F_batch _ast_flt_unsigned_max_t

#undef ERR /* who co-opted this namespace? */

#if S2F_scan

typedef int (*S2F_get_f)(void *, int);

#define ERR(e)
#define GET(p) (*get)(p, 0)
#define NON(p) (*get)(p, 1)
#define PUT(p)
#define REV(p, t, b)
#define SET(p, t, b)

#else

#define ERR(e) (errno = (e))
#define NON(p)

#if S2F_size
#define GET(p) (((p) < (z)) ? (*p++) : (back = 0))
#define PUT(p) (end ? (*end = (char *)p - back) : NULL)
#define REV(p, t, b) (p = t, back = b)
#define SET(p, t, b) (t = p, b = back)
#else
#define GET(p) (*p++)
#define PUT(p) (end ? (*end = (char *)p - 1) : NULL)
#define REV(p, t, b) (p = t)
#define SET(p, t, b) (t = p)
#endif

#endif

typedef struct S2F_part_s {
    S2F_batch batch;
    int digits;
} S2F_part_t;

#if !defined(ERANGE)
#define ERANGE EINVAL
#endif

#if S2F_static > 0
static
#endif
    S2F_number
#if S2F_scan
    S2F_function(void *s, S2F_get_f get)
#else
#if S2F_size
S2F_function(const char *str, size_t size, char **end)
#else
S2F_function(const char *str, char **end)
#endif
#endif
{
#if !S2F_scan
    unsigned char *s = (unsigned char *)str;
#if S2F_size
    unsigned char *z = s + size;
    int back = 1;
    int b;
#endif
    unsigned char *t;
#endif
    S2F_batch n;
    int c;
    int digits;
    int m;
    unsigned char *cv;
    int negative;
    int enegative;
    int fraction;
    int decimal = 0;
    int thousand = 0;
    int part = 0;
    int back_part;
    S2F_batch back_n;
    S2F_number v;
    S2F_number p;
    S2F_part_t parts[16];

    /*
     * radix char and thousands separator are locale specific
     */
    SFSETLOCALE(&decimal, &thousand)
    SFCVINIT()

    /*
     * skip initial blanks
     */

    do
        c = GET(s);
    while (isspace(c));
    SET(s, t, b);

    /*
     * get the sign
     */

    if ((negative = (c == '-')) || c == '+') c = GET(s);

    /*
     * drop leading 0's
     */

    digits = 0;
    fraction = -1;
    if (c == '0') {
        c = GET(s);
        if (c == 'x' || c == 'X') {
            /*
             * hex floating point -- easy
             */

            cv = _Sfcv36;
            v = 0;
            for (;;) {
                c = GET(s);
                if ((part = cv[c]) < 16) {
                    digits++;
                    v *= 16;
                    v += part;
                } else if (c == decimal) {
                    decimal = -1;
                    fraction = digits;
                } else
                    break;
            }
            m = 0;
            if (c == 'p' || c == 'P') {
                c = GET(s);
                if ((enegative = c == '-') || c == '+') c = GET(s);
                while (c >= '0' && c <= '9') {
                    m = (m << 3) + (m << 1) + (c - '0');
                    c = GET(s);
                }
                if (enegative) m = -m;
            }

#if S2F_qualifier

            /*
             * consume the optional suffix
             */

            switch (c) {
                case 'f':
                case 'F':
                case 'l':
                case 'L':
                    c = GET(s);
                    break;
            }
#endif
            PUT(s);
            if (v == 0) return negative ? -v : v;
            if (fraction >= 0) m -= 4 * (digits - fraction);
            if (m < S2F_exp_2_min) {
                if ((m -= S2F_exp_2_min) < S2F_exp_2_min) {
                    ERR(ERANGE);
                    return 0;
                }
                v = S2F_ldexp(v, S2F_exp_2_min);
            } else if (m > S2F_exp_2_max) {
                ERR(ERANGE);
                return negative ? -S2F_inf : S2F_inf;
            }
            v = S2F_ldexp(v, m);
            goto check;
        }
        while (c == '0') c = GET(s);
    } else if (c == decimal) {
        decimal = -1;
        fraction = 0;
        for (;;) {
            c = GET(s);
            if (c != '0') break;
            digits++;
        }
    } else if (c == 'i' || c == 'I') {
        if (((c = GET(s)) != 'n' && c != 'N') || ((c = GET(s)) != 'f' && c != 'F')) {
            REV(s, t, b);
            PUT(s);
            return 0;
        }
        c = GET(s);
        SET(s, t, b);
        if (((c) == 'i' || c == 'I') && ((c = GET(s)) == 'n' || c == 'N') &&
            ((c = GET(s)) == 'i' || c == 'I') && ((c = GET(s)) == 't' || c == 'T') &&
            ((c = GET(s)) == 'y' || c == 'Y')) {
            SET(s, t, b);
        }
        REV(s, t, b);
        PUT(s);
        return negative ? -S2F_inf : S2F_inf;
    } else if (c == 'n' || c == 'N') {
        if (((c = GET(s)) != 'a' && c != 'A') || ((c = GET(s)) != 'n' && c != 'N')) {
            REV(s, t, b);
            PUT(s);
            return 0;
        }
        do
            c = GET(s);
        while (c && !isspace(c));
        PUT(s);
        return negative ? -S2F_nan : S2F_nan;
    } else if (c < '1' || c > '9') {
        REV(s, t, b);
        PUT(s);
        NON(s);
        return 0;
    }

    /*
     * consume the integral and fractional parts
     */

    n = 0;
    m = 0;
    for (;;) {
        if (c >= '0' && c <= '9') {
            digits++;
            n = (n << 3) + (n << 1) + (c - '0');
            if (n >= ((~((S2F_batch)0)) / 10) && part < elementsof(parts)) {
                parts[part].batch = n;
                n = 0;
                parts[part].digits = digits;
                part++;
            }
        } else if (m && (digits - m) != 3)
            break;
        else if (c == decimal) {
            decimal = -1;
            thousand = -1;
            m = 0;
            fraction = digits;
        } else if (c != thousand || (c == thousand && decimal == -1))
            break;
        else if (!(m = digits)) {
            SET(s, t, b);
            break;
        } else {
            SET(s, t, b);
            back_n = n;
            back_part = part;
        }
        c = GET(s);
    }
    if (m && (digits - m) != 3) {
        REV(s, t, b);
        n = back_n;
        part = back_part;
    }

    /*
     * don't forget the last part
     */

    if (n && part < elementsof(parts)) {
        parts[part].batch = n;
        parts[part].digits = digits;
        part++;
    }

    /*
     * consume the exponent
     */

    if (fraction >= 0) digits = fraction;
    if (c == 'e' || c == 'E') {
        c = GET(s);
        if ((enegative = (c == '-')) || c == '+') c = GET(s);
        n = 0;
        while (c >= '0' && c <= '9') {
            n = (n << 3) + (n << 1) + (c - '0');
            c = GET(s);
        }
        if (enegative)
            digits -= n;
        else
            digits += n;
    }

#if S2F_qualifier

    /*
     * consume the optional suffix
     */

    switch (c) {
        case 'f':
        case 'F':
        case 'l':
        case 'L':
            c = GET(s);
            break;
    }
#endif
    PUT(s);

    /*
     * adjust for at most one multiply per part
     * and at most one divide overall
     */

    v = 0;
    if (!part)
        return negative ? -v : v;
    else if ((m = parts[part - 1].digits - digits) > 0)
        digits += m;
    else
        m = 0;

    /*
     * combine the parts
     */

    while (part--) {
        p = parts[part].batch;
        c = digits - parts[part].digits;
        if (c > S2F_exp_10_max) {
            ERR(ERANGE);
            return negative ? -S2F_inf : S2F_inf;
        }
        if (c > 0) {
#if _ast_mpy_overflow_fpe
            if ((S2F_max / p) < S2F_pow10[c]) {
                ERR(ERANGE);
                return negative ? -S2F_inf : S2F_inf;
            }
#endif
            p *= S2F_pow10[c];
        }
        v += p;
    }
    if (m) {
        while (m > S2F_exp_10_max) {
            m -= S2F_exp_10_max;
            v /= S2F_pow10[S2F_exp_10_max];
        }
#if _ast_div_underflow_fpe
        if ((S2F_min * p) > S2F_pow10[c]) {
            ERR(ERANGE);
            return negative ? -S2F_inf : S2F_inf;
        }
#endif
        v /= S2F_pow10[m];
    }

    /*
     * check the range
     */

check:
    if (v < S2F_min) {
        ERR(ERANGE);
        v = 0;
    } else if (v > S2F_max) {
        ERR(ERANGE);
        v = S2F_inf;
    }

    /*
     * done
     */

    return negative ? -v : v;
}

#endif  // _SFSTRTOF_H
