Annotation of parser3/src/main/pa_int.C, revision 1.4

1.1       moko        1: /** @file
                      2:        Parser: pa_int support functions
                      3: 
                      4:        Copyright (c) 2001-2026 Art. Lebedev Studio (http://www.artlebedev.com)
                      5:        Authors: Konstantin Morshnev <moko@design.ru>, Alexandr Petrosian <paf@design.ru>
                      6: */
                      7: 
                      8: #include "pa_int.h"
                      9: #include "pa_string.h"
                     10: #include "pa_exception.h"
                     11: 
1.4     ! moko       12: volatile const char * IDENT_PA_INT_C="$Id: pa_int.C,v 1.3 2026/01/06 16:36:39 moko Exp $" IDENT_PA_INT_H;
1.1       moko       13: 
1.3       moko       14: #ifdef PA_WIDE_INT
                     15: int check4int(pa_wint avalue){
                     16:        if( (avalue > INT_MAX) || (avalue < INT_MIN))
                     17:                throw Exception("number.format", 0, "%s is out of regular int range", pa_itoa(avalue));
                     18:        return (int)avalue;
                     19: }
                     20: #endif
1.1       moko       21: // pa_atoui is based on Manuel Novoa III _strto_l for uClibc
                     22: 
                     23: template<typename T> inline T pa_ato_any(const char *str, int base, const String* problem_source,const T max){
                     24:        T result = 0;
                     25:        const char *pos = str;
                     26: 
                     27:        while (isspace(*pos)) /* skip leading whitespace */
                     28:                ++pos;
                     29: 
                     30:        if (base == 16 && *pos == '0') { /* handle option prefix */
                     31:                ++pos;
                     32:                if (*pos == 'x' || *pos == 'X') {
                     33:                        ++pos;
                     34:                }
                     35:        }
                     36: 
                     37:        if (base == 0) { /* dynamic base */
                     38:                base = 10; /* default is 10 */
                     39:                if (*pos == '0') {
                     40:                        ++pos;
                     41:                        if (*pos == 'x' || *pos == 'X'){
                     42:                                ++pos;
                     43:                                base=16;
                     44:                        }
                     45:                }
                     46:        }
                     47: 
                     48:        if (base < 2 || base > 16) /* illegal base */
                     49:                throw Exception(PARSER_RUNTIME, 0, "base to must be an integer from 2 to 16");
                     50:        if (*pos == '-')
                     51:                throw Exception("number.format", problem_source, problem_source ? "out of range (negative)" : "'%s' is out if range (negative)", str);
                     52: 
                     53:        T cutoff = max / base;
                     54:        int cutoff_digit = (int)(max - cutoff * base);
                     55: 
                     56:        while(true) {
                     57:                int digit;
                     58:                
                     59:                if ((*pos >= '0') && (*pos <= '9')) {
                     60:                        digit = (*pos - '0');
                     61:                } else if (*pos >= 'a') {
                     62:                        digit = (*pos - 'a' + 10);
                     63:                } else if (*pos >= 'A') {
                     64:                        digit = (*pos - 'A' + 10);
                     65:                } else break;
                     66: 
                     67:                if (digit >= base) {
                     68:                        break;
                     69:                }
                     70:                
                     71:                ++pos;
                     72:                
                     73:                /* adjust number, with overflow check */
                     74:                if ((result > cutoff) || ((result == cutoff) && (digit > cutoff_digit))) {
                     75:                        throw Exception("number.format", problem_source, problem_source ? "out of range (int)" : "'%s' is out of range (int)", str);
                     76:                } else {
                     77:                        result  = result * base + digit;
                     78:                }
                     79:        }
                     80: 
                     81:        while(char c=*pos++)
                     82:                if(!isspace(c))
                     83:                        throw Exception("number.format", problem_source, problem_source ? "invalid number (int)" : "'%s' is an invalid number (int)", str);
                     84: 
                     85:        return result;
                     86: }
                     87: 
                     88: unsigned int pa_atoui(const char *str, int base, const String* problem_source){
                     89:        if(!str)
                     90:                return 0;
                     91: 
                     92:        return pa_ato_any<unsigned int>(str, base, problem_source, UINT_MAX);
                     93: }
                     94: 
                     95: uint64_t pa_atoul(const char *str, int base, const String* problem_source){
                     96:        if(!str)
                     97:                return 0;
                     98: 
                     99:        return pa_ato_any<uint64_t>(str, base, problem_source, ULLONG_MAX);
                    100: }
                    101: 
                    102: int pa_atoi(const char* str, int base, const String* problem_source) {
                    103:        if(!str)
                    104:                return 0;
                    105: 
                    106:        while(isspace(*str))
                    107:                str++;
                    108: 
                    109:        if(!*str)
                    110:                return 0;
                    111: 
                    112:        const char *str_copy=str;
                    113:        bool negative=false;
                    114:        if(str[0]=='-') {
                    115:                negative=true;
                    116:                str++;
                    117:                if(!*str || isspace(*str))
                    118:                        throw Exception("number.format", problem_source, problem_source ? "invalid number (int)" : "'%s' is an invalid number (int)", str_copy);
                    119:        } else if(str[0]=='+') {
                    120:                str++;
                    121:                if(!*str || isspace(*str))
                    122:                        throw Exception("number.format", problem_source, problem_source ? "invalid number (int)" : "'%s' is an invalid number (int)", str_copy);
                    123:        }
                    124: 
                    125:        if(negative){
                    126:                const uint min_abs = (uint)(-( (uint)INT_MIN + 1 )) + 1;
                    127:                uint result=pa_ato_any<uint>(str, base, problem_source, min_abs);
                    128:                if(result==min_abs) return INT_MIN;
                    129:                return -(int)result;
                    130:        } else {
                    131:                return (int)pa_ato_any<uint>(str, base, problem_source, INT_MAX);
                    132:        }
                    133: }
                    134: 
                    135: pa_wint pa_atowi(const char* str, int base, const String* problem_source) {
                    136:        if(!str)
                    137:                return 0;
                    138: 
                    139:        while(isspace(*str))
                    140:                str++;
                    141: 
                    142:        if(!*str)
                    143:                return 0;
                    144: 
                    145:        const char *str_copy=str;
                    146:        bool negative=false;
                    147:        if(str[0]=='-') {
                    148:                negative=true;
                    149:                str++;
                    150:                if(!*str || isspace(*str))
                    151:                        throw Exception("number.format", problem_source, problem_source ? "invalid number (int)" : "'%s' is an invalid number (int)", str_copy);
                    152:        } else if(str[0]=='+') {
                    153:                str++;
                    154:                if(!*str || isspace(*str))
                    155:                        throw Exception("number.format", problem_source, problem_source ? "invalid number (int)" : "'%s' is an invalid number (int)", str_copy);
                    156:        }
                    157: 
                    158:        if(negative){
                    159:                const pa_uwint min_abs = (pa_uwint)(-( (pa_wint)PA_WINT_MIN + 1 )) + 1;
                    160:                pa_uwint result=pa_ato_any<pa_uwint>(str, base, problem_source, min_abs);
                    161:                if(result==min_abs) return PA_WINT_MIN;
                    162:                return -(pa_wint)result;
                    163:        } else {
                    164:                return (pa_wint)pa_ato_any<pa_uwint>(str, base, problem_source, PA_WINT_MAX);
                    165:        }
                    166: }
                    167: 
                    168: double pa_atod(const char* str, const String* problem_source /* never null */) {
                    169:        if(!str)
                    170:                return 0;
                    171: 
                    172:        while(isspace(*str))
                    173:                str++;
                    174: 
                    175:        if(!*str)
                    176:                return 0;
                    177: 
                    178:        bool negative=false;
                    179:        if(str[0]=='-') {
                    180:                negative=true;
                    181:                str++;
                    182:                if(!*str || isspace(*str))
                    183:                        throw Exception("number.format", problem_source, "invalid number (double)");
                    184:        } else if(str[0]=='+') {
                    185:                str++;
                    186:                if(!*str || isspace(*str))
                    187:                        throw Exception("number.format", problem_source, "invalid number (double)");
                    188:        }
                    189: 
                    190:        double result;
                    191:        if(str[0]=='0') {
                    192:                if(str[1]=='x' || str[1]=='X') {
                    193:                        // 0xABC
                    194:                        result=(double)pa_atoul(str, 0, problem_source);
                    195:                        return negative ? -result : result;
                    196:                } else {
                    197:                         // skip leading 0000, to disable octal interpretation
                    198:                        do str++; while(*str=='0');
                    199:                }
                    200:        }
                    201: 
                    202:        char *error_pos;
                    203:        result=strtod(str, &error_pos);
                    204: 
                    205:        while(const char c=*error_pos++)
                    206:                if(!isspace(c))
                    207:                        throw Exception("number.format", problem_source, "invalid number (double)");
                    208: 
                    209:        return negative ? -result : result;
                    210: }
                    211: 
