main
Download
273 lines 8.4 KiBCModified 2025-12-31 raw
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
// 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 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) {
    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
    if (fwrite(obj, sizeof(INT_T), 1, out) != sizeof(INT_T)) fail("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("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) != sizeof(INT_T)) fail("Failed to read full integer");
#else
    UINT_T z = 0;
    for (size_t shift = 0;; shift += 7) {
        int i = fgetc(in);
        if (i == EOF) fail("Failed to read whole integer");
        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
}

#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 ? 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
PUREFUNC 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
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