aboutsummaryrefslogtreecommitdiff
path: root/stdlib
diff options
context:
space:
mode:
Diffstat (limited to 'stdlib')
-rw-r--r--stdlib/metamethods.c164
-rw-r--r--stdlib/types.c4
-rw-r--r--stdlib/types.h43
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;