1 /*@ Implementation of icodec.h: idec.
2 *
3 * Copyright (c) 2017 - 2020 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
4 * SPDX-License-Identifier: ISC
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 #undef su_FILE
19 #define su_FILE su_icodec_dec
20 #define su_SOURCE
21 #define su_SOURCE_ICODEC_DEC
22
23 #include "su/code.h"
24
25 #include "su/cs.h"
26
27 #include "su/icodec.h"
28 #include "su/code-in.h"
29
30 static u8 const a_icod_atoi[256] = {
31 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
32 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
33 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
34 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
35 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x01,
36 0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0xFF,0xFF,
37 0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,
38 0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,
39 0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,
40 0x23,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0A,0x0B,0x0C,
41 0x0D,0x0E,0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,
42 0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,0x20,
43 0x21,0x22,0x23,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
44 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
45 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
46 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
47 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
48 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
49 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
50 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
51 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
52 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
53 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
54 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
55 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
56 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
57 };
58
59 #undef a_X
60 #define a_X(X) (U64_MAX / (X))
61 static u64 const a_icod_cutlimit[35] = {
62 a_X( 2), a_X( 3), a_X( 4), a_X( 5), a_X( 6), a_X( 7), a_X( 8),
63 a_X( 9), a_X(10), a_X(11), a_X(12), a_X(13), a_X(14), a_X(15),
64 a_X(16), a_X(17), a_X(18), a_X(19), a_X(20), a_X(21), a_X(22),
65 a_X(23), a_X(24), a_X(25), a_X(26), a_X(27), a_X(28), a_X(29),
66 a_X(30), a_X(31), a_X(32), a_X(33), a_X(34), a_X(35), a_X(36)
67 };
68 #undef a_X
69
70 u32
su_idec(void * resp,char const * cbuf,uz clen,u8 base,u32 idec_mode,char const ** endptr_or_nil)71 su_idec(void *resp, char const *cbuf, uz clen, u8 base, u32 idec_mode,
72 char const **endptr_or_nil){
73 /* XXX Brute simple and */
74 u8 currc;
75 u64 res, cut;
76 u32 rv;
77 NYD_IN;
78 ASSERT(resp != NIL);
79 ASSERT_EXEC(cbuf != NIL || clen == 0, clen = 0);
80
81 idec_mode &= su__IDEC_MODE_MASK;
82 rv = su_IDEC_STATE_NONE | idec_mode;
83 res = 0;
84
85 if(clen == UZ_MAX){
86 if(*cbuf == '\0')
87 goto jeinval;
88 }else if(clen == 0)
89 goto jeinval;
90
91 ASSERT(base != 1 && base <= 36); /* XXX _RET_VAL! */
92 /*if(base == 1 || base > 36)
93 * goto jeinval;*/
94
95 jnumber_sign_rescan:
96 /* Leading WS */
97 while(su_cs_is_space(*cbuf))
98 if(*++cbuf == '\0' || --clen == 0)
99 goto jeinval;
100
101 /* Check sign */
102 switch(*cbuf){
103 case '-':
104 rv |= su_IDEC_STATE_SEEN_MINUS;
105 /* FALLTHROUGH */
106 case '+':
107 if(*++cbuf == '\0' || --clen == 0)
108 goto jeinval;
109 break;
110 }
111
112 /* Base detection/skip */
113 if(*cbuf != '0'){
114 if(base == 0){
115 base = 10;
116
117 /* Support BASE#number prefix, where BASE is decimal 2-36 XXX ASCII */
118 if(clen > 1){
119 char c1, c2, c3;
120
121 if(((c1 = cbuf[0]) >= '0' && c1 <= '9') &&
122 (((c2 = cbuf[1]) == '#') ||
123 (c2 >= '0' && c2 <= '9' && clen > 2 && cbuf[2] == '#'))){
124 base = a_icod_atoi[S(u8,c1)];
125 if(c2 == '#')
126 c3 = cbuf[2];
127 else{
128 c3 = cbuf[3];
129 base *= 10; /* xxx Inline atoi decimal base */
130 base += a_icod_atoi[S(u8,c2)];
131 }
132
133 /* We do not interpret this as BASE#number at all if either we
134 * did not get a valid base or if the first char is not valid
135 * according to base, to comply to the latest interpretation
136 * of "prefix", see comment for standard prefixes below */
137 if(base < 2 || base > 36 ||
138 (a_icod_atoi[S(u8,c3)] >= base &&
139 !(rv & su_IDEC_MODE_BASE0_NUMBER_SIGN_RESCAN)))
140 base = 10;
141 else{
142 if(c2 == '#')
143 clen -= 2, cbuf += 2;
144 else
145 clen -= 3, cbuf += 3;
146
147 if(rv & su_IDEC_MODE_BASE0_NUMBER_SIGN_RESCAN)
148 goto jnumber_sign_rescan;
149 }
150 }
151 }
152 }
153
154 /* Character must be valid for base */
155 currc = a_icod_atoi[S(u8,*cbuf)];
156 if(currc >= base)
157 goto jeinval;
158 }else{
159 /* 0 always valid as is, fallback base 10 */
160 if(*++cbuf == '\0' || --clen == 0)
161 goto jleave;
162
163 /* Base "detection" */
164 if(base == 0 || base == 2 || base == 16){
165 switch(*cbuf){
166 case 'x':
167 case 'X':
168 if((base & 2) == 0){
169 base = 0x10;
170 goto jprefix_skip;
171 }
172 break;
173 case 'b':
174 case 'B':
175 if((base & 16) == 0){
176 base = 2; /* 0b10 */
177 /* Char after prefix must be valid. However, after some error
178 * in the tor software all libraries (which had to) turned to
179 * an interpretation of the C standard which says that the
180 * prefix may optionally precede an otherwise valid sequence,
181 * which means that "0x" is not a STATE_INVAL error but gives
182 * a "0" result with a "STATE_BASE" error and a rest of "x" */
183 jprefix_skip:
184 #if 1
185 if(clen > 1 && a_icod_atoi[S(u8,cbuf[1])] < base)
186 --clen, ++cbuf;
187 #else
188 if(*++cbuf == '\0' || --clen == 0)
189 goto jeinval;
190
191 /* Character must be valid for base, invalid otherwise */
192 currc = a_icod_atoi[S(u8,*cbuf)];
193 if(currc >= base)
194 goto jeinval;
195 #endif
196 }
197 break;
198 default:
199 if(base == 0)
200 base = 010;
201 break;
202 }
203 }
204
205 /* Character must be valid for base, _EBASE otherwise */
206 currc = a_icod_atoi[S(u8,*cbuf)];
207 if(currc >= base)
208 goto jebase;
209 }
210
211 for(cut = a_icod_cutlimit[base - 2];;){
212 if(res >= cut){
213 if(res == cut){
214 res *= base;
215 if(res > U64_MAX - currc)
216 goto jeover;
217 res += currc;
218 }else
219 goto jeover;
220 }else{
221 res *= base;
222 res += currc;
223 }
224
225 if(*++cbuf == '\0' || --clen == 0)
226 break;
227
228 currc = a_icod_atoi[S(u8,*cbuf)];
229 if(currc >= base)
230 goto jebase;
231 }
232
233 jleave:
234 do{
235 u64 umask;
236
237 switch(rv & su__IDEC_MODE_LIMIT_MASK){
238 case su_IDEC_MODE_LIMIT_8BIT: umask = U8_MAX; break;
239 case su_IDEC_MODE_LIMIT_16BIT: umask = U16_MAX; break;
240 case su_IDEC_MODE_LIMIT_32BIT: umask = U32_MAX; break;
241 default: umask = U64_MAX; break;
242 }
243 if((rv & su_IDEC_MODE_SIGNED_TYPE) &&
244 (!(rv & su_IDEC_MODE_POW2BASE_UNSIGNED) || !IS_POW2(base)))
245 umask >>= 1;
246
247 if(res & ~umask){
248 if((rv & (su_IDEC_MODE_SIGNED_TYPE | su_IDEC_STATE_SEEN_MINUS)
249 ) == (su_IDEC_MODE_SIGNED_TYPE | su_IDEC_STATE_SEEN_MINUS)){
250 if(res > umask + 1){
251 res = umask << 1;
252 res &= ~umask;
253 }else{
254 res = -res;
255 break;
256 }
257 }else
258 res = umask;
259 rv |= su_IDEC_STATE_EOVERFLOW;
260 }else if(rv & su_IDEC_STATE_SEEN_MINUS)
261 res = -res;
262 }while(0);
263
264 switch(rv & su__IDEC_MODE_LIMIT_MASK){
265 case su_IDEC_MODE_LIMIT_8BIT:
266 if(rv & su_IDEC_MODE_SIGNED_TYPE)
267 *S(s8*,resp) = S(s8,res);
268 else
269 *S(u8*,resp) = S(u8,res);
270 break;
271 case su_IDEC_MODE_LIMIT_16BIT:
272 if(rv & su_IDEC_MODE_SIGNED_TYPE)
273 *S(s16*,resp) = S(s16,res);
274 else
275 *S(u16*,resp) = S(u16,res);
276 break;
277 case su_IDEC_MODE_LIMIT_32BIT:
278 if(rv & su_IDEC_MODE_SIGNED_TYPE)
279 *S(s32*,resp) = S(s32,res);
280 else
281 *S(u32*,resp) = S(u32,res);
282 break;
283 default:
284 if(rv & su_IDEC_MODE_SIGNED_TYPE)
285 *S(s64*,resp) = S(s64,res);
286 else
287 *S(u64*,resp) = S(u64,res);
288 break;
289 }
290 if(rv & su_IDEC_MODE_LIMIT_NOERROR)
291 rv &= ~su_IDEC_STATE_EOVERFLOW;
292
293 if(endptr_or_nil != NIL)
294 *endptr_or_nil = cbuf;
295 if(*cbuf == '\0' || clen == 0)
296 rv |= su_IDEC_STATE_CONSUMED;
297 NYD_OU;
298 return rv;
299
300 jeinval:
301 rv |= su_IDEC_STATE_EINVAL;
302 goto j_maxval;
303 jebase:
304 /* Not a base error for terminator and whitespace! */
305 if(*cbuf != '\0' && !su_cs_is_space(*cbuf))
306 rv |= su_IDEC_STATE_EBASE;
307 goto jleave;
308
309 jeover:
310 /* Overflow error: consume input until bad character or length out */
311 for(;;){
312 if(*++cbuf == '\0' || --clen == 0)
313 break;
314 currc = a_icod_atoi[S(u8,*cbuf)];
315 if(currc >= base)
316 break;
317 }
318
319 rv |= su_IDEC_STATE_EOVERFLOW;
320 j_maxval:
321 if(rv & su_IDEC_MODE_SIGNED_TYPE)
322 res = (rv & su_IDEC_STATE_SEEN_MINUS) ? S(u64,S64_MIN) : S(u64,S64_MAX);
323 else
324 res = U64_MAX;
325 rv &= ~su_IDEC_STATE_SEEN_MINUS;
326 goto jleave;
327 }
328
329 #include "su/code-ou.h"
330 /* s-it-mode */
331