diff --git a/ast.c b/ast.c
index 750245f..a191608 100644
--- a/ast.c
+++ b/ast.c
@@ -161,6 +161,8 @@ CORD ast_to_xml(ast_t *ast)
T(DocTest, "%r", optional_tagged("expression", data.expr), xml_escape(data.output))
T(Use, "", optional_tagged("var", data.var), xml_escape(data.path))
T(InlineCCode, "%r", xml_escape(data.code))
+ T(Serialize, "%r", ast_to_xml(data.value))
+ T(Deserialize, "%r%r", type_ast_to_xml(data.type), ast_to_xml(data.value))
default: return "???";
#undef T
}
diff --git a/ast.h b/ast.h
index 01ca953..0cd5a03 100644
--- a/ast.h
+++ b/ast.h
@@ -145,6 +145,7 @@ typedef enum {
DocTest,
Use,
InlineCCode,
+ Serialize, Deserialize,
} ast_e;
struct ast_s {
@@ -326,6 +327,13 @@ struct ast_s {
struct type_s *type;
type_ast_t *type_ast;
} InlineCCode;
+ struct {
+ ast_t *value;
+ } Serialize;
+ struct {
+ ast_t *value;
+ type_ast_t *type;
+ } Deserialize;
} __data;
};
diff --git a/compile.c b/compile.c
index a426d09..4e094df 100644
--- a/compile.c
+++ b/compile.c
@@ -50,7 +50,7 @@ CORD promote_to_optional(type_t *t, CORD code)
} else if (t->tag == ByteType) {
return CORD_all("((OptionalByte_t){", code, "})");
} else if (t->tag == StructType) {
- return CORD_all("({ ", compile_type(Type(OptionalType, .type=t)), " nonnull = {.value=", code, "}; nonnull.is_null = false; nonnull; })");
+ return CORD_all("({ ", compile_type(Type(OptionalType, .type=t)), " nonnull = {.value=", code, "}; nonnull.is_none = false; nonnull; })");
} else {
return code;
}
@@ -205,6 +205,7 @@ CORD compile_declaration(type_t *t, CORD name)
code = CORD_all(code, compile_type(arg->type));
if (arg->next) code = CORD_cat(code, ", ");
}
+ if (!fn->args) code = CORD_all(code, "void");
return CORD_all(code, ")");
} else if (t->tag != ModuleType) {
return CORD_all(compile_type(t), " ", name);
@@ -269,6 +270,8 @@ CORD compile_type(type_t *t)
code = CORD_all(code, compile_type(arg->type));
if (arg->next) code = CORD_cat(code, ", ");
}
+ if (!fn->args)
+ code = CORD_all(code, "void");
return CORD_all(code, ")");
}
case ClosureType: return "Closure_t";
@@ -425,7 +428,7 @@ CORD check_null(type_t *t, CORD value)
else if (t->tag == TextType)
return CORD_all("((", value, ").length < 0)");
else if (t->tag == IntType || t->tag == ByteType || t->tag == StructType)
- return CORD_all("(", value, ").is_null");
+ return CORD_all("(", value, ").is_none");
else if (t->tag == EnumType)
return CORD_all("((", value, ").tag == 0)");
else if (t->tag == MomentType)
@@ -911,7 +914,7 @@ CORD compile_statement(env_t *env, ast_t *ast)
all_args = CORD_all(all_args, "$", arg->name, arg->next ? ", " : CORD_EMPTY);
CORD pop_code = CORD_EMPTY;
- if (fndef->cache->tag == Int && !cache_size.is_null && cache_size.i > 0) {
+ if (fndef->cache->tag == Int && !cache_size.is_none && cache_size.i > 0) {
pop_code = CORD_all("if (cache.entries.length > ", CORD_asprintf("%ld", cache_size.i),
") Table$remove(&cache, cache.entries.data + cache.entries.stride*RNG$int64(default_rng, I(0), I(cache.entries.length-1)), table_type);\n");
}
@@ -1904,7 +1907,7 @@ CORD compile_null(type_t *t)
case PointerType: return CORD_all("((", compile_type(t), ")NULL)");
case ClosureType: return "NONE_CLOSURE";
case NumType: return "nan(\"null\")";
- case StructType: return CORD_all("((", compile_type(Type(OptionalType, .type=t)), "){.is_null=yes})");
+ case StructType: return CORD_all("((", compile_type(Type(OptionalType, .type=t)), "){.is_none=true})");
case EnumType: {
env_t *enum_env = Match(t, EnumType)->env;
return CORD_all("((", compile_type(t), "){", namespace_prefix(enum_env, enum_env->namespace), "null})");
@@ -2723,6 +2726,14 @@ CORD compile(env_t *env, ast_t *ast)
case MethodCall: {
auto call = Match(ast, MethodCall);
type_t *self_t = get_type(env, call->self);
+
+ if (streq(call->name, "serialize")) {
+ if (call->args)
+ code_err(ast, ":serialize() doesn't take any arguments");
+ return CORD_all("generic_serialize((", compile_declaration(self_t, "[1]"), "){",
+ compile(env, call->self), "}, ", compile_type_info(env, self_t), ")");
+ }
+
int64_t pointer_depth = 0;
type_t *self_value_t = self_t;
for (; self_value_t->tag == PointerType; self_value_t = Match(self_value_t, PointerType)->pointed)
@@ -3191,6 +3202,21 @@ CORD compile(env_t *env, ast_t *ast)
code_err(call->fn, "This is not a function, it's a %T", fn_t);
}
}
+ case Serialize: {
+ ast_t *value = Match(ast, Serialize)->value;
+ type_t *t = get_type(env, value);
+ return CORD_all("generic_serialize((", compile_declaration(t, "[1]"), "){", compile(env, value), "}, ", compile_type_info(env, t), ")");
+ }
+ case Deserialize: {
+ ast_t *value = Match(ast, Deserialize)->value;
+ type_t *value_type = get_type(env, value);
+ if (!type_eq(value_type, Type(ArrayType, Type(ByteType))))
+ code_err(value, "This value should be an array of bytes, not a %T", value_type);
+ type_t *t = parse_type_ast(env, Match(ast, Deserialize)->type);
+ return CORD_all("({ ", compile_declaration(t, "deserialized"), ";\n"
+ "generic_deserialize(", compile(env, value), ", &deserialized, ", compile_type_info(env, t), ");\n"
+ "deserialized; })");
+ }
case When: {
auto original = Match(ast, When);
ast_t *when_var = WrapAST(ast, Var, .name="when");
diff --git a/parse.c b/parse.c
index 407710b..152a770 100644
--- a/parse.c
+++ b/parse.c
@@ -54,6 +54,7 @@ static const char *keywords[] = {
"yes", "xor", "while", "when", "use", "unless", "struct", "stop", "skip", "return",
"or", "not", "no", "mod1", "mod", "pass", "lang", "inline", "in", "if",
"func", "for", "extern", "enum", "else", "do", "defer", "and", "NONE", "_min_", "_max_",
+ "DESERIALIZE",
NULL,
};
@@ -139,6 +140,7 @@ static PARSER(parse_use);
static PARSER(parse_var);
static PARSER(parse_when);
static PARSER(parse_while);
+static PARSER(parse_deserialize);
//
// Print a parse error and exit (or use the on_err longjmp)
@@ -1547,6 +1549,22 @@ PARSER(parse_none) {
return NewAST(ctx->file, start, type->end, None, .type=type);
}
+PARSER(parse_deserialize) {
+ const char *start = pos;
+ if (!match_word(&pos, "DESERIALIZE"))
+ return NULL;
+
+ spaces(&pos);
+ ast_t *value = expect(ctx, start, &pos, parse_parens, "I expected an expression here");
+
+ spaces(&pos);
+ if (!match(&pos, ":"))
+ parser_err(ctx, pos, pos, "I expected a ':' and a type here");
+
+ type_ast_t *type = expect(ctx, start, &pos, parse_type, "I couldn't parse the type for this deserialization");
+ return NewAST(ctx->file, start, pos, Deserialize, .value=value, .type=type);
+}
+
PARSER(parse_var) {
const char *start = pos;
const char* name = get_id(&pos);
@@ -1572,6 +1590,7 @@ PARSER(parse_term_no_suffix) {
|| (term=parse_parens(ctx, pos))
|| (term=parse_table(ctx, pos))
|| (term=parse_set(ctx, pos))
+ || (term=parse_deserialize(ctx, pos))
|| (term=parse_var(ctx, pos))
|| (term=parse_array(ctx, pos))
|| (term=parse_channel(ctx, pos))
diff --git a/stdlib/arrays.c b/stdlib/arrays.c
index ff21920..8dcc48a 100644
--- a/stdlib/arrays.c
+++ b/stdlib/arrays.c
@@ -6,6 +6,7 @@
#include
#include "arrays.h"
+#include "integers.h"
#include "metamethods.h"
#include "optionals.h"
#include "rng.h"
@@ -701,4 +702,46 @@ public PUREFUNC bool Array$is_none(const void *obj, const TypeInfo_t*)
return ((Array_t*)obj)->length < 0;
}
+public void Array$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type)
+{
+ Array_t arr = *(Array_t*)obj;
+ int64_t len = arr.length;
+ Int64$serialize(&len, out, pointers, &Int64$info);
+ auto item_serialize = type->ArrayInfo.item->metamethods.serialize;
+ if (item_serialize) {
+ for (int64_t i = 0; i < len; i++)
+ item_serialize(arr.data + i*arr.stride, out, pointers, type->ArrayInfo.item);
+ } else if (arr.stride == type->ArrayInfo.item->size) {
+ fwrite(arr.data, (size_t)type->ArrayInfo.item->size, (size_t)len, out);
+ } else {
+ for (int64_t i = 0; i < len; i++)
+ fwrite(arr.data + i*arr.stride, (size_t)type->ArrayInfo.item->size, 1, out);
+ }
+}
+
+public void Array$deserialize(FILE *in, void *obj, Array_t *pointers, const TypeInfo_t *type)
+{
+ int64_t len = -1;
+ Int64$deserialize(in, &len, pointers, &Int64$info);
+ int64_t padded_size = type->ArrayInfo.item->size;
+ if (type->ArrayInfo.item->align > 0 && padded_size % type->ArrayInfo.item->align > 0)
+ padded_size += type->ArrayInfo.item->align - (padded_size % type->ArrayInfo.item->align);
+ Array_t arr = {
+ .length=len,
+ .data=GC_MALLOC((size_t)(len*padded_size)),
+ .stride=padded_size,
+ };
+ auto item_deserialize = type->ArrayInfo.item->metamethods.deserialize;
+ if (item_deserialize) {
+ for (int64_t i = 0; i < len; i++)
+ item_deserialize(in, arr.data + i*arr.stride, pointers, type->ArrayInfo.item);
+ } else if (arr.stride == type->ArrayInfo.item->size) {
+ fread(arr.data, (size_t)type->ArrayInfo.item->size, (size_t)len, in);
+ } else {
+ for (int64_t i = 0; i < len; i++)
+ fread(arr.data + i*arr.stride, (size_t)type->ArrayInfo.item->size, 1, in);
+ }
+ *(Array_t*)obj = arr;
+}
+
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
diff --git a/stdlib/arrays.h b/stdlib/arrays.h
index 192de80..4f7161b 100644
--- a/stdlib/arrays.h
+++ b/stdlib/arrays.h
@@ -100,6 +100,8 @@ void Array$heap_pop(Array_t *heap, Closure_t comparison, int64_t padded_item_siz
Int_t Array$binary_search(Array_t array, void *target, Closure_t comparison);
#define Array$binary_search_value(array, target, comparison) \
({ __typeof(target) _target = target; Array$binary_search(array, &_target, comparison); })
+void Array$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type);
+void Array$deserialize(FILE *in, void *obj, Array_t *pointers, const TypeInfo_t *type);
#define Array$metamethods ((metamethods_t){ \
.as_text=Array$as_text, \
@@ -107,6 +109,8 @@ Int_t Array$binary_search(Array_t array, void *target, Closure_t comparison);
.equal=Array$equal, \
.hash=Array$hash, \
.is_none=Array$is_none, \
+ .serialize=Array$serialize, \
+ .deserialize=Array$deserialize, \
})
#define Array$info(item_info) &((TypeInfo_t){.size=sizeof(Array_t), .align=__alignof__(Array_t), \
diff --git a/stdlib/bytes.h b/stdlib/bytes.h
index 757f986..9bd4e1c 100644
--- a/stdlib/bytes.h
+++ b/stdlib/bytes.h
@@ -32,9 +32,9 @@ Text_t Byte$hex(Byte_t byte, bool uppercase, bool prefix);
typedef struct {
Byte_t value;
- bool is_null:1;
+ bool has_value:1;
} OptionalByte_t;
-#define NONE_BYTE ((OptionalByte_t){.is_null=true})
+#define NONE_BYTE ((OptionalByte_t){.has_value=false})
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
diff --git a/stdlib/c_strings.c b/stdlib/c_strings.c
index 99632a5..45f69e1 100644
--- a/stdlib/c_strings.c
+++ b/stdlib/c_strings.c
@@ -4,6 +4,7 @@
#include
#include
#include
+#include
#include
#include "text.h"
@@ -50,6 +51,24 @@ PUREFUNC public bool CString$is_none(const void *c_str, const TypeInfo_t*)
return *(char**)c_str == NULL;
}
+static void CString$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t*)
+{
+ const char *str = *(const char **)obj;
+ int64_t len = (int64_t)strlen(str);
+ Int64$serialize(&len, out, pointers, &Int64$info);
+ fwrite(str, sizeof(char), (size_t)len, out);
+}
+
+static void CString$deserialize(FILE *in, void *out, Array_t *pointers, const TypeInfo_t *)
+{
+ int64_t len = -1;
+ Int64$deserialize(in, &len, pointers, &Int64$info);
+ char *str = GC_MALLOC_ATOMIC((size_t)len+1);
+ fread(str, sizeof(char), (size_t)len, in);
+ str[len+1] = '\0';
+ *(char**)out = str;
+}
+
public const TypeInfo_t CString$info = {
.size=sizeof(char*),
.align=__alignof__(char*),
@@ -59,6 +78,8 @@ public const TypeInfo_t CString$info = {
.equal=CString$equal,
.as_text=CString$as_text,
.is_none=CString$is_none,
+ .serialize=CString$serialize,
+ .deserialize=CString$deserialize,
},
};
diff --git a/stdlib/channels.c b/stdlib/channels.c
index 8ab4e66..ee7ebde 100644
--- a/stdlib/channels.c
+++ b/stdlib/channels.c
@@ -137,4 +137,21 @@ public PUREFUNC bool Channel$is_none(const void *obj, const TypeInfo_t*)
return *(void**)obj == NULL;
}
+public void Channel$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type)
+{
+ Channel_t *channel = (Channel_t*)obj;
+ Array$serialize(&channel->items, out, pointers, Array$info(type->ChannelInfo.item));
+ Int64$serialize(&channel->max_size, out, pointers, &Int64$info);
+}
+
+public void Channel$deserialize(FILE *in, void *outval, Array_t *pointers, const TypeInfo_t *type)
+{
+ Channel_t *channel = new(Channel_t);
+ channel->mutex = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
+ channel->cond = (pthread_cond_t)PTHREAD_COND_INITIALIZER;
+ Array$deserialize(in, &channel->items, pointers, Array$info(type->ChannelInfo.item));
+ Int64$deserialize(in, &channel->max_size, pointers, &Int64$info);
+ *(Channel_t**)outval = channel;
+}
+
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
diff --git a/stdlib/channels.h b/stdlib/channels.h
index 9a84810..cd0a82f 100644
--- a/stdlib/channels.h
+++ b/stdlib/channels.h
@@ -25,6 +25,8 @@ PUREFUNC int32_t Channel$compare(const void *x, const void *y, const TypeInfo_t
PUREFUNC bool Channel$equal(const void *x, const void *y, const TypeInfo_t *type);
Text_t Channel$as_text(const void *channel, bool colorize, const TypeInfo_t *type);
PUREFUNC bool Channel$is_none(const void *obj, const TypeInfo_t*);
+void Channel$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t*);
+void Channel$deserialize(FILE *in, void *outval, Array_t *pointers, const TypeInfo_t*);
#define Channel$metamethods ((metamethods_t){ \
.as_text=Channel$as_text, \
@@ -32,6 +34,8 @@ PUREFUNC bool Channel$is_none(const void *obj, const TypeInfo_t*);
.equal=Channel$equal, \
.hash=Channel$hash, \
.is_none=Channel$is_none, \
+ .serialize=Channel$serialize, \
+ .deserialize=Channel$deserialize, \
})
#define Channel$info(item_info) &((TypeInfo_t){.size=sizeof(Channel_t), .align=__alignof__(Channel_t), \
diff --git a/stdlib/enums.c b/stdlib/enums.c
index ea2a443..a797c11 100644
--- a/stdlib/enums.c
+++ b/stdlib/enums.c
@@ -7,6 +7,7 @@
#include "bools.h"
#include "channels.h"
#include "functiontype.h"
+#include "integers.h"
#include "metamethods.h"
#include "optionals.h"
#include "pointers.h"
@@ -89,4 +90,32 @@ PUREFUNC public bool Enum$is_none(const void *x, const TypeInfo_t*)
return *(int32_t*)x == 0;
}
+public void Enum$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type)
+{
+ int32_t tag = *(int32_t*)obj;
+ Int32$serialize(&tag, out, pointers, &Int32$info);
+
+ NamedType_t value = type->EnumInfo.tags[tag-1];
+ if (value.type && value.type->size > 0) {
+ 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);
+ _serialize(obj + byte_offset, out, pointers, value.type);
+ }
+}
+
+public void Enum$deserialize(FILE *in, void *outval, Array_t *pointers, const TypeInfo_t *type)
+{
+ int32_t tag = 0;
+ Int32$deserialize(in, &tag, pointers, &Int32$info);
+
+ NamedType_t value = type->EnumInfo.tags[tag-1];
+ if (value.type && value.type->size > 0) {
+ 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);
+ _deserialize(in, outval + byte_offset, pointers, value.type);
+ }
+}
+
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
diff --git a/stdlib/enums.h b/stdlib/enums.h
index 8afa5c9..35c3a22 100644
--- a/stdlib/enums.h
+++ b/stdlib/enums.h
@@ -12,6 +12,8 @@ PUREFUNC int32_t Enum$compare(const void *x, const void *y, const TypeInfo_t *ty
PUREFUNC bool Enum$equal(const void *x, const void *y, const TypeInfo_t *type);
PUREFUNC Text_t Enum$as_text(const void *obj, bool colorize, const TypeInfo_t *type);
PUREFUNC bool Enum$is_none(const void *obj, const TypeInfo_t *type);
+void Enum$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type);
+void Enum$deserialize(FILE *in, void *outval, Array_t *pointers, const TypeInfo_t *type);
#define Enum$metamethods ((metamethods_t){ \
.as_text=Enum$as_text, \
@@ -19,6 +21,8 @@ PUREFUNC bool Enum$is_none(const void *obj, const TypeInfo_t *type);
.equal=Enum$equal, \
.hash=Enum$hash, \
.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/functiontype.h b/stdlib/functiontype.h
index 7745bff..4dfef3b 100644
--- a/stdlib/functiontype.h
+++ b/stdlib/functiontype.h
@@ -2,6 +2,7 @@
#include
+#include "metamethods.h"
#include "types.h"
#include "util.h"
@@ -15,6 +16,8 @@ PUREFUNC bool Func$is_none(const void *obj, const TypeInfo_t*);
#define Func$metamethods ((metamethods_t){ \
.as_text=Func$as_text, \
.is_none=Func$is_none, \
+ .serialize=cannot_serialize, \
+ .deserialize=cannot_deserialize, \
})
#define Function$info(typestr) &((TypeInfo_t){.size=sizeof(void*), .align=__alignof__(void*), \
diff --git a/stdlib/integers.c b/stdlib/integers.c
index 6c00527..b40657b 100644
--- a/stdlib/integers.c
+++ b/stdlib/integers.c
@@ -1,4 +1,6 @@
// Integer type infos and methods
+#include // Must be before gmp.h
+
#include
#include
#include
@@ -367,6 +369,21 @@ static bool Int$is_none(const void *i, const TypeInfo_t*)
return ((Int_t*)i)->small == 0;
}
+static void Int$serialize(const void *obj, FILE *out, Table_t*, const TypeInfo_t*)
+{
+ mpz_t n;
+ mpz_init_set_int(n, *(Int_t*)obj);
+ mpz_out_raw(out, n);
+}
+
+static void Int$deserialize(FILE *in, void *obj, Array_t*, const TypeInfo_t*)
+{
+ mpz_t n;
+ mpz_init(n);
+ mpz_inp_raw(n, in);
+ *(Int_t*)obj = Int$from_mpz(n);
+}
+
public const TypeInfo_t Int$info = {
.size=sizeof(Int_t),
.align=__alignof__(Int_t),
@@ -376,9 +393,61 @@ public const TypeInfo_t Int$info = {
.hash=Int$hash,
.as_text=Int$as_text,
.is_none=Int$is_none,
+ .serialize=Int$serialize,
+ .deserialize=Int$deserialize,
},
};
+public void Int64$serialize(const void *obj, FILE *out, Table_t*, const TypeInfo_t*)
+{
+ int64_t i = *(int64_t*)obj;
+ uint64_t z = (uint64_t)((i << 1) ^ (i >> 63)); // Zigzag encode
+ while (z >= 0x80) {
+ fputc((uint8_t)(z | 0x80), out);
+ z >>= 7;
+ }
+ fputc((uint8_t)z, out);
+}
+
+public void Int64$deserialize(FILE *in, void *outval, Array_t*, const TypeInfo_t*)
+{
+ uint64_t z = 0;
+ for(size_t shift = 0; ; shift += 7) {
+ uint8_t byte = (uint8_t)fgetc(in);
+ z |= ((uint64_t)(byte & 0x7F)) << shift;
+ if ((byte & 0x80) == 0) break;
+ }
+ *(int64_t*)outval = (int64_t)((z >> 1) ^ -(z & 1)); // Zigzag decode
+}
+
+public void Int32$serialize(const void *obj, FILE *out, Table_t*, const TypeInfo_t*)
+{
+ int32_t i = *(int32_t*)obj;
+ uint32_t z = (uint32_t)((i << 1) ^ (i >> 31)); // Zigzag encode
+ while (z >= 0x80) {
+ fputc((uint8_t)(z | 0x80), out);
+ z >>= 7;
+ }
+ fputc((uint8_t)z, out);
+}
+
+public void Int32$deserialize(FILE *in, void *outval, Array_t*, const TypeInfo_t*)
+{
+ uint32_t z = 0;
+ for(size_t shift = 0; ; shift += 7) {
+ uint8_t byte = (uint8_t)fgetc(in);
+ z |= ((uint32_t)(byte & 0x7F)) << shift;
+ if ((byte & 0x80) == 0) break;
+ }
+ *(int32_t*)outval = (int32_t)((z >> 1) ^ -(z & 1)); // Zigzag decode
+}
+
+// The space savings for smaller ints are not worth having:
+#define Int16$serialize NULL
+#define Int16$deserialize NULL
+#define Int8$serialize NULL
+#define Int8$deserialize NULL
+
#define DEFINE_INT_TYPE(c_type, KindOfInt, fmt, min_val, max_val, to_attr)\
public Text_t KindOfInt ## $as_text(const void *i, bool colorize, const TypeInfo_t*) { \
if (!i) return Text(#KindOfInt); \
@@ -416,12 +485,12 @@ public const TypeInfo_t Int$info = {
} \
public PUREFUNC Optional ## KindOfInt ## _t KindOfInt ## $parse(Text_t text) { \
OptionalInt_t full_int = Int$parse(text); \
- if (full_int.small == 0) return (Optional ## KindOfInt ## _t){.is_null=true}; \
+ if (full_int.small == 0) return (Optional ## KindOfInt ## _t){.is_none=true}; \
if (Int$compare_value(full_int, I(min_val)) < 0) { \
- return (Optional ## KindOfInt ## _t){.is_null=true}; \
+ return (Optional ## KindOfInt ## _t){.is_none=true}; \
} \
if (Int$compare_value(full_int, I(max_val)) > 0) { \
- return (Optional ## KindOfInt ## _t){.is_null=true}; \
+ return (Optional ## KindOfInt ## _t){.is_none=true}; \
} \
return (Optional ## KindOfInt ## _t){.i=Int_to_ ## KindOfInt(full_int, true)}; \
} \
@@ -433,6 +502,8 @@ public const TypeInfo_t Int$info = {
.metamethods={ \
.compare=KindOfInt##$compare, \
.as_text=KindOfInt##$as_text, \
+ .serialize=KindOfInt##$serialize, \
+ .deserialize=KindOfInt##$deserialize, \
}, \
};
diff --git a/stdlib/integers.h b/stdlib/integers.h
index a5016d4..5556c19 100644
--- a/stdlib/integers.h
+++ b/stdlib/integers.h
@@ -25,7 +25,7 @@
#define DEFINE_INT_TYPE(c_type, type_name, to_attr) \
typedef struct { \
c_type i; \
- bool is_null:1; \
+ bool is_none:1; \
} Optional ## type_name ## _t; \
Text_t type_name ## $as_text(const void *i, bool colorize, const TypeInfo_t *type); \
PUREFUNC int32_t type_name ## $compare(const void *x, const void *y, const TypeInfo_t *type); \
@@ -73,16 +73,21 @@ DEFINE_INT_TYPE(int16_t, Int16, CONSTFUNC)
DEFINE_INT_TYPE(int8_t, Int8, CONSTFUNC)
#undef DEFINE_INT_TYPE
-#define NONE_INT64 ((OptionalInt64_t){.is_null=true})
-#define NONE_INT32 ((OptionalInt32_t){.is_null=true})
-#define NONE_INT16 ((OptionalInt16_t){.is_null=true})
-#define NONE_INT8 ((OptionalInt8_t){.is_null=true})
+#define NONE_INT64 ((OptionalInt64_t){.is_none=true})
+#define NONE_INT32 ((OptionalInt32_t){.is_none=true})
+#define NONE_INT16 ((OptionalInt16_t){.is_none=true})
+#define NONE_INT8 ((OptionalInt8_t){.is_none=true})
#define Int64$abs(...) I64(labs(__VA_ARGS__))
#define Int32$abs(...) I32(abs(__VA_ARGS__))
#define Int16$abs(...) I16(abs(__VA_ARGS__))
#define Int8$abs(...) I8(abs(__VA_ARGS__))
+void Int64$serialize(const void *obj, FILE *out, Table_t*, const TypeInfo_t*);
+void Int64$deserialize(FILE *in, void *outval, Array_t*, const TypeInfo_t*);
+void Int32$serialize(const void *obj, FILE *out, Table_t*, const TypeInfo_t*);
+void Int32$deserialize(FILE *in, void *outval, Array_t*, const TypeInfo_t*);
+
#define OptionalInt_t Int_t
Text_t Int$as_text(const void *i, bool colorize, const TypeInfo_t *type);
diff --git a/stdlib/memory.c b/stdlib/memory.c
index 70ca574..1805fb6 100644
--- a/stdlib/memory.c
+++ b/stdlib/memory.c
@@ -8,6 +8,7 @@
#include
#include "memory.h"
+#include "metamethods.h"
#include "text.h"
#include "types.h"
#include "util.h"
@@ -22,6 +23,8 @@ public const TypeInfo_t Memory$info = {
.align=0,
.metamethods={
.as_text=Memory$as_text,
+ .serialize=cannot_serialize,
+ .deserialize=cannot_deserialize,
},
};
diff --git a/stdlib/metamethods.c b/stdlib/metamethods.c
index 28d9773..2871a96 100644
--- a/stdlib/metamethods.c
+++ b/stdlib/metamethods.c
@@ -5,14 +5,17 @@
#include "arrays.h"
#include "bools.h"
+#include "bytes.h"
#include "channels.h"
#include "functiontype.h"
+#include "integers.h"
#include "metamethods.h"
#include "optionals.h"
#include "pointers.h"
#include "siphash.h"
#include "tables.h"
#include "text.h"
+#include "types.h"
#include "util.h"
PUREFUNC public uint64_t generic_hash(const void *obj, const TypeInfo_t *type)
@@ -51,10 +54,72 @@ public Text_t generic_as_text(const void *obj, bool colorize, const TypeInfo_t *
return type->metamethods.as_text(obj, colorize, type);
}
+public void _serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type)
+{
+ if (type->metamethods.serialize)
+ return type->metamethods.serialize(obj, out, pointers, type);
+
+ fwrite(obj, (size_t)type->size, 1, out);
+}
+
+public Array_t generic_serialize(const void *x, const TypeInfo_t *type)
+{
+ char *buf = NULL;
+ size_t size = 0;
+ FILE *stream = open_memstream(&buf, &size);
+ Table_t pointers = {};
+ _serialize(x, stream, &pointers, type);
+ fclose(stream);
+ Array_t bytes = {
+ .data=GC_MALLOC_ATOMIC(size),
+ .length=(int64_t)size,
+ .stride=1,
+ .atomic=1,
+ };
+ memcpy(bytes.data, buf, size);
+ free(buf);
+ return bytes;
+}
+
+public void _deserialize(FILE *input, void *outval, Array_t *pointers, const TypeInfo_t *type)
+{
+ if (type->metamethods.deserialize) {
+ type->metamethods.deserialize(input, outval, pointers, type);
+ return;
+ }
+
+ fread(outval, (size_t)type->size, 1, input);
+}
+
+public void generic_deserialize(Array_t bytes, void *outval, const TypeInfo_t *type)
+{
+ if (bytes.stride != 1)
+ Array$compact(&bytes, 1);
+
+ FILE *input = fmemopen(bytes.data, (size_t)bytes.length, "r");
+ Array_t pointers = {};
+ _deserialize(input, outval, &pointers, type);
+ fclose(input);
+}
+
public int generic_print(const void *obj, bool colorize, const TypeInfo_t *type)
{
Text_t text = generic_as_text(obj, colorize, type);
return Text$print(stdout, text) + printf("\n");
}
+__attribute__((noreturn))
+public void cannot_serialize(const void*, FILE*, Table_t*, const TypeInfo_t *type)
+{
+ Text_t typestr = generic_as_text(NULL, false, type);
+ fail("Values of type %k cannot be serialized or deserialized!", &typestr);
+}
+
+__attribute__((noreturn))
+public void cannot_deserialize(FILE*, void*, Array_t*, const TypeInfo_t *type)
+{
+ Text_t typestr = generic_as_text(NULL, false, type);
+ fail("Values of type %k cannot be serialized or deserialized!", &typestr);
+}
+
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
diff --git a/stdlib/metamethods.h b/stdlib/metamethods.h
index d77f41c..a75fcf7 100644
--- a/stdlib/metamethods.h
+++ b/stdlib/metamethods.h
@@ -3,6 +3,7 @@
#include
+#include "datatypes.h"
#include "types.h"
#include "util.h"
@@ -10,6 +11,12 @@ PUREFUNC uint64_t generic_hash(const void *obj, const TypeInfo_t *type);
PUREFUNC int32_t generic_compare(const void *x, const void *y, const TypeInfo_t *type);
PUREFUNC bool generic_equal(const void *x, const void *y, const TypeInfo_t *type);
Text_t generic_as_text(const void *obj, bool colorize, const TypeInfo_t *type);
+void _serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type);
+Array_t generic_serialize(const void *x, const TypeInfo_t *type);
+void _deserialize(FILE *input, void *outval, Array_t *pointers, const TypeInfo_t *type);
+void generic_deserialize(Array_t bytes, void *outval, const TypeInfo_t *type);
int generic_print(const void *obj, bool colorize, const TypeInfo_t *type);
+void cannot_serialize(const void*, FILE*, Table_t*, const TypeInfo_t *type);
+void cannot_deserialize(FILE*, void*, Array_t*, const TypeInfo_t *type);
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
diff --git a/stdlib/moments.c b/stdlib/moments.c
index ba72ff7..e83bae5 100644
--- a/stdlib/moments.c
+++ b/stdlib/moments.c
@@ -2,6 +2,7 @@
#include
#include
#include
+#include
#include
#include
#include
diff --git a/stdlib/optionals.c b/stdlib/optionals.c
index 810f617..38cd470 100644
--- a/stdlib/optionals.c
+++ b/stdlib/optionals.c
@@ -13,7 +13,7 @@
#include "threads.h"
#include "util.h"
-public PUREFUNC bool is_null(const void *obj, const TypeInfo_t *non_optional_type)
+public PUREFUNC bool is_none(const void *obj, const TypeInfo_t *non_optional_type)
{
if (non_optional_type->metamethods.is_none)
return non_optional_type->metamethods.is_none(obj, non_optional_type);
@@ -23,14 +23,14 @@ public PUREFUNC bool is_null(const void *obj, const TypeInfo_t *non_optional_typ
PUREFUNC public uint64_t Optional$hash(const void *obj, const TypeInfo_t *type)
{
- return is_null(obj, type->OptionalInfo.type) ? 0 : generic_hash(obj, type->OptionalInfo.type);
+ return is_none(obj, type->OptionalInfo.type) ? 0 : generic_hash(obj, type->OptionalInfo.type);
}
PUREFUNC public int32_t Optional$compare(const void *x, const void *y, const TypeInfo_t *type)
{
if (x == y) return 0;
- bool x_is_null = is_null(x, type->OptionalInfo.type);
- bool y_is_null = is_null(y, type->OptionalInfo.type);
+ bool x_is_null = is_none(x, type->OptionalInfo.type);
+ bool y_is_null = is_none(y, type->OptionalInfo.type);
if (x_is_null && y_is_null) return 0;
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);
@@ -40,8 +40,8 @@ PUREFUNC public bool Optional$equal(const void *x, const void *y, const TypeInfo
{
if (x == y) return true;
- bool x_is_null = is_null(x, type->OptionalInfo.type);
- bool y_is_null = is_null(y, type->OptionalInfo.type);
+ bool x_is_null = is_none(x, type->OptionalInfo.type);
+ bool y_is_null = is_none(y, type->OptionalInfo.type);
if (x_is_null && y_is_null) return true;
else if (x_is_null != y_is_null) return false;
else return generic_equal(x, y, type->OptionalInfo.type);
@@ -52,9 +52,42 @@ public Text_t Optional$as_text(const void *obj, bool colorize, const TypeInfo_t
if (!obj)
return Text$concat(generic_as_text(obj, colorize, type->OptionalInfo.type), Text("?"));
- if (is_null(obj, type->OptionalInfo.type))
+ if (is_none(obj, type->OptionalInfo.type))
return colorize ? Text("\x1b[31mNONE\x1b[m") : Text("NONE");
return generic_as_text(obj, colorize, type->OptionalInfo.type);
}
+public void Optional$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type)
+{
+ bool has_value = !is_none(obj, type->OptionalInfo.type);
+ fputc((int)has_value, out);
+ if (has_value)
+ _serialize(obj, out, pointers, type->OptionalInfo.type);
+}
+
+public void Optional$deserialize(FILE *in, void *outval, Array_t *pointers, const TypeInfo_t *type)
+{
+ bool has_value = (bool)fgetc(in);
+ const TypeInfo_t *nonnull = type->OptionalInfo.type;
+ if (has_value) {
+ memset(outval, 0, (size_t)type->size);
+ _deserialize(in, outval, pointers, nonnull);
+ } else {
+ if (nonnull->tag == TextInfo)
+ *(Text_t*)outval = (Text_t){.length=-1};
+ else if (nonnull->tag == ArrayInfo)
+ *(Array_t*)outval = (Array_t){.length=-1};
+ else if (nonnull->tag == TableInfo)
+ *(Table_t*)outval = (Table_t){.entries={.length=-1}};
+ else if (nonnull == &Num$info)
+ *(double*)outval = NAN;
+ else if (nonnull == &Num32$info)
+ *(float*)outval = NAN;
+ else if (nonnull->tag == StructInfo || (nonnull->tag == OpaqueInfo && type->size > nonnull->size))
+ memset(outval + type->size, -1, (size_t)(type->size - nonnull->size));
+ else
+ memset(outval, 0, (size_t)type->size);
+ }
+}
+
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1
diff --git a/stdlib/optionals.h b/stdlib/optionals.h
index 53f2aa9..4e6bf7e 100644
--- a/stdlib/optionals.h
+++ b/stdlib/optionals.h
@@ -29,12 +29,16 @@ PUREFUNC uint64_t Optional$hash(const void *obj, const TypeInfo_t *type);
PUREFUNC int32_t Optional$compare(const void *x, const void *y, const TypeInfo_t *type);
PUREFUNC bool Optional$equal(const void *x, const void *y, const TypeInfo_t *type);
Text_t Optional$as_text(const void *obj, bool colorize, const TypeInfo_t *type);
+void Optional$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type);
+void Optional$deserialize(FILE *in, void *outval, Array_t *pointers, const TypeInfo_t *type);
#define Optional$metamethods ((metamethods_t){ \
.hash=Optional$hash, \
.compare=Optional$compare, \
.equal=Optional$equal, \
.as_text=Optional$as_text, \
+ .serialize=Optional$serialize, \
+ .deserialize=Optional$deserialize, \
})
#define Optional$info(_size, _align, t) &((TypeInfo_t){.size=_size, .align=_align, \
diff --git a/stdlib/pointers.c b/stdlib/pointers.c
index 97c7f5d..4034896 100644
--- a/stdlib/pointers.c
+++ b/stdlib/pointers.c
@@ -83,4 +83,43 @@ PUREFUNC public bool Pointer$is_none(const void *x, const TypeInfo_t*)
return *(void**)x == NULL;
}
+public void Pointer$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type)
+{
+ void *ptr = *(void**)obj;
+ assert(ptr != NULL);
+
+ const TypeInfo_t ptr_to_int_table = {.size=sizeof(Table_t), .align=__alignof__(Table_t),
+ .tag=TableInfo, .TableInfo.key=type, .TableInfo.value=&Int64$info};
+
+ int64_t *id_ptr = Table$get(*pointers, &ptr, &ptr_to_int_table);
+ int64_t id;
+ if (id_ptr) {
+ id = *id_ptr;
+ } else {
+ id = pointers->entries.length + 1;
+ Table$set(pointers, &ptr, &id, &ptr_to_int_table);
+ }
+
+ Int64$serialize(&id, out, pointers, &Int64$info);
+
+ if (!id_ptr)
+ _serialize(ptr, out, pointers, type->PointerInfo.pointed);
+}
+
+public void Pointer$deserialize(FILE *in, void *outval, Array_t *pointers, const TypeInfo_t *type)
+{
+ int64_t id = 0;
+ Int64$deserialize(in, &id, pointers, &Int64$info);
+ assert(id != 0);
+
+ if (id > pointers->length) {
+ void *obj = GC_MALLOC((size_t)type->PointerInfo.pointed->size);
+ Array$insert(pointers, &obj, I(0), sizeof(void*));
+ _deserialize(in, obj, pointers, type->PointerInfo.pointed);
+ *(void**)outval = obj;
+ } else {
+ *(void**)outval = *(void**)(pointers->data + (id-1)*pointers->stride);
+ }
+}
+
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
diff --git a/stdlib/pointers.h b/stdlib/pointers.h
index 0c9f2ad..1cd7636 100644
--- a/stdlib/pointers.h
+++ b/stdlib/pointers.h
@@ -5,6 +5,7 @@
#include
#include
+#include "datatypes.h"
#include "types.h"
#include "util.h"
@@ -12,6 +13,8 @@ Text_t Pointer$as_text(const void *x, bool colorize, const TypeInfo_t *type);
PUREFUNC int32_t Pointer$compare(const void *x, const void *y, const TypeInfo_t *type);
PUREFUNC bool Pointer$equal(const void *x, const void *y, const TypeInfo_t *type);
PUREFUNC bool Pointer$is_none(const void *x, const TypeInfo_t*);
+void Pointer$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type);
+void Pointer$deserialize(FILE *in, void *outval, Array_t *pointers, const TypeInfo_t *type);
#define Null(t) (t*)NULL
#define POINTER_TYPE(_sigil, _pointed) (&(TypeInfo_t){\
@@ -22,6 +25,8 @@ PUREFUNC bool Pointer$is_none(const void *x, const TypeInfo_t*);
.compare=Pointer$compare, \
.equal=Pointer$equal, \
.is_none=Pointer$is_none, \
+ .serialize=Pointer$serialize, \
+ .deserialize=Pointer$deserialize, \
})
#define Pointer$info(sigil_expr, pointed_info) &((TypeInfo_t){.size=sizeof(void*), .align=__alignof__(void*), \
diff --git a/stdlib/stdlib.c b/stdlib/stdlib.c
index 6d0ddca..d56db0e 100644
--- a/stdlib/stdlib.c
+++ b/stdlib/stdlib.c
@@ -76,24 +76,24 @@ static bool parse_single_arg(const TypeInfo_t *info, char *arg, void *dest)
return parsed.small != 0;
} else if (info == &Int64$info) {
OptionalInt64_t parsed = Int64$parse(Text$from_str(arg));
- if (!parsed.is_null)
+ if (!parsed.is_none)
*(OptionalInt64_t*)dest = parsed;
- return !parsed.is_null;
+ return !parsed.is_none;
} else if (info == &Int32$info) {
OptionalInt32_t parsed = Int32$parse(Text$from_str(arg));
- if (!parsed.is_null)
+ if (!parsed.is_none)
*(OptionalInt32_t*)dest = parsed;
- return !parsed.is_null;
+ return !parsed.is_none;
} else if (info == &Int16$info) {
OptionalInt16_t parsed = Int16$parse(Text$from_str(arg));
- if (!parsed.is_null)
+ if (!parsed.is_none)
*(OptionalInt16_t*)dest = parsed;
- return !parsed.is_null;
+ return !parsed.is_none;
} else if (info == &Int8$info) {
OptionalInt8_t parsed = Int8$parse(Text$from_str(arg));
- if (!parsed.is_null)
+ if (!parsed.is_none)
*(OptionalInt8_t*)dest = parsed;
- return !parsed.is_null;
+ return !parsed.is_none;
} else if (info == &Bool$info) {
OptionalBool_t parsed = Bool$parse(Text$from_str(arg));
if (parsed != NONE_BOOL)
diff --git a/stdlib/structs.c b/stdlib/structs.c
index c23e4b7..624fe93 100644
--- a/stdlib/structs.c
+++ b/stdlib/structs.c
@@ -167,4 +167,58 @@ 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
diff --git a/stdlib/structs.h b/stdlib/structs.h
index b41dde7..6553cc8 100644
--- a/stdlib/structs.h
+++ b/stdlib/structs.h
@@ -12,6 +12,8 @@ PUREFUNC int32_t Struct$compare(const void *x, const void *y, const TypeInfo_t *
PUREFUNC bool Struct$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);
+void Struct$deserialize(FILE *in, void *outval, Array_t *pointers, const TypeInfo_t *type);
#define Struct$metamethods ((metamethods_t){ \
.hash=Struct$hash, \
@@ -19,6 +21,8 @@ PUREFUNC bool Struct$is_none(const void *obj, const TypeInfo_t *type);
.equal=Struct$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/stdlib/tables.c b/stdlib/tables.c
index 97fee8a..449f37c 100644
--- a/stdlib/tables.c
+++ b/stdlib/tables.c
@@ -647,4 +647,41 @@ PUREFUNC public bool Table$is_none(const void *obj, const TypeInfo_t*)
return ((Table_t*)obj)->entries.length < 0;
}
+public void Table$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type)
+{
+ Table_t *t = (Table_t*)obj;
+ int64_t len = t->entries.length;
+ Int64$serialize(&len, out, pointers, &Int64$info);
+
+ size_t offset = value_offset(type);
+ for (int64_t i = 0; i < len; i++) {
+ _serialize(t->entries.data + i*t->entries.stride, out, pointers, type->TableInfo.key);
+ _serialize(t->entries.data + i*t->entries.stride + offset, out, pointers, type->TableInfo.value);
+ }
+
+ Optional$serialize(&t->fallback, out, pointers, Optional$info(sizeof(void*), __alignof__(void*), Pointer$info("&", type)));
+}
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstack-protector"
+public void Table$deserialize(FILE *in, void *outval, Array_t *pointers, const TypeInfo_t *type)
+{
+ int64_t len;
+ Int64$deserialize(in, &len, pointers, &Int$info);
+
+ Table_t t = {};
+ for (int64_t i = 0; i < len; i++) {
+ char key[type->TableInfo.key->size];
+ _deserialize(in, key, pointers, type->TableInfo.key);
+ char value[type->TableInfo.value->size];
+ _deserialize(in, value, pointers, type->TableInfo.value);
+ Table$set(&t, key, value, type);
+ }
+
+ Optional$deserialize(in, &t.fallback, pointers, Optional$info(sizeof(void*), __alignof__(void*), Pointer$info("&", type)));
+
+ *(Table_t*)outval = t;
+}
+#pragma GCC diagnostic pop
+
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1
diff --git a/stdlib/tables.h b/stdlib/tables.h
index 1f62a13..cd19efd 100644
--- a/stdlib/tables.h
+++ b/stdlib/tables.h
@@ -77,6 +77,8 @@ PUREFUNC void *Table$str_get_raw(Table_t t, const char *key);
void Table$str_set(Table_t *t, const char *key, const void *value);
void *Table$str_reserve(Table_t *t, const char *key, const void *value);
void Table$str_remove(Table_t *t, const char *key);
+void Table$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *type);
+void Table$deserialize(FILE *in, void *outval, Array_t *pointers, const TypeInfo_t *type);
#define Table$length(t) ((t).entries.length)
@@ -88,6 +90,8 @@ extern const TypeInfo_t CStrToVoidStarTable;
.equal=Table$equal, \
.hash=Table$hash, \
.is_none=Table$is_none, \
+ .serialize=Table$serialize, \
+ .deserialize=Table$deserialize, \
})
#define Table$info(key_expr, value_expr) &((TypeInfo_t){.size=sizeof(Table_t), .align=__alignof__(Table_t), \
diff --git a/stdlib/text.c b/stdlib/text.c
index 40a3988..fed113e 100644
--- a/stdlib/text.c
+++ b/stdlib/text.c
@@ -1377,6 +1377,24 @@ PUREFUNC public bool Text$is_none(const void *t, const TypeInfo_t*)
return ((Text_t*)t)->length < 0;
}
+public void Text$serialize(const void *obj, FILE *out, Table_t *pointers, const TypeInfo_t *)
+{
+ const char *str = Text$as_c_string(*(Text_t*)obj);
+ int64_t len = (int64_t)strlen(str);
+ Int64$serialize(&len, out, pointers, &Int64$info);
+ fwrite(str, sizeof(char), (size_t)len, out);
+}
+
+public void Text$deserialize(FILE *in, void *out, Array_t *pointers, const TypeInfo_t *)
+{
+ int64_t len = -1;
+ Int64$deserialize(in, &len, pointers, &Int64$info);
+ char *buf = GC_MALLOC_ATOMIC((size_t)len+1);
+ fread(buf, sizeof(char), (size_t)len, in);
+ buf[len+1] = '\0';
+ *(Text_t*)out = Text$from_strn(buf, (size_t)len);
+}
+
public const TypeInfo_t Text$info = {
.size=sizeof(Text_t),
.align=__alignof__(Text_t),
diff --git a/stdlib/text.h b/stdlib/text.h
index 04c867c..b493f4a 100644
--- a/stdlib/text.h
+++ b/stdlib/text.h
@@ -60,6 +60,8 @@ Text_t Text$join(Text_t glue, Array_t pieces);
Text_t Text$repeat(Text_t text, Int_t count);
int32_t Text$get_grapheme_fast(TextIter_t *state, int64_t index);
uint32_t Text$get_main_grapheme_fast(TextIter_t *state, int64_t index);
+void Text$serialize(const void *obj, FILE *out, Table_t *, const TypeInfo_t *);
+void Text$deserialize(FILE *in, void *out, Array_t *, const TypeInfo_t *);
MACROLIKE int32_t Text$get_grapheme(Text_t text, int64_t index)
{
@@ -75,6 +77,8 @@ extern const TypeInfo_t Text$info;
.compare=Text$compare, \
.equal=Text$equal, \
.is_none=Text$is_none, \
+ .serialize=Text$serialize, \
+ .deserialize=Text$deserialize, \
})
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0
diff --git a/stdlib/threads.c b/stdlib/threads.c
index 860b488..c569011 100644
--- a/stdlib/threads.c
+++ b/stdlib/threads.c
@@ -14,6 +14,7 @@
#include "arrays.h"
#include "datatypes.h"
+#include "metamethods.h"
#include "rng.h"
#include "text.h"
#include "threads.h"
@@ -71,6 +72,8 @@ public const TypeInfo_t Thread$info = {
.metamethods={
.as_text=Thread$as_text,
.is_none=Thread$is_none,
+ .serialize=cannot_serialize,
+ .deserialize=cannot_deserialize,
},
};
diff --git a/stdlib/types.h b/stdlib/types.h
index 652a2e7..faaa531 100644
--- a/stdlib/types.h
+++ b/stdlib/types.h
@@ -4,6 +4,7 @@
#include
#include
+#include
#include "datatypes.h"
@@ -15,6 +16,8 @@ typedef struct {
bool (*equal)(const void*, const void*, const TypeInfo_t*);
Text_t (*as_text)(const void*, bool, const TypeInfo_t*);
bool (*is_none)(const void*, const TypeInfo_t*);
+ void (*serialize)(const void*, FILE*, Table_t*, const TypeInfo_t*);
+ void (*deserialize)(FILE*, void*, Array_t*, const TypeInfo_t*);
} metamethods_t;
typedef struct {
@@ -68,7 +71,8 @@ struct TypeInfo_s {
};
#define Type$info(typestr) &((TypeInfo_t){.size=sizeof(TypeInfo_t), .align=__alignof__(TypeInfo_t), \
- .tag=TypeInfoInfo, .TypeInfoInfo.type_str=typestr})
+ .tag=TypeInfoInfo, .TypeInfoInfo.type_str=typestr, \
+ .metamethods={.serialize=cannot_serialize, .deserialize=cannot_deserialize}})
extern const TypeInfo_t Void$info;
extern const TypeInfo_t Abort$info;
diff --git a/structs.c b/structs.c
index da57ff4..ce5116e 100644
--- a/structs.c
+++ b/structs.c
@@ -71,7 +71,7 @@ CORD compile_struct_header(env_t *env, ast_t *ast)
full_name, "_t value;\n"
"struct {\n"
"char _padding[", heap_strf("%zu", unpadded_struct_size(t)), "];\n",
- "Bool_t is_null;\n"
+ "Bool_t is_none:1;\n"
"};\n"
"};\n"
"} ", namespace_prefix(env, env->namespace), "$Optional", def->name, "_t;\n"
diff --git a/test/serialization.tm b/test/serialization.tm
new file mode 100644
index 0000000..218b033
--- /dev/null
+++ b/test/serialization.tm
@@ -0,0 +1,95 @@
+
+struct Foo(name:Text, next=NONE:@Foo)
+
+enum MyEnum(Zero, One(x:Int), Two(x:Num, y:Text))
+
+func main():
+ do:
+ >> obj := now()
+ >> bytes := obj:serialize()
+ >> DESERIALIZE(bytes):Moment == obj
+ = yes
+
+ do:
+ >> obj := Int64(123)
+ >> bytes := obj:serialize()
+ >> DESERIALIZE(bytes):Int64 == obj
+ = yes
+
+ do:
+ >> obj := 5
+ >> bytes := obj:serialize()
+ >> DESERIALIZE(bytes):Int == obj
+ = yes
+
+ do:
+ >> obj := 9999999999999999999999999999999999999999999999999999
+ >> bytes := obj:serialize()
+ >> DESERIALIZE(bytes):Int == obj
+ = yes
+
+ do:
+ >> obj := "Héllo"
+ >> bytes := obj:serialize()
+ >> DESERIALIZE(bytes):Text
+ >> DESERIALIZE(bytes):Text == obj
+ = yes
+
+ do:
+ >> obj := [Int64(10), Int64(20), Int64(30)]:reversed()
+ >> bytes := obj:serialize()
+ >> DESERIALIZE(bytes):[Int64] == obj
+ = yes
+
+ do:
+ >> obj := yes
+ >> bytes := obj:serialize()
+ >> DESERIALIZE(bytes):Bool == obj
+ = yes
+
+ do:
+ >> obj := @[10, 20]
+ >> bytes := obj:serialize()
+ >> roundtrip := DESERIALIZE(bytes):@[Int]
+ >> roundtrip == obj
+ = no
+ >> roundtrip[] == obj[]
+ = yes
+
+ do:
+ >> obj := {"A":10, "B":20; fallback={"C":30}}
+ >> bytes := obj:serialize()
+ >> DESERIALIZE(bytes):{Text:Int} == obj
+ = yes
+
+ do:
+ >> obj := @Foo("root")
+ >> obj.next = @Foo("abcdef", next=obj)
+ >> bytes := obj:serialize()
+ >> DESERIALIZE(bytes):@Foo
+ = @Foo(name="root", next=@Foo(name="abcdef", next=@~1))
+
+ do:
+ >> obj := MyEnum.Two(123, "OKAY")
+ >> bytes := obj:serialize()
+ >> DESERIALIZE(bytes):MyEnum == obj
+ = yes
+
+ do:
+ >> obj := "Hello"?
+ >> bytes := obj:serialize()
+ >> DESERIALIZE(bytes):Text? == obj
+ = yes
+
+ do:
+ >> obj := {10, 20, 30}
+ >> bytes := obj:serialize()
+ >> DESERIALIZE(bytes):{Int} == obj
+ = yes
+
+ do:
+ >> obj := NONE:Num
+ >> bytes := obj:serialize()
+ >> DESERIALIZE(bytes):Num? == obj
+ = yes
+
diff --git a/typecheck.c b/typecheck.c
index a37357d..ba22830 100644
--- a/typecheck.c
+++ b/typecheck.c
@@ -721,6 +721,11 @@ type_t *get_type(env_t *env, ast_t *ast)
}
case FunctionCall: {
auto call = Match(ast, FunctionCall);
+
+ // HACK:
+ if (call->fn->tag == Var && streq(Match(call->fn, Var)->name, "serialize"))
+ return Type(ArrayType, Type(ByteType));
+
type_t *fn_type_t = get_type(env, call->fn);
if (!fn_type_t)
code_err(call->fn, "I couldn't find this function");
@@ -741,6 +746,10 @@ type_t *get_type(env_t *env, ast_t *ast)
}
case MethodCall: {
auto call = Match(ast, MethodCall);
+
+ if (streq(call->name, "serialize"))
+ return Type(ArrayType, Type(ByteType));
+
type_t *self_value_t = value_type(get_type(env, call->self));
switch (self_value_t->tag) {
case ArrayType: {
@@ -1245,6 +1254,8 @@ type_t *get_type(env_t *env, ast_t *ast)
}
case Moment: return Type(MomentType);
case Unknown: code_err(ast, "I can't figure out the type of: %W", ast);
+ case Serialize: return Type(ArrayType, Type(ByteType));
+ case Deserialize: return parse_type_ast(env, Match(ast, Deserialize)->type);
}
#pragma GCC diagnostic pop
code_err(ast, "I can't figure out the type of: %W", ast);