main
Download
217 lines 7.9 KiBCModified 2025-12-23 raw
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
// Metamethods for structs

#include <stdint.h>
#include <string.h>

#include "bools.h"
#include "metamethods.h"
#include "siphash.h"
#include "structs.h"
#include "text.h"
#include "util.h"

#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstack-protector"
#endif
PUREFUNC public uint64_t Struct$hash(const void *obj, const TypeInfo_t *type) {
    if (type->StructInfo.num_fields == 0) return 0;

    if (type->StructInfo.num_fields == 1) return generic_hash(obj, type->StructInfo.fields[0].type);

    uint64_t field_hashes[type->StructInfo.num_fields];
    ptrdiff_t byte_offset = 0;
    ptrdiff_t bit_offset = 0;
    for (int i = 0; i < type->StructInfo.num_fields; i++) {
        NamedType_t field = type->StructInfo.fields[i];
        if (field.type == &Bool$info) {
            bool b = ((*(char *)(obj + byte_offset)) >> bit_offset) & 0x1;
            field_hashes[i] = (uint32_t)b;
            bit_offset += 1;
            if (bit_offset >= 8) {
                byte_offset += 1;
                bit_offset = 0;
            }
        } else {
            if (bit_offset > 0) {
                byte_offset += 1;
                bit_offset = 0;
            }
            if (field.type->align && byte_offset % field.type->align > 0)
                byte_offset += field.type->align - (byte_offset % field.type->align);
            field_hashes[i] = generic_hash(obj + byte_offset, field.type);
            byte_offset += field.type->size;
        }
    }
    return siphash24((void *)field_hashes, sizeof(field_hashes));
}
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif

PUREFUNC public uint64_t PackedData$hash(const void *obj, const TypeInfo_t *type) {
    if (type->StructInfo.num_fields == 0) return 0;

    return siphash24(obj, (size_t)type->size);
}

PUREFUNC public int32_t Struct$compare(const void *x, const void *y, const TypeInfo_t *type) {
    if (x == y) return 0;

    ptrdiff_t byte_offset = 0;
    ptrdiff_t bit_offset = 0;
    for (int i = 0; i < type->StructInfo.num_fields; i++) {
        NamedType_t field = type->StructInfo.fields[i];
        if (field.type == &Bool$info) {
            bool bx = ((*(char *)(x + byte_offset)) >> bit_offset) & 0x1;
            bool by = ((*(char *)(y + byte_offset)) >> bit_offset) & 0x1;
            if (bx != by) return (int32_t)bx - (int32_t)by;
            bit_offset += 1;
            if (bit_offset >= 8) {
                byte_offset += 1;
                bit_offset = 0;
            }
        } else {
            if (bit_offset > 0) {
                byte_offset += 1;
                bit_offset = 0;
            }
            if (field.type->align && byte_offset % field.type->align > 0)
                byte_offset += field.type->align - (byte_offset % field.type->align);
            int32_t cmp = generic_compare(x + byte_offset, y + byte_offset, field.type);
            if (cmp != 0) return cmp;
            byte_offset += field.type->size;
        }
    }
    return 0;
}

PUREFUNC public bool Struct$equal(const void *x, const void *y, const TypeInfo_t *type) {
    if (x == y) return true;

    ptrdiff_t byte_offset = 0;
    ptrdiff_t bit_offset = 0;
    for (int i = 0; i < type->StructInfo.num_fields; i++) {
        NamedType_t field = type->StructInfo.fields[i];
        if (field.type == &Bool$info) {
            bool bx = ((*(char *)(x + byte_offset)) >> bit_offset) & 0x1;
            bool by = ((*(char *)(y + byte_offset)) >> bit_offset) & 0x1;
            if (bx != by) return false;
            bit_offset += 1;
            if (bit_offset >= 8) {
                byte_offset += 1;
                bit_offset = 0;
            }
        } else {
            if (bit_offset > 0) {
                byte_offset += 1;
                bit_offset = 0;
            }
            if (field.type->align && byte_offset % field.type->align > 0)
                byte_offset += field.type->align - (byte_offset % field.type->align);
            if (!generic_equal(x + byte_offset, y + byte_offset, field.type)) return false;
            byte_offset += field.type->size;
        }
    }
    return true;
}

