llm
Download
347 lines 10.2 KiBCModified 2026-08-31 raw
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
// 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 <gmp.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

#include "../util.h"
#include "datatypes/closure.h"
#include "datatypes/float.h"
#include "datatypes/num.h"
#include "datatypes/typeinfo.h"
#include "fail.h"
#include "integers.h"
#include "metamethods.h"
#include "text.h"

#ifndef INTX_C_H__INT_BITS
#define INTX_C_H__INT_BITS 32
#endif

#define CAT_(a, b) a##b
#define CAT(a, b) CAT_(a, b)
#define STRINGIFY_(s) #s
#define STRINGIFY(s) STRINGIFY_(s)

#define INT_T CAT(CAT(int, INTX_C_H__INT_BITS), _t)
#define UINT_T CAT(CAT(uint, INTX_C_H__INT_BITS), _t)
#define OPT_T CAT(CAT(OptionalInt, INTX_C_H__INT_BITS), _t)

#define NAME_STR "Int" STRINGIFY(INTX_C_H__INT_BITS)

#define NAMESPACED(method_name) CAT(CAT(Int, INTX_C_H__INT_BITS), CAT($, method_name))

static Text_t _int64_to_text(int64_t n) {
    char buf[INT64_DECIMAL_MAX];
    size_t len;
    char *digits = int64_to_decimal(n, buf, &len);
    return Text$from_strn(digits, len);
}

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
    if (fwrite(obj, sizeof(INT_T), 1, out) != 1) fail_text(Text("Failed to write whole integer"));
#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) {
        if (fputc((uint8_t)(z | 0x80L), out) == EOF) fail_text(Text("Failed to write full integer"));
        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
    if (fread(outval, sizeof(INT_T), 1, in) != 1) deserialization_failed();
#else
    UINT_T z = 0;
    for (size_t shift = 0;; shift += 7) {
        if (shift >= 8 * sizeof(UINT_T)) deserialization_failed(); // Overlong varint
        int i = fgetc(in);
        if (i == EOF) deserialization_failed();
        uint8_t byte = (uint8_t)i;
        z |= ((UINT_T)(byte & 0x7F)) << shift;
        if ((byte & 0x80) == 0) break;
    }
    *(INT_T *)outval = (INT_T)((z >> 1L) ^ -(z & 1L)); // Zigzag decode
#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 ? Text$concat(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) || (high <= x && x <= low);
}
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
OPT_T NAMESPACED(parse)(Text_t text, OptionalInt_t base, Text_t *remainder) {
    OptionalInt_t full_int = Int$parse(text, base, 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
INT_T NAMESPACED(from_num)(Num_t n, bool truncate) {
    return NAMESPACED(from_int)(Int$from_num(n, truncate), truncate);
}

public
PUREFUNC INT_T NAMESPACED(from_float64)(Float64_t n, bool truncate) {
    INT_T i = (INT_T)n;
    if (!truncate && unlikely((Float64_t)i != n))
        fail_text(Text$concat(Text("Could not convert Num to an " NAME_STR " without truncation: "),
                              Float64$value_as_text(n)));
    return i;
}

public
PUREFUNC INT_T NAMESPACED(from_float32)(Float32_t n, bool truncate) {
    INT_T i = (INT_T)n;
    if (!truncate && unlikely((Float32_t)i != n))
        fail_text(Text$concat(Text("Could not convert Float32 to an " NAME_STR " without truncation: "),
                              Float32$value_as_text(n)));
    return i;
}

public
PUREFUNC INT_T NAMESPACED(from_int)(Int_t i, bool truncate) {
    if likely (i.small & 1L) {
        INT_T ret = i.small >> 2L;
#if INTX_C_H__INT_BITS < 32
        if (!truncate && unlikely((int64_t)ret != (i.small >> 2L)))
            fail("Integer is too big to fit in an " NAME_STR ": ", i);
#endif
        return ret;
    }
    if (!truncate && unlikely(!mpz_fits_slong_p(i.big))) fail("Integer is too big to fit in an " NAME_STR ": ", i);
    return mpz_get_si(i.big);
}

#if INTX_C_H__INT_BITS < 64
public
PUREFUNC INT_T NAMESPACED(from_int64)(Int64_t i64, bool truncate) {
    INT_T i = (INT_T)i64;
    if (!truncate && unlikely((int64_t)i != i64)) fail("Integer is too big to fit in an " NAME_STR ": ", i64);
    return i;
}
#elif INTX_C_H__INT_BITS > 64
public
CONSTFUNC INT_T NAMESPACED(from_int64)(Int64_t i) {
    return (INT_T)i;
}
#endif

#if INTX_C_H__INT_BITS < 32
public
PUREFUNC INT_T NAMESPACED(from_int32)(Int32_t i32, bool truncate) {
    INT_T i = (INT_T)i32;
    if (!truncate && unlikely((int32_t)i != i32)) fail("Integer is too big to fit in an " NAME_STR ": ", i32);
    return i;
}
#elif INTX_C_H__INT_BITS > 32
public
CONSTFUNC INT_T NAMESPACED(from_int32)(Int32_t i) {
    return (INT_T)i;
}
#endif

#if INTX_C_H__INT_BITS < 16
public
PUREFUNC INT_T NAMESPACED(from_int16)(Int16_t i16, bool truncate) {
    INT_T i = (INT_T)i16;
    if (!truncate && unlikely((int16_t)i != i16)) fail("Integer is too big to fit in an " NAME_STR ": ", i16);
    return i;
}
#elif INTX_C_H__INT_BITS > 16
public
CONSTFUNC INT_T NAMESPACED(from_int16)(Int16_t i) {
    return (INT_T)i;
}
#endif

#if INTX_C_H__INT_BITS > 8
public
CONSTFUNC INT_T NAMESPACED(from_int8)(Int8_t i) {
    return (INT_T)i;
}
#endif

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 CAT_
#undef CAT
#undef STRINGIFY_
#undef STRINGIFY
#undef INT_T
#undef UINT_T
#undef OPT_T
#undef NAME_STR
#undef NAMESPACED
#undef INTX_C_H__INT_BITS