diff options
Diffstat (limited to 'stdlib')
| -rw-r--r-- | stdlib/metamethods.c | 164 | ||||
| -rw-r--r-- | stdlib/types.c | 4 | ||||
| -rw-r--r-- | stdlib/types.h | 43 |
3 files changed, 183 insertions, 28 deletions
diff --git a/stdlib/metamethods.c b/stdlib/metamethods.c index 8243e644..0e7d3489 100644 --- a/stdlib/metamethods.c +++ b/stdlib/metamethods.c @@ -4,6 +4,7 @@ #include <string.h> #include "arrays.h" +#include "bools.h" #include "channels.h" #include "functiontype.h" #include "metamethods.h" @@ -15,6 +16,7 @@ #include "util.h" +#pragma GCC diagnostic ignored "-Wstack-protector" PUREFUNC public uint64_t generic_hash(const void *obj, const TypeInfo_t *type) { switch (type->tag) { @@ -23,8 +25,54 @@ PUREFUNC public uint64_t generic_hash(const void *obj, const TypeInfo_t *type) case ChannelInfo: return Channel$hash((Channel_t**)obj, type); case TableInfo: return Table$hash(obj, type); case OptionalInfo: return is_null(obj, type->OptionalInfo.type) ? 0 : generic_hash(obj, type->OptionalInfo.type); - case EmptyStructInfo: return 0; - case CustomInfo: case StructInfo: case EnumInfo: case CStringInfo: // These all share the same info + case StructInfo: { + if (type->StructInfo.num_fields == 0) { + return 0; + } else if (type->StructInfo.num_fields == 1) { + return generic_hash(obj, type->StructInfo.fields[0].type); + } else { + uint32_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)); + } + } + case EnumInfo: { + int32_t tag = *(int32_t*)obj; + uint32_t components[2] = {(uint32_t)tag, 0}; + + const TypeInfo_t *value = type->EnumInfo.tags[tag-1].type; + if (value && value->size > 0) { + ptrdiff_t byte_offset = sizeof(int32_t); + if (value->align && byte_offset % value->align > 0) + byte_offset += value->align - (byte_offset % value->align); + components[1] = generic_hash(obj + byte_offset, value); + } + return siphash24((void*)components, sizeof(components)); + + } + case CustomInfo: case CStringInfo: // These all share the same info if (!type->CustomInfo.hash) goto hash_data; return type->CustomInfo.hash(obj, type); @@ -52,8 +100,52 @@ PUREFUNC public int32_t generic_compare(const void *x, const void *y, const Type else if (x_is_null != y_is_null) return (int32_t)y_is_null - (int32_t)x_is_null; else return generic_compare(x, y, type->OptionalInfo.type); } - case EmptyStructInfo: return 0; - case CustomInfo: case StructInfo: case EnumInfo: case CStringInfo: // These all share the same info + case StructInfo: { + 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; + } + case EnumInfo: { + int32_t x_tag = *(int32_t*)x; + int32_t y_tag = *(int32_t*)y; + if (x_tag != y_tag) + return x_tag > y_tag ? 1 : -1; + + const TypeInfo_t *value = type->EnumInfo.tags[x_tag-1].type; + if (value && value->size > 0) { + ptrdiff_t byte_offset = sizeof(int32_t); + if (value->align && byte_offset % value->align > 0) + byte_offset += value->align - (byte_offset % value->align); + return generic_compare(x + byte_offset, y + byte_offset, value); + } + return 0; + } + case CustomInfo: case CStringInfo: // These all share the same info if (!type->CustomInfo.compare) goto compare_data; return type->CustomInfo.compare(x, y, type); @@ -73,7 +165,6 @@ PUREFUNC public bool generic_equal(const void *x, const void *y, const TypeInfo_ case ArrayInfo: return Array$equal(x, y, type); case ChannelInfo: return Channel$equal((Channel_t**)x, (Channel_t**)y, type); case TableInfo: return Table$equal(x, y, type); - case EmptyStructInfo: return true; case OptionalInfo: { bool x_is_null = is_null(x, type->OptionalInfo.type); bool y_is_null = is_null(y, type->OptionalInfo.type); @@ -81,7 +172,9 @@ PUREFUNC public bool generic_equal(const void *x, const void *y, const TypeInfo_ else if (x_is_null != y_is_null) return false; else return generic_equal(x, y, type->OptionalInfo.type); } - case CustomInfo: case StructInfo: case EnumInfo: case CStringInfo: // These all share the same info + case StructInfo: case EnumInfo: + return (generic_compare(x, y, type) == 0); + case CustomInfo: case CStringInfo: // These all share the same info if (!type->CustomInfo.equal) goto use_generic_compare; return type->CustomInfo.equal(x, y, type); @@ -102,15 +195,64 @@ public Text_t generic_as_text(const void *obj, bool colorize, const TypeInfo_t * case TableInfo: return Table$as_text(obj, colorize, type); case TypeInfoInfo: return Type$as_text(obj, colorize, type); case OptionalInfo: return Optional$as_text(obj, colorize, type); - case EmptyStructInfo: return colorize ? - Text$concat(Text("\x1b[0;1m"), Text$from_str(type->EmptyStructInfo.name), Text("\x1b[m()")) - : Text$concat(Text$from_str(type->EmptyStructInfo.name), Text("()")); - case CustomInfo: case StructInfo: case EnumInfo: case CStringInfo: // These all share the same info + case StructInfo: { + if (!obj) return Text$from_str(type->StructInfo.name); + + if (type->StructInfo.is_secret) + 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(")")); + } + case EnumInfo: { + if (!obj) return Text$from_str(type->EnumInfo.name); + + int32_t tag = *(int32_t*)obj; + NamedType_t value = type->EnumInfo.tags[tag-1]; + if (!value.type || value.type->size == 0) + return Text$format(colorize ? "\x1b[36;1m%s\x1b[m.\x1b[1m%s\x1b[m" : "%s.%s", type->EnumInfo.name, value.name); + + ptrdiff_t byte_offset = sizeof(int32_t); + if (value.type->align && byte_offset % value.type->align > 0) + byte_offset += value.type->align - (byte_offset % value.type->align); + return Text$concat(Text$format(colorize ? "\x1b[36;1m%s\x1b[m." : "%s.", type->EnumInfo.name), + generic_as_text(obj + byte_offset, colorize, value.type)); + } + case CustomInfo: case CStringInfo: // These all share the same info if (!type->CustomInfo.as_text) fail("No text function provided for type!\n"); return type->CustomInfo.as_text(obj, colorize, type); case OpaqueInfo: return Text("???"); - default: errx(1, "Invalid type tag: %d", type->tag); + default: fail("Invalid type tag: %d", type->tag); } } diff --git a/stdlib/types.c b/stdlib/types.c index ebc91563..073bb5b8 100644 --- a/stdlib/types.c +++ b/stdlib/types.c @@ -25,7 +25,7 @@ public Text_t Type$as_text(const void *typeinfo, bool colorize, const TypeInfo_t return Text$from_str(type->TypeInfoInfo.type_str); } -public const TypeInfo_t Void$info = {.size=0, .align=0, .tag=EmptyStructInfo}; -public const TypeInfo_t Abort$info = {.size=0, .align=0, .tag=EmptyStructInfo}; +public const TypeInfo_t Void$info = {.size=0, .align=0, .tag=StructInfo}; +public const TypeInfo_t Abort$info = {.size=0, .align=0, .tag=StructInfo}; // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/stdlib/types.h b/stdlib/types.h index 7e113dc0..f9b32af5 100644 --- a/stdlib/types.h +++ b/stdlib/types.h @@ -7,18 +7,23 @@ #include "datatypes.h" -struct TypeInfo_t; +typedef struct TypeInfo_s TypeInfo_t; -typedef uint64_t (*hash_fn_t)(const void*, const struct TypeInfo_t*); -typedef int32_t (*compare_fn_t)(const void*, const void*, const struct TypeInfo_t*); -typedef bool (*equal_fn_t)(const void*, const void*, const struct TypeInfo_t*); -typedef Text_t (*text_fn_t)(const void*, bool, const struct TypeInfo_t*); +typedef uint64_t (*hash_fn_t)(const void*, const TypeInfo_t*); +typedef int32_t (*compare_fn_t)(const void*, const void*, const TypeInfo_t*); +typedef bool (*equal_fn_t)(const void*, const void*, const TypeInfo_t*); +typedef Text_t (*text_fn_t)(const void*, bool, const TypeInfo_t*); -typedef struct TypeInfo_t { +typedef struct { + const char *name; + const TypeInfo_t *type; +} NamedType_t; + +struct TypeInfo_s { int64_t size, align; struct { // Anonymous tagged union for convenience - enum { CustomInfo, StructInfo, EnumInfo, PointerInfo, TextInfo, ArrayInfo, ChannelInfo, TableInfo, FunctionInfo, - OptionalInfo, TypeInfoInfo, OpaqueInfo, EmptyStructInfo, CStringInfo } tag; + enum { Invalid, CustomInfo, StructInfo, EnumInfo, PointerInfo, TextInfo, ArrayInfo, ChannelInfo, TableInfo, FunctionInfo, + OptionalInfo, TypeInfoInfo, OpaqueInfo, CStringInfo } tag; union { struct { equal_fn_t equal; @@ -28,16 +33,16 @@ typedef struct TypeInfo_t { } CustomInfo; struct { const char *sigil; - const struct TypeInfo_t *pointed; + const TypeInfo_t *pointed; } PointerInfo; struct { const char *lang; } TextInfo; struct { - const struct TypeInfo_t *item; + const TypeInfo_t *item; } ArrayInfo, ChannelInfo; struct { - const struct TypeInfo_t *key, *value; + const TypeInfo_t *key, *value; } TableInfo; struct { const char *type_str; @@ -46,16 +51,24 @@ typedef struct TypeInfo_t { const char *type_str; } TypeInfoInfo; struct { - const struct TypeInfo_t *type; + const TypeInfo_t *type; } OptionalInfo; #pragma GCC diagnostic ignored "-Wpedantic" struct {} OpaqueInfo; struct { const char *name; - } EmptyStructInfo; + int num_tags; + NamedType_t *tags; + } EnumInfo; + struct { + const char *name; + int num_fields; + bool is_secret:1; + NamedType_t *fields; + } StructInfo; }; }; -} TypeInfo_t; +}; #define Pointer$info(sigil_expr, pointed_info) &((TypeInfo_t){.size=sizeof(void*), .align=__alignof__(void*), \ .tag=PointerInfo, .PointerInfo={.sigil=sigil_expr, .pointed=pointed_info}}) @@ -73,7 +86,7 @@ typedef struct TypeInfo_t { .tag=FunctionInfo, .FunctionInfo.type_str=typestr}) #define Type$info(typestr) &((TypeInfo_t){.size=sizeof(TypeInfo_t), .align=__alignof__(TypeInfo_t), \ .tag=TypeInfoInfo, .TypeInfoInfo.type_str=typestr}) -#define Optional$info(t) &((TypeInfo_t){.size=(t)->size, .align=(t)->align, \ +#define Optional$info(_size, _align, t) &((TypeInfo_t){.size=_size, .align=_align, \ .tag=OptionalInfo, .OptionalInfo.type=t}) extern const TypeInfo_t Void$info; |
