]> git.draconx.ca Git - cdecl99.git/blob - src/intconv.h
libcdecl: Implement portable alternative to strtoumax.
[cdecl99.git] / src / intconv.h
1 /*
2  * Copyright © 2023 Nick Bowler
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
16  */
17
18 #ifndef CDECL99_INTCONV_H_
19 #define CDECL99_INTCONV_H_
20
21 #include <string.h>
22 #include <stdbool.h>
23 #include <inttypes.h>
24
25 enum { INTCONV_OCTAL = 8, INTCONV_DECIMAL = 10, INTCONV_HEXADECIMAL = 16 };
26
27 /*
28  * Multiply *v by base, which must be one of the above enumeration constants,
29  * and add digit, updating *v with the result.
30  *
31  * If the result does not fit in uintmax_t, then 0 is returned.  Otherwise,
32  * a non-zero result is returned.
33  */
34 static inline bool intconv_shift(uintmax_t *v, unsigned base, unsigned digit)
35 {
36         uintmax_t old_v = *v;
37
38         if (old_v > (uintmax_t)-1 / base)
39                 return false;
40         old_v *= base;
41
42         return (*v = old_v + digit) >= old_v;
43 }
44
45 /*
46  * Assuming d is a hexadecimal digit character (converted to unsigned char),
47  * return the corresponding value of that digit (between 0 and 15, inclusive).
48  */
49 static inline unsigned char intconv_digit(unsigned char d)
50 {
51         if (d >= '0' && d <= '9') {
52                 return d - '0';
53         } else {
54 #if ('A' & 7) == 1 && ('B' & 7) == 2 && ('C' & 7) == 3 \
55  && ('a' & 7) == 1 && ('b' & 7) == 2 && ('c' & 7) == 3 \
56  && ('D' & 7) == 4 && ('E' & 7) == 5 && ('F' & 7) == 6 \
57  && ('d' & 7) == 4 && ('e' & 7) == 5 && ('f' & 7) == 6
58                 /* EBCDIC or ASCII-like encoding */
59                 return 9 + (d & 7);
60 #else
61                 /* Something else */
62                 static const char idx[] = "abcdef..ABCDEF";
63                 return 10 + (((char *)memchr(idx, d, sizeof idx) - idx) & 7);
64 #endif
65         }
66 }
67
68 #endif