PUREFUNC public bool PackedData$equal(const void *x, const void *y, const TypeInfo_t *type) {
    if (x == y) return true;
    return (memcmp(x, y, (size_t)type->size) == 0);
}

PUREFUNC public Text_t Struct$as_text(const void *obj, bool colorize, const TypeInfo_t *type) {
    if (!obj) return Text$from_str(type->StructInfo.name);

    Text_t name = Text$from_str(type->StructInfo.name);
    if (type->StructInfo.is_secret || type->StructInfo.is_opaque) {
        return colorize ? Text$concat(Text("\x1b[0;1m"), name, Text("\x1b[m(...)")) : Text$concat(name, Text("(...)"));
    }

    Text_t text = colorize ? Text$concat(Text("\x1b[0;1m"), name, Text("\x1b[m(")) : Text$concat(name, Text("("));
    ptrdiff_t byte_offset = 0;
    ptrdiff_t bit_offset = 0;
    for (int i = 0; i < type->StructInfo.num_fields; i++) {
        NamedType_t field = type->StructInfo.fields[i];
        if (i > 0) text = Text$concat(text, Text(", "));

        if (type->StructInfo.num_fields > 1) text = Text$concat(text, Text$from_str(field.name), Text("="));

        if (field.type == &Bool$info) {
            bool b = ((*(char *)(obj + byte_offset)) >> bit_offset) & 0x1;
            text = Text$concat(
                text, Text$from_str(colorize ? (b ? "\x1b[35myes\x1b[m" : "\x1b[35mno\x1b[m") : (b ? "yes" : "no")));
            bit_offset += 1;
            if (bit_offset >= 8) {
                byte_offset += 1;
                bit_offset = 0;
            }
        } else {
            if (bit_offset > 0) {
                byte_offset += 1;
                bit_offset = 0;
            }
            if (field.type->align && byte_offset % field.type->align > 0)
                byte_offset += field.type->align - (byte_offset % field.type->align);
            text = Text$concat(text, generic_as_text(obj + byte_offset, colorize, field.type));
            byte_offset += field.type->size;
        }
    }
    return Text$concat(text, Text(")"));
}

public

void Struct$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type) {
    ptrdiff_t byte_offset = 0;
    ptrdiff_t bit_offset = 0;
    for (int i = 0; i < type->StructInfo.num_fields; i++) {
        NamedType_t field = type->StructInfo.fields[i];
        if (field.type == &Bool$info) {
            bool b = ((*(char *)(obj + byte_offset)) >> bit_offset) & 0x1;
            fputc((int)b, out);
            bit_offset += 1;
            if (bit_offset >= 8) {
                byte_offset += 1;
                bit_offset = 0;
            }
        } else {
            if (bit_offset > 0) {
                byte_offset += 1;
                bit_offset = 0;
            }
            if (field.type->align && byte_offset % field.type->align > 0)
                byte_offset += field.type->align - (byte_offset % field.type->align);
            _serialize(obj + byte_offset, out, pointers, field.type);
            byte_offset += field.type->size;
        }
    }
}

public
void Struct$deserialize(FILE *in, void *outval, List_t *pointers, const TypeInfo_t *type) {
    ptrdiff_t byte_offset = 0;
    ptrdiff_t bit_offset = 0;
    for (int i = 0; i < type->StructInfo.num_fields; i++) {
        NamedType_t field = type->StructInfo.fields[i];
        if (field.type == &Bool$info) {
            bool b = (bool)fgetc(in);
            *(char *)(outval + byte_offset) |= (b << bit_offset);
            bit_offset += 1;
            if (bit_offset >= 8) {
                byte_offset += 1;
                bit_offset = 0;
            }
        } else {
            if (bit_offset > 0) {
                byte_offset += 1;
                bit_offset = 0;
            }
            if (field.type->align && byte_offset % field.type->align > 0)
                byte_offset += field.type->align - (byte_offset % field.type->align);
            _deserialize(in, outval + byte_offset, pointers, field.type);
            byte_offset += field.type->size;
        }
    }
}