diff options
Diffstat (limited to 'src/stdlib/structs.c')
| -rw-r--r-- | src/stdlib/structs.c | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/src/stdlib/structs.c b/src/stdlib/structs.c new file mode 100644 index 00000000..ca88262c --- /dev/null +++ b/src/stdlib/structs.c @@ -0,0 +1,237 @@ +// Metamethods for structs + +#include <stdint.h> +#include <string.h> + +#include "arrays.h" +#include "bools.h" +#include "functiontype.h" +#include "metamethods.h" +#include "optionals.h" +#include "pointers.h" +#include "siphash.h" +#include "tables.h" +#include "text.h" +#include "util.h" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstack-protector" +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)); +} +#pragma GCC diagnostic pop + +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); + + if (type->StructInfo.is_secret || type->StructInfo.is_opaque) + return Text$format(colorize ? "\x1b[0;1m%s\x1b[m(...)" : "%s(...)", type->StructInfo.name); + + Text_t text = Text$format(colorize ? "\x1b[0;1m%s\x1b[m(" : "%s(", type->StructInfo.name); + 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(")")); +} + +PUREFUNC public bool Struct$is_none(const void *obj, const TypeInfo_t *type) +{ + return *(bool*)(obj + type->size); +} + +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, Array_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; + } + } +} + +// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 |
