diff options
author | Kevin Newton <[email protected]> | 2024-02-23 14:15:18 -0500 |
---|---|---|
committer | git <[email protected]> | 2024-02-23 21:54:01 +0000 |
commit | 96907f94953d0fac3ca518f755b54ea5098ae5eb (patch) | |
tree | d2cdacf30657f82a11039c7b39423a9c06fdd269 /prism/util/pm_integer.c | |
parent | e45849419427963a353b2580d9ae4c4faaf2f3ba (diff) |
[ruby/prism] Convert pm_integer_t to strings
https://github.com/ruby/prism/commit/fa9a30ad91
Diffstat (limited to 'prism/util/pm_integer.c')
-rw-r--r-- | prism/util/pm_integer.c | 105 |
1 files changed, 105 insertions, 0 deletions
diff --git a/prism/util/pm_integer.c b/prism/util/pm_integer.c index e39108a05b..98a09243ff 100644 --- a/prism/util/pm_integer.c +++ b/prism/util/pm_integer.c @@ -15,6 +15,31 @@ pm_integer_node_create(pm_integer_t *integer, uint32_t value) { } /** + * Copy one integer onto another. + */ +static void +pm_integer_copy(pm_integer_t *dest, const pm_integer_t *src) { + dest->negative = src->negative; + dest->length = 0; + + dest->head.value = src->head.value; + dest->head.next = NULL; + + pm_integer_word_t *dest_current = &dest->head; + const pm_integer_word_t *src_current = src->head.next; + + while (src_current != NULL) { + dest_current->next = pm_integer_node_create(dest, src_current->value); + if (dest_current->next == NULL) return; + + dest_current = dest_current->next; + src_current = src_current->next; + } + + dest_current->next = NULL; +} + +/** * Add a 32-bit integer to an integer. */ static void @@ -59,6 +84,37 @@ pm_integer_multiply(pm_integer_t *integer, uint32_t multiplier) { } /** + * Divide an individual word by a 32-bit integer. This will recursively divide + * any subsequent nodes in the linked list. + */ +static uint32_t +pm_integer_divide_word(pm_integer_t *integer, pm_integer_word_t *word, uint32_t dividend) { + uint32_t remainder = 0; + if (word->next != NULL) { + remainder = pm_integer_divide_word(integer, word->next, dividend); + + if (integer->length > 0 && word->next->value == 0) { + free(word->next); + word->next = NULL; + integer->length--; + } + } + + uint64_t value = ((uint64_t) remainder << 32) | word->value; + word->value = (uint32_t) (value / dividend); + return (uint32_t) (value % dividend); +} + +/** + * Divide an integer by a 32-bit integer. In practice, this is only 10 so that + * we can format it as a string. It returns the remainder of the division. + */ +static uint32_t +pm_integer_divide(pm_integer_t *integer, uint32_t dividend) { + return pm_integer_divide_word(integer, &integer->head, dividend); +} + +/** * Return the value of a digit in a uint32_t. */ static uint32_t @@ -178,6 +234,55 @@ pm_integer_compare(const pm_integer_t *left, const pm_integer_t *right) { } /** + * Convert an integer to a decimal string. + */ +PRISM_EXPORTED_FUNCTION void +pm_integer_string(pm_buffer_t *buffer, const pm_integer_t *integer) { + if (integer->negative) { + pm_buffer_append_byte(buffer, '-'); + } + + switch (integer->length) { + case 0: { + const uint32_t value = integer->head.value; + pm_buffer_append_format(buffer, "%" PRIu32, value); + return; + } + case 1: { + const uint64_t value = ((uint64_t) integer->head.value) | (((uint64_t) integer->head.next->value) << 32); + pm_buffer_append_format(buffer, "%" PRIu64, value); + return; + } + default: { + // First, allocate a buffer that we'll copy the decimal digits into. + size_t length = (integer->length + 1) * 10; + char *digits = calloc(length, sizeof(char)); + if (digits == NULL) return; + + // Next, create a new integer that we'll use to store the result of + // the division and modulo operations. + pm_integer_t copy; + pm_integer_copy(©, integer); + + // Then, iterate through the integer, dividing by 10 and storing the + // result in the buffer. + char *ending = digits + length - 1; + char *current = ending; + + while (copy.length > 0 || copy.head.value > 0) { + uint32_t remainder = pm_integer_divide(©, 10); + *current-- = (char) ('0' + remainder); + } + + // Finally, append the string to the buffer and free the digits. + pm_buffer_append_string(buffer, current + 1, (size_t) (ending - current)); + free(digits); + return; + } + } +} + +/** * Recursively destroy the linked list of an integer. */ static void |