comment non-obvious de bruijn sequence code in int parser
[musl] / src / internal / intparse.c
1 #include <stdint.h>
2 #include <limits.h>
3 #include <stdlib.h>
4 #include <errno.h>
5 #include "intparse.h"
6
7 /* Lookup table for digit values. -1==255>=36 -> invalid */
8 static const unsigned char digits[] = {
9 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
10 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
11 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
12  0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1,
13 -1,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,
14 25,26,27,28,29,30,31,32,33,34,35,-1,-1,-1,-1,-1,
15 -1,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,
16 25,26,27,28,29,30,31,32,33,34,35,-1,-1,-1,-1,-1,
17 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
18 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
19 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
20 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
21 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
22 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
23 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
24 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
25 };
26
27 #define SLIM (UINT_MAX/36-1)
28 #define LLIM (UINTMAX_MAX/36-1)
29
30 int __intparse(struct intparse *v, const void *buf, size_t n)
31 {
32         const unsigned char *s = buf;
33         int d, b = v->base;
34
35         v->cnt += n;
36         for (; n; n--, s++) switch (v->state) {
37         case 0:
38                 v->err = EINVAL;
39                 v->state++;
40                 if (*s=='+' || *s=='-') {
41                         v->neg = *s=='-';
42                         continue;
43                 }
44         case 1:
45                 v->state++;
46                 if (*s=='0' && (!b || b==16)) continue;
47                 if (!b) v->base = b = 10;
48                 v->state++;
49                 goto firstdigit;
50         case 2:
51                 v->state++;
52                 if ((!b || b==16) && (*s|32) == 'x') {
53                         v->err = 0;
54                         v->base = b = 16;
55                         continue;
56                 }
57                 if (!b) v->base = b = 8;
58                 goto seconddigit;
59         case 3:
60         firstdigit:
61                 if (digits[*s] >= b) {
62                         n++;
63                         goto finished;
64                 }
65         seconddigit:
66                 v->err = 0;
67                 v->state++;
68         case 4:
69                 if (b==10) {
70                         for (; n && *s-'0'<10U && v->small<=SLIM; n--, s++)
71                                 v->small = v->small * 10 + (*s-'0');
72                 } else if ((b&-b) == b) {
73                         /* Compute bitshift for power-of-two bases
74                          * using a De Bruijn B(2,3) sequence. */
75                         int bs = "\0\1\2\4\7\3\6\5"[(0x17*b)>>5&7];
76                         for (; n && (d=digits[*s])<b && v->small<=SLIM; n--, s++)
77                                 v->small = (v->small<<bs) + d;
78                 } else {
79                         for (; n && (d=digits[*s])<b && v->small<=SLIM; n--, s++)
80                                 v->small = v->small * b + d;
81                 }
82                 if (!n) return 1;
83                 v->state++;
84                 v->val = v->small;
85         case 5:
86                 for (; n && (d=digits[*s])<b && v->val<=LLIM; n--, s++)
87                         v->val = v->val * b + d;
88                 if (!n) return 1;
89                 if (d >= b) goto finished;
90                 if (v->val < (UINTMAX_MAX-d)/b)
91                         v->val = v->val * b + d;
92                 else
93                         v->err = ERANGE;
94                 v->state++;
95                 n--; s++;
96         case 6:
97                 if (n && digits[*s]<b) {
98                         v->err = ERANGE;
99                         v->val = UINTMAX_MAX;
100                         n--; s++;
101                         for (; n && digits[*s]<b; n--, s++);
102                 }
103                 if (!n) return 1;
104                 goto finished;
105         }
106         return 1;
107 finished:
108         v->cnt -= n;
109         return 0;
110 }