X-Git-Url: https://gerrit.o-ran-sc.org/r/gitweb?a=blobdiff_plain;ds=sidebyside;f=libasn1parser%2Fasn1p_integer.c;fp=libasn1parser%2Fasn1p_integer.c;h=64d39db6f71d243e05a2255d15115e67b802b5cf;hb=70ee6fc793ec8e828067a3569849b6c216054497;hp=0000000000000000000000000000000000000000;hpb=59f84608ec15c016958a6e0e0ddd813f376c0925;p=com%2Fasn1c.git diff --git a/libasn1parser/asn1p_integer.c b/libasn1parser/asn1p_integer.c new file mode 100644 index 0000000..64d39db --- /dev/null +++ b/libasn1parser/asn1p_integer.c @@ -0,0 +1,188 @@ +#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; +} + +abuf * +asn1p_integer_as_INTEGER(asn1c_integer_t value) { + abuf *ab = abuf_new(); + uint8_t buf[sizeof(value) + 1]; + uint8_t *bp = buf; + + do { + *bp++ = value; + value >>= 8; + } while(!((value == 0 && !(bp[-1] & 0x80)) + || (value == -1 && (bp[-1] & 0x80)))); + + abuf_printf(ab, "{ (uint8_t *)\""); + + for(const uint8_t *p = bp-1; p >= buf; p--) { + abuf_printf(ab, "\\x%02x", *p); + } + + abuf_printf(ab, "\\0\", %zu }", bp - buf); + + return ab; +}