Add serialization and deserialization
This commit is contained in:
parent
4b5e4cd1f2
commit
f66f8ad711
2
ast.c
2
ast.c
@ -161,6 +161,8 @@ CORD ast_to_xml(ast_t *ast)
|
||||
T(DocTest, "<DocTest>%r<output>%r</output></DocTest>", optional_tagged("expression", data.expr), xml_escape(data.output))
|
||||
T(Use, "<Use>%r%r</Use>", optional_tagged("var", data.var), xml_escape(data.path))
|
||||
T(InlineCCode, "<InlineCode>%r</InlineCode>", xml_escape(data.code))
|
||||
T(Serialize, "<Serialize>%r</Serialize>", ast_to_xml(data.value))
|
||||
T(Deserialize, "<Deserialize><type>%r</type>%r</Deserialize>", type_ast_to_xml(data.type), ast_to_xml(data.value))
|
||||
default: return "???";
|
||||
#undef T
|
||||
}
|
||||
|
8
ast.h
8
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;
|
||||
};
|
||||
|
||||
|
34
compile.c
34
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");
|
||||
|
19
parse.c
19
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))
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <sys/param.h>
|
||||
|
||||
#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
|
||||
|
@ -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), \
|
||||
|
@ -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
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <gc.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -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), \
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#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*), \
|
||||
|
@ -1,4 +1,6 @@
|
||||
// Integer type infos and methods
|
||||
#include <stdio.h> // Must be before gmp.h
|
||||
|
||||
#include <ctype.h>
|
||||
#include <gc.h>
|
||||
#include <gmp.h>
|
||||
@ -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, \
|
||||
}, \
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <err.h>
|
||||
|
||||
#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,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#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
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include <ctype.h>
|
||||
#include <gc.h>
|
||||
#include <err.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
@ -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
|
||||
|
@ -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, \
|
||||
|
@ -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
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#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*), \
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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), \
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#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;
|
||||
|
@ -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"
|
||||
|
95
test/serialization.tm
Normal file
95
test/serialization.tm
Normal file
@ -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
|
||||
|
11
typecheck.c
11
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);
|
||||
|
Loading…
Reference in New Issue
Block a user