diff options
Diffstat (limited to 'libj2_str_to_j2u_sign.c')
| -rw-r--r-- | libj2_str_to_j2u_sign.c | 350 |
1 files changed, 350 insertions, 0 deletions
diff --git a/libj2_str_to_j2u_sign.c b/libj2_str_to_j2u_sign.c new file mode 100644 index 0000000..4ec0651 --- /dev/null +++ b/libj2_str_to_j2u_sign.c @@ -0,0 +1,350 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#include <errno.h> +#ifndef TEST + +/* TODO Add man page */ + + +static int +from_generic(const unsigned char *s, size_t slen, const unsigned char **end, + const intmax_t symbols[256], unsigned radix, struct libj2_j2u *a) +{ + const uintmax_t high_required_at = (UINTMAX_MAX - radix + 1U) / radix; + uintmax_t radix_pow[9]; + uintmax_t v; + size_t i, x; + + for (i = 0; i < slen; i++) + if (symbols[s[i]] < 0) + break; + slen = i; + *end = &s[slen]; + + for (;;) { + if (!slen) + return 0; + a->low *= (uintmax_t)radix; + a->low += (uintmax_t)symbols[*s++]; + slen--; + if (a->low > high_required_at) { + if (!slen) + return 0; + break; + } + } + + radix_pow[0] = 1U; + radix_pow[1] = (uintmax_t)radix; + radix_pow[2] = (uintmax_t)radix * radix_pow[1]; + radix_pow[3] = (uintmax_t)radix * radix_pow[2]; + radix_pow[4] = (uintmax_t)radix * radix_pow[3]; + radix_pow[5] = (uintmax_t)radix * radix_pow[4]; + radix_pow[6] = (uintmax_t)radix * radix_pow[5]; + radix_pow[7] = (uintmax_t)radix * radix_pow[6]; + radix_pow[8] = (uintmax_t)radix * radix_pow[7]; + + x = slen & 7U; + x = x ? x : 8U; + slen -= x; + if (libj2_j2u_mul_ju_overflow(a, radix_pow[x])) + return ERANGE; + v = 0; + switch (x) { + while (slen) { + slen -= 8U; + if (libj2_j2u_mul_ju_overflow(a, radix_pow[8])) + return ERANGE; + v = 0; + /* This isn't even a case, but GCC warns fall-through here */ + /* fall-through */ + case 8: + v += (uintmax_t)symbols[*s++] * radix_pow[7]; /* fall-through */ + case 7: + v += (uintmax_t)symbols[*s++] * radix_pow[6]; /* fall-through */ + case 6: + v += (uintmax_t)symbols[*s++] * radix_pow[5]; /* fall-through */ + case 5: + v += (uintmax_t)symbols[*s++] * radix_pow[4]; /* fall-through */ + case 4: + v += (uintmax_t)symbols[*s++] * radix_pow[3]; /* fall-through */ + case 3: + v += (uintmax_t)symbols[*s++] * radix_pow[2]; /* fall-through */ + case 2: + v += (uintmax_t)symbols[*s++] * radix_pow[1]; /* fall-through */ + case 1: + v += (uintmax_t)symbols[*s++]; + if (libj2_j2u_add_ju_overflow(a, v)) + return ERANGE; + } + } + + return 0; +} + + +static int +from_power_of_two(const unsigned char *s, size_t slen, const unsigned char **end, + const intmax_t symbols[256], unsigned radix, struct libj2_j2u *a) +{ + uintmax_t high_required_at; + uintmax_t v; + unsigned shift, u, shift_mul[9]; + size_t i, x; + + shift = 0; + for (u = radix; u >>= 1;) + shift += 1U; + + for (i = 0; i < slen; i++) + if (symbols[s[i]] < 0) + break; + slen = i; + *end = &s[slen]; + + high_required_at = (uintmax_t)1 << (LIBJ2_JU_BIT - shift); + for (;;) { + if (!slen) + return 0; + a->low <<= shift; + a->low |= (uintmax_t)symbols[*s++]; + slen--; + if (a->low >= high_required_at) { + if (!slen) + return 0; + break; + } + } + + shift_mul[0] = 0U; + shift_mul[1] = shift_mul[0] + shift; + shift_mul[2] = shift_mul[1] + shift; + shift_mul[3] = shift_mul[2] + shift; + shift_mul[4] = shift_mul[3] + shift; + shift_mul[5] = shift_mul[4] + shift; + shift_mul[6] = shift_mul[5] + shift; + shift_mul[7] = shift_mul[6] + shift; + shift_mul[8] = shift_mul[7] + shift; + + x = slen & 7U; + x = x ? x : 8U; + slen -= x; + if (libj2_j2u_lsh_overflow(a, shift_mul[x])) + return ERANGE; + v = 0; + switch (x) { + while (slen) { + slen -= 8U; + if (libj2_j2u_lsh_overflow(a, shift_mul[8])) + return ERANGE; + v = 0; + /* This isn't even a case, but GCC warns fall-through here */ + /* fall-through */ + case 8: + v |= (uintmax_t)symbols[*s++] << shift_mul[7]; /* fall-through */ + case 7: + v |= (uintmax_t)symbols[*s++] << shift_mul[6]; /* fall-through */ + case 6: + v |= (uintmax_t)symbols[*s++] << shift_mul[5]; /* fall-through */ + case 5: + v |= (uintmax_t)symbols[*s++] << shift_mul[4]; /* fall-through */ + case 4: + v |= (uintmax_t)symbols[*s++] << shift_mul[3]; /* fall-through */ + case 3: + v |= (uintmax_t)symbols[*s++] << shift_mul[2]; /* fall-through */ + case 2: + v |= (uintmax_t)symbols[*s++] << shift_mul[1]; /* fall-through */ + case 1: + v |= (uintmax_t)symbols[*s++]; + libj2_j2u_or_ju(a, v); + } + } + + return 0; +} + + +int +libj2_str_to_j2u_sign(const char *str, size_t slen, char **end, const char *digits1, + const char *digits2, struct libj2_j2u *a, int *negative) +{ + const unsigned char *s = (const unsigned char *)str; + intmax_t symbols[256]; + unsigned i, radix = 0; + struct libj2_j2u a_discard; + char *end_discard; + int r; + + if (negative) + *negative = 0; + + if (!end) + end = &end_discard; + if (!a) + a = &a_discard; + libj2_j2u_zero(a); + + if (!s) + slen = 0; + if (!digits1) { + digits1 = "0123456789"; + if (digits2) + goto einval; + } + + for (i = 0; i < sizeof(symbols) / sizeof(*symbols); i++) + symbols[i] = -2; + symbols[' '] = -1; + symbols['\r'] = -1; + symbols['\t'] = -1; + symbols['\f'] = -1; + symbols['\v'] = -1; + symbols['\n'] = -1; + symbols['+'] = -1; + if (negative) + symbols['-'] = -3; + while (digits1[radix]) { + if (symbols[(unsigned char)digits1[radix]] >= 0) + goto einval; + symbols[(unsigned char)digits1[radix]] = (intmax_t)radix; + radix++; + } + + if (digits2) { + for (i = 0; digits2[i]; i++) { + if (symbols[(unsigned char)digits2[i]] >= 256) + goto einval; + else if (symbols[(unsigned char)digits2[i]] >= 0) + if (symbols[(unsigned char)digits2[i]] != (intmax_t)i) + goto einval; + symbols[(unsigned char)digits2[i]] = (intmax_t)(i | 256U); + } + if (i != radix) + goto einval; + for (i = 0; digits2[i]; i++) + symbols[(unsigned char)digits2[i]] &= 255; + } + + if (radix < 2) { + einval: + *(const unsigned char **)(void *)end = s; + return EINVAL; + } + + for (;; slen--, s++) { + if (!slen) + goto einval; + if (symbols[*s] == -3) { + *negative ^= 1; + } else if (symbols[*s] != -1) { + if (symbols[*s] >= 0) + break; + goto einval; + } + } + if (symbols[(unsigned char)'-'] == -3) + symbols[(unsigned char)'-'] = -1; + + while (slen && symbols[*s] == 0) { + s++; + slen--; + } + + if ((radix & (radix - 1U)) == 0U) + r = from_power_of_two(s, slen, (void *)end, symbols, radix, a); + else + r = from_generic(s, slen, (void *)end, symbols, radix, a); + if (r == ERANGE) + libj2_j2u_max(a); + return r; +} + + +#else + +int +main(void) +{ + /* Primarily tested via libj2_str_to_j2u */ + + struct libj2_j2u a; + int neg; + + EXPECT(libj2_str_to_j2u_sign("-1", SIZE_MAX, NULL, NULL, NULL, &a, NULL) == EINVAL); + + a = (struct libj2_j2u){111, 222}; + neg = 333; + EXPECT(libj2_str_to_j2u_sign("1", SIZE_MAX, NULL, NULL, NULL, &a, &neg) == 0); + EXPECT(neg == 0); + EXPECT(libj2_j2u_eq_ju(&a, 1)); + + a = (struct libj2_j2u){111, 222}; + neg = 333; + EXPECT(libj2_str_to_j2u_sign("-1", SIZE_MAX, NULL, NULL, NULL, &a, &neg) == 0); + EXPECT(neg == 1); + EXPECT(libj2_j2u_eq_ju(&a, 1)); + + a = (struct libj2_j2u){111, 222}; + neg = 333; + EXPECT(libj2_str_to_j2u_sign("0", SIZE_MAX, NULL, NULL, NULL, &a, &neg) == 0); + EXPECT(neg == 0); + EXPECT(libj2_j2u_eq_ju(&a, 0)); + + a = (struct libj2_j2u){111, 222}; + neg = 333; + EXPECT(libj2_str_to_j2u_sign("-0", SIZE_MAX, NULL, NULL, NULL, &a, &neg) == 0); + EXPECT(neg == 1); + EXPECT(libj2_j2u_eq_ju(&a, 0)); + + a = (struct libj2_j2u){111, 222}; + neg = 333; + EXPECT(libj2_str_to_j2u_sign("--1", SIZE_MAX, NULL, NULL, NULL, &a, &neg) == 0); + EXPECT(neg == 0); + EXPECT(libj2_j2u_eq_ju(&a, 1)); + + a = (struct libj2_j2u){111, 222}; + neg = 333; + EXPECT(libj2_str_to_j2u_sign("---1", SIZE_MAX, NULL, NULL, NULL, &a, &neg) == 0); + EXPECT(neg == 1); + EXPECT(libj2_j2u_eq_ju(&a, 1)); + + a = (struct libj2_j2u){111, 222}; + neg = 333; + EXPECT(libj2_str_to_j2u_sign(" - - - 123", SIZE_MAX, NULL, NULL, NULL, &a, &neg) == 0); + EXPECT(neg == 1); + EXPECT(libj2_j2u_eq_ju(&a, 123)); + + a = (struct libj2_j2u){111, 222}; + neg = 333; + EXPECT(libj2_str_to_j2u_sign("-", SIZE_MAX, NULL, "*+-", NULL, &a, &neg) == 0); + EXPECT(neg == 0); + EXPECT(libj2_j2u_eq_ju(&a, 2)); + + a = (struct libj2_j2u){111, 222}; + neg = 333; + EXPECT(libj2_str_to_j2u_sign("-+1", SIZE_MAX, NULL, NULL, NULL, &a, &neg) == 0); + EXPECT(neg == 1); + EXPECT(libj2_j2u_eq_ju(&a, 1)); + + a = (struct libj2_j2u){111, 222}; + neg = 333; + EXPECT(libj2_str_to_j2u_sign("+-1", SIZE_MAX, NULL, NULL, NULL, &a, &neg) == 0); + EXPECT(neg == 1); + EXPECT(libj2_j2u_eq_ju(&a, 1)); + + a = (struct libj2_j2u){111, 222}; + neg = 333; + EXPECT(libj2_str_to_j2u_sign("- 1", SIZE_MAX, NULL, NULL, NULL, &a, &neg) == 0); + EXPECT(neg == 1); + EXPECT(libj2_j2u_eq_ju(&a, 1)); + + a = (struct libj2_j2u){111, 222}; + neg = 333; + EXPECT(libj2_str_to_j2u_sign(" -1", SIZE_MAX, NULL, NULL, NULL, &a, &neg) == 0); + EXPECT(neg == 1); + EXPECT(libj2_j2u_eq_ju(&a, 1)); + + return 0; +} + +#endif |
