summaryrefslogtreecommitdiff
path: root/prism/util/pm_integer.c
diff options
context:
space:
mode:
authorKevin Newton <[email protected]>2024-02-23 14:15:18 -0500
committergit <[email protected]>2024-02-23 21:54:01 +0000
commit96907f94953d0fac3ca518f755b54ea5098ae5eb (patch)
treed2cdacf30657f82a11039c7b39423a9c06fdd269 /prism/util/pm_integer.c
parente45849419427963a353b2580d9ae4c4faaf2f3ba (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.c105
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(&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(&copy, 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