|
|
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.2 ! moko 12: volatile const char * IDENT_PA_INT_C="$Id: pa_int.C,v 1.1 2026/01/06 13:07:58 moko Exp $" IDENT_PA_INT_H;
1.1 moko 13:
14: // pa_atoui is based on Manuel Novoa III _strto_l for uClibc
15:
16: template<typename T> inline T pa_ato_any(const char *str, int base, const String* problem_source,const T max){
17: T result = 0;
18: const char *pos = str;
19:
20: while (isspace(*pos)) /* skip leading whitespace */
21: ++pos;
22:
23: if (base == 16 && *pos == '0') { /* handle option prefix */
24: ++pos;
25: if (*pos == 'x' || *pos == 'X') {
26: ++pos;
27: }
28: }
29:
30: if (base == 0) { /* dynamic base */
31: base = 10; /* default is 10 */
32: if (*pos == '0') {
33: ++pos;
34: if (*pos == 'x' || *pos == 'X'){
35: ++pos;
36: base=16;
37: }
38: }
39: }
40:
41: if (base < 2 || base > 16) /* illegal base */
42: throw Exception(PARSER_RUNTIME, 0, "base to must be an integer from 2 to 16");
43: if (*pos == '-')
44: throw Exception("number.format", problem_source, problem_source ? "out of range (negative)" : "'%s' is out if range (negative)", str);
45:
46: T cutoff = max / base;
47: int cutoff_digit = (int)(max - cutoff * base);
48:
49: while(true) {
50: int digit;
51:
52: if ((*pos >= '0') && (*pos <= '9')) {
53: digit = (*pos - '0');
54: } else if (*pos >= 'a') {
55: digit = (*pos - 'a' + 10);
56: } else if (*pos >= 'A') {
57: digit = (*pos - 'A' + 10);
58: } else break;
59:
60: if (digit >= base) {
61: break;
62: }
63:
64: ++pos;
65:
66: /* adjust number, with overflow check */
67: if ((result > cutoff) || ((result == cutoff) && (digit > cutoff_digit))) {
68: throw Exception("number.format", problem_source, problem_source ? "out of range (int)" : "'%s' is out of range (int)", str);
69: } else {
70: result = result * base + digit;
71: }
72: }
73:
74: while(char c=*pos++)
75: if(!isspace(c))
76: throw Exception("number.format", problem_source, problem_source ? "invalid number (int)" : "'%s' is an invalid number (int)", str);
77:
78: return result;
79: }
80:
81: unsigned int pa_atoui(const char *str, int base, const String* problem_source){
82: if(!str)
83: return 0;
84:
85: return pa_ato_any<unsigned int>(str, base, problem_source, UINT_MAX);
86: }
87:
88: uint64_t pa_atoul(const char *str, int base, const String* problem_source){
89: if(!str)
90: return 0;
91:
92: return pa_ato_any<uint64_t>(str, base, problem_source, ULLONG_MAX);
93: }
94:
95: int pa_atoi(const char* str, int base, const String* problem_source) {
96: if(!str)
97: return 0;
98:
99: while(isspace(*str))
100: str++;
101:
102: if(!*str)
103: return 0;
104:
105: const char *str_copy=str;
106: bool negative=false;
107: if(str[0]=='-') {
108: negative=true;
109: str++;
110: if(!*str || isspace(*str))
111: throw Exception("number.format", problem_source, problem_source ? "invalid number (int)" : "'%s' is an invalid number (int)", str_copy);
112: } else if(str[0]=='+') {
113: str++;
114: if(!*str || isspace(*str))
115: throw Exception("number.format", problem_source, problem_source ? "invalid number (int)" : "'%s' is an invalid number (int)", str_copy);
116: }
117:
118: if(negative){
119: const uint min_abs = (uint)(-( (uint)INT_MIN + 1 )) + 1;
120: uint result=pa_ato_any<uint>(str, base, problem_source, min_abs);
121: if(result==min_abs) return INT_MIN;
122: return -(int)result;
123: } else {
124: return (int)pa_ato_any<uint>(str, base, problem_source, INT_MAX);
125: }
126: }
127:
128: pa_wint pa_atowi(const char* str, int base, const String* problem_source) {
129: if(!str)
130: return 0;
131:
132: while(isspace(*str))
133: str++;
134:
135: if(!*str)
136: return 0;
137:
138: const char *str_copy=str;
139: bool negative=false;
140: if(str[0]=='-') {
141: negative=true;
142: str++;
143: if(!*str || isspace(*str))
144: throw Exception("number.format", problem_source, problem_source ? "invalid number (int)" : "'%s' is an invalid number (int)", str_copy);
145: } else if(str[0]=='+') {
146: str++;
147: if(!*str || isspace(*str))
148: throw Exception("number.format", problem_source, problem_source ? "invalid number (int)" : "'%s' is an invalid number (int)", str_copy);
149: }
150:
151: if(negative){
152: const pa_uwint min_abs = (pa_uwint)(-( (pa_wint)PA_WINT_MIN + 1 )) + 1;
153: pa_uwint result=pa_ato_any<pa_uwint>(str, base, problem_source, min_abs);
154: if(result==min_abs) return PA_WINT_MIN;
155: return -(pa_wint)result;
156: } else {
157: return (pa_wint)pa_ato_any<pa_uwint>(str, base, problem_source, PA_WINT_MAX);
158: }
159: }
160:
161: double pa_atod(const char* str, const String* problem_source /* never null */) {
162: if(!str)
163: return 0;
164:
165: while(isspace(*str))
166: str++;
167:
168: if(!*str)
169: return 0;
170:
171: bool negative=false;
172: if(str[0]=='-') {
173: negative=true;
174: str++;
175: if(!*str || isspace(*str))
176: throw Exception("number.format", problem_source, "invalid number (double)");
177: } else if(str[0]=='+') {
178: str++;
179: if(!*str || isspace(*str))
180: throw Exception("number.format", problem_source, "invalid number (double)");
181: }
182:
183: double result;
184: if(str[0]=='0') {
185: if(str[1]=='x' || str[1]=='X') {
186: // 0xABC
187: result=(double)pa_atoul(str, 0, problem_source);
188: return negative ? -result : result;
189: } else {
190: // skip leading 0000, to disable octal interpretation
191: do str++; while(*str=='0');
192: }
193: }
194:
195: char *error_pos;
196: result=strtod(str, &error_pos);
197:
198: while(const char c=*error_pos++)
199: if(!isspace(c))
200: throw Exception("number.format", problem_source, "invalid number (double)");
201:
202: return negative ? -result : result;
203: }
204: