| #include <stdio.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <limits.h> |
| #include <assert.h> |
| #include <errno.h> |
| |
| #include "asn1p_integer.h" |
| |
| #define ASN_INTEGER_MAX \ |
| (~((asn1c_integer_t)0) \ |
| & ~((asn1c_integer_t)1 << (8 * sizeof(asn1c_integer_t) - 1))) |
| #define ASN_INTEGER_MIN (-(ASN_INTEGER_MAX)-1) |
| |
| /* |
| * Parse the number in the given string until the given *end position, |
| * returning the position after the last parsed character back using the |
| * same (*end) pointer. |
| * WARNING: This behavior is different from the standard strtol/strtoimax(3). |
| */ |
| enum strtox_result_e { |
| STRTOX_ERROR_RANGE = -3, /* Input outside of supported numeric range */ |
| STRTOX_ERROR_INVAL = -2, /* Invalid data encountered (e.g., "+-") */ |
| STRTOX_EXPECT_MORE = -1, /* More data expected (e.g. "+") */ |
| STRTOX_OK = 0, /* Conversion succeded, number ends at (*end) */ |
| STRTOX_EXTRA_DATA = |
| 1 /* Conversion succeded, but the string has extra stuff */ |
| }; |
| |
| static enum strtox_result_e |
| strtoaint_lim(const char *str, const char **end, asn1c_integer_t *intp) { |
| const asn1c_integer_t upper_boundary = ASN_INTEGER_MAX / 10; |
| int last_digit_max = ASN_INTEGER_MAX % 10; |
| int sign = 1; |
| asn1c_integer_t value; |
| |
| if(str >= *end) return STRTOX_ERROR_INVAL; |
| |
| switch(*str) { |
| case '-': |
| last_digit_max++; |
| sign = -1; |
| /* FALL THROUGH */ |
| case '+': |
| str++; |
| if(str >= *end) { |
| *end = str; |
| return STRTOX_EXPECT_MORE; |
| } |
| } |
| |
| for(value = 0; str < (*end); str++) { |
| switch(*str) { |
| case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: |
| case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: { |
| int d = *str - '0'; |
| if(value < upper_boundary) { |
| value = value * 10 + d; |
| } else if(value == upper_boundary) { |
| if(d <= last_digit_max) { |
| if(sign > 0) { |
| value = value * 10 + d; |
| } else { |
| sign = 1; |
| value = -value * 10 - d; |
| } |
| } else { |
| *end = str; |
| return STRTOX_ERROR_RANGE; |
| } |
| } else { |
| *end = str; |
| return STRTOX_ERROR_RANGE; |
| } |
| } |
| continue; |
| default: |
| *end = str; |
| *intp = sign * value; |
| return STRTOX_EXTRA_DATA; |
| } |
| } |
| |
| *end = str; |
| *intp = sign * value; |
| return STRTOX_OK; |
| } |
| |
| int |
| asn1p_atoi(const char *ptr, asn1c_integer_t *value) { |
| const char *end = ptr + strlen(ptr); |
| if(strtoaint_lim(ptr, &end, value) == STRTOX_OK) { |
| return 0; |
| } else { |
| return -1; |
| } |
| } |
| |
| const char *asn1p_itoa(asn1c_integer_t v) { |
| static char static_buf[128]; |
| int ret = asn1p_itoa_s(static_buf, sizeof(static_buf), v); |
| if(ret > 0) { |
| assert((size_t)ret < sizeof(static_buf)); |
| return static_buf; |
| } else { |
| return NULL; |
| } |
| } |
| |
| int asn1p_itoa_s(char *buf, size_t size, asn1c_integer_t v) { |
| char tmp_buf[128]; |
| |
| if(v >= LONG_MIN && v < LONG_MAX) { |
| int ret = snprintf(buf, size, "%ld", (long)v); |
| if(ret < 0 || (size_t)ret >= size) { |
| return -1; |
| } |
| return ret; |
| } |
| |
| int sign = 0; |
| if(v < 0) { |
| if(v == ASN_INTEGER_MIN) { |
| switch(sizeof(v)) { |
| case 16: |
| if(size < 41) |
| return -1; |
| memcpy(buf, "-170141183460469231731687303715884105729", 41); |
| return 41; |
| case 8: |
| if(size < 21) |
| return -1; |
| memcpy(buf, "-9223372036854775809", 21); |
| return 21; |
| default: |
| assert(!"unreachable"); |
| } |
| } |
| |
| sign = -1; |
| v = -v; /* Ditch the sign */ |
| } |
| |
| assert(v > 1000000000L); |
| char restbuf[10] = "000000000\0"; |
| const char *rest = asn1p_itoa((long)(v % 1000000000L)); |
| size_t restlen = strlen(rest); |
| assert(restlen <= 9); |
| memcpy(restbuf + (9 - restlen), rest, restlen); |
| rest = restbuf; |
| |
| const char *head = asn1p_itoa(v / 1000000000L); |
| assert(head); |
| int ret = snprintf(tmp_buf, sizeof(tmp_buf), "%s%s%s", sign ? "-" : "", |
| head, rest); |
| if(ret < 0 || (size_t)ret >= size) { |
| assert(ret > 0 && (size_t)ret < sizeof(tmp_buf)); |
| return -1; |
| } |
| memcpy(buf, tmp_buf, ret); |
| buf[ret] = '\0'; |
| return ret; |
| } |
| |