1.3       moko      212: 
                    213: // format: %[flags][width][.precision]type          https://msdn.microsoft.com/ru-ru/library/56e442dc(en-us,VS.80).aspx
                    214: //             flags: '-', '+', ' ', '#', '0'      https://msdn.microsoft.com/ru-ru/library/8aky45ct(en-us,VS.80).aspx
                    215: //             width, precision: non negative decimal number
                    216: enum FormatType {
                    217:        FormatInvalid,
                    218:        FormatInt,
                    219:        FormatUInt,
                    220:        FormatDouble
                    221: };
                    222: 
                    223: FormatType format_type(const char* fmt){
                    224:        enum FormatState {
                    225:                Percent,
                    226:                Flags,
                    227:                Width,
                    228:                Precision,
                    229:                Done
                    230:        } state=Percent;
                    231: 
                    232:        FormatType result=FormatInvalid;
                    233: 
                    234:        const char* pos=fmt;
                    235:        while(char c=*(pos++)){
                    236:                switch(state){
                    237:                        case Percent:
                    238:                                if(c=='%'){
                    239:                                        state=Flags;
                    240:                                } else {
                    241:                                        return FormatInvalid; // 1st char must be '%' only
                    242:                                }
                    243:                                break;
                    244:                        case Flags:
                    245:                                if(strchr("-+ #0", c)!=0){
                    246:                                        break;
                    247:                                }
                    248:                                // go to the next step
                    249:                        case Width:
                    250:                                if(c=='.'){
                    251:                                        state=Precision;
                    252:                                        break;
                    253:                                }
                    254:                                // go to the next step
                    255:                        case Precision:
                    256:                                if(c>='0' && c<='9'){
                    257:                                        if(state == Flags) state=Width; // no more flags
                    258:                                        break;
                    259:                                } else if(c=='d' || c=='i'){
                    260:                                        result=FormatInt;
                    261:                                } else if(strchr("feEgG", c)!=0){
                    262:                                        result=FormatDouble;
                    263:                                } else if(strchr("uoxX", c)!=0){
                    264:                                        result=FormatUInt;
                    265:                                } else {
                    266:                                        return FormatInvalid; // invalid char
                    267:                                }
                    268:                                state=Done;
                    269:                                break;
                    270:                        case Done:
                    271:                                return FormatInvalid; // no chars allowed after 'type'
                    272:                }
                    273:        }
                    274:        return result;
                    275: }
                    276: 
