|
|
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.3 ! moko 12: volatile const char * IDENT_PA_INT_C="$Id: pa_int.C,v 1.2 2026/01/06 13:18:23 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:
! 277:
! 278: const char* format_double(double value, const char* fmt) {
! 279: char local_buf[MAX_NUMBER];
! 280: int size=-1;
! 281:
! 282: if(fmt && strlen(fmt)){
! 283: switch(format_type(fmt)){
! 284: case FormatDouble:
! 285: size=snprintf(local_buf, sizeof(local_buf), fmt, value);
! 286: break;
! 287: case FormatUInt:
! 288: if(value >= 0){ // on Apple M1 (uint)<negative value> is 0
! 289: size=snprintf(local_buf, sizeof(local_buf), fmt, clip2uint(value));
! 290: break;
! 291: }
! 292: case FormatInt:
! 293: size=snprintf(local_buf, sizeof(local_buf), fmt, clip2int(value));
! 294: break;
! 295: case FormatInvalid:
! 296: throw Exception(PARSER_RUNTIME, 0, "Incorrect format string '%s' was specified.", fmt);
! 297: }
! 298: } else
! 299: return pa_itoa(clip2wint(value));
! 300:
! 301: if(size < 0 || size >= MAX_NUMBER-1){ // on win32 we manually reduce max size while printing
! 302: throw Exception(PARSER_RUNTIME, 0, "Error occurred white executing snprintf with format string '%s'.", fmt);
! 303: }
! 304:
! 305: return pa_strdup(local_buf, (size_t)size);
! 306: }
! 307: