diff options
| author | Mattias Andrée <m@maandree.se> | 2025-12-07 00:47:10 +0100 |
|---|---|---|
| committer | Mattias Andrée <m@maandree.se> | 2025-12-07 00:47:10 +0100 |
| commit | cf6866f033e1946af7f5abf9786c831a20d19f91 (patch) | |
| tree | 4e71880ecbbd0c0710270d9c3702fbe16e029e85 /libj2_j2u_to_str.c | |
| parent | Add saturated arithmetic operations (diff) | |
| download | libj2-cf6866f033e1946af7f5abf9786c831a20d19f91.tar.gz libj2-cf6866f033e1946af7f5abf9786c831a20d19f91.tar.bz2 libj2-cf6866f033e1946af7f5abf9786c831a20d19f91.tar.xz | |
Add libj2_j2u_to_str and libj2_str_to_j2u
Signed-off-by: Mattias Andrée <m@maandree.se>
Diffstat (limited to '')
| -rw-r--r-- | libj2_j2u_to_str.c | 542 |
1 files changed, 542 insertions, 0 deletions
diff --git a/libj2_j2u_to_str.c b/libj2_j2u_to_str.c new file mode 100644 index 0000000..ab963fd --- /dev/null +++ b/libj2_j2u_to_str.c @@ -0,0 +1,542 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#include <errno.h> +#include <stdio.h> +#ifndef TEST + +/* TODO Add man page */ + + +static size_t +to_power_of_two(const struct libj2_j2u *a, char *buf, size_t bufsize, const char *digits, unsigned radix) +{ + unsigned i, shift, bits, mask; + unsigned ret, cur_shift; + uintmax_t v; + + shift = 0; + for (i = radix; i >>= 1;) + shift += 1U; + + mask = radix - 1U; + bits = libj2_fls_j2u(a); + if (bits % shift) + bits += shift - bits % shift; + cur_shift = bits; + ret = bits / shift; + + do { + if (!bufsize) + break; + cur_shift -= shift; + if (cur_shift >= LIBJ2_JU_BIT) { + v = a->high >> (cur_shift - LIBJ2_JU_BIT); + } else if (cur_shift + shift <= LIBJ2_JU_BIT) { + v = a->low >> cur_shift; + } else { + v = a->low >> cur_shift; + v |= a->high << (LIBJ2_JU_BIT % shift); + } + v &= mask; + *buf++ = digits[v]; + bufsize--; + } while (cur_shift); + + return (size_t)ret; +} + + +static size_t +to_generic(const struct libj2_j2u *a, char *buf, size_t bufsize, const char *digits, unsigned radix) +{ + struct libj2_j2u v = *a, digit; + uintmax_t b = (uintmax_t)radix; + char charstack[LIBJ2_J2U_BIT]; + size_t n = 0, i; + + while (v.high) { + libj2_j2u_divmod_ju_to_j2u_j2u(&v, b, &v, &digit); + charstack[n++] = digits[digit.low]; + } + while (v.low) { + charstack[n++] = digits[v.low % b]; + v.low /= b; + } + + for (i = n; i && bufsize; bufsize--) + *buf++ = charstack[--i]; + + return n; +} + + +static size_t +to_decimal(const struct libj2_j2u *a, char *buf, size_t bufsize) +{ +#define C(X) X"0" X"1" X"2" X"3" X"4" X"5" X"6" X"7" X"8" X"9" +#define B(X) C(X"0") C(X"1") C(X"2") C(X"3") C(X"4") C(X"5") C(X"6") C(X"7") C(X"8") C(X"9") +#define A() B("0") B("1") B("2") B("3") B("4") B("5") B("6") B("7") B("8") B("9") + + static const char text[] = A(); + +#undef A +#undef B +#undef C + + struct libj2_j2u v = *a, digits; + char charstack[LIBJ2_J2U_BIT]; + size_t n = 0, i; + + while (v.high) { + libj2_j2u_divmod_ju_to_j2u_j2u(&v, 1000, &v, &digits); + charstack[n++] = text[digits.low * 3U + 2U]; + charstack[n++] = text[digits.low * 3U + 1U]; + charstack[n++] = text[digits.low * 3U + 0U]; + } + while (v.low) { + digits.low = v.low % 1000; + v.low /= 1000; + charstack[n++] = text[digits.low * 3U + 2U]; + charstack[n++] = text[digits.low * 3U + 1U]; + charstack[n++] = text[digits.low * 3U + 0U]; + } + + while (charstack[n - 1U] == '0') + n -= 1U; + for (i = n; i && bufsize; bufsize--) + *buf++ = charstack[--i]; + + return n; +} + + +size_t +libj2_j2u_to_str(const struct libj2_j2u *a, char *buf, size_t bufsize, const char *digits) +{ + char symbols[256]; + unsigned i, radix = 0; + size_t n, bufsize_m1; + + if (!buf) + bufsize = 0; + if (!digits) + digits = "0123456789"; + + for (i = 0; i < sizeof(symbols); i++) + symbols[i] = 0; + while (digits[radix]) { + if (symbols[(unsigned char)digits[radix]]) + return 0; + symbols[(unsigned char)digits[radix]] = 1; + radix++; + } + + if (radix < 2) + return 0; + + if (libj2_j2u_is_zero(a)) { + if (bufsize) { + buf[0] = digits[0]; + buf[bufsize > 1] = '\0'; + } + return 1U; + } + + bufsize_m1 = bufsize - (size_t)(bufsize > 0); + if ((radix & (radix - 1U)) == 0U) { + n = to_power_of_two(a, buf, bufsize_m1, digits, radix); + goto out; + } + if (radix != 10) + goto generic; + for (i = 0; i < 10; i++) + if (digits[i] != (char)((unsigned)'0' + i)) + goto generic; + n = to_decimal(a, buf, bufsize_m1); + goto out; +generic: + n = to_generic(a, buf, bufsize_m1, digits, radix); +out: + if (bufsize) + buf[n < bufsize_m1 ? n : bufsize_m1] = '\0'; + return n; +} + + +#else + +#define BUFFER_SIZE 1024 + + +static uintmax_t +random_ju(void) +{ + size_t n = LIBJ2_JU_BIT; + uintmax_t r = 0; + while (n--) + if (rand() < rand()) + r |= (uintmax_t)1 << n; + return r; +} + + +static void +check_(struct libj2_j2u *a, size_t bufsize, const char *digits, const char *expected_s, size_t expected_n) +{ + char buf[BUFFER_SIZE], *end; + struct libj2_j2u a_saved = *a, v; + size_t n; + + memset(buf, '~', sizeof(buf)); + EXPECT(libj2_j2u_to_str(a, buf, bufsize, digits) == expected_n); + EXPECT(libj2_j2u_eq_j2u(a, &a_saved)); + if (expected_s) { + n = strlen(expected_s); + EXPECT(!memcmp(buf, expected_s, n)); + EXPECT(buf[n] == '\0'); + EXPECT(buf[n + 1U] == '~'); + + if (!n) + return; + + v = (struct libj2_j2u){111, 222}; + end = NULL; + EXPECT(libj2_str_to_j2u(expected_s, strlen(expected_s), &end, digits, NULL, &v) == 0); + EXPECT(libj2_j2u_eq_j2u(&v, a)); + EXPECT(end); + EXPECT(!*end); + } else { + EXPECT(buf[0] == '~'); + return; + } + + for (; n; n--) { + memset(buf, '~', sizeof(buf)); + EXPECT(libj2_j2u_to_str(a, buf, n + 1U, digits) == expected_n); + EXPECT(libj2_j2u_eq_j2u(a, &a_saved)); + EXPECT(!memcmp(buf, expected_s, n)); + EXPECT(buf[n] == '\0'); + EXPECT(buf[n + 1U] == '~'); + } + + memset(buf, '~', sizeof(buf)); + EXPECT(libj2_j2u_to_str(a, buf, 1, digits) == expected_n); + EXPECT(libj2_j2u_eq_j2u(a, &a_saved)); + EXPECT(buf[0] == '\0'); + EXPECT(buf[1] == '~'); + + memset(buf, '~', sizeof(buf)); + EXPECT(libj2_j2u_to_str(a, buf, 0, digits) == expected_n); + EXPECT(libj2_j2u_eq_j2u(a, &a_saved)); + EXPECT(buf[0] == '~'); +} + + +static void +check(struct libj2_j2u *a, size_t bufsize, const char *digits, const char *expected_s, size_t expected_n) +{ + char buf[BUFFER_SIZE], alphabet[17]; + size_t i; + struct libj2_j2u a_saved = *a; + + check_(a, bufsize, digits, expected_s, expected_n); + EXPECT(libj2_j2u_eq_j2u(a, &a_saved)); + + if (!digits) + digits = "0123456789"; + if (strlen(digits) > sizeof(alphabet) - 1U) + return; + if (!expected_s) + return; + + for (i = 0; digits[i]; i++) { + alphabet[i] = (char)((unsigned)'a' + i); + if (i < 10) { + if (digits[i] != (char)((unsigned)'0' + i)) + return; + } else if (digits[i] != (char)((unsigned)'a' + i - 10)) { + if (digits[i] != (char)((unsigned)'A' + i - 10)) + return; + } + } + alphabet[i] = '\0'; + + for (i = 0; expected_s[i]; i++) { + if (expected_s[i] <= '9') + buf[i] = (char)('a' - '0' + expected_s[i]); + else if (expected_s[i] <= 'Z') + buf[i] = (char)('k' - 'A' + expected_s[i]); + else + buf[i] = (char)('k' - 'a' + expected_s[i]); + } + buf[i] = '\0'; + + check_(a, bufsize, alphabet, buf, expected_n); + EXPECT(libj2_j2u_eq_j2u(a, &a_saved)); +} + + +static void +check_zero_low(const struct libj2_j2u *a_with_low, const char *digits, const char *expected) +{ + char buf1[BUFFER_SIZE], buf2[BUFFER_SIZE], *e; + struct libj2_j2u a; + size_t i, j, ndigits = strlen(digits); + int borrow; + + for (i = 0; i < ndigits; i++) + EXPECT(digits[i] == (char)((unsigned)'0' + i)); + + a = *a_with_low; + a.high = 0; + EXPECT(libj2_j2u_to_str(&a, buf1, BUFFER_SIZE, digits) > 0); + i = strlen(expected); + j = strlen(buf1); + EXPECT(j <= i); + + memcpy(buf2, expected, i + 1U); + borrow = 0; + while (i--, j--) { + int x = (int)(buf2[i] - '0'); + int y = (int)(buf1[j] - '0'); + int z = x - y - borrow; + borrow = z < 0; + if (borrow) + z += (int)ndigits; + buf2[i] = digits[z]; + } + if (borrow) { + while (buf2[i] == '0') + buf2[i--] = digits[ndigits - 1U]; + buf2[i] = (char)((int)buf2[i] - 1); + } + e = buf2; + while (e[0] == '0' && e[1]) + e++; + EXPECT(*e); + + a = *a_with_low; + a.low = 0; + check(&a, BUFFER_SIZE, digits, e, strlen(e)); +} + + +int +main(void) +{ + char expected[BUFFER_SIZE]; + struct libj2_j2u a; + unsigned i, j; + uintmax_t t; + int n, m; + + srand((unsigned)time(NULL)); + + libj2_j2u_zero(&a); + EXPECT(libj2_j2u_to_str(&a, NULL, SIZE_MAX, "") == 0); + EXPECT(libj2_j2u_is_zero(&a)); + EXPECT(libj2_j2u_to_str(&a, NULL, SIZE_MAX, "0") == 0); + EXPECT(libj2_j2u_is_zero(&a)); + EXPECT(libj2_j2u_to_str(&a, NULL, SIZE_MAX, "01") == 1); + EXPECT(libj2_j2u_is_zero(&a)); + EXPECT(libj2_j2u_to_str(&a, NULL, SIZE_MAX, "01234") == 1); + EXPECT(libj2_j2u_is_zero(&a)); + EXPECT(libj2_j2u_to_str(&a, NULL, SIZE_MAX, "01234567") == 1); + EXPECT(libj2_j2u_is_zero(&a)); + EXPECT(libj2_j2u_to_str(&a, NULL, SIZE_MAX, "0123456789") == 1); + EXPECT(libj2_j2u_is_zero(&a)); + EXPECT(libj2_j2u_to_str(&a, NULL, SIZE_MAX, "00") == 0); + EXPECT(libj2_j2u_is_zero(&a)); + + libj2_j2u_zero(&a); + check(&a, BUFFER_SIZE, "", NULL, 0); + check(&a, BUFFER_SIZE, "0", NULL, 0); + check(&a, BUFFER_SIZE, "01", "0", 1); + check(&a, BUFFER_SIZE, "01234", "0", 1); + check(&a, BUFFER_SIZE, "01234567", "0", 1); + check(&a, BUFFER_SIZE, "0123456789", "0", 1); + check(&a, BUFFER_SIZE, "0123456789ab", "0", 1); + check(&a, BUFFER_SIZE, NULL, "0", 1); + + libj2_j2u_zero(&a); + check(&a, 2, "", NULL, 0); + check(&a, 2, "0", NULL, 0); + check(&a, 2, "01", "0", 1); + check(&a, 2, "01234", "0", 1); + check(&a, 2, "01234567", "0", 1); + check(&a, 2, "0123456789", "0", 1); + check(&a, 2, "0123456789ab", "0", 1); + check(&a, 2, NULL, "0", 1); + + libj2_j2u_zero(&a); + check(&a, 2, "ab", "a", 1); + check(&a, 2, "abcde", "a", 1); + check(&a, 2, "abcdefgh", "a", 1); + check(&a, 2, "abcdefghij", "a", 1); + check(&a, 2, "abcdefghijkl", "a", 1); + + libj2_j2u_zero(&a); + check(&a, 1, "", NULL, 0); + check(&a, 1, "0", NULL, 0); + check(&a, 1, "01", "", 1); + check(&a, 1, "01234", "", 1); + check(&a, 1, "01234567", "", 1); + check(&a, 1, "0123456789", "", 1); + check(&a, 1, "0123456789ab", "", 1); + check(&a, 1, NULL, "", 1); + + for (i = 0; i < 32; i++) { + a.high = UINTMAX_MAX ^ (UINTMAX_MAX >> 1); + a.high |= random_ju(); + a.low = 0; + + for (j = 0; j < LIBJ2_JU_BIT - 1U; j++) { + n = sprintf(expected, "%jx%0*jx", a.high, (int)LIBJ2_JU_BIT / 4, a.low); + EXPECT(n > 0); + check(&a, BUFFER_SIZE, "0123456789abcdef", expected, (size_t)n); + + n = sprintf(expected, "%jX%0*jX", a.high, (int)LIBJ2_JU_BIT / 4, a.low); + EXPECT(n > 0); + check(&a, BUFFER_SIZE, "0123456789ABCDEF", expected, (size_t)n); + + a.high >>= 1; + } + + a.high = UINTMAX_MAX ^ (UINTMAX_MAX >> 1); + a.high |= random_ju(); + a.low = random_ju(); + + for (j = 0; j < LIBJ2_JU_BIT - 1U; j++) { + n = sprintf(expected, "%jx%0*jx", a.high, (int)LIBJ2_JU_BIT / 4, a.low); + EXPECT(n > 0); + check(&a, BUFFER_SIZE, "0123456789abcdef", expected, (size_t)n); + + n = sprintf(expected, "%jX%0*jX", a.high, (int)LIBJ2_JU_BIT / 4, a.low); + EXPECT(n > 0); + check(&a, BUFFER_SIZE, "0123456789ABCDEF", expected, (size_t)n); + + a.high >>= 1; + } + + for (j = 0; j < LIBJ2_J2U_BIT; j++) { + a.low >>= 1; + a.low |= (a.high & 1U) << (LIBJ2_JU_BIT - 1U); + a.high >>= 1; + + n = sprintf(expected, "%jx", a.low); + EXPECT(n > 0); + check(&a, BUFFER_SIZE, "0123456789abcdef", expected, (size_t)n); + + n = sprintf(expected, "%jX", a.low); + EXPECT(n > 0); + check(&a, BUFFER_SIZE, "0123456789ABCDEF", expected, (size_t)n); + + n = sprintf(expected, "%jo", a.low); + EXPECT(n > 0); + check(&a, BUFFER_SIZE, "01234567", expected, (size_t)n); + + n = sprintf(expected, "%ju", a.low); + EXPECT(n > 0); + check(&a, BUFFER_SIZE, "0123456789", expected, (size_t)n); + + n = sprintf(expected, "%ju", a.low); + EXPECT(n > 0); + check(&a, BUFFER_SIZE, NULL, expected, (size_t)n); + } + + a.high = 0; + a.low = random_ju(); + a.low |= UINTMAX_MAX ^ (UINTMAX_MAX >> 1); + n = sprintf(expected, "%jo", a.low); + EXPECT(n > 0); + libj2_j2u_lsh(&a, LIBJ2_JU_BIT - LIBJ2_JU_BIT % 3U); + t = ((uintmax_t)1 << ((LIBJ2_JU_BIT - LIBJ2_JU_BIT % 3) / 3U)) - 1U; + t &= random_ju(); + m = sprintf(&expected[n], "%0*jo", (int)LIBJ2_JU_BIT / 3, t); + EXPECT(m > 0); + n += m; + libj2_j2u_or_ju(&a, t); + check(&a, BUFFER_SIZE, "01234567", expected, (size_t)n); + while (libj2_j2u_rsh(&a, 3), !libj2_j2u_is_zero(&a)) { + expected[--n] = '\0'; + check(&a, BUFFER_SIZE, "01234567", expected, (size_t)n); + } + + a.high = 0; + do { + a.low = random_ju(); + } while (!a.low); + n = sprintf(expected, "%ju", a.low); + EXPECT(n > 0); + while (!libj2_j2u_mul_ju_overflow_p(&a, 10)) { + libj2_j2u_mul_ju(&a, 10); + j = (unsigned)random_ju() % 10U; + if (libj2_j2u_add_ju_overflow_p(&a, j)) { + expected[n++] = '0'; + break; + } + libj2_j2u_add_ju(&a, j); + expected[n++] = (char)((unsigned)'0' + j); + } + expected[n] = '\0'; + check(&a, BUFFER_SIZE, "0123456789", expected, (size_t)n); + check(&a, BUFFER_SIZE, NULL, expected, (size_t)n); + check_zero_low(&a, "0123456789", expected); + while (libj2_j2u_div_ju(&a, 10), !libj2_j2u_is_zero(&a)) { + expected[--n] = '\0'; + check(&a, BUFFER_SIZE, NULL, expected, (size_t)n); + } + + a.high = 0; + do { + a.low = random_ju() % 5U; + } while (!a.low); + n = 0; + expected[n++] = (char)((unsigned)'0' + a.low); + expected[n] = '\0'; + check(&a, BUFFER_SIZE, "01234", expected, (size_t)n); + while (!libj2_j2u_mul_ju_overflow_p(&a, 5)) { + libj2_j2u_mul_ju(&a, 5); + j = (unsigned)random_ju() % 5U; + if (libj2_j2u_add_ju_overflow_p(&a, j)) { + expected[n++] = '0'; + expected[n] = '\0'; + check(&a, BUFFER_SIZE, "01234", expected, (size_t)n); + break; + } + libj2_j2u_add_ju(&a, j); + expected[n++] = (char)((unsigned)'0' + j); + expected[n] = '\0'; + check(&a, BUFFER_SIZE, "01234", expected, (size_t)n); + } + check_zero_low(&a, "01234", expected); + + a.high = 0; + do { + a.low = random_ju() % 11U; + } while (!a.low); + n = 0; + expected[n++] = (char)((unsigned)'0' + a.low); + expected[n] = '\0'; + check(&a, BUFFER_SIZE, "0123456789:", expected, (size_t)n); + while (!libj2_j2u_mul_ju_overflow_p(&a, 11)) { + libj2_j2u_mul_ju(&a, 11); + j = (unsigned)random_ju() % 11U; + if (libj2_j2u_add_ju_overflow_p(&a, j)) { + expected[n++] = '0'; + expected[n] = '\0'; + check(&a, BUFFER_SIZE, "0123456789:", expected, (size_t)n); + break; + } + libj2_j2u_add_ju(&a, j); + expected[n++] = (char)((unsigned)'0' + j); + expected[n] = '\0'; + check(&a, BUFFER_SIZE, "0123456789:", expected, (size_t)n); + } + check_zero_low(&a, "0123456789:", expected); + } + + return 0; +} + +#endif |
