]> git.lizzy.rs Git - protogen.git/commitdiff
Rename library to protogen
authorElias Fleckenstein <eliasfleckenstein@web.de>
Wed, 13 Apr 2022 10:13:42 +0000 (12:13 +0200)
committerElias Fleckenstein <eliasfleckenstein@web.de>
Wed, 13 Apr 2022 10:13:42 +0000 (12:13 +0200)
README.md
protogen.lua [new file with mode: 0755]
typegen.lua [deleted file]

index fe56719324a1a433493ed6076ef4460ebbbf589e..1c99dd5211e4ea56dc655b4078077b22728352be 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,2 +1,8 @@
-# dragontype
-Dynamic type generator for dragonblocks alpha
+# protogen
+![protogen](https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/e3d0b655-d164-42f3-9254-51afe51b8850/de2erqu-19c5a186-0554-437b-a5be-47ee3cf842b0.png?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7InBhdGgiOiJcL2ZcL2UzZDBiNjU1LWQxNjQtNDJmMy05MjU0LTUxYWZlNTFiODg1MFwvZGUyZXJxdS0xOWM1YTE4Ni0wNTU0LTQzN2ItYTViZS00N2VlM2NmODQyYjAucG5nIn1dXSwiYXVkIjpbInVybjpzZXJ2aWNlOmZpbGUuZG93bmxvYWQiXX0.q5P0pgGtHUgDkPRq9S93nzt3lxh3Fe7JF8Ps3buJKR4)
+Protocol generator for dragonnet.
+
+Generates C code for serialization functions from a types.def definitions file.
+Also generates extra utility for scalar and vector types.
+Made for use with dragonnet, but can be used without (USE_DRAGONNET macro).
+Generates readable code.
diff --git a/protogen.lua b/protogen.lua
new file mode 100755 (executable)
index 0000000..3a503fe
--- /dev/null
@@ -0,0 +1,850 @@
+#!/usr/bin/env lua
+require "luax"
+
+local h   = io.open("types.h",   "w")
+local c   = io.open("types.c",   "w")
+local def = io.open("types.def", "r")
+
+local function emit_h(str)
+       h:write(str)
+end
+
+local function emit_c(str)
+       c:write(str)
+end
+
+local function emit(fun, code)
+       emit_h(fun .. ";\n")
+       emit_c(fun ..  "\n" .. code)
+end
+
+-- fn prefixes
+local export_prefix = ""
+local local_prefix = "__attribute__((unused)) static inline "
+
+-- head
+
+local disclaimer = [[
+/*
+       This file was automatically generated by Protogen.
+       DO NOT EDIT it manually. Instead, edit types.def and re-run protogen.
+*/
+
+]]
+
+emit_h(disclaimer)
+emit_h([[
+#ifndef _PROTOGEN_TYPES_H_
+#define _PROTOGEN_TYPES_H_
+
+#ifdef USE_DRAGONNET
+#include <dragonnet/peer.h>
+#endif
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+typedef char *String;
+
+typedef struct {
+       size_t siz;
+       unsigned char *data;
+} Blob;
+
+]]
+)
+
+emit_c(disclaimer)
+emit_c([[
+#ifdef USE_DRAGONNET
+#include <dragonnet/send.h>
+#include <dragonnet/recv.h>
+#endif
+
+#include <endian.h/endian.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdlib.h>
+#include <string.h>
+#include <zlib.h>
+
+#define htobe8(x) (x)
+#define be8toh(x) (x)
+
+#include "types.h"
+
+]] .. local_prefix ..  [[void raw_write(Blob *buffer, const void *data, size_t len)
+{
+       buffer->data = realloc(buffer->data, len + buffer->siz);
+       memcpy(&buffer->data[buffer->siz], data, len);
+       buffer->siz += len;
+}
+
+]] .. local_prefix ..  [[bool raw_read(Blob *buffer, void *data, size_t len)
+{
+       if (len == 0)
+               return true;
+
+       if (buffer->siz < len) {
+               fprintf(stderr, "[warning] buffer exhausted (requested bytes: %lu, remaining bytes: %lu)\n", len, buffer->siz);
+               return false;
+       }
+
+       memcpy(data, buffer->data, len);
+       buffer->data += len;
+       buffer->siz -= len;
+       return true;
+}
+
+]]
+)
+
+-- existing types
+local existing_types = {}
+local has_deallocator = {}
+
+-- vector components
+
+local base_vector_components = {"x", "y", "z", "w"}
+local vector_components = {}
+
+for i = 2, 4 do
+       local components = {}
+
+       for j = 1, i do
+               table.insert(components, base_vector_components[j])
+       end
+
+       vector_components[i] = components
+end
+
+-- numeric types
+
+local function emit_vector(type, l)
+       local name = "v" .. l .. type
+       local box = "aabb" .. l .. type
+
+       existing_types[name] = true
+       has_deallocator[name] = false
+
+       existing_types[box] = true
+       has_deallocator[box] = false
+
+       local typedef, equals, add, clamp, cmp, scale, mix, write, read, send, recv =
+                  "",     "",  "",    "",  "",    "",  "",    "",   "",   "",   ""
+
+       typedef = typedef .. "\t" .. type .. " "
+       equals  = equals  .. "\treturn "
+       add     = add     .. "\treturn (" .. name .. ") {"
+       clamp   = clamp   .. "\treturn (" .. name .. ") {"
+       cmp     = cmp     .. "\tint i;\n"
+       scale   = scale   .. "\treturn (" .. name .. ") {"
+       mix     = mix     .. "\treturn (" .. name .. ") {"
+
+       for i, c in ipairs(vector_components[l]) do
+               local last = i == l
+
+               typedef = typedef
+                       .. c ..
+                       (last
+                               and ";\n"
+                               or ", "
+                       )
+
+               equals = equals
+                       .. "a." .. c .. " == "
+                       .. "b." .. c ..
+                       (last
+                               and ";\n"
+                               or " && "
+                       )
+
+               add = add
+                       .. "a." .. c .. " + "
+                       .. "b." .. c ..
+                       (last
+                               and "};\n"
+                               or ", "
+                       )
+
+               clamp = clamp
+                       .. type .. "_clamp("
+                       .. "val." .. c .. ", "
+                       .. "min." .. c .. ", "
+                       .. "max." .. c .. ")" ..
+                       (last
+                               and "};\n"
+                               or ", "
+                       )
+
+               cmp = cmp
+                       .. "\tif ((i = " .. type .. "_cmp("
+                       .. "&((const " .. name .. " *) a)->" .. c .. ", "
+                       .. "&((const " .. name .. " *) b)->" .. c .. ")) != 0)"
+                       .. "\n\t\treturn i;\n"
+
+               scale = scale
+                       .. "v." .. c .. " * s" ..
+                       (last
+                               and "};\n"
+                               or ", "
+                       )
+
+               mix = mix
+                       .. type .. "_mix("
+                       .. "a." .. c .. ", "
+                       .. "b." .. c .. ", "
+                       .. "f" .. ")" ..
+                       (last
+                               and "};\n"
+                               or ", "
+                       )
+
+               write = write
+                       .. "\t" .. type .. "_write(buffer, &val->" .. c .. ");\n"
+
+               read = read
+                       .. "\tif (!" .. type .. "_read(buffer, &val->" .. c .. "))\n\t\treturn false;\n"
+
+               send = send
+                       .. "\tif (!" .. type .. "_send(peer, " .. (last and "submit" or "false") .. ", &val->" .. c .. "))\n\t\treturn false;\n"
+
+               recv = recv
+                       .. "\tif (!" .. type .. "_recv(peer, &val->" .. c .. "))\n\t\treturn false;\n"
+       end
+
+       emit_h("typedef struct {\n" .. typedef ..  "} " .. name .. ";\n")
+
+       emit(export_prefix .. "bool " .. name .. "_equals(" .. name .. " a, " .. name .. " b)", "{\n" .. equals .. "}\n\n")
+       emit(export_prefix .. name .. " " .. name .. "_add(" .. name .. " a, " .. name .. " b)", "{\n" .. add .. "}\n\n")
+       emit(export_prefix .. name .. " " .. name .. "_clamp(" .. name .. " val, " .. name .. " min, " .. name .. " max)", "{\n" .. clamp .. "}\n\n")
+       emit(export_prefix .. "int " .. name .. "_cmp(const void *a, const void *b)", "{\n" .. cmp .. "\treturn 0;\n}\n\n")
+       emit(export_prefix .. name .. " " .. name .. "_scale(" .. name .. " v, " .. type .. " s)", "{\n" .. scale .. "}\n\n")
+
+       if type:sub(1, 1) == "f" then
+               emit(export_prefix .. name .. " " .. name .. "_mix(" .. name .. " a, " .. name .. " b, " .. type .. " f)", "{\n" .. mix .. "}\n\n")
+       end
+
+       emit(export_prefix .. "void " .. name .. "_write(Blob *buffer, " .. name .. " *val)", "{\n" .. write .. "}\n\n")
+       emit(export_prefix .. "bool " .. name .. "_read(Blob *buffer, " .. name .. " *val)", "{\n" .. read .. "\treturn true;\n}\n\n")
+
+       emit_c("#ifdef USE_DRAGONNET\n")
+       emit_c(local_prefix .. "bool " .. name .. "_send(DragonnetPeer *peer, bool submit, " .. name .. " *val)\n{\n" .. send .. "\treturn true;\n}\n\n")
+       emit_c(local_prefix .. "bool " .. name .. "_recv(DragonnetPeer *peer, " .. name .. " *val)\n{\n" .. recv .. "\treturn true;\n}\n")
+       emit_c("#endif\n\n")
+
+       emit_h("\n")
+
+       emit_h("typedef struct {\n\t" .. name .. " min, max;\n} " .. box .. ";\n")
+
+       emit(export_prefix .. "void " .. box .. "_write(Blob *buffer, " .. box .. " *val)", "{\n\t" .. name .. "_write(buffer, &val->min);\n\t" .. name .. "_write(buffer, &val->max);\n}\n\n")
+       emit(export_prefix .. "bool " .. box .. "_read(Blob *buffer, " .. box .. " *val)", "{\n\tif (!" .. name .. "_read(buffer, &val->min))\n\t\treturn false;\n\tif (!" .. name .. "_read(buffer, &val->max))\n\t\treturn false;\n\treturn true;\n}\n\n")
+
+       emit_c("#ifdef USE_DRAGONNET\n")
+       emit_c(local_prefix .. "bool " .. box .. "_send(DragonnetPeer *peer, bool submit, " .. box .. " *val)\n{\n\tif (!" .. name .. "_send(peer, false, &val->min))\n\t\treturn false;\n\tif (!" .. name .. "_send(peer, submit, &val->max))\n\t\treturn false;\n\treturn true;\n}\n\n")
+       emit_c(local_prefix .. "bool " .. box .. "_recv(DragonnetPeer *peer, " .. box .. " *val)\n{\n\tif (!" .. name .. "_recv(peer, &val->min))\n\t\treturn false;\n\t if (!" .. name .. "_recv(peer, &val->max))\n\t\treturn false;\n\treturn true;\n}\n")
+       emit_c("#endif\n\n")
+
+       emit_h("\n")
+end
+
+local function emit_numeric(class, bits, alias)
+       local name = class .. bits
+
+       existing_types[name] = true
+       has_deallocator[name] = false
+
+       emit_h("typedef " .. alias .. " " .. name .. ";\n")
+
+       emit(export_prefix .. name .. " " .. name .. "_min(" .. name .. " a, " .. name .. " b)", "{\n\treturn a < b ? a : b;\n}\n\n")
+       emit(export_prefix .. name .. " " .. name .. "_max(" .. name .. " a, " .. name .. " b)", "{\n\treturn a > b ? a : b;\n}\n\n")
+       emit(export_prefix .. name .. " " .. name .. "_clamp(" .. name .. " val, " .. name .. " min, " .. name .. " max)", "{\n\treturn val < min ? min : val > max ? max : val;\n}\n\n")
+       emit(export_prefix .. "int " .. name .. "_cmp(const void *a, const void *b)", "{\n\treturn\n\t\t*(const " .. name .. " *) a < *(const " .. name .. " *) b ? -1 :\n\t\t*(const " .. name .. " *) a > *(const " .. name .. " *) b ? +1 :\n\t\t0;\n}\n\n")
+
+       if class == "f" then
+               emit(export_prefix .. name .. " " .. name .. "_mix(" .. name .. " a, " .. name .. " b, " .. name .. " f)", "{\n\treturn (1.0 - f) * a + b * f;\n}\n\n")
+       end
+
+       emit(export_prefix .. "void " .. name .. "_write(Blob *buffer, " .. name .. " *val)", "{\n" .. (class == "u"
+               and "\t" .. name .. " be = htobe" .. bits .. "(*val);\n\traw_write(buffer, &be, sizeof be);\n"
+               or  "\tu" .. bits .. "_write(buffer, (u" .. bits .. " *) val);\n"
+       ) .. "}\n\n")
+       emit(export_prefix .. "bool " .. name .. "_read(Blob *buffer, " .. name .. " *val)", "{\n" .. (class == "u"
+               and "\t" .. name .. " be;\n\tif (!raw_read(buffer, &be, sizeof be))\n\t\treturn false;\n\t*val = be" .. bits .. "toh(be);\n\treturn true;\n"
+               or  "\treturn u" .. bits .. "_read(buffer, (u" .. bits .. " *) val);\n"
+       ) .. "}\n\n")
+
+       emit_c("#ifdef USE_DRAGONNET\n")
+       emit_c(local_prefix .. "bool " .. name .. "_send(DragonnetPeer *peer, bool submit, " .. name .. " *val)\n{\n" .. (class == "u"
+               and "\t" .. name .. " be = htobe" .. bits .. "(*val);\n\treturn dragonnet_send_raw(peer, submit, &be, sizeof be);\n"
+               or  "\treturn u" .. bits .. "_send(peer, submit, (u" .. bits .. " *) val);\n"
+       ) .. "}\n\n")
+       emit_c(local_prefix .. "bool " .. name .. "_recv(DragonnetPeer *peer, " .. name .. " *val)\n{\n" .. (class == "u"
+               and "\t" .. name .. " be;\n\tif (!dragonnet_recv_raw(peer, &be, sizeof be))\n\t\treturn false;\n\t*val = be" .. bits .. "toh(be);\n\treturn true;\n"
+               or  "\treturn u" .. bits .. "_recv(peer, (u" .. bits .. " *) val);\n"
+       ) .. "}\n")
+       emit_c("#endif\n\n")
+
+       emit_h("\n")
+
+       for i = 2, 4 do
+               emit_vector(name, i)
+       end
+end
+
+for i = 0, 3 do
+       local bytes = math.floor(2 ^ i)
+       local bits = 8 * bytes
+
+       emit_numeric("u", bits, "uint" .. bits .. "_t")
+       emit_numeric("s", bits,  "int" .. bits .. "_t")
+
+       if i >= 2 then
+               emit_numeric("f", bits, ({"float", "double"})[i - 1])
+       end
+end
+
+-- string
+
+existing_types.String = true
+has_deallocator.String = true
+
+emit(
+export_prefix .. "void String_free(String *val)", [[
+{
+       if (*val)
+               free(*val);
+}
+
+]]
+)
+
+emit(
+export_prefix .. "void String_write(Blob *buffer, String *val)", [[
+{
+       *val ? raw_write(buffer, *val, strlen(*val) + 1) : raw_write(buffer, "", 1);
+}
+
+]]
+)
+
+emit(
+export_prefix .. "bool String_read(Blob *buffer, String *val)", [[
+{
+       String v = malloc(1 + (1 << 16));
+
+       char c;
+       for (u16 i = 0;; i++) {
+               if (!raw_read(buffer, &c, 1)) {
+                       free(v);
+                       return false;
+               }
+
+               v[i] = c;
+               if (c == '\0')
+                       break;
+       }
+
+       *val = realloc(v, strlen(v) + 1);
+
+       return true;
+}
+
+]]
+)
+
+emit_c(
+[[
+#ifdef USE_DRAGONNET
+]] .. local_prefix .. [[bool String_send(DragonnetPeer *peer, bool submit, String *val)
+{
+       return *val ? dragonnet_send_raw(peer, submit, *val, strlen(*val) + 1) : dragonnet_send_raw(peer, submit, "", 1);
+}
+
+]] .. local_prefix .. [[bool String_recv(DragonnetPeer *peer, String *val)
+{
+       String v = malloc(1 + (1 << 16));
+
+       char c;
+       for (u16 i = 0;; i++) {
+               if (!dragonnet_recv_raw(peer, &c, 1)) {
+                       free(v);
+                       return false;
+               }
+
+               v[i] = c;
+               if (c == '\0')
+                       break;
+       }
+
+       *val = realloc(v, strlen(v) + 1);
+
+       return true;
+}
+#endif
+
+]]
+)
+
+emit_h("\n")
+
+-- blob
+
+existing_types.Blob = true
+has_deallocator.Blob = true
+
+emit(
+export_prefix .. "void Blob_compress(Blob *buffer, Blob *val)", [[
+{
+       buffer->siz = 8 + 2 + val->siz;
+       buffer->data = malloc(buffer->siz);
+
+       *(u64 *) buffer->data = val->siz;
+
+       z_stream s;
+       s.zalloc = Z_NULL;
+       s.zfree = Z_NULL;
+       s.opaque = Z_NULL;
+
+       s.avail_in = val->siz;
+       s.next_in = (Bytef *) val->data;
+       s.avail_out = buffer->siz - 8 + 1;
+       s.next_out = (Bytef *) buffer->data + 8;
+
+       deflateInit(&s, Z_BEST_COMPRESSION);
+       deflate(&s, Z_FINISH);
+       deflateEnd(&s);
+
+       buffer->siz = s.total_out + 8;
+}
+
+]]
+)
+
+emit(
+export_prefix .. "void Blob_decompress(Blob *buffer, Blob *val)", [[
+{
+       buffer->siz = *(u64 *) val->data;
+       buffer->data = malloc(buffer->siz);
+
+       z_stream s;
+       s.zalloc = Z_NULL;
+       s.zfree = Z_NULL;
+       s.opaque = Z_NULL;
+
+       s.avail_in = val->siz - 8;
+       s.next_in = val->data + 8;
+       s.avail_out = buffer->siz;
+       s.next_out = (Bytef *) buffer->data;
+
+       inflateInit(&s);
+       inflate(&s, Z_NO_FLUSH);
+       inflateEnd(&s);
+
+       buffer->siz = s.total_out;
+}
+
+]]
+)
+
+emit(
+export_prefix .. "void Blob_shrink(Blob *val)", [[
+{
+       val->data = realloc(val->data, val->siz);
+}
+
+]]
+)
+
+emit(
+export_prefix .. "void Blob_free(Blob *val)", [[
+{
+       if (val->data)
+               free(val->data);
+}
+
+]]
+)
+
+emit(
+export_prefix .. "void Blob_write(Blob *buffer, Blob *val)", [[
+{
+       u64_write(buffer, &val->siz);
+       raw_write(buffer, val->data, val->siz);
+}
+
+]]
+)
+
+emit(
+export_prefix .. "bool Blob_read(Blob *buffer, Blob *val)", [[
+{
+       if (!u64_read(buffer, &val->siz))
+               return false;
+
+       if (!raw_read(buffer, val->data = malloc(val->siz), val->siz)) {
+               free(val->data);
+               val->data = NULL;
+               return false;
+       }
+
+       return true;
+}
+
+]]
+)
+
+emit_c(
+[[
+#ifdef USE_DRAGONNET
+]] .. local_prefix .. [[bool Blob_send(DragonnetPeer *peer, bool submit, Blob *val)
+{
+       if (!u64_send(peer, false, &val->siz))
+               return false;
+       return dragonnet_send_raw(peer, submit, val->data, val->siz);
+}
+
+]] .. local_prefix .. [[bool Blob_recv(DragonnetPeer *peer, Blob *val)
+{
+       if (!u64_recv(peer, &val->siz))
+               return false;
+
+       if (!dragonnet_recv_raw(peer, val->data = malloc(val->siz), val->siz)) {
+               free(val->data);
+               val->data = NULL;
+               return false;
+       }
+
+       return true;
+}
+#endif
+
+]]
+)
+
+emit_h("\n")
+
+
+emit_c(
+[[
+]] .. local_prefix .. [[void raw_write_compressed(Blob *buffer, void *val, void (*val_write)(Blob *, void *))
+{
+       Blob compressed, raw = {0};
+       val_write(&raw, val);
+       Blob_compress(&compressed, &raw);
+       Blob_write(buffer, &compressed);
+
+       Blob_free(&compressed);
+       Blob_free(&raw);
+}
+
+]] .. local_prefix .. [[bool raw_read_compressed(Blob *buffer, void *val, bool (*val_read)(Blob *, void *))
+{
+       Blob compressed, raw = {0};
+       bool success = Blob_read(buffer, &compressed);
+
+       if (success) {
+               Blob_decompress(&raw, &compressed);
+               Blob raw_buffer = raw;
+               success = success && val_read(&raw_buffer, val);
+       }
+
+       Blob_free(&compressed);
+       Blob_free(&raw);
+
+       return success;
+}
+
+#ifdef USE_DRAGONNET
+]] .. local_prefix .. [[bool raw_send_compressed(DragonnetPeer *peer, bool submit, void *val, void (*val_write)(Blob *, void *))
+{
+       Blob compressed, raw = {0};
+       val_write(&raw, val);
+       Blob_compress(&compressed, &raw);
+       bool success = Blob_send(peer, submit, &compressed);
+
+       Blob_free(&compressed);
+       Blob_free(&raw);
+
+       return success;
+}
+
+]] .. local_prefix .. [[bool raw_recv_compressed(DragonnetPeer *peer, void *val, bool (*val_read)(Blob *, void *))
+{
+       Blob compressed, raw = {0};
+       bool success = Blob_recv(peer, &compressed);
+
+       if (success) {
+               Blob_decompress(&raw, &compressed);
+               Blob raw_buffer = raw;
+               success = success && val_read(&raw_buffer, val);
+       }
+
+       Blob_free(&compressed);
+       Blob_free(&raw);
+
+       return success;
+}
+#endif
+
+]]
+)
+
+local custom_types = {}
+local current_type
+
+local function consume_name(name)
+       if current_type then
+               table.insert(custom_types, current_type)
+       end
+
+       if name == "" then
+               current_type = nil
+       else
+               current_type = {components = {}, component_names = {}}
+
+               current_type.flags = name:split(" ")
+               current_type.name = table.remove(current_type.flags, #current_type.flags)
+
+               if existing_types[current_type.name] then
+                       error("redeclaration of type " .. current_type.name)
+               end
+
+               existing_types[current_type.name] = true
+               has_deallocator[current_type.name] = false
+       end
+end
+
+local function consume_comp(comp)
+       if not current_type then
+               error("component without a type: " .. comp)
+       end
+
+       local component = {}
+
+       component.flags = comp:split(" ")
+       component.name = table.remove(component.flags, #component.flags)
+       component.type = table.remove(component.flags, #component.flags)
+
+       component.full_name = current_type.name .. "." .. component.name
+
+       local base_type = ""
+       local met_brace = false
+       local brace_level = 0
+
+       for i = 1, #component.type do
+               local c = component.type:sub(i, i)
+               local append = false
+
+               if c == "[" then
+                       if not met_brace then
+                               component.array = {}
+                       end
+
+                       met_brace = true
+                       brace_level = brace_level + 1
+
+                       if brace_level == 1 then
+                               table.insert(component.array, "")
+                       else
+                               append = true
+                       end
+               elseif c == "]" then
+                       brace_level = brace_level - 1
+
+                       if brace_level < 0 then
+                               error("missing [ in " .. component.full_name)
+                       elseif brace_level > 0 then
+                               append = true
+                       end
+               elseif not met_brace then
+                       base_type = base_type .. c
+               elseif brace_level == 1 then
+                       append = true
+               elseif brace_level == 0 then
+                       error("invalid character " .. c .. " outside of braces in " .. component.full_name)
+               end
+
+               if append then
+                       component.array[#component.array] = component.array[#component.array] .. c
+               end
+       end
+
+       component.type = base_type
+
+       if brace_level > 0 then
+               error("missing ] in " .. component.full_name)
+       end
+
+       if not existing_types[component.type] then
+               error("type " .. component.type .. " of " .. component.full_name .. " does not exist ")
+       end
+
+       has_deallocator[current_type.name] = has_deallocator[current_type.name] or has_deallocator[component.type]
+
+       if current_type.component_names[component.name] then
+               error("component " .. component.full_name .. " redeclared")
+       end
+
+       current_type.component_names[component.name] = true
+
+       table.insert(current_type.components, component)
+end
+
+if def then
+       for l in def:lines() do
+               local f = l:sub(1, 1)
+
+               if f == "\t" then
+                       consume_comp(l:sub(2, #l))
+               elseif f == "#" then
+                       emit_h(l .. "\n\n")
+               elseif f ~= ";" then
+                       consume_name(l)
+               end
+       end
+end
+
+consume_name("")
+
+local dragonnet_types_h = ""
+local dragonnet_types_c = ""
+
+for _, t in ipairs(custom_types) do
+       local pkt
+
+       for _, f in pairs(t.flags) do
+               if f == "pkt" then
+                       pkt = true
+               else
+                       error("invalid flag " .. f .. " for " .. t.name)
+               end
+       end
+
+       local typedef, free, write, read, send, recv =
+                  "",   "",    "",   "",   "",   ""
+
+       for ic, c in ipairs(t.components) do
+               typedef = typedef
+                       .. "\t" .. c.type .. " " .. c.name
+
+               if c.array then
+                       typedef = typedef
+                               .. "[" .. table.concat(c.array, "][") .. "]"
+               end
+
+               typedef = typedef
+                       .. ";\n"
+
+               local compressed = false
+
+               for _, f in pairs(c.flags) do
+                       if f == "compressed" then
+                               compressed = true
+                       else
+                               error("invalid flag " .. f .. " for " .. c.full_name)
+                       end
+               end
+
+               local indent = "\t"
+               local loop = ""
+               local index = ""
+               local array_submit = ""
+
+               if c.array then
+                       for ia, a in ipairs(c.array) do
+                               local it = "i" .. ia
+
+                               loop = loop .. indent .. "for (int " .. it .. " = 0; " .. it .. " < " .. a .. "; " .. it .. "++)\n"
+                               indent = indent .. "\t"
+
+                               index = index .. "[" .. it .. "]"
+
+                               array_submit = array_submit .. " && " .. it .. " == " .. a
+                       end
+               end
+
+               local addr = "&val->" .. c.name .. index
+
+               if has_deallocator[c.type] then
+                       free = free
+                               .. loop .. indent .. c.type .. "_free(" .. addr .. ");\n"
+               end
+
+               write = write
+                       .. loop .. indent .. (compressed
+                               and "raw_write_compressed(buffer, " .. addr .. ", (void *) &" .. c.type .. "_write);\n"
+                               or      c.type .. "_write(buffer, " .. addr .. ");\n"
+                       )
+
+               read = read
+                       .. loop .. indent .. "if (!" .. (compressed
+                               and "raw_read_compressed(buffer, " .. addr .. ", (void *) &" .. c.type .. "_read)"
+                               or      c.type .. "_read(buffer, " .. addr .. ")"
+                       ) .. ")\n" .. indent .. "\treturn false;\n"
+
+               local submit = ic == #t.components and "submit" .. array_submit or "false"
+               send = send
+                       .. loop .. indent .. "if (!" .. (compressed
+                               and "raw_send_compressed(peer, " .. submit .. ", " .. addr .. ", (void *) &" .. c.type .. "_write)"
+                               or      c.type .. "_send(peer, " .. submit .. ", " .. addr .. ")"
+                       ) .. ")\n" .. indent .. "\treturn false;\n"
+
+               recv = recv
+                       .. loop .. indent .. "if (!" .. (compressed
+                               and "raw_recv_compressed(peer, " .. addr .. ", (void *) &" .. c.type .. "_read)"
+                               or      c.type .. "_recv(peer, " .. addr .. ")"
+                       ) .. ")\n" .. indent .. "\treturn false;\n"
+       end
+
+       emit_h("typedef struct {\n" .. typedef .. "} " .. t.name .. ";\n")
+
+       if has_deallocator[t.name] then
+               emit(export_prefix .. "void " .. t.name .. "_free(" .. t.name .. " *val)", "{\n" .. free .. "}\n\n")
+       end
+
+       emit(export_prefix .. "void " .. t.name .. "_write(Blob *buffer, " .. t.name .. " *val)", "{\n" .. write .. "}\n\n")
+       emit(export_prefix .. "bool " .. t.name .. "_read(Blob *buffer, " .. t.name .. " *val)", "{\n" .. read .. "\treturn true;\n}\n\n")
+
+       if pkt then
+               emit_h("#ifdef USE_DRAGONNET\n")
+       end
+
+       emit_c("#ifdef USE_DRAGONNET\n")
+
+       emit_c(local_prefix .. "bool " .. t.name .. "_send(DragonnetPeer *peer, bool submit, " .. t.name .. " *val)\n{\n" .. send .. "\treturn true;\n}\n\n")
+       emit_c(local_prefix .. "bool " .. t.name .. "_recv(DragonnetPeer *peer, " .. t.name .. " *val)\n{\n" .. recv .. "\treturn true;\n}\n")
+
+       if pkt then
+               emit_c("\n")
+               emit_c("static DragonnetTypeId " .. t.name .. "_type_id = DRAGONNET_TYPE_" .. t.name .. ";\n")
+
+               emit("bool dragonnet_peer_send_" .. t.name .. "(DragonnetPeer *peer, " .. t.name .. " *val)", "{\n\tpthread_mutex_lock(&peer->mtx);\n\tif (!u16_send(peer, false, &" .. t.name .. "_type_id))\n\t\treturn false;\n\tif (!" .. t.name .. "_send(peer, true, val))\n\t\treturn false;\n\treturn true;\n}\n")
+               emit_h("#endif\n")
+
+               dragonnet_types_h = dragonnet_types_h
+                       .. "\tDRAGONNET_TYPE_" .. t.name .. ",\n"
+
+               dragonnet_types_c = dragonnet_types_c
+                       .. "\t{\n\t\t.siz = sizeof(" .. t.name .. "),\n\t\t.deserialize = (void *) &" .. t.name .. "_recv,\n\t\t.free = " .. (has_deallocator[t.name] and "(void *) &" .. t.name .. "_free" or "NULL") .. ",\n\t},\n"
+       end
+
+       emit_c("#endif\n\n")
+
+       emit_h("\n")
+end
+
+emit_h("#ifdef USE_DRAGONNET\n")
+emit_h("typedef enum {\n" .. dragonnet_types_h .. "\tDRAGONNET_NUM_TYPES\n} DragonnetTypeNum;\n")
+emit_h("#endif\n\n")
+
+emit_c("#ifdef USE_DRAGONNET\n")
+emit_c("DragonnetTypeId dragonnet_num_types = DRAGONNET_NUM_TYPES;\nDragonnetType dragonnet_types[] = {\n" .. dragonnet_types_c .. "};\n")
+emit_c("#endif\n")
+
+emit_h("#endif\n")
+
+h:close()
+c:close()
+def:close()
diff --git a/typegen.lua b/typegen.lua
deleted file mode 100755 (executable)
index 3f58de7..0000000
+++ /dev/null
@@ -1,850 +0,0 @@
-#!/usr/bin/env lua
-require "luax"
-
-local h   = io.open("types.h",   "w")
-local c   = io.open("types.c",   "w")
-local def = io.open("types.def", "r")
-
-local function emit_h(str)
-       h:write(str)
-end
-
-local function emit_c(str)
-       c:write(str)
-end
-
-local function emit(fun, code)
-       emit_h(fun .. ";\n")
-       emit_c(fun ..  "\n" .. code)
-end
-
--- fn prefixes
-local export_prefix = ""
-local local_prefix = "__attribute__((unused)) static inline "
-
--- head
-
-local disclaimer = [[
-/*
-       This file was automatically generated by Dragontype.
-       DO NOT EDIT it manually. Instead, edit types.def and re-run dragontype typegen.
-*/
-
-]]
-
-emit_h(disclaimer)
-emit_h([[
-#ifndef _DRAGONTYPE_TYPES_H_
-#define _DRAGONTYPE_TYPES_H_
-
-#ifdef USE_DRAGONNET
-#include <dragonnet/peer.h>
-#endif
-
-#include <stdbool.h>
-#include <stddef.h>
-#include <stdint.h>
-
-typedef char *String;
-
-typedef struct {
-       size_t siz;
-       unsigned char *data;
-} Blob;
-
-]]
-)
-
-emit_c(disclaimer)
-emit_c([[
-#ifdef USE_DRAGONNET
-#include <dragonnet/send.h>
-#include <dragonnet/recv.h>
-#endif
-
-#include <endian.h/endian.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdlib.h>
-#include <string.h>
-#include <zlib.h>
-
-#define htobe8(x) (x)
-#define be8toh(x) (x)
-
-#include "types.h"
-
-]] .. local_prefix ..  [[void raw_write(Blob *buffer, const void *data, size_t len)
-{
-       buffer->data = realloc(buffer->data, len + buffer->siz);
-       memcpy(&buffer->data[buffer->siz], data, len);
-       buffer->siz += len;
-}
-
-]] .. local_prefix ..  [[bool raw_read(Blob *buffer, void *data, size_t len)
-{
-       if (len == 0)
-               return true;
-
-       if (buffer->siz < len) {
-               fprintf(stderr, "[warning] buffer exhausted (requested bytes: %lu, remaining bytes: %lu)\n", len, buffer->siz);
-               return false;
-       }
-
-       memcpy(data, buffer->data, len);
-       buffer->data += len;
-       buffer->siz -= len;
-       return true;
-}
-
-]]
-)
-
--- existing types
-local existing_types = {}
-local has_deallocator = {}
-
--- vector components
-
-local base_vector_components = {"x", "y", "z", "w"}
-local vector_components = {}
-
-for i = 2, 4 do
-       local components = {}
-
-       for j = 1, i do
-               table.insert(components, base_vector_components[j])
-       end
-
-       vector_components[i] = components
-end
-
--- numeric types
-
-local function emit_vector(type, l)
-       local name = "v" .. l .. type
-       local box = "aabb" .. l .. type
-
-       existing_types[name] = true
-       has_deallocator[name] = false
-
-       existing_types[box] = true
-       has_deallocator[box] = false
-
-       local typedef, equals, add, clamp, cmp, scale, mix, write, read, send, recv =
-                  "",     "",  "",    "",  "",    "",  "",    "",   "",   "",   ""
-
-       typedef = typedef .. "\t" .. type .. " "
-       equals  = equals  .. "\treturn "
-       add     = add     .. "\treturn (" .. name .. ") {"
-       clamp   = clamp   .. "\treturn (" .. name .. ") {"
-       cmp     = cmp     .. "\tint i;\n"
-       scale   = scale   .. "\treturn (" .. name .. ") {"
-       mix     = mix     .. "\treturn (" .. name .. ") {"
-
-       for i, c in ipairs(vector_components[l]) do
-               local last = i == l
-
-               typedef = typedef
-                       .. c ..
-                       (last
-                               and ";\n"
-                               or ", "
-                       )
-
-               equals = equals
-                       .. "a." .. c .. " == "
-                       .. "b." .. c ..
-                       (last
-                               and ";\n"
-                               or " && "
-                       )
-
-               add = add
-                       .. "a." .. c .. " + "
-                       .. "b." .. c ..
-                       (last
-                               and "};\n"
-                               or ", "
-                       )
-
-               clamp = clamp
-                       .. type .. "_clamp("
-                       .. "val." .. c .. ", "
-                       .. "min." .. c .. ", "
-                       .. "max." .. c .. ")" ..
-                       (last
-                               and "};\n"
-                               or ", "
-                       )
-
-               cmp = cmp
-                       .. "\tif ((i = " .. type .. "_cmp("
-                       .. "&((const " .. name .. " *) a)->" .. c .. ", "
-                       .. "&((const " .. name .. " *) b)->" .. c .. ")) != 0)"
-                       .. "\n\t\treturn i;\n"
-
-               scale = scale
-                       .. "v." .. c .. " * s" ..
-                       (last
-                               and "};\n"
-                               or ", "
-                       )
-
-               mix = mix
-                       .. type .. "_mix("
-                       .. "a." .. c .. ", "
-                       .. "b." .. c .. ", "
-                       .. "f" .. ")" ..
-                       (last
-                               and "};\n"
-                               or ", "
-                       )
-
-               write = write
-                       .. "\t" .. type .. "_write(buffer, &val->" .. c .. ");\n"
-
-               read = read
-                       .. "\tif (!" .. type .. "_read(buffer, &val->" .. c .. "))\n\t\treturn false;\n"
-
-               send = send
-                       .. "\tif (!" .. type .. "_send(peer, " .. (last and "submit" or "false") .. ", &val->" .. c .. "))\n\t\treturn false;\n"
-
-               recv = recv
-                       .. "\tif (!" .. type .. "_recv(peer, &val->" .. c .. "))\n\t\treturn false;\n"
-       end
-
-       emit_h("typedef struct {\n" .. typedef ..  "} " .. name .. ";\n")
-
-       emit(export_prefix .. "bool " .. name .. "_equals(" .. name .. " a, " .. name .. " b)", "{\n" .. equals .. "}\n\n")
-       emit(export_prefix .. name .. " " .. name .. "_add(" .. name .. " a, " .. name .. " b)", "{\n" .. add .. "}\n\n")
-       emit(export_prefix .. name .. " " .. name .. "_clamp(" .. name .. " val, " .. name .. " min, " .. name .. " max)", "{\n" .. clamp .. "}\n\n")
-       emit(export_prefix .. "int " .. name .. "_cmp(const void *a, const void *b)", "{\n" .. cmp .. "\treturn 0;\n}\n\n")
-       emit(export_prefix .. name .. " " .. name .. "_scale(" .. name .. " v, " .. type .. " s)", "{\n" .. scale .. "}\n\n")
-
-       if type:sub(1, 1) == "f" then
-               emit(export_prefix .. name .. " " .. name .. "_mix(" .. name .. " a, " .. name .. " b, " .. type .. " f)", "{\n" .. mix .. "}\n\n")
-       end
-
-       emit(export_prefix .. "void " .. name .. "_write(Blob *buffer, " .. name .. " *val)", "{\n" .. write .. "}\n\n")
-       emit(export_prefix .. "bool " .. name .. "_read(Blob *buffer, " .. name .. " *val)", "{\n" .. read .. "\treturn true;\n}\n\n")
-
-       emit_c("#ifdef USE_DRAGONNET\n")
-       emit_c(local_prefix .. "bool " .. name .. "_send(DragonnetPeer *peer, bool submit, " .. name .. " *val)\n{\n" .. send .. "\treturn true;\n}\n\n")
-       emit_c(local_prefix .. "bool " .. name .. "_recv(DragonnetPeer *peer, " .. name .. " *val)\n{\n" .. recv .. "\treturn true;\n}\n")
-       emit_c("#endif\n\n")
-
-       emit_h("\n")
-
-       emit_h("typedef struct {\n\t" .. name .. " min, max;\n} " .. box .. ";\n")
-
-       emit(export_prefix .. "void " .. box .. "_write(Blob *buffer, " .. box .. " *val)", "{\n\t" .. name .. "_write(buffer, &val->min);\n\t" .. name .. "_write(buffer, &val->max);\n}\n\n")
-       emit(export_prefix .. "bool " .. box .. "_read(Blob *buffer, " .. box .. " *val)", "{\n\tif (!" .. name .. "_read(buffer, &val->min))\n\t\treturn false;\n\tif (!" .. name .. "_read(buffer, &val->max))\n\t\treturn false;\n\treturn true;\n}\n\n")
-
-       emit_c("#ifdef USE_DRAGONNET\n")
-       emit_c(local_prefix .. "bool " .. box .. "_send(DragonnetPeer *peer, bool submit, " .. box .. " *val)\n{\n\tif (!" .. name .. "_send(peer, false, &val->min))\n\t\treturn false;\n\tif (!" .. name .. "_send(peer, submit, &val->max))\n\t\treturn false;\n\treturn true;\n}\n\n")
-       emit_c(local_prefix .. "bool " .. box .. "_recv(DragonnetPeer *peer, " .. box .. " *val)\n{\n\tif (!" .. name .. "_recv(peer, &val->min))\n\t\treturn false;\n\t if (!" .. name .. "_recv(peer, &val->max))\n\t\treturn false;\n\treturn true;\n}\n")
-       emit_c("#endif\n\n")
-
-       emit_h("\n")
-end
-
-local function emit_numeric(class, bits, alias)
-       local name = class .. bits
-
-       existing_types[name] = true
-       has_deallocator[name] = false
-
-       emit_h("typedef " .. alias .. " " .. name .. ";\n")
-
-       emit(export_prefix .. name .. " " .. name .. "_min(" .. name .. " a, " .. name .. " b)", "{\n\treturn a < b ? a : b;\n}\n\n")
-       emit(export_prefix .. name .. " " .. name .. "_max(" .. name .. " a, " .. name .. " b)", "{\n\treturn a > b ? a : b;\n}\n\n")
-       emit(export_prefix .. name .. " " .. name .. "_clamp(" .. name .. " val, " .. name .. " min, " .. name .. " max)", "{\n\treturn val < min ? min : val > max ? max : val;\n}\n\n")
-       emit(export_prefix .. "int " .. name .. "_cmp(const void *a, const void *b)", "{\n\treturn\n\t\t*(const " .. name .. " *) a < *(const " .. name .. " *) b ? -1 :\n\t\t*(const " .. name .. " *) a > *(const " .. name .. " *) b ? +1 :\n\t\t0;\n}\n\n")
-
-       if class == "f" then
-               emit(export_prefix .. name .. " " .. name .. "_mix(" .. name .. " a, " .. name .. " b, " .. name .. " f)", "{\n\treturn (1.0 - f) * a + b * f;\n}\n\n")
-       end
-
-       emit(export_prefix .. "void " .. name .. "_write(Blob *buffer, " .. name .. " *val)", "{\n" .. (class == "u"
-               and "\t" .. name .. " be = htobe" .. bits .. "(*val);\n\traw_write(buffer, &be, sizeof be);\n"
-               or  "\tu" .. bits .. "_write(buffer, (u" .. bits .. " *) val);\n"
-       ) .. "}\n\n")
-       emit(export_prefix .. "bool " .. name .. "_read(Blob *buffer, " .. name .. " *val)", "{\n" .. (class == "u"
-               and "\t" .. name .. " be;\n\tif (!raw_read(buffer, &be, sizeof be))\n\t\treturn false;\n\t*val = be" .. bits .. "toh(be);\n\treturn true;\n"
-               or  "\treturn u" .. bits .. "_read(buffer, (u" .. bits .. " *) val);\n"
-       ) .. "}\n\n")
-
-       emit_c("#ifdef USE_DRAGONNET\n")
-       emit_c(local_prefix .. "bool " .. name .. "_send(DragonnetPeer *peer, bool submit, " .. name .. " *val)\n{\n" .. (class == "u"
-               and "\t" .. name .. " be = htobe" .. bits .. "(*val);\n\treturn dragonnet_send_raw(peer, submit, &be, sizeof be);\n"
-               or  "\treturn u" .. bits .. "_send(peer, submit, (u" .. bits .. " *) val);\n"
-       ) .. "}\n\n")
-       emit_c(local_prefix .. "bool " .. name .. "_recv(DragonnetPeer *peer, " .. name .. " *val)\n{\n" .. (class == "u"
-               and "\t" .. name .. " be;\n\tif (!dragonnet_recv_raw(peer, &be, sizeof be))\n\t\treturn false;\n\t*val = be" .. bits .. "toh(be);\n\treturn true;\n"
-               or  "\treturn u" .. bits .. "_recv(peer, (u" .. bits .. " *) val);\n"
-       ) .. "}\n")
-       emit_c("#endif\n\n")
-
-       emit_h("\n")
-
-       for i = 2, 4 do
-               emit_vector(name, i)
-       end
-end
-
-for i = 0, 3 do
-       local bytes = math.floor(2 ^ i)
-       local bits = 8 * bytes
-
-       emit_numeric("u", bits, "uint" .. bits .. "_t")
-       emit_numeric("s", bits,  "int" .. bits .. "_t")
-
-       if i >= 2 then
-               emit_numeric("f", bits, ({"float", "double"})[i - 1])
-       end
-end
-
--- string
-
-existing_types.String = true
-has_deallocator.String = true
-
-emit(
-export_prefix .. "void String_free(String *val)", [[
-{
-       if (*val)
-               free(*val);
-}
-
-]]
-)
-
-emit(
-export_prefix .. "void String_write(Blob *buffer, String *val)", [[
-{
-       *val ? raw_write(buffer, *val, strlen(*val) + 1) : raw_write(buffer, "", 1);
-}
-
-]]
-)
-
-emit(
-export_prefix .. "bool String_read(Blob *buffer, String *val)", [[
-{
-       String v = malloc(1 + (1 << 16));
-
-       char c;
-       for (u16 i = 0;; i++) {
-               if (!raw_read(buffer, &c, 1)) {
-                       free(v);
-                       return false;
-               }
-
-               v[i] = c;
-               if (c == '\0')
-                       break;
-       }
-
-       *val = realloc(v, strlen(v) + 1);
-
-       return true;
-}
-
-]]
-)
-
-emit_c(
-[[
-#ifdef USE_DRAGONNET
-]] .. local_prefix .. [[bool String_send(DragonnetPeer *peer, bool submit, String *val)
-{
-       return *val ? dragonnet_send_raw(peer, submit, *val, strlen(*val) + 1) : dragonnet_send_raw(peer, submit, "", 1);
-}
-
-]] .. local_prefix .. [[bool String_recv(DragonnetPeer *peer, String *val)
-{
-       String v = malloc(1 + (1 << 16));
-
-       char c;
-       for (u16 i = 0;; i++) {
-               if (!dragonnet_recv_raw(peer, &c, 1)) {
-                       free(v);
-                       return false;
-               }
-
-               v[i] = c;
-               if (c == '\0')
-                       break;
-       }
-
-       *val = realloc(v, strlen(v) + 1);
-
-       return true;
-}
-#endif
-
-]]
-)
-
-emit_h("\n")
-
--- blob
-
-existing_types.Blob = true
-has_deallocator.Blob = true
-
-emit(
-export_prefix .. "void Blob_compress(Blob *buffer, Blob *val)", [[
-{
-       buffer->siz = 8 + 2 + val->siz;
-       buffer->data = malloc(buffer->siz);
-
-       *(u64 *) buffer->data = val->siz;
-
-       z_stream s;
-       s.zalloc = Z_NULL;
-       s.zfree = Z_NULL;
-       s.opaque = Z_NULL;
-
-       s.avail_in = val->siz;
-       s.next_in = (Bytef *) val->data;
-       s.avail_out = buffer->siz - 8 + 1;
-       s.next_out = (Bytef *) buffer->data + 8;
-
-       deflateInit(&s, Z_BEST_COMPRESSION);
-       deflate(&s, Z_FINISH);
-       deflateEnd(&s);
-
-       buffer->siz = s.total_out + 8;
-}
-
-]]
-)
-
-emit(
-export_prefix .. "void Blob_decompress(Blob *buffer, Blob *val)", [[
-{
-       buffer->siz = *(u64 *) val->data;
-       buffer->data = malloc(buffer->siz);
-
-       z_stream s;
-       s.zalloc = Z_NULL;
-       s.zfree = Z_NULL;
-       s.opaque = Z_NULL;
-
-       s.avail_in = val->siz - 8;
-       s.next_in = val->data + 8;
-       s.avail_out = buffer->siz;
-       s.next_out = (Bytef *) buffer->data;
-
-       inflateInit(&s);
-       inflate(&s, Z_NO_FLUSH);
-       inflateEnd(&s);
-
-       buffer->siz = s.total_out;
-}
-
-]]
-)
-
-emit(
-export_prefix .. "void Blob_shrink(Blob *val)", [[
-{
-       val->data = realloc(val->data, val->siz);
-}
-
-]]
-)
-
-emit(
-export_prefix .. "void Blob_free(Blob *val)", [[
-{
-       if (val->data)
-               free(val->data);
-}
-
-]]
-)
-
-emit(
-export_prefix .. "void Blob_write(Blob *buffer, Blob *val)", [[
-{
-       u64_write(buffer, &val->siz);
-       raw_write(buffer, val->data, val->siz);
-}
-
-]]
-)
-
-emit(
-export_prefix .. "bool Blob_read(Blob *buffer, Blob *val)", [[
-{
-       if (!u64_read(buffer, &val->siz))
-               return false;
-
-       if (!raw_read(buffer, val->data = malloc(val->siz), val->siz)) {
-               free(val->data);
-               val->data = NULL;
-               return false;
-       }
-
-       return true;
-}
-
-]]
-)
-
-emit_c(
-[[
-#ifdef USE_DRAGONNET
-]] .. local_prefix .. [[bool Blob_send(DragonnetPeer *peer, bool submit, Blob *val)
-{
-       if (!u64_send(peer, false, &val->siz))
-               return false;
-       return dragonnet_send_raw(peer, submit, val->data, val->siz);
-}
-
-]] .. local_prefix .. [[bool Blob_recv(DragonnetPeer *peer, Blob *val)
-{
-       if (!u64_recv(peer, &val->siz))
-               return false;
-
-       if (!dragonnet_recv_raw(peer, val->data = malloc(val->siz), val->siz)) {
-               free(val->data);
-               val->data = NULL;
-               return false;
-       }
-
-       return true;
-}
-#endif
-
-]]
-)
-
-emit_h("\n")
-
-
-emit_c(
-[[
-]] .. local_prefix .. [[void raw_write_compressed(Blob *buffer, void *val, void (*val_write)(Blob *, void *))
-{
-       Blob compressed, raw = {0};
-       val_write(&raw, val);
-       Blob_compress(&compressed, &raw);
-       Blob_write(buffer, &compressed);
-
-       Blob_free(&compressed);
-       Blob_free(&raw);
-}
-
-]] .. local_prefix .. [[bool raw_read_compressed(Blob *buffer, void *val, bool (*val_read)(Blob *, void *))
-{
-       Blob compressed, raw = {0};
-       bool success = Blob_read(buffer, &compressed);
-
-       if (success) {
-               Blob_decompress(&raw, &compressed);
-               Blob raw_buffer = raw;
-               success = success && val_read(&raw_buffer, val);
-       }
-
-       Blob_free(&compressed);
-       Blob_free(&raw);
-
-       return success;
-}
-
-#ifdef USE_DRAGONNET
-]] .. local_prefix .. [[bool raw_send_compressed(DragonnetPeer *peer, bool submit, void *val, void (*val_write)(Blob *, void *))
-{
-       Blob compressed, raw = {0};
-       val_write(&raw, val);
-       Blob_compress(&compressed, &raw);
-       bool success = Blob_send(peer, submit, &compressed);
-
-       Blob_free(&compressed);
-       Blob_free(&raw);
-
-       return success;
-}
-
-]] .. local_prefix .. [[bool raw_recv_compressed(DragonnetPeer *peer, void *val, bool (*val_read)(Blob *, void *))
-{
-       Blob compressed, raw = {0};
-       bool success = Blob_recv(peer, &compressed);
-
-       if (success) {
-               Blob_decompress(&raw, &compressed);
-               Blob raw_buffer = raw;
-               success = success && val_read(&raw_buffer, val);
-       }
-
-       Blob_free(&compressed);
-       Blob_free(&raw);
-
-       return success;
-}
-#endif
-
-]]
-)
-
-local custom_types = {}
-local current_type
-
-local function consume_name(name)
-       if current_type then
-               table.insert(custom_types, current_type)
-       end
-
-       if name == "" then
-               current_type = nil
-       else
-               current_type = {components = {}, component_names = {}}
-
-               current_type.flags = name:split(" ")
-               current_type.name = table.remove(current_type.flags, #current_type.flags)
-
-               if existing_types[current_type.name] then
-                       error("redeclaration of type " .. current_type.name)
-               end
-
-               existing_types[current_type.name] = true
-               has_deallocator[current_type.name] = false
-       end
-end
-
-local function consume_comp(comp)
-       if not current_type then
-               error("component without a type: " .. comp)
-       end
-
-       local component = {}
-
-       component.flags = comp:split(" ")
-       component.name = table.remove(component.flags, #component.flags)
-       component.type = table.remove(component.flags, #component.flags)
-
-       component.full_name = current_type.name .. "." .. component.name
-
-       local base_type = ""
-       local met_brace = false
-       local brace_level = 0
-
-       for i = 1, #component.type do
-               local c = component.type:sub(i, i)
-               local append = false
-
-               if c == "[" then
-                       if not met_brace then
-                               component.array = {}
-                       end
-
-                       met_brace = true
-                       brace_level = brace_level + 1
-
-                       if brace_level == 1 then
-                               table.insert(component.array, "")
-                       else
-                               append = true
-                       end
-               elseif c == "]" then
-                       brace_level = brace_level - 1
-
-                       if brace_level < 0 then
-                               error("missing [ in " .. component.full_name)
-                       elseif brace_level > 0 then
-                               append = true
-                       end
-               elseif not met_brace then
-                       base_type = base_type .. c
-               elseif brace_level == 1 then
-                       append = true
-               elseif brace_level == 0 then
-                       error("invalid character " .. c .. " outside of braces in " .. component.full_name)
-               end
-
-               if append then
-                       component.array[#component.array] = component.array[#component.array] .. c
-               end
-       end
-
-       component.type = base_type
-
-       if brace_level > 0 then
-               error("missing ] in " .. component.full_name)
-       end
-
-       if not existing_types[component.type] then
-               error("type " .. component.type .. " of " .. component.full_name .. " does not exist ")
-       end
-
-       has_deallocator[current_type.name] = has_deallocator[current_type.name] or has_deallocator[component.type]
-
-       if current_type.component_names[component.name] then
-               error("component " .. component.full_name .. " redeclared")
-       end
-
-       current_type.component_names[component.name] = true
-
-       table.insert(current_type.components, component)
-end
-
-if def then
-       for l in def:lines() do
-               local f = l:sub(1, 1)
-
-               if f == "\t" then
-                       consume_comp(l:sub(2, #l))
-               elseif f == "#" then
-                       emit_h(l .. "\n\n")
-               elseif f ~= ";" then
-                       consume_name(l)
-               end
-       end
-end
-
-consume_name("")
-
-local dragonnet_types_h = ""
-local dragonnet_types_c = ""
-
-for _, t in ipairs(custom_types) do
-       local pkt
-
-       for _, f in pairs(t.flags) do
-               if f == "pkt" then
-                       pkt = true
-               else
-                       error("invalid flag " .. f .. " for " .. t.name)
-               end
-       end
-
-       local typedef, free, write, read, send, recv =
-                  "",   "",    "",   "",   "",   ""
-
-       for ic, c in ipairs(t.components) do
-               typedef = typedef
-                       .. "\t" .. c.type .. " " .. c.name
-
-               if c.array then
-                       typedef = typedef
-                               .. "[" .. table.concat(c.array, "][") .. "]"
-               end
-
-               typedef = typedef
-                       .. ";\n"
-
-               local compressed = false
-
-               for _, f in pairs(c.flags) do
-                       if f == "compressed" then
-                               compressed = true
-                       else
-                               error("invalid flag " .. f .. " for " .. c.full_name)
-                       end
-               end
-
-               local indent = "\t"
-               local loop = ""
-               local index = ""
-               local array_submit = ""
-
-               if c.array then
-                       for ia, a in ipairs(c.array) do
-                               local it = "i" .. ia
-
-                               loop = loop .. indent .. "for (int " .. it .. " = 0; " .. it .. " < " .. a .. "; " .. it .. "++)\n"
-                               indent = indent .. "\t"
-
-                               index = index .. "[" .. it .. "]"
-
-                               array_submit = array_submit .. " && " .. it .. " == " .. a
-                       end
-               end
-
-               local addr = "&val->" .. c.name .. index
-
-               if has_deallocator[c.type] then
-                       free = free
-                               .. loop .. indent .. c.type .. "_free(" .. addr .. ");\n"
-               end
-
-               write = write
-                       .. loop .. indent .. (compressed
-                               and "raw_write_compressed(buffer, " .. addr .. ", (void *) &" .. c.type .. "_write);\n"
-                               or      c.type .. "_write(buffer, " .. addr .. ");\n"
-                       )
-
-               read = read
-                       .. loop .. indent .. "if (!" .. (compressed
-                               and "raw_read_compressed(buffer, " .. addr .. ", (void *) &" .. c.type .. "_read)"
-                               or      c.type .. "_read(buffer, " .. addr .. ")"
-                       ) .. ")\n" .. indent .. "\treturn false;\n"
-
-               local submit = ic == #t.components and "submit" .. array_submit or "false"
-               send = send
-                       .. loop .. indent .. "if (!" .. (compressed
-                               and "raw_send_compressed(peer, " .. submit .. ", " .. addr .. ", (void *) &" .. c.type .. "_write)"
-                               or      c.type .. "_send(peer, " .. submit .. ", " .. addr .. ")"
-                       ) .. ")\n" .. indent .. "\treturn false;\n"
-
-               recv = recv
-                       .. loop .. indent .. "if (!" .. (compressed
-                               and "raw_recv_compressed(peer, " .. addr .. ", (void *) &" .. c.type .. "_read)"
-                               or      c.type .. "_recv(peer, " .. addr .. ")"
-                       ) .. ")\n" .. indent .. "\treturn false;\n"
-       end
-
-       emit_h("typedef struct {\n" .. typedef .. "} " .. t.name .. ";\n")
-
-       if has_deallocator[t.name] then
-               emit(export_prefix .. "void " .. t.name .. "_free(" .. t.name .. " *val)", "{\n" .. free .. "}\n\n")
-       end
-
-       emit(export_prefix .. "void " .. t.name .. "_write(Blob *buffer, " .. t.name .. " *val)", "{\n" .. write .. "}\n\n")
-       emit(export_prefix .. "bool " .. t.name .. "_read(Blob *buffer, " .. t.name .. " *val)", "{\n" .. read .. "\treturn true;\n}\n\n")
-
-       if pkt then
-               emit_h("#ifdef USE_DRAGONNET\n")
-       end
-
-       emit_c("#ifdef USE_DRAGONNET\n")
-
-       emit_c(local_prefix .. "bool " .. t.name .. "_send(DragonnetPeer *peer, bool submit, " .. t.name .. " *val)\n{\n" .. send .. "\treturn true;\n}\n\n")
-       emit_c(local_prefix .. "bool " .. t.name .. "_recv(DragonnetPeer *peer, " .. t.name .. " *val)\n{\n" .. recv .. "\treturn true;\n}\n")
-
-       if pkt then
-               emit_c("\n")
-               emit_c("static DragonnetTypeId " .. t.name .. "_type_id = DRAGONNET_TYPE_" .. t.name .. ";\n")
-
-               emit("bool dragonnet_peer_send_" .. t.name .. "(DragonnetPeer *peer, " .. t.name .. " *val)", "{\n\tpthread_mutex_lock(&peer->mtx);\n\tif (!u16_send(peer, false, &" .. t.name .. "_type_id))\n\t\treturn false;\n\tif (!" .. t.name .. "_send(peer, true, val))\n\t\treturn false;\n\treturn true;\n}\n")
-               emit_h("#endif\n")
-
-               dragonnet_types_h = dragonnet_types_h
-                       .. "\tDRAGONNET_TYPE_" .. t.name .. ",\n"
-
-               dragonnet_types_c = dragonnet_types_c
-                       .. "\t{\n\t\t.siz = sizeof(" .. t.name .. "),\n\t\t.deserialize = (void *) &" .. t.name .. "_recv,\n\t\t.free = " .. (has_deallocator[t.name] and "(void *) &" .. t.name .. "_free" or "NULL") .. ",\n\t},\n"
-       end
-
-       emit_c("#endif\n\n")
-
-       emit_h("\n")
-end
-
-emit_h("#ifdef USE_DRAGONNET\n")
-emit_h("typedef enum {\n" .. dragonnet_types_h .. "\tDRAGONNET_NUM_TYPES\n} DragonnetTypeNum;\n")
-emit_h("#endif\n\n")
-
-emit_c("#ifdef USE_DRAGONNET\n")
-emit_c("DragonnetTypeId dragonnet_num_types = DRAGONNET_NUM_TYPES;\nDragonnetType dragonnet_types[] = {\n" .. dragonnet_types_c .. "};\n")
-emit_c("#endif\n")
-
-emit_h("#endif\n")
-
-h:close()
-c:close()
-def:close()