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