64d39db6f71d243e05a2255d15115e67b802b5cf
[com/asn1c.git] / libasn1parser / asn1p_integer.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <limits.h>
7 #include <assert.h>
8 #include <errno.h>
9
10 #include "asn1p_integer.h"
11
12 #define ASN_INTEGER_MAX    \
13     (~((asn1c_integer_t)0) \
14      & ~((asn1c_integer_t)1 << (8 * sizeof(asn1c_integer_t) - 1)))
15 #define ASN_INTEGER_MIN (-(ASN_INTEGER_MAX)-1)
16
17 /*
18  * Parse the number in the given string until the given *end position,
19  * returning the position after the last parsed character back using the
20  * same (*end) pointer.
21  * WARNING: This behavior is different from the standard strtol/strtoimax(3).
22  */
23 enum strtox_result_e {
24     STRTOX_ERROR_RANGE = -3, /* Input outside of supported numeric range */
25     STRTOX_ERROR_INVAL = -2, /* Invalid data encountered (e.g., "+-") */
26     STRTOX_EXPECT_MORE = -1, /* More data expected (e.g. "+") */
27     STRTOX_OK = 0,           /* Conversion succeded, number ends at (*end) */
28     STRTOX_EXTRA_DATA =
29         1 /* Conversion succeded, but the string has extra stuff */
30 };
31
32 static enum strtox_result_e
33 strtoaint_lim(const char *str, const char **end, asn1c_integer_t *intp) {
34         const asn1c_integer_t upper_boundary = ASN_INTEGER_MAX / 10;
35         int last_digit_max = ASN_INTEGER_MAX % 10;
36         int sign = 1;
37         asn1c_integer_t value;
38
39         if(str >= *end) return STRTOX_ERROR_INVAL;
40
41         switch(*str) {
42         case '-':
43                 last_digit_max++;
44                 sign = -1;
45                 /* FALL THROUGH */
46         case '+':
47                 str++;
48                 if(str >= *end) {
49                         *end = str;
50                         return STRTOX_EXPECT_MORE;
51                 }
52         }
53
54         for(value = 0; str < (*end); str++) {
55                 switch(*str) {
56                 case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
57                 case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: {
58                         int d = *str - '0';
59                         if(value < upper_boundary) {
60                                 value = value * 10 + d;
61                         } else if(value == upper_boundary) {
62                                 if(d <= last_digit_max) {
63                                         if(sign > 0) {
64                                                 value = value * 10 + d;
65                                         } else {
66                                                 sign = 1;
67                                                 value = -value * 10 - d;
68                                         }
69                                 } else {
70                                         *end = str;
71                                         return STRTOX_ERROR_RANGE;
72                                 }
73                         } else {
74                                 *end = str;
75                                 return STRTOX_ERROR_RANGE;
76                         }
77                     }
78                     continue;
79                 default:
80                     *end = str;
81                     *intp = sign * value;
82                     return STRTOX_EXTRA_DATA;
83                 }
84         }
85
86         *end = str;
87         *intp = sign * value;
88         return STRTOX_OK;
89 }
90
91 int
92 asn1p_atoi(const char *ptr, asn1c_integer_t *value) {
93     const char *end = ptr + strlen(ptr);
94     if(strtoaint_lim(ptr, &end, value) == STRTOX_OK) {
95         return 0;
96     } else {
97         return -1;
98     }
99 }
100
101 const char *asn1p_itoa(asn1c_integer_t v) {
102     static char static_buf[128];
103     int ret = asn1p_itoa_s(static_buf, sizeof(static_buf), v);
104     if(ret > 0) {
105         assert((size_t)ret < sizeof(static_buf));
106         return static_buf;
107     } else {
108         return NULL;
109     }
110 }
111
112 int asn1p_itoa_s(char *buf, size_t size, asn1c_integer_t v) {
113     char tmp_buf[128];
114
115     if(v >= LONG_MIN && v < LONG_MAX) {
116         int ret = snprintf(buf, size, "%ld", (long)v);
117         if(ret < 0 || (size_t)ret >= size) {
118             return -1;
119         }
120         return ret;
121     }
122
123     int sign = 0;
124     if(v < 0) {
125         if(v == ASN_INTEGER_MIN) {
126             switch(sizeof(v)) {
127             case 16:
128                 if(size < 41)
129                     return -1;
130                 memcpy(buf, "-170141183460469231731687303715884105729", 41);
131                 return 41;
132             case 8:
133                 if(size < 21)
134                     return -1;
135                 memcpy(buf, "-9223372036854775809", 21);
136                 return 21;
137             default:
138                 assert(!"unreachable");
139             }
140         }
141
142         sign = -1;
143         v = -v; /* Ditch the sign */
144     }
145
146     assert(v > 1000000000L);
147     char restbuf[10] = "000000000\0";
148     const char *rest = asn1p_itoa((long)(v % 1000000000L));
149     size_t restlen = strlen(rest);
150     assert(restlen <= 9);
151     memcpy(restbuf + (9 - restlen), rest, restlen);
152     rest = restbuf;
153
154     const char *head = asn1p_itoa(v / 1000000000L);
155     assert(head);
156     int ret = snprintf(tmp_buf, sizeof(tmp_buf), "%s%s%s", sign ? "-" : "",
157                        head, rest);
158     if(ret < 0 || (size_t)ret >= size) {
159         assert(ret > 0 && (size_t)ret < sizeof(tmp_buf));
160         return -1;
161     }
162     memcpy(buf, tmp_buf, ret);
163     buf[ret] = '\0';
164     return ret;
165 }
166
167 abuf *
168 asn1p_integer_as_INTEGER(asn1c_integer_t value) {
169     abuf *ab = abuf_new();
170     uint8_t buf[sizeof(value) + 1];
171     uint8_t *bp = buf;
172
173     do {
174         *bp++ = value;
175         value >>= 8;
176     } while(!((value == 0 && !(bp[-1] & 0x80))
177               || (value == -1 && (bp[-1] & 0x80))));
178
179     abuf_printf(ab, "{ (uint8_t *)\"");
180
181     for(const uint8_t *p = bp-1; p >= buf; p--) {
182         abuf_printf(ab, "\\x%02x", *p);
183     }
184
185     abuf_printf(ab, "\\0\", %zu }", bp - buf);
186
187     return ab;
188 }