summaryrefslogtreecommitdiff
path: root/xC-gen-proto-c.awk
diff options
context:
space:
mode:
authorPřemysl Eric Janouch <p@janouch.name>2022-08-08 04:39:20 +0200
committerPřemysl Eric Janouch <p@janouch.name>2022-09-05 14:26:00 +0200
commit1639235a48dbed75c2563c9a497b41c31a2a1bae (patch)
tree18193b72fa47e6bcac1358289ac9c36ed00c70ac /xC-gen-proto-c.awk
parent2160d037943ef0a3adbf4c6e30a91ee0f205c3f3 (diff)
downloadxK-1639235a48dbed75c2563c9a497b41c31a2a1bae.tar.gz
xK-1639235a48dbed75c2563c9a497b41c31a2a1bae.tar.xz
xK-1639235a48dbed75c2563c9a497b41c31a2a1bae.zip
Start X11 and web frontends for xC
For this, we needed a wire protocol. After surveying available options, it was decided to implement an XDR-like protocol code generator in portable AWK. It now has two backends, per each of: - xF, the X11 frontend, is in C, and is meant to be the primary user interface in the future. - xP, the web frontend, relies on a protocol proxy written in Go, and is meant for use on-the-go (no pun intended). They are very much work-in-progress proofs of concept right now, and the relay protocol is certain to change.
Diffstat (limited to 'xC-gen-proto-c.awk')
-rw-r--r--xC-gen-proto-c.awk325
1 files changed, 325 insertions, 0 deletions
diff --git a/xC-gen-proto-c.awk b/xC-gen-proto-c.awk
new file mode 100644
index 0000000..e7faef0
--- /dev/null
+++ b/xC-gen-proto-c.awk
@@ -0,0 +1,325 @@
+# xC-gen-proto-c.awk: C backend for xC-gen-proto.awk.
+#
+# Copyright (c) 2022, Přemysl Eric Janouch <p@janouch.name>
+# SPDX-License-Identifier: 0BSD
+#
+# Neither *_new() nor *_destroy() functions are provided, because they'd only
+# be useful for top-levels, and are merely extra malloc()/free() calls.
+# Users are expected to reuse buffers.
+#
+# Similarly, no constructors are produced--those are easy to write manually.
+#
+# All arrays are deserialized zero-terminated, so u8<> and i8<> can be directly
+# used as C strings.
+#
+# All types must be able to dispose partially zero values going from the back,
+# i.e., in the reverse order of deserialization.
+
+function define_internal(name, ctype) {
+ Types[name] = "internal"
+ CodegenCType[name] = ctype
+}
+
+function define_int(shortname, ctype) {
+ define_internal(shortname, ctype)
+ CodegenSerialize[shortname] = \
+ "\tstr_pack_" shortname "(w, %s);\n"
+ CodegenDeserialize[shortname] = \
+ "\tif (!msg_unpacker_" shortname "(r, &%s))\n" \
+ "\t\treturn false;\n"
+}
+
+function define_sint(size) { define_int("i" size, "int" size "_t") }
+function define_uint(size) { define_int("u" size, "uint" size "_t") }
+
+function codegen_begin() {
+ define_sint("8")
+ define_sint("16")
+ define_sint("32")
+ define_sint("64")
+ define_uint("8")
+ define_uint("16")
+ define_uint("32")
+ define_uint("64")
+
+ define_internal("string", "struct str")
+ CodegenDispose["string"] = "\tstr_free(&%s);\n"
+ CodegenSerialize["string"] = \
+ "\tif (!proto_string_serialize(&%s, w))\n" \
+ "\t\treturn false;\n"
+ CodegenDeserialize["string"] = \
+ "\tif (!proto_string_deserialize(&%s, r))\n" \
+ "\t\treturn false;\n"
+
+ define_internal("bool", "bool")
+ CodegenSerialize["bool"] = \
+ "\tstr_pack_u8(w, !!%s);\n"
+ CodegenDeserialize["bool"] = \
+ "\t{\n" \
+ "\t\tuint8_t v = 0;\n" \
+ "\t\tif (!msg_unpacker_u8(r, &v))\n" \
+ "\t\t\treturn false;\n" \
+ "\t\t%s = !!v;\n" \
+ "\t}\n"
+
+ print "// This file directly depends on liberty.c, but doesn't include it."
+
+ print ""
+ print "static bool"
+ print "proto_string_serialize(const struct str *s, struct str *w) {"
+ print "\tif (s->len > UINT32_MAX)"
+ print "\t\treturn false;"
+ print "\tstr_pack_u32(w, s->len);"
+ print "\tstr_append_str(w, s);"
+ print "\treturn true;"
+ print "}"
+
+ print ""
+ print "static bool"
+ print "proto_string_deserialize(struct str *s, struct msg_unpacker *r) {"
+ print "\tuint32_t len = 0;"
+ print "\tif (!msg_unpacker_u32(r, &len))"
+ print "\t\treturn false;"
+ print "\tif (msg_unpacker_get_available(r) < len)"
+ print "\t\treturn false;"
+ print "\t*s = str_make();"
+ print "\tstr_append_data(s, r->data + r->offset, len);"
+ print "\tr->offset += len;"
+ print "\tif (!utf8_validate (s->str, s->len))"
+ print "\t\treturn false;"
+ print "\treturn true;"
+ print "}"
+}
+
+function codegen_constant(name, value) {
+ print ""
+ print "enum { " PrefixUpper name " = " value " };"
+}
+
+function codegen_enum_value(name, subname, value, cg) {
+ append(cg, "fields",
+ "\t" PrefixUpper toupper(cameltosnake(name)) "_" subname \
+ " = " value ",\n")
+}
+
+function codegen_enum(name, cg, ctype) {
+ ctype = "enum " PrefixLower cameltosnake(name)
+ print ""
+ print ctype " {"
+ print cg["fields"] "};"
+
+ # XXX: This should also check if it isn't out-of-range for any reason,
+ # but our usage of sprintf() stands in the way a bit.
+ CodegenSerialize[name] = "\tstr_pack_i32(w, %s);\n"
+ CodegenDeserialize[name] = \
+ "\t{\n" \
+ "\t\tint32_t v = 0;\n" \
+ "\t\tif (!msg_unpacker_i32(r, &v) || !v)\n" \
+ "\t\t\treturn false;\n" \
+ "\t\t%s = v;\n" \
+ "\t}\n"
+
+ CodegenCType[name] = ctype
+ for (i in cg)
+ delete cg[i]
+}
+
+function codegen_struct_tag(d, cg, f) {
+ f = "self->" d["name"]
+ append(cg, "fields", "\t" CodegenCType[d["type"]] " " d["name"] ";\n")
+ append(cg, "dispose", sprintf(CodegenDispose[d["type"]], f))
+ append(cg, "serialize", sprintf(CodegenSerialize[d["type"]], f))
+ # Do not deserialize here, that would be out of order.
+}
+
+function codegen_struct_field(d, cg, f, dispose, serialize, deserialize) {
+ f = "self->" d["name"]
+ dispose = CodegenDispose[d["type"]]
+ serialize = CodegenSerialize[d["type"]]
+ deserialize = CodegenDeserialize[d["type"]]
+ if (!d["isarray"]) {
+ append(cg, "fields", "\t" CodegenCType[d["type"]] " " d["name"] ";\n")
+ append(cg, "dispose", sprintf(dispose, f))
+ append(cg, "serialize", sprintf(serialize, f))
+ append(cg, "deserialize", sprintf(deserialize, f))
+ return
+ }
+
+ append(cg, "fields",
+ "\t" CodegenCType["u32"] " " d["name"] "_len;\n" \
+ "\t" CodegenCType[d["type"]] " *" d["name"] ";\n")
+
+ if (dispose)
+ append(cg, "dispose", "\tif (" f ")\n" \
+ "\t\tfor (size_t i = 0; i < " f "_len; i++)\n" \
+ indent(indent(sprintf(dispose, f "[i]"))))
+ append(cg, "dispose", "\tfree(" f ");\n")
+
+ append(cg, "serialize", sprintf(CodegenSerialize["u32"], f "_len"))
+ if (d["type"] == "u8" || d["type"] == "i8") {
+ append(cg, "serialize",
+ "\tstr_append_data(w, " f ", " f "_len);\n")
+ } else if (serialize) {
+ append(cg, "serialize",
+ "\tfor (size_t i = 0; i < " f "_len; i++)\n" \
+ indent(sprintf(serialize, f "[i]")))
+ }
+
+ append(cg, "deserialize", sprintf(CodegenDeserialize["u32"], f "_len") \
+ "\tif (!(" f " = calloc(" f "_len + 1, sizeof *" f ")))\n" \
+ "\t\treturn false;\n")
+ if (d["type"] == "u8" || d["type"] == "i8") {
+ append(cg, "deserialize",
+ "\tif (msg_unpacker_get_available(r) < " f "_len)\n" \
+ "\t\treturn false;\n" \
+ "\tmemcpy(" f ", r->data + r->offset, " f "_len);\n" \
+ "\tr->offset += " f "_len;\n")
+ } else if (deserialize) {
+ append(cg, "deserialize",
+ "\tfor (size_t i = 0; i < " f "_len; i++)\n" \
+ indent(sprintf(deserialize, f "[i]")))
+ }
+}
+
+function codegen_struct(name, cg, ctype, funcname) {
+ ctype = "struct " PrefixLower cameltosnake(name)
+ print ""
+ print ctype " {"
+ print cg["fields"] "};"
+
+ if (cg["dispose"]) {
+ funcname = PrefixLower cameltosnake(name) "_free"
+ print ""
+ print "static void\n" funcname "(" ctype " *self) {"
+ print cg["dispose"] "}"
+
+ CodegenDispose[name] = "\t" funcname "(&%s);\n"
+ }
+ if (cg["serialize"]) {
+ funcname = PrefixLower cameltosnake(name) "_serialize"
+ print ""
+ print "static bool\n" \
+ funcname "(\n\t\t" ctype " *self, struct str *w) {"
+ print cg["serialize"] "\treturn true;"
+ print "}"
+
+ CodegenSerialize[name] = "\tif (!" funcname "(&%s, w))\n" \
+ "\t\treturn false;\n"
+ }
+ if (cg["deserialize"]) {
+ funcname = PrefixLower cameltosnake(name) "_deserialize"
+ print ""
+ print "static bool\n" \
+ funcname "(\n\t\t" ctype " *self, struct msg_unpacker *r) {"
+ print cg["deserialize"] "\treturn true;"
+ print "}"
+
+ CodegenDeserialize[name] = "\tif (!" funcname "(&%s, r))\n" \
+ "\t\treturn false;\n"
+ }
+
+ CodegenCType[name] = ctype
+ for (i in cg)
+ delete cg[i]
+}
+
+function codegen_union_tag(d, cg) {
+ cg["tagtype"] = d["type"]
+ cg["tagname"] = d["name"]
+ append(cg, "fields", "\t" CodegenCType[d["type"]] " " d["name"] ";\n")
+}
+
+function codegen_union_struct( \
+ name, casename, cg, scg, structname, fieldname, fullcasename) {
+ # Don't generate obviously useless structs.
+ fullcasename = toupper(cameltosnake(cg["tagtype"])) "_" casename
+ if (!scg["dispose"] && !scg["deserialize"]) {
+ append(cg, "structless", "\tcase " PrefixUpper fullcasename ":\n")
+ for (i in scg)
+ delete scg[i]
+ return
+ }
+
+ # And thus not all generated structs are present in Types.
+ structname = name "_" casename
+ fieldname = tolower(casename)
+ codegen_struct(structname, scg)
+
+ append(cg, "fields", "\t" CodegenCType[structname] " " fieldname ";\n")
+ if (CodegenDispose[structname])
+ append(cg, "dispose", "\tcase " PrefixUpper fullcasename ":\n" \
+ indent(sprintf(CodegenDispose[structname], "self->" fieldname)) \
+ "\t\tbreak;\n")
+
+ # With no de/serialization code, this will simply recognize the tag.
+ append(cg, "serialize", "\tcase " PrefixUpper fullcasename ":\n" \
+ indent(sprintf(CodegenSerialize[structname], "self->" fieldname)) \
+ "\t\tbreak;\n")
+ append(cg, "deserialize", "\tcase " PrefixUpper fullcasename ":\n" \
+ indent(sprintf(CodegenDeserialize[structname], "self->" fieldname)) \
+ "\t\tbreak;\n")
+}
+
+function codegen_union(name, cg, f, ctype, funcname) {
+ ctype = "union " PrefixLower cameltosnake(name)
+ print ""
+ print ctype " {"
+ print cg["fields"] "};"
+
+ f = "self->" cg["tagname"]
+ if (cg["dispose"]) {
+ funcname = PrefixLower cameltosnake(name) "_free"
+ print ""
+ print "static void\n" funcname "(" ctype " *self) {"
+ print "\tswitch (" f ") {"
+ if (cg["structless"])
+ print cg["structless"] \
+ indent(sprintf(CodegenDispose[cg["tagtype"]], f)) "\t\tbreak;"
+ print cg["dispose"] "\tdefault:"
+ print "\t\tbreak;"
+ print "\t}"
+ print "}"
+
+ CodegenDispose[name] = "\t" funcname "(&%s);\n"
+ }
+ if (cg["serialize"]) {
+ funcname = PrefixLower cameltosnake(name) "_serialize"
+ print ""
+ print "static bool\n" \
+ funcname "(\n\t\t" ctype " *self, struct str *w) {"
+ print "\tswitch (" f ") {"
+ if (cg["structless"])
+ print cg["structless"] \
+ indent(sprintf(CodegenSerialize[cg["tagtype"]], f)) "\t\tbreak;"
+ print cg["serialize"] "\tdefault:"
+ print "\t\treturn false;"
+ print "\t}"
+ print "\treturn true;"
+ print "}"
+
+ CodegenSerialize[name] = "\tif (!" funcname "(&%s, w))\n" \
+ "\t\treturn false;\n"
+ }
+ if (cg["deserialize"]) {
+ funcname = PrefixLower cameltosnake(name) "_deserialize"
+ print ""
+ print "static bool\n" \
+ funcname "(\n\t\t" ctype " *self, struct msg_unpacker *r) {"
+ print sprintf(CodegenDeserialize[cg["tagtype"]], f)
+ print "\tswitch (" f ") {"
+ if (cg["structless"])
+ print cg["structless"] "\t\tbreak;"
+ print cg["deserialize"] "\tdefault:"
+ print "\t\treturn false;"
+ print "\t}"
+ print "\treturn true;"
+ print "}"
+
+ CodegenDeserialize[name] = "\tif (!" funcname "(&%s, r))\n" \
+ "\t\treturn false;\n"
+ }
+
+ CodegenCType[name] = ctype
+ for (i in cg)
+ delete cg[i]
+}