aboutsummaryrefslogtreecommitdiffstats
path: root/libj2_str_to_j2u.c
diff options
context:
space:
mode:
Diffstat (limited to 'libj2_str_to_j2u.c')
-rw-r--r--libj2_str_to_j2u.c455
1 files changed, 455 insertions, 0 deletions
diff --git a/libj2_str_to_j2u.c b/libj2_str_to_j2u.c
new file mode 100644
index 0000000..b7a1bc0
--- /dev/null
+++ b/libj2_str_to_j2u.c
@@ -0,0 +1,455 @@
+/* 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(const char *str, size_t slen, char **end, const char *digits1,
+ const char *digits2, struct libj2_j2u *a)
+{
+ 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 (!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;
+ 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;
+ 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] != -1) {
+ if (symbols[*s] >= 0)
+ break;
+ goto einval;
+ }
+ }
+
+ 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
+
+CONST int
+main(void)
+{
+ /* Primarily tested in libj2_j2u_to_str.c */
+
+ char buf[sizeof(uintmax_t) * 8U];
+ struct libj2_j2u a;
+ char *end;
+
+ a = (struct libj2_j2u){111, 222};
+ EXPECT(libj2_str_to_j2u("10000", SIZE_MAX, NULL, NULL, NULL, &a) == 0);
+ EXPECT(libj2_j2u_eq_ju(&a, 10000));
+
+ a = (struct libj2_j2u){111, 222};
+ EXPECT(libj2_str_to_j2u("10000", 6, NULL, NULL, NULL, &a) == 0);
+ EXPECT(libj2_j2u_eq_ju(&a, 10000));
+
+ a = (struct libj2_j2u){111, 222};
+ EXPECT(libj2_str_to_j2u("10000", 5, NULL, NULL, NULL, &a) == 0);
+ EXPECT(libj2_j2u_eq_ju(&a, 10000));
+
+ a = (struct libj2_j2u){111, 222};
+ EXPECT(libj2_str_to_j2u("10000", 4, NULL, NULL, NULL, &a) == 0);
+ EXPECT(libj2_j2u_eq_ju(&a, 1000));
+
+ a = (struct libj2_j2u){111, 222};
+ end = NULL;
+ EXPECT(libj2_str_to_j2u("10000", 4, &end, NULL, "0123456789", &a) == EINVAL);
+ EXPECT(libj2_j2u_is_zero(&a));
+ EXPECT(end);
+ EXPECT(!strcmp(end, "10000"));
+
+ a = (struct libj2_j2u){111, 222};
+ EXPECT(libj2_str_to_j2u("10000", 3, NULL, NULL, NULL, &a) == 0);
+ EXPECT(libj2_j2u_eq_ju(&a, 100));
+
+ a = (struct libj2_j2u){111, 222};
+ EXPECT(libj2_str_to_j2u("10000", 2, NULL, NULL, NULL, &a) == 0);
+ EXPECT(libj2_j2u_eq_ju(&a, 10));
+
+ a = (struct libj2_j2u){111, 222};
+ EXPECT(libj2_str_to_j2u("10000", 1, NULL, NULL, NULL, &a) == 0);
+ EXPECT(libj2_j2u_eq_ju(&a, 1));
+
+ a = (struct libj2_j2u){111, 222};
+ EXPECT(libj2_str_to_j2u("10000", 0, NULL, NULL, NULL, &a) == EINVAL);
+ EXPECT(libj2_j2u_is_zero(&a));
+
+ a = (struct libj2_j2u){111, 222};
+ end = NULL;
+ EXPECT(libj2_str_to_j2u(" \r\n\v+\t\f + 10000", SIZE_MAX, &end, NULL, NULL, &a) == 0);
+ EXPECT(libj2_j2u_eq_ju(&a, 10000));
+ EXPECT(end);
+ EXPECT(!*end);
+
+ a = (struct libj2_j2u){111, 222};
+ end = NULL;
+ EXPECT(libj2_str_to_j2u(" \r\n\v+\t\f + 10000 0", SIZE_MAX, &end, NULL, NULL, &a) == 0);
+ EXPECT(libj2_j2u_eq_ju(&a, 10000));
+ EXPECT(end);
+ EXPECT(!strcmp(end, " 0"));
+
+ a = (struct libj2_j2u){111, 222};
+ end = NULL;
+ EXPECT(libj2_str_to_j2u(" \r\n\v+\t\f + 10000x0", SIZE_MAX, &end, NULL, NULL, &a) == 0);
+ EXPECT(libj2_j2u_eq_ju(&a, 10000));
+ EXPECT(end);
+ EXPECT(!strcmp(end, "x0"));
+
+ a = (struct libj2_j2u){111, 222};
+ end = NULL;
+ EXPECT(libj2_str_to_j2u(" +-- ", SIZE_MAX, &end, "-+", NULL, &a) == 0);
+ EXPECT(libj2_j2u_eq_ju(&a, 4));
+ EXPECT(end);
+ EXPECT(!strcmp(end, " "));
+
+ a = (struct libj2_j2u){111, 222};
+ end = NULL;
+ EXPECT(libj2_str_to_j2u(" +-- ", SIZE_MAX, &end, "- +", NULL, &a) == 0);
+ EXPECT(libj2_j2u_eq_ju(&a, 136));
+ EXPECT(end);
+ EXPECT(!*end);
+
+ a = (struct libj2_j2u){111, 222};
+ end = NULL;
+ EXPECT(libj2_str_to_j2u("ba", SIZE_MAX, &end, "ab", "ab", &a) == 0);
+ EXPECT(libj2_j2u_eq_ju(&a, 2));
+ EXPECT(end);
+ EXPECT(!*end);
+
+ a = (struct libj2_j2u){111, 222};
+ end = NULL;
+ EXPECT(libj2_str_to_j2u("ba", SIZE_MAX, &end, "Ab", "Ab", &a) == 0);
+ EXPECT(libj2_j2u_eq_ju(&a, 1));
+ EXPECT(end);
+ EXPECT(*end == 'a');
+
+ a = (struct libj2_j2u){111, 222};
+ end = NULL;
+ EXPECT(libj2_str_to_j2u("ba", SIZE_MAX, &end, "ab", "Ab", &a) == 0);
+ EXPECT(libj2_j2u_eq_ju(&a, 2));
+ EXPECT(end);
+ EXPECT(!*end);
+
+ a = (struct libj2_j2u){111, 222};
+ end = NULL;
+ EXPECT(libj2_str_to_j2u("ba", SIZE_MAX, &end, "ab", "AB", &a) == 0);
+ EXPECT(libj2_j2u_eq_ju(&a, 2));
+ EXPECT(end);
+ EXPECT(!*end);
+
+ a = (struct libj2_j2u){111, 222};
+ end = NULL;
+ EXPECT(libj2_str_to_j2u("ba", SIZE_MAX, &end, "AB", "AB", &a) == EINVAL);
+ EXPECT(libj2_j2u_is_zero(&a));
+ EXPECT(end);
+ EXPECT(*end == 'b');
+
+ a = (struct libj2_j2u){111, 222};
+ end = NULL;
+ EXPECT(libj2_str_to_j2u("ba", SIZE_MAX, &end, "AB", "ab", &a) == 0);
+ EXPECT(libj2_j2u_eq_ju(&a, 2));
+ EXPECT(end);
+ EXPECT(!*end);
+
+ a = (struct libj2_j2u){111, 222};
+ end = NULL;
+ EXPECT(libj2_str_to_j2u("BA", SIZE_MAX, &end, "ab", "AB", &a) == 0);
+ EXPECT(libj2_j2u_eq_ju(&a, 2));
+ EXPECT(end);
+ EXPECT(!*end);
+
+ a = (struct libj2_j2u){111, 222};
+ end = NULL;
+ EXPECT(libj2_str_to_j2u("BA", SIZE_MAX, &end, "AB", "AB", &a) == 0);
+ EXPECT(libj2_j2u_eq_ju(&a, 2));
+ EXPECT(end);
+ EXPECT(!*end);
+
+ a = (struct libj2_j2u){111, 222};
+ end = NULL;
+ EXPECT(libj2_str_to_j2u("BA", SIZE_MAX, &end, "AB", "ab", &a) == 0);
+ EXPECT(libj2_j2u_eq_ju(&a, 2));
+ EXPECT(end);
+ EXPECT(!*end);
+
+ a = (struct libj2_j2u){111, 222};
+ end = NULL;
+ EXPECT(libj2_str_to_j2u("BA", SIZE_MAX, &end, "ABB", "abb", &a) == EINVAL);
+ EXPECT(libj2_j2u_is_zero(&a));
+ EXPECT(end);
+ EXPECT(*end == 'B');
+
+ a = (struct libj2_j2u){111, 222};
+ end = NULL;
+ EXPECT(libj2_str_to_j2u("BA", SIZE_MAX, &end, "AB", "abc", &a) == EINVAL);
+ EXPECT(libj2_j2u_is_zero(&a));
+ EXPECT(end);
+ EXPECT(*end == 'B');
+
+ a = (struct libj2_j2u){111, 222};
+ end = NULL;
+ EXPECT(libj2_str_to_j2u("BA", SIZE_MAX, &end, "ABC", "ab", &a) == EINVAL);
+ EXPECT(libj2_j2u_is_zero(&a));
+ EXPECT(end);
+ EXPECT(*end == 'B');
+
+ sprintf(buf, "%jx%jx", UINTMAX_MAX, UINTMAX_MAX);
+ a = (struct libj2_j2u){111, 222};
+ end = NULL;
+ EXPECT(libj2_str_to_j2u(buf, SIZE_MAX, &end, "0123456789abcdef", NULL, &a) == 0);
+ EXPECT(libj2_j2u_is_max(&a));
+ EXPECT(end);
+ EXPECT(!*end);
+
+ sprintf(buf, "%jx%jx", UINTMAX_MAX, UINTMAX_MAX);
+ a = (struct libj2_j2u){111, 222};
+ end = NULL;
+ EXPECT(libj2_str_to_j2u(buf, SIZE_MAX, &end, "0123456789abcdefg", NULL, &a) == ERANGE);
+ EXPECT(libj2_j2u_is_max(&a));
+ EXPECT(end);
+ EXPECT(!*end);
+
+ sprintf(buf, "%jx%jx0", UINTMAX_MAX, UINTMAX_MAX);
+ a = (struct libj2_j2u){111, 222};
+ end = NULL;
+ EXPECT(libj2_str_to_j2u(buf, SIZE_MAX, &end, "0123456789abcdef", NULL, &a) == ERANGE);
+ EXPECT(libj2_j2u_is_max(&a));
+ EXPECT(end);
+ EXPECT(!*end);
+
+ sprintf(buf, "1%jx%jx", UINTMAX_MAX, UINTMAX_MAX);
+ a = (struct libj2_j2u){111, 222};
+ end = NULL;
+ EXPECT(libj2_str_to_j2u(buf, SIZE_MAX, &end, "0123456789abcdef", NULL, &a) == ERANGE);
+ EXPECT(libj2_j2u_is_max(&a));
+ EXPECT(end);
+ EXPECT(!*end);
+
+ return 0;
+}
+
+#endif