aboutsummaryrefslogtreecommitdiff
path: root/src/stdlib/intX.c.h
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2025-10-19 14:07:53 -0400
committerBruce Hill <bruce@bruce-hill.com>2025-10-19 14:07:53 -0400
commit1713c56dbf8ffaa01e559c7564928721e363ca39 (patch)
treeb1e1886076b3e44d95e082e2d1a8eb6db1437aec /src/stdlib/intX.c.h
parent3a2077067343a20f631ec36838e197b34ff422f4 (diff)
Move integer implementation details into separate header/C files, backed
by template headers that use an INT_BITS macro to redefine implementations for different int sizes.
Diffstat (limited to 'src/stdlib/intX.c.h')
-rw-r--r--src/stdlib/intX.c.h275
1 files changed, 275 insertions, 0 deletions
diff --git a/src/stdlib/intX.c.h b/src/stdlib/intX.c.h
new file mode 100644
index 00000000..9c096a19
--- /dev/null
+++ b/src/stdlib/intX.c.h
@@ -0,0 +1,275 @@
+// Fixed-width integer type infos and methods
+// This file is intended to be used by defining `INTX_C_H__INT_BITS` before including:
+//
+// #define INTX_C_H__INT_BITS 32
+// #include "intX.c.h"
+//
+
+#include <gc.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "datatypes.h"
+#include "integers.h"
+#include "text.h"
+#include "types.h"
+
+#ifndef INTX_C_H__INT_BITS
+#define INTX_C_H__INT_BITS 32
+#endif
+
+#define PASTE3_(a, b, c) a##b##c
+#define PASTE3(a, b, c) PASTE3_(a, b, c)
+#define INT_T PASTE3(int, INTX_C_H__INT_BITS, _t)
+
+#define STRINGIFY_(s) #s
+#define STRINGIFY(s) STRINGIFY_(s)
+#define NAME_STR "Int" STRINGIFY(INTX_C_H__INT_BITS)
+
+#define UNSIGNED_(t) u##t
+#define UNSIGNED(t) UNSIGNED_(t)
+#define UINT_T UNSIGNED(INT_T)
+
+#define OPT_T PASTE3(OptionalInt, INTX_C_H__INT_BITS, _t)
+
+#define PASTE4_(a, b, c, d) a##b##c##d
+#define PASTE4(a, b, c, d) PASTE4_(a, b, c, d)
+#define NAMESPACED(method_name) PASTE4(Int, INTX_C_H__INT_BITS, $, method_name)
+
+static Text_t _int64_to_text(int64_t n) {
+ if (n == INT64_MIN) return Text("-9223372036854775808");
+
+ char buf[21] = {[20] = 0}; // Big enough for INT64_MIN + '\0'
+ char *p = &buf[19];
+ bool negative = n < 0;
+ if (negative) n = -n; // Safe to do because we checked for INT64_MIN earlier
+
+ do {
+ *(p--) = '0' + (n % 10);
+ n /= 10;
+ } while (n > 0);
+
+ if (negative) *(p--) = '-';
+
+ return Text$from_strn(p + 1, (size_t)(&buf[19] - p));
+}
+
+public
+void NAMESPACED(serialize)(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *info) {
+ (void)info, (void)pointers;
+#if INTX_C_H__INT_BITS < 32
+ fwrite(obj, sizeof(INT_T), 1, out);
+#else
+ INT_T i = *(INT_T *)obj;
+ UINT_T z = (UINT_T)((i << 1L) ^ (i >> (INTX_C_H__INT_BITS - 1L))); // Zigzag encode
+ while (z >= 0x80L) {
+ fputc((uint8_t)(z | 0x80L), out);
+ z >>= 7L;
+ }
+ fputc((uint8_t)z, out);
+#endif
+}
+
+public
+void NAMESPACED(deserialize)(FILE *in, void *outval, List_t *pointers, const TypeInfo_t *info) {
+ (void)info, (void)pointers;
+#if INTX_C_H__INT_BITS < 32
+ fread(outval, sizeof(INT_T), 1, in);
+#else
+ UINT_T z = 0;
+ for (size_t shift = 0;; shift += 7) {
+ uint8_t byte = (uint8_t)fgetc(in);
+ z |= ((UINT_T)(byte & 0x7F)) << shift;
+ if ((byte & 0x80) == 0) break;
+ }
+ *(INT_T *)outval = (INT_T)((z >> 1L) ^ -(z & 1L)); // Zigzag decode
+#endif
+}
+
+#ifdef __TINYC__
+#define __builtin_add_overflow(x, y, result) \
+ ({ \
+ *(result) = (x) + (y); \
+ false; \
+ })
+#endif
+
+public
+Text_t NAMESPACED(as_text)(const void *i, bool colorize, const TypeInfo_t *info) {
+ (void)info;
+ if (!i) return Text(NAME_STR);
+ Text_t text = _int64_to_text((int64_t)(*(INT_T *)i));
+ return colorize ? Texts(Text("\033[35m"), text, Text("\033[m")) : text;
+}
+public
+Text_t NAMESPACED(value_as_text)(INT_T i) { return _int64_to_text((int64_t)i); }
+public
+PUREFUNC int32_t NAMESPACED(compare)(const void *x, const void *y, const TypeInfo_t *info) {
+ (void)info;
+ return (*(INT_T *)x > *(INT_T *)y) - (*(INT_T *)x < *(INT_T *)y);
+}
+public
+PUREFUNC bool NAMESPACED(equal)(const void *x, const void *y, const TypeInfo_t *info) {
+ (void)info;
+ return *(INT_T *)x == *(INT_T *)y;
+}
+public
+CONSTFUNC bool NAMESPACED(is_between)(const INT_T x, const INT_T low, const INT_T high) {
+ return low <= x && x <= high;
+}
+public
+CONSTFUNC INT_T NAMESPACED(clamped)(INT_T x, INT_T min, INT_T max) { return x < min ? min : (x > max ? max : x); }
+public
+Text_t NAMESPACED(hex)(INT_T i, Int_t digits_int, bool uppercase, bool prefix) {
+ Int_t as_int = Int$from_int64((int64_t)i);
+ return Int$hex(as_int, digits_int, uppercase, prefix);
+}
+public
+Text_t NAMESPACED(octal)(INT_T i, Int_t digits_int, bool prefix) {
+ Int_t as_int = Int$from_int64((int64_t)i);
+ return Int$octal(as_int, digits_int, prefix);
+}
+public
+List_t NAMESPACED(bits)(INT_T x) {
+ List_t bit_list = (List_t){.data = GC_MALLOC_ATOMIC(sizeof(bool[8 * sizeof(INT_T)])),
+ .atomic = 1,
+ .stride = sizeof(bool),
+ .length = 8 * sizeof(INT_T)};
+ bool *bits = bit_list.data + sizeof(INT_T) * 8;
+ for (size_t i = 0; i < 8 * sizeof(INT_T); i++) {
+ *(bits--) = x & 1;
+ x >>= 1;
+ }
+ return bit_list;
+}
+public
+bool NAMESPACED(get_bit)(INT_T x, Int_t bit_index) {
+ if (Int$compare_value(bit_index, I(1)) < 0) fail("Invalid bit index (expected 1 or higher): ", bit_index);
+ if (Int$compare_value(bit_index, Int$from_int64(sizeof(INT_T) * 8)) > 0)
+ fail("Bit index is too large! There are only ", (uint64_t)sizeof(INT_T) * 8,
+ " bits, but index is: ", bit_index);
+ return ((x & (INT_T)(1L << (Int64$from_int(bit_index, true) - 1L))) != 0);
+}
+typedef struct {
+ OPT_T current, last;
+ INT_T step;
+} NAMESPACED(Range_t);
+
+static OPT_T _next_int(NAMESPACED(Range_t) * info) {
+ OPT_T i = info->current;
+ if (i.has_value) {
+ INT_T next;
+ bool overflow = __builtin_add_overflow(i.value, info->step, &next);
+ if (overflow || (info->last.has_value && (info->step >= 0 ? next > info->last.value : next < info->last.value)))
+ info->current = (OPT_T){.has_value = false};
+ else info->current = (OPT_T){.has_value = true, .value = next};
+ }
+ return i;
+}
+
+public
+#if INTX_C_H__INT_BITS < 64
+CONSTFUNC
+#endif
+Closure_t NAMESPACED(to)(INT_T first, INT_T last, OPT_T step) {
+ NAMESPACED(Range_t) *range = GC_MALLOC(sizeof(NAMESPACED(Range_t)));
+ range->current = (OPT_T){.has_value = true, .value = first};
+ range->last = (OPT_T){.has_value = true, .value = last};
+ range->step = step.has_value ? step.value : (last >= first ? 1 : -1);
+ return (Closure_t){.fn = _next_int, .userdata = range};
+}
+
+public
+#if INTX_C_H__INT_BITS < 64
+CONSTFUNC
+#endif
+Closure_t NAMESPACED(onward)(INT_T first, INT_T step) {
+ NAMESPACED(Range_t) *range = GC_MALLOC(sizeof(NAMESPACED(Range_t)));
+ range->current = (OPT_T){.has_value = true, .value = first};
+ range->last = (OPT_T){.has_value = false};
+ range->step = step;
+ return (Closure_t){.fn = _next_int, .userdata = range};
+}
+public
+PUREFUNC OPT_T NAMESPACED(parse)(Text_t text, Text_t *remainder) {
+ OptionalInt_t full_int = Int$parse(text, remainder);
+ if (full_int.small == 0L) return (OPT_T){.has_value = false};
+ if (Int$compare_value(full_int, I(NAMESPACED(min))) < 0) {
+ return (OPT_T){.has_value = false};
+ }
+ if (Int$compare_value(full_int, I(NAMESPACED(max))) > 0) {
+ return (OPT_T){.has_value = false};
+ }
+ return (OPT_T){.has_value = true, .value = NAMESPACED(from_int)(full_int, true)};
+}
+
+public
+CONSTFUNC INT_T NAMESPACED(gcd)(INT_T x, INT_T y) {
+ if (x == 0 || y == 0) return 0;
+ x = NAMESPACED(abs)(x);
+ y = NAMESPACED(abs)(y);
+ while (x != y) {
+ if (x > y) x -= y;
+ else y -= x;
+ }
+ return x;
+}
+
+public
+const INT_T NAMESPACED(min) =
+#if INTX_C_H__INT_BITS == 64
+ INT64_MIN
+#elif INTX_C_H__INT_BITS == 32
+ INT32_MIN
+#elif INTX_C_H__INT_BITS == 16
+ INT16_MIN
+#elif INTX_C_H__INT_BITS == 8
+ INT8_MIN
+#else
+#error "Unsupported integer bit width"
+#endif
+ ;
+
+public
+const INT_T NAMESPACED(max) =
+#if INTX_C_H__INT_BITS == 64
+ INT64_MAX
+#elif INTX_C_H__INT_BITS == 32
+ INT32_MAX
+#elif INTX_C_H__INT_BITS == 16
+ INT16_MAX
+#elif INTX_C_H__INT_BITS == 8
+ INT8_MAX
+#else
+#error "Unsupported integer bit width"
+#endif
+ ;
+
+public
+const TypeInfo_t NAMESPACED(info) = {
+ .size = sizeof(INT_T),
+ .align = __alignof__(INT_T),
+ .metamethods =
+ {
+ .compare = NAMESPACED(compare),
+ .as_text = NAMESPACED(as_text),
+ .serialize = NAMESPACED(serialize),
+ .deserialize = NAMESPACED(deserialize),
+ },
+};
+
+#undef PASTE3_
+#undef PASTE3
+#undef INT_T
+#undef STRINGIFY_
+#undef STRINGIFY
+#undef NAME_STR
+#undef UNSIGNED_
+#undef UNSIGNED
+#undef UINT_T
+#undef OPT_T
+#undef PASTE4_
+#undef PASTE4
+#undef NAMESPACED