From 61292ea8a973ba03f93ebf4acde705071e15ccaf Mon Sep 17 00:00:00 2001 From: Elias Fleckenstein Date: Sat, 12 Feb 2022 23:11:31 +0100 Subject: [PATCH] Initial implementation --- .gitmodules | 3 + luax | 1 + typegen.lua | 826 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 830 insertions(+) create mode 100644 .gitmodules create mode 160000 luax create mode 100755 typegen.lua diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..6c7642f --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "luax"] + path = luax + url = https://github.com/EliasFleckenstein03/luax diff --git a/luax b/luax new file mode 160000 index 0000000..130a68d --- /dev/null +++ b/luax @@ -0,0 +1 @@ +Subproject commit 130a68dc27f3461838be005b3cb7b8a115e2c21e diff --git a/typegen.lua b/typegen.lua new file mode 100755 index 0000000..67afab0 --- /dev/null +++ b/typegen.lua @@ -0,0 +1,826 @@ +#! /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 +#endif + +#include +#include +#include + +typedef char *String; + +typedef struct { + size_t siz; + unsigned char *data; +} Blob; + +]] +) + +emit_c(disclaimer) +emit_c([[ +#ifdef USE_DRAGONNET +#include +#include +#endif + +#include +#include +#include +#include +#include +#include + +#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 (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 + + local typedef, equals, add, clamp, mix, write, read, send, recv = + "", "", "", "", "", "", "", "", "" + + typedef = typedef .. "\t" .. type .. " " + equals = equals .. "\treturn " + add = add .. "\treturn (" .. name .. ") {" + clamp = clamp .. "\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 ", " + ) + + 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") + + 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") + + 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)", [[ +{ + raw_write(buffer, *val, strlen(*val) + 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 dragonnet_send_raw(peer, submit, *val, strlen(*val) + 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() -- 2.44.0