blob: 97e6465618f63ca3fee412196c684c860d9be2e1 [file] [log] [blame]
Lev Walkinda997b12017-08-04 01:38:41 -07001#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 */
23enum 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
32static enum strtox_result_e
33strtoaint_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
91int
92asn1p_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
101const 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) {
Lev Walkin7268f332017-08-05 18:20:52 -0700105 assert((size_t)ret < sizeof(static_buf));
Lev Walkinda997b12017-08-04 01:38:41 -0700106 return static_buf;
107 } else {
108 return NULL;
109 }
110}
111
112int 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);
Lev Walkin2888f6c2017-08-05 23:49:03 -0700117 if(ret < 0 || (size_t)ret >= size) {
Lev Walkinda997b12017-08-04 01:38:41 -0700118 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);
Lev Walkin2888f6c2017-08-05 23:49:03 -0700158 if(ret < 0 || (size_t)ret >= size) {
159 assert(ret > 0 && (size_t)ret < sizeof(tmp_buf));
Lev Walkinda997b12017-08-04 01:38:41 -0700160 return -1;
Lev Walkin2888f6c2017-08-05 23:49:03 -0700161 }
Lev Walkinda997b12017-08-04 01:38:41 -0700162 memcpy(buf, tmp_buf, ret);
163 buf[ret] = '\0';
164 return ret;
165}
166