diff --git a/enums.c b/enums.c index 2dcb714..354d975 100644 --- a/enums.c +++ b/enums.c @@ -46,9 +46,10 @@ void compile_enum_def(env_t *env, ast_t *ast) num_tags += 1; type_t *t = Table$str_get(*env->types, def->name); - CORD typeinfo = CORD_asprintf("public const TypeInfo_t %r = {%zu, %zu, .metamethods=Enum$metamethods, {.tag=EnumInfo, .EnumInfo={.name=\"%s\", " + const char *metamethods = is_packed_data(t) ? "PackedDataEnum$metamethods" : "Enum$metamethods"; + CORD typeinfo = CORD_asprintf("public const TypeInfo_t %r = {%zu, %zu, .metamethods=%s, {.tag=EnumInfo, .EnumInfo={.name=\"%s\", " ".num_tags=%d, .tags=(NamedType_t[]){", - full_name, type_size(t), type_align(t), def->name, num_tags); + full_name, type_size(t), type_align(t), metamethods, def->name, num_tags); for (tag_ast_t *tag = def->tags; tag; tag = tag->next) { const char *tag_type_name = heap_strf("%s$%s", def->name, tag->name); diff --git a/stdlib/enums.h b/stdlib/enums.h index 35c3a22..a61daac 100644 --- a/stdlib/enums.h +++ b/stdlib/enums.h @@ -25,4 +25,14 @@ void Enum$deserialize(FILE *in, void *outval, Array_t *pointers, const TypeInfo_ .deserialize=Enum$deserialize, \ }) +#define PackedDataEnum$metamethods ((metamethods_t){ \ + .hash=PackedData$hash, \ + .compare=Enum$compare, \ + .equal=PackedData$equal, \ + .as_text=Enum$as_text, \ + .is_none=Enum$is_none, \ + .serialize=Enum$serialize, \ + .deserialize=Enum$deserialize, \ +}) + // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/stdlib/structs.c b/stdlib/structs.c index 010b49f..1ff5a91 100644 --- a/stdlib/structs.c +++ b/stdlib/structs.c @@ -53,6 +53,14 @@ PUREFUNC public uint64_t Struct$hash(const void *obj, const TypeInfo_t *type) } #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) @@ -122,6 +130,12 @@ PUREFUNC public bool Struct$equal(const void *x, const void *y, const TypeInfo_t 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); diff --git a/stdlib/structs.h b/stdlib/structs.h index 6553cc8..b027a01 100644 --- a/stdlib/structs.h +++ b/stdlib/structs.h @@ -8,8 +8,10 @@ #include "util.h" PUREFUNC uint64_t Struct$hash(const void *obj, const TypeInfo_t *type); +PUREFUNC uint64_t PackedData$hash(const void *obj, const TypeInfo_t *type); PUREFUNC int32_t Struct$compare(const void *x, const void *y, const TypeInfo_t *type); PUREFUNC bool Struct$equal(const void *x, const void *y, const TypeInfo_t *type); +PUREFUNC bool PackedData$equal(const void *x, const void *y, const TypeInfo_t *type); PUREFUNC Text_t Struct$as_text(const void *obj, bool colorize, const TypeInfo_t *type); PUREFUNC bool Struct$is_none(const void *obj, const TypeInfo_t *type); void Struct$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type); @@ -25,4 +27,14 @@ void Struct$deserialize(FILE *in, void *outval, Array_t *pointers, const TypeInf .deserialize=Struct$deserialize, \ }) +#define PackedData$metamethods ((metamethods_t){ \ + .hash=PackedData$hash, \ + .compare=Struct$compare, \ + .equal=PackedData$equal, \ + .as_text=Struct$as_text, \ + .is_none=Struct$is_none, \ + .serialize=Struct$serialize, \ + .deserialize=Struct$deserialize, \ +}) + // vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/structs.c b/structs.c index ce5116e..8e18426 100644 --- a/structs.c +++ b/structs.c @@ -27,10 +27,11 @@ void compile_struct_def(env_t *env, ast_t *ast) short_name = strrchr(short_name, '$') + 1; env->code->typeinfos = CORD_all("public const TypeInfo_t ", full_name, ";\n", env->code->typeinfos); - CORD typeinfo = CORD_asprintf("public const TypeInfo_t %r = {.size=%zu, .align=%zu, .metamethods=Struct$metamethods, " + const char *metamethods = is_packed_data(t) ? "PackedData$metamethods" : "Struct$metamethods"; + CORD typeinfo = CORD_asprintf("public const TypeInfo_t %r = {.size=%zu, .align=%zu, .metamethods=%s, " ".tag=StructInfo, .StructInfo.name=\"%s\"%s, " ".StructInfo.num_fields=%ld", - full_name, type_size(t), type_align(t), short_name, def->secret ? ", .StructInfo.is_secret=true" : "", + full_name, type_size(t), type_align(t), metamethods, short_name, def->secret ? ", .StructInfo.is_secret=true" : "", num_fields); if (def->fields) { typeinfo = CORD_asprintf("%r, .StructInfo.fields=(NamedType_t[%d]){", typeinfo, num_fields); diff --git a/types.c b/types.c index 2b421b4..5c37a68 100644 --- a/types.c +++ b/types.c @@ -444,21 +444,55 @@ PUREFUNC bool is_numeric_type(type_t *t) return t->tag == IntType || t->tag == BigIntType || t->tag == NumType || t->tag == ByteType; } +PUREFUNC bool is_packed_data(type_t *t) +{ + if (t->tag == IntType || t->tag == NumType || t->tag == ByteType || t->tag == PointerType || t->tag == BoolType || t->tag == FunctionType) { + return true; + } else if (t->tag == StructType) { + for (arg_t *field = Match(t, StructType)->fields; field; field = field->next) { + if (!is_packed_data(field->type)) + return false; + } + return true; + } else if (t->tag == EnumType) { + for (tag_t *tag = Match(t, EnumType)->tags; tag; tag = tag->next) { + if (!is_packed_data(tag->type)) + return false; + } + return true; + } else { + return false; + } +} + PUREFUNC size_t unpadded_struct_size(type_t *t) { arg_t *fields = Match(t, StructType)->fields; size_t size = 0; + size_t bit_offset = 0; for (arg_t *field = fields; field; field = field->next) { type_t *field_type = field->type; if (field_type->tag == BoolType) { - size += 1; // Bit packing + bit_offset += 1; + if (bit_offset >= 8) { + size += 1; + bit_offset = 0; + } } else { + if (bit_offset > 0) { + size += 1; + bit_offset = 0; + } size_t align = type_align(field_type); if (align > 1 && size % align > 0) size += align - (size % align); // Padding size += type_size(field_type); } } + if (bit_offset > 0) { + size += 1; + bit_offset = 0; + } return size; } diff --git a/types.h b/types.h index 0163c4d..2909883 100644 --- a/types.h +++ b/types.h @@ -150,6 +150,7 @@ PUREFUNC bool can_promote(type_t *actual, type_t *needed); PUREFUNC const char *enum_single_value_tag(type_t *enum_type, type_t *t); PUREFUNC bool is_int_type(type_t *t); PUREFUNC bool is_numeric_type(type_t *t); +PUREFUNC bool is_packed_data(type_t *t); PUREFUNC size_t type_size(type_t *t); PUREFUNC size_t type_align(type_t *t); PUREFUNC size_t unpadded_struct_size(type_t *t);