#include #include #include #include #include #include #include #include #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; }