1.4     ! moko      277: #ifdef PA_WIDE_INT
        !           278: 
        !           279: #if defined(_MSC_VER) && (_MSC_VER < 1900)
        !           280: // old VS: %I64d
        !           281: #define PA_INTMOD "I64"
        !           282: #else
        !           283: // C99: %lld
        !           284: #define PA_INTMOD "ll"
        !           285: #endif
        !           286: 
        !           287: static const char* wide_int_fmt(const char *fmt, char *dst){
        !           288:        const size_t len = strlen(fmt);
        !           289:        const size_t head = len - 1;
        !           290:        memcpy(dst, fmt, head);
        !           291:        memcpy(dst + head, PA_INTMOD, strlen(PA_INTMOD));
        !           292:        dst[head + strlen(PA_INTMOD)] = fmt[head];
        !           293:        dst[head + strlen(PA_INTMOD) + 1] = '\0';
        !           294:        return dst;
        !           295: }
        !           296: #endif
        !           297: 
1.3       moko      298: 
                    299: const char* format_double(double value, const char* fmt) {
                    300:        char local_buf[MAX_NUMBER];
                    301:        int size=-1;
                    302: 
                    303:        if(fmt && strlen(fmt)){
                    304:                switch(format_type(fmt)){
                    305:                        case FormatDouble:
                    306:                                size=snprintf(local_buf, sizeof(local_buf), fmt, value);
                    307:                                break;
                    308:                        case FormatUInt:
                    309:                                if(value >= 0){ // on Apple M1 (uint)<negative value> is 0
1.4     ! moko      310: #ifdef PA_WIDE_INT
        !           311:                                        char fmt_buf[strlen(fmt)+4];
        !           312:                                        size=snprintf(local_buf, sizeof(local_buf), wide_int_fmt(fmt, fmt_buf), (unsigned long long)clip2wint(value)); // WUINT_MAX == WINT_MAX
        !           313: #else
1.3       moko      314:                                        size=snprintf(local_buf, sizeof(local_buf), fmt, clip2uint(value));
1.4     ! moko      315: #endif
        !           316:                                } else {
        !           317:                                        // for unsigned formats, wrap negatives to uint as unsigned wint would exceed the 53-bit range
        !           318:                                        size=snprintf(local_buf, sizeof(local_buf), fmt, clip2int(value));
1.3       moko      319:                                }
1.4     ! moko      320:                                break;
        !           321:                        case FormatInt:{
        !           322: #ifdef PA_WIDE_INT
        !           323:                                char fmt_buf[strlen(fmt)+4];
        !           324:                                size=snprintf(local_buf, sizeof(local_buf), wide_int_fmt(fmt, fmt_buf), (long long)clip2wint(value));
        !           325: #else
1.3       moko      326:                                size=snprintf(local_buf, sizeof(local_buf), fmt, clip2int(value));
1.4     ! moko      327: #endif
1.3       moko      328:                                break;
1.4     ! moko      329:                                }
1.3       moko      330:                        case FormatInvalid:
                    331:                                throw Exception(PARSER_RUNTIME, 0, "Incorrect format string '%s' was specified.", fmt);
                    332:                }
                    333:        } else
                    334:                return pa_itoa(clip2wint(value));
                    335: 
                    336:        if(size < 0 || size >= MAX_NUMBER-1){ // on win32 we manually reduce max size while printing
                    337:                throw Exception(PARSER_RUNTIME, 0, "Error occurred white executing snprintf with format string '%s'.", fmt);
                    338:        }
                    339: 
                    340:        return pa_strdup(local_buf, (size_t)size);
                    341: }
                    342: 

E-mail: