summaryrefslogtreecommitdiff
path: root/lib9p/protogen
diff options
context:
space:
mode:
Diffstat (limited to 'lib9p/protogen')
-rw-r--r--lib9p/protogen/c.py125
-rw-r--r--lib9p/protogen/c9util.py47
-rw-r--r--lib9p/protogen/c_format.py142
-rw-r--r--lib9p/protogen/c_marshal.py47
-rw-r--r--lib9p/protogen/c_unmarshal.py13
-rw-r--r--lib9p/protogen/c_validate.py28
-rw-r--r--lib9p/protogen/h.py270
7 files changed, 481 insertions, 191 deletions
diff --git a/lib9p/protogen/c.py b/lib9p/protogen/c.py
index 5e67939..a6824ce 100644
--- a/lib9p/protogen/c.py
+++ b/lib9p/protogen/c.py
@@ -7,7 +7,7 @@ import sys
import idl
-from . import c9util, c_marshal, c_unmarshal, c_validate, cutil
+from . import c9util, c_format, c_marshal, c_unmarshal, c_validate, cutil
# This strives to be "general-purpose" in that it just acts on the
# *.9p inputs; but (unfortunately?) there are a few special-cases in
@@ -29,11 +29,21 @@ def gen_c(versions: set[str], typs: list[idl.UserType]) -> str:
#include <string.h> /* for memset() */
#include <libmisc/assert.h>
+#include <libmisc/endian.h>
#include <lib9p/9p.h>
-#include "internal.h"
+#include "tables.h"
+#include "utf8.h"
"""
+ # libobj vtables ###########################################################
+ ret += """
+/* libobj vtables *************************************************************/
+"""
+ for typ in typs:
+ ret += cutil.ifdef_push(1, c9util.ver_ifdef(typ.in_versions))
+ ret += f"LO_IMPLEMENTATION_C(fmt_formatter, {c9util.typename(typ)}, {c9util.basename(typ)}, static);\n"
+ ret += cutil.ifdef_pop(0)
# utilities ################################################################
ret += """
@@ -44,28 +54,6 @@ def gen_c(versions: set[str], typs: list[idl.UserType]) -> str:
for msg in [msg for msg in typs if isinstance(msg, idl.Message)]:
id2typ[msg.msgid] = msg
- def msg_table(grp: str, meth: str, tentry: str, rng: tuple[int, int, int]) -> str:
- ret = f"const {tentry} {c9util.ident(f'_table_{grp}_{meth}')}[{c9util.ver_enum('NUM')}][{hex(len(range(*rng)))}] = {{\n"
- for ver in ["unknown", *sorted(versions)]:
- if ver != "unknown":
- ret += cutil.ifdef_push(1, c9util.ver_ifdef({ver}))
- ret += f"\t[{c9util.ver_enum(ver)}] = {{\n"
- for n in range(*rng):
- xmsg: idl.Message | None = id2typ.get(n, None)
- if xmsg:
- if ver == "unknown": # SPECIAL (initialization)
- if xmsg.typname not in ["Tversion", "Rversion", "Rerror"]:
- xmsg = None
- else:
- if ver not in xmsg.in_versions:
- xmsg = None
- if xmsg:
- ret += f"\t\t_MSG_{meth.upper()}({xmsg.typname}),\n"
- ret += "\t},\n"
- ret += cutil.ifdef_pop(0)
- ret += "};\n"
- return ret
-
for v in sorted(versions):
ret += f"#if CONFIG_9P_ENABLE_{v.replace('.', '_')}\n"
ret += (
@@ -83,23 +71,6 @@ def gen_c(versions: set[str], typs: list[idl.UserType]) -> str:
ret += " */\n"
ret += "#define is_ver(ctx, ver) _is_ver_##ver((ctx)->version)\n"
- # strings ##################################################################
- ret += f"""
-/* strings ********************************************************************/
-
-const char *const {c9util.ident('_table_ver_name')}[{c9util.ver_enum('NUM')}] = {{
-"""
- for ver in ["unknown", *sorted(versions)]:
- if ver in versions:
- ret += cutil.ifdef_push(1, c9util.ver_ifdef({ver}))
- ret += f'\t[{c9util.ver_enum(ver)}] = "{ver}",\n'
- ret += cutil.ifdef_pop(0)
- ret += "};\n"
-
- ret += "\n"
- ret += f"#define _MSG_NAME(typ) [{c9util.Ident('TYP_')}##typ] = #typ\n"
- ret += msg_table("msg", "name", "char *const", (0, 0x100, 1))
-
# bitmasks #################################################################
ret += """
/* bitmasks *******************************************************************/
@@ -118,7 +89,7 @@ const char *const {c9util.ident('_table_ver_name')}[{c9util.ver_enum('NUM')}] =
+ "".join(
(
"1"
- if bit.cat in (idl.BitCat.USED, idl.BitCat.SUBFIELD)
+ if (bit.cat == "USED" or isinstance(bit.cat, idl.BitNum))
and ver in bit.in_versions
else "0"
)
@@ -139,25 +110,63 @@ const char *const {c9util.ident('_table_ver_name')}[{c9util.ver_enum('NUM')}] =
# marshal_* ################################################################
ret += c_marshal.gen_c_marshal(versions, typs)
- # function tables ##########################################################
+ # *_format #################################################################
+ ret += c_format.gen_c_format(versions, typs)
+
+ # tables.h #################################################################
ret += """
-/* function tables ************************************************************/
+/* tables.h *******************************************************************/
"""
ret += "\n"
- ret += f"const uint32_t {c9util.ident('_table_msg_min_size')}[{c9util.ver_enum('NUM')}] = {{\n"
+ ret += f"const struct {c9util.ident('_ver_tentry')} {c9util.ident('_table_ver')}[{c9util.ver_enum('NUM')}] = {{\n"
rerror = next(typ for typ in typs if typ.typname == "Rerror")
- ret += f"\t[{c9util.ver_enum('unknown')}] = {rerror.min_size('9P2000')},\n" # SPECIAL (initialization)
- for ver in sorted(versions):
- ret += cutil.ifdef_push(1, c9util.ver_ifdef({ver}))
- ret += f"\t[{c9util.ver_enum(ver)}] = {rerror.min_size(ver)},\n"
+ for ver in ["unknown", *sorted(versions)]:
+ if ver == "unknown":
+ min_msg_size = rerror.min_size("9P2000") # SPECIAL (initialization)
+ else:
+ ret += cutil.ifdef_push(1, c9util.ver_ifdef({ver}))
+ min_msg_size = rerror.min_size(ver)
+ ret += f'\t[{c9util.ver_enum(ver)}] = {{.name="{ver}", .min_msg_size={min_msg_size}}},\n'
ret += cutil.ifdef_pop(0)
ret += "};\n"
+ def msg_table(
+ cstruct: str, cname: str, each: str, _range: tuple[int, int, int]
+ ) -> str:
+ ret = f"const struct {c9util.ident(cstruct)} {c9util.ident(cname)}[{c9util.ver_enum('NUM')}][{hex(len(range(*_range)))}] = {{\n"
+ for ver in ["unknown", *sorted(versions)]:
+ if ver != "unknown":
+ ret += cutil.ifdef_push(1, c9util.ver_ifdef({ver}))
+ ret += f"\t[{c9util.ver_enum(ver)}] = {{\n"
+ for n in range(*_range):
+ xmsg: idl.Message | None = id2typ.get(n, None)
+ if xmsg:
+ if ver == "unknown": # SPECIAL (initialization)
+ if xmsg.typname not in ["Tversion", "Rversion", "Rerror"]:
+ xmsg = None
+ else:
+ if ver not in xmsg.in_versions:
+ xmsg = None
+ if xmsg:
+ ret += f"\t\t{each}({xmsg.typname}),\n"
+ ret += "\t},\n"
+ ret += cutil.ifdef_pop(0)
+ ret += "};\n"
+ return ret
+
+ ret += "\n"
+ ret += cutil.macro(
+ f"#define _MSG(typ) [{c9util.Ident('TYP_')}##typ] = {{\n"
+ f"\t\t.name = #typ,\n"
+ f"\t\t.box_as_fmt_formatter = (_box_as_fmt_formatter_fn_t)lo_box_{c9util.ident('msg_')}##typ##_as_fmt_formatter,\n"
+ f"\t}}\n"
+ )
+ ret += msg_table("_msg_tentry", "_table_msg", "_MSG", (0, 0x100, 1))
+
ret += "\n"
ret += cutil.macro(
f"#define _MSG_RECV(typ) [{c9util.Ident('TYP_')}##typ/2] = {{\n"
- f"\t\t.basesize = sizeof(struct {c9util.ident('msg_')}##typ),\n"
f"\t\t.validate = validate_##typ,\n"
f"\t\t.unmarshal = (_unmarshal_fn_t)unmarshal_##typ,\n"
f"\t}}\n"
@@ -168,21 +177,13 @@ const char *const {c9util.ident('_table_ver_name')}[{c9util.ver_enum('NUM')}] =
f"\t}}\n"
)
ret += "\n"
- ret += msg_table(
- "Tmsg", "recv", f"struct {c9util.ident('_recv_tentry')}", (0, 0x100, 2)
- )
+ ret += msg_table("_recv_tentry", "_table_Tmsg_recv", "_MSG_RECV", (0, 0x100, 2))
ret += "\n"
- ret += msg_table(
- "Rmsg", "recv", f"struct {c9util.ident('_recv_tentry')}", (1, 0x100, 2)
- )
+ ret += msg_table("_recv_tentry", "_table_Rmsg_recv", "_MSG_RECV", (1, 0x100, 2))
ret += "\n"
- ret += msg_table(
- "Tmsg", "send", f"struct {c9util.ident('_send_tentry')}", (0, 0x100, 2)
- )
+ ret += msg_table("_send_tentry", "_table_Tmsg_send", "_MSG_SEND", (0, 0x100, 2))
ret += "\n"
- ret += msg_table(
- "Rmsg", "send", f"struct {c9util.ident('_send_tentry')}", (1, 0x100, 2)
- )
+ ret += msg_table("_send_tentry", "_table_Rmsg_send", "_MSG_SEND", (1, 0x100, 2))
ret += f"""
LM_FLATTEN ssize_t {c9util.ident('_stat_validate')}(struct lib9p_ctx *ctx, uint32_t net_size, uint8_t *net_bytes, uint32_t *ret_net_size) {{
diff --git a/lib9p/protogen/c9util.py b/lib9p/protogen/c9util.py
index e7ad999..cf91951 100644
--- a/lib9p/protogen/c9util.py
+++ b/lib9p/protogen/c9util.py
@@ -3,6 +3,7 @@
# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
# SPDX-License-Identifier: AGPL-3.0-or-later
+import re
import typing
import idl
@@ -72,6 +73,20 @@ def ver_cond(versions: typing.Collection[str]) -> str:
# misc #########################################################################
+def basename(typ: idl.UserType) -> str:
+ match typ:
+ case idl.Number():
+ return ident(typ.typname)
+ case idl.Bitfield():
+ return ident(typ.typname)
+ case idl.Message():
+ return ident(f"msg_{typ.typname}")
+ case idl.Struct():
+ return ident(typ.typname)
+ case _:
+ raise ValueError(f"not a defined type: {typ.__class__.__name__}")
+
+
def typename(typ: idl.Type, parent: idl.StructMember | None = None) -> str:
match typ:
case idl.Primitive():
@@ -79,31 +94,41 @@ def typename(typ: idl.Type, parent: idl.StructMember | None = None) -> str:
return "[[gnu::nonstring]] char"
return f"uint{typ.value*8}_t"
case idl.Number():
- return ident(f"{typ.typname}_t")
+ return f"{basename(typ)}_t"
case idl.Bitfield():
- return ident(f"{typ.typname}_t")
+ return f"{basename(typ)}_t"
case idl.Message():
- return f"struct {ident(f'msg_{typ.typname}')}"
+ return f"struct {basename(typ)}"
case idl.Struct():
- return f"struct {ident(typ.typname)}"
+ return f"struct {basename(typ)}"
case _:
raise ValueError(f"not a type: {typ.__class__.__name__}")
-def idl_expr(expr: idl.Expr, lookup_sym: typing.Callable[[str], str]) -> str:
+def idl_expr(
+ expr: idl.Expr, lookup_sym: typing.Callable[[str], str], bitwidth: int = 0
+) -> str:
ret: list[str] = []
for tok in expr.tokens:
match tok:
case idl.ExprOp():
ret.append(tok.op)
case idl.ExprLit():
- ret.append(str(tok.val))
- case idl.ExprSym(symname="s32_max"):
- ret.append("INT32_MAX")
- case idl.ExprSym(symname="s64_max"):
- ret.append("INT64_MAX")
+ if bitwidth:
+ ret.append(f"{tok.val:#0{bitwidth}b}")
+ else:
+ ret.append(str(tok.val))
case idl.ExprSym():
- ret.append(lookup_sym(tok.symname))
+ if m := re.fullmatch(r"^u(8|16|32|64)_max$", tok.symname):
+ ret.append(f"UINT{m.group(1)}_MAX")
+ elif m := re.fullmatch(r"^s(8|16|32|64)_max$", tok.symname):
+ ret.append(f"INT{m.group(1)}_MAX")
+ else:
+ ret.append(lookup_sym(tok.symname))
+ case idl.ExprOff():
+ ret.append(lookup_sym("&" + tok.membname))
+ case idl.ExprNum():
+ ret.append(Ident(add_prefix(f"{tok.numname}_".upper(), tok.valname)))
case _:
assert False
return " ".join(ret)
diff --git a/lib9p/protogen/c_format.py b/lib9p/protogen/c_format.py
new file mode 100644
index 0000000..a1bcbf3
--- /dev/null
+++ b/lib9p/protogen/c_format.py
@@ -0,0 +1,142 @@
+# lib9p/protogen/c_format.py - Generate C pretty-print functions
+#
+# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+
+import idl
+
+from . import c9util, cutil
+
+# This strives to be "general-purpose" in that it just acts on the
+# *.9p inputs; but (unfortunately?) there are a few special-cases in
+# this script, marked with "SPECIAL".
+
+# pylint: disable=unused-variable
+__all__ = ["gen_c_format"]
+
+
+def bf_numname(typ: idl.Bitfield, num: idl.BitNum, base: str) -> str:
+ prefix = f"{typ.typname}_{num.numname}_".upper()
+ return c9util.Ident(c9util.add_prefix(prefix, base))
+
+
+def gen_c_format(versions: set[str], typs: list[idl.UserType]) -> str:
+ ret = """
+/* *_format *******************************************************************/
+"""
+ for typ in typs:
+ ret += "\n"
+ ret += cutil.ifdef_push(1, c9util.ver_ifdef(typ.in_versions))
+ ret += f"static void {c9util.basename(typ)}_format({c9util.typename(typ)} *self, struct fmt_state *state) {{\n"
+ match typ:
+ case idl.Number():
+ if typ.vals:
+ ret += "\tswitch (*self) {\n"
+ for name in typ.vals:
+ ret += f"\tcase {c9util.Ident(c9util.add_prefix(f'{typ.typname}_'.upper(), name))}:\n"
+ ret += f'\t\tfmt_state_puts(state, "{name}");\n'
+ ret += "\t\tbreak;\n"
+ ret += "\tdefault:\n"
+ ret += f'\t\tfmt_state_printf(state, "%"PRIu{typ.static_size*8}, *self);\n'
+ ret += "\t}\n"
+ else:
+ ret += f'\t\tfmt_state_printf(state, "%"PRIu{typ.static_size*8}, *self);\n'
+ case idl.Bitfield():
+ val = "*self"
+ if typ.typname == "dm": # SPECIAL (pretty file permissions)
+ val = f"(*self & ~(({c9util.typename(typ)})0777))"
+ ret += "\tbool empty = true;\n"
+ ret += "\tfmt_state_putchar(state, '(');\n"
+ nums: set[str] = set()
+
+ for bit in reversed(typ.bits):
+ match bit.cat:
+ case "UNUSED" | "USED" | "RESERVED":
+ if bit.cat == "UNUSED":
+ bitname = f"1<<{bit.num}"
+ else:
+ bitname = bit.bitname
+ ret += f"\tif ({val} & (UINT{typ.static_size*8}_C(1)<<{bit.num})) {{\n"
+ ret += "\t\tif (!empty)\n"
+ ret += "\t\t\tfmt_state_putchar(state, '|');\n"
+ ret += f'\t\tfmt_state_puts(state, "{bitname}");\n'
+ ret += "\t\tempty = false;\n"
+ ret += "\t}\n"
+ case idl.BitNum():
+ if bit.cat.numname in nums:
+ continue
+ ret += f"\tswitch ({val} & {bf_numname(typ, bit.cat, 'MASK')}) {{\n"
+ for name in bit.cat.vals:
+ ret += f"\tcase {bf_numname(typ, bit.cat, name)}:\n"
+ bitname = c9util.add_prefix(
+ f"{bit.cat.numname}_".upper(), name
+ )
+ ret += "\t\tif (!empty)\n"
+ ret += "\t\t\tfmt_state_putchar(state, '|');\n"
+ ret += f'\t\tfmt_state_puts(state, "{bitname}");\n'
+ ret += "\t\tempty = false;\n"
+ ret += "\t\tbreak;\n"
+ ret += "\tdefault:\n"
+ ret += "\t\tif (!empty)\n"
+ ret += "\t\t\tfmt_state_putchar(state, '|');\n"
+ ret += f'\t\tfmt_state_printf(state, "%"PRIu{typ.static_size*8}, {val} & {bf_numname(typ, bit.cat, 'MASK')});\n'
+ ret += "\t\tempty = false;\n"
+ ret += "\t}\n"
+ nums.add(bit.cat.numname)
+ if typ.typname == "dm": # SPECIAL (pretty file permissions)
+ ret += "\tif (!empty)\n"
+ ret += "\t\tfmt_state_putchar(state, '|');\n"
+ ret += f'\tfmt_state_printf(state, "%#04"PRIo{typ.static_size*8}, *self & 0777);\n'
+ else:
+ ret += "\tif (empty)\n"
+ ret += "\t\tfmt_state_putchar(state, '0');\n"
+ ret += "\tfmt_state_putchar(state, ')');\n"
+ case idl.Struct(typname="s"): # SPECIAL(string)
+ ret += "\t/* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47781 */\n"
+ ret += "#pragma GCC diagnostic push\n"
+ ret += '#pragma GCC diagnostic ignored "-Wformat"\n'
+ ret += '#pragma GCC diagnostic ignored "-Wformat-extra-args"\n'
+ ret += '\tfmt_state_printf(state, "%.*q", self->len, self->utf8);\n'
+ ret += "#pragma GCC diagnostic pop\n"
+ case idl.Struct(): # and idl.Message():
+ if isinstance(typ, idl.Message):
+ ret += f'\tfmt_state_puts(state, "{typ.typname} {{");\n'
+ else:
+ ret += "\tfmt_state_putchar(state, '{');\n"
+ for member in typ.members:
+ if member.val:
+ continue
+ ret += cutil.ifdef_push(2, c9util.ver_ifdef(member.in_versions))
+ if member.cnt:
+ if member.typ.static_size == 1: # SPECIAL (data)
+ ret += f'\tfmt_state_puts(state, " {member.membname}=<bytedata>");\n'
+ continue
+ if isinstance(member.cnt, int):
+ cnt_str = str(member.cnt)
+ cnt_typ = "size_t"
+ else:
+ cnt_str = f"self->{member.cnt.membname}"
+ cnt_typ = c9util.typename(member.cnt.typ)
+ ret += f'\tfmt_state_puts(state, " {member.membname}=[");\n'
+ ret += f"\tfor ({cnt_typ} i = 0; i < {cnt_str}; i++) {{\n"
+ ret += "\t\tif (i)\n"
+ ret += '\t\t\tfmt_state_puts(state, ", ");\n'
+ if isinstance(member.typ, idl.Primitive):
+ ret += f'\t\tfmt_state_printf(state, "%"PRIu{member.typ.static_size*8}, self->{member.membname}[i]);\n'
+ else:
+ ret += f"\t\t{c9util.basename(member.typ)}_format(&self->{member.membname}[i], state);\n"
+ ret += "\t}\n"
+ ret += '\tfmt_state_puts(state, " ]");\n'
+ else:
+ ret += f'\tfmt_state_puts(state, " {member.membname}=");\n'
+ if isinstance(member.typ, idl.Primitive):
+ ret += f'\tfmt_state_printf(state, "%"PRIu{member.typ.static_size*8}, self->{member.membname});\n'
+ else:
+ ret += f"\t{c9util.basename(member.typ)}_format(&self->{member.membname}, state);\n"
+ ret += cutil.ifdef_pop(1)
+ ret += '\tfmt_state_puts(state, " }");\n'
+ ret += "}\n"
+ ret += cutil.ifdef_pop(0)
+
+ return ret
diff --git a/lib9p/protogen/c_marshal.py b/lib9p/protogen/c_marshal.py
index 74b64f5..4dab864 100644
--- a/lib9p/protogen/c_marshal.py
+++ b/lib9p/protogen/c_marshal.py
@@ -23,7 +23,7 @@ __all__ = ["gen_c_marshal"]
class OffsetExpr:
static: int
cond: dict[frozenset[str], "OffsetExpr"]
- rep: list[tuple[idlutil.Path, "OffsetExpr"]]
+ rep: list[tuple[idlutil.Path | int, "OffsetExpr"]]
def __init__(self) -> None:
self.static = 0
@@ -52,14 +52,20 @@ class OffsetExpr:
if self.static:
oneline.append(str(self.static))
for cnt, sub in self.rep:
+ if isinstance(cnt, int):
+ cnt_str = str(cnt)
+ cnt_typ = "size_t"
+ else:
+ cnt_str = cnt.c_str(root)
+ cnt_typ = c9util.typename(cnt.elems[-1].typ)
if not sub.cond and not sub.rep:
if sub.static == 1:
- oneline.append(cnt.c_str(root))
+ oneline.append(cnt_str)
else:
- oneline.append(f"({cnt.c_str(root)})*{sub.static}")
+ oneline.append(f"({cnt_str})*{sub.static}")
continue
loopvar = chr(ord("i") + loop_depth)
- multiline += f"{'\t'*indent_depth}for ({c9util.typename(cnt.elems[-1].typ)} {loopvar} = 0; {loopvar} < {cnt.c_str(root)}; {loopvar}++) {{\n"
+ multiline += f"{'\t'*indent_depth}for ({cnt_typ} {loopvar} = 0; {loopvar} < {cnt_str}; {loopvar}++) {{\n"
multiline += sub.gen_c("", dstvar, root, indent_depth + 1, loop_depth + 1)
multiline += f"{'\t'*indent_depth}}}\n"
for vers, sub in self.cond.items():
@@ -113,8 +119,12 @@ def get_offset_expr(typ: idl.UserType, recurse: OffsetExprRecursion) -> OffsetEx
member_path = expr_stack[-1].path
member = member_path.elems[-1]
assert member.cnt
- cnt_path = member_path.parent().add(member.cnt)
- expr_stack[-2].expr.rep.append((cnt_path, expr_stack[-1].expr))
+ cnt: idlutil.Path | int
+ if isinstance(member.cnt, int):
+ cnt = member.cnt
+ else:
+ cnt = member_path.parent().add(member.cnt)
+ expr_stack[-2].expr.rep.append((cnt, expr_stack[-1].expr))
expr_stack = expr_stack[:-1]
def handle(
@@ -268,11 +278,13 @@ def gen_c_marshal(versions: set[str], typs: list[idl.UserType]) -> str:
if not member.val:
continue
for tok in member.val.tokens:
- if not isinstance(tok, idl.ExprSym):
- continue
- if tok.symname == "end" or tok.symname.startswith("&"):
- if tok.symname not in offsets:
- offsets.append(tok.symname)
+ match tok:
+ case idl.ExprSym(symname="end"):
+ if tok.symname not in offsets:
+ offsets.append(tok.symname)
+ case idl.ExprOff():
+ if f"&{tok.membname}" not in offsets:
+ offsets.append(f"&{tok.membname}")
for name in offsets:
name_prefix = f"offsetof{''.join('_'+m.membname for m in path.elems)}_"
if name == "end":
@@ -313,15 +325,20 @@ def gen_c_marshal(versions: set[str], typs: list[idl.UserType]) -> str:
)
indent_stack.append(IndentLevel(ifdef=True))
if child.cnt:
- cnt_path = path.parent().add(child.cnt)
+ if isinstance(child.cnt, int):
+ cnt_str = str(child.cnt)
+ cnt_typ = "size_t"
+ else:
+ cnt_str = path.parent().add(child.cnt).c_str("val->")
+ cnt_typ = c9util.typename(child.cnt.typ)
if child.typ.static_size == 1: # SPECIAL (zerocopy)
if path.root.typname == "stat": # SPECIAL (stat)
- ret += f"{'\t'*indent_lvl()}MARSHAL_BYTES(ctx, {path.c_str('val->')[:-3]}, {cnt_path.c_str('val->')});\n"
+ ret += f"{'\t'*indent_lvl()}MARSHAL_BYTES(ctx, {path.c_str('val->')[:-3]}, {cnt_str});\n"
else:
- ret += f"{'\t'*indent_lvl()}MARSHAL_BYTES_ZEROCOPY(ctx, {path.c_str('val->')[:-3]}, {cnt_path.c_str('val->')});\n"
+ ret += f"{'\t'*indent_lvl()}MARSHAL_BYTES_ZEROCOPY(ctx, {path.c_str('val->')[:-3]}, {cnt_str});\n"
return idlutil.WalkCmd.KEEP_GOING, pop
loopvar = chr(ord("i") + loopdepth - 1)
- ret += f"{'\t'*indent_lvl()}for ({c9util.typename(child.cnt.typ)} {loopvar} = 0; {loopvar} < {cnt_path.c_str('val->')}; {loopvar}++) {{\n"
+ ret += f"{'\t'*indent_lvl()}for ({cnt_typ} {loopvar} = 0; {loopvar} < {cnt_str}; {loopvar}++) {{\n"
indent_stack.append(IndentLevel(ifdef=False))
if not isinstance(child.typ, idl.Struct):
if child.val:
diff --git a/lib9p/protogen/c_unmarshal.py b/lib9p/protogen/c_unmarshal.py
index 018d750..34635f9 100644
--- a/lib9p/protogen/c_unmarshal.py
+++ b/lib9p/protogen/c_unmarshal.py
@@ -93,15 +93,20 @@ def gen_c_unmarshal(versions: set[str], typs: list[idl.UserType]) -> str:
)
indent_stack.append(IndentLevel(ifdef=True))
if child.cnt:
- cnt_path = path.parent().add(child.cnt)
+ if isinstance(child.cnt, int):
+ cnt_str = str(child.cnt)
+ cnt_typ = "size_t"
+ else:
+ cnt_str = path.parent().add(child.cnt).c_str("out->")
+ cnt_typ = c9util.typename(child.cnt.typ)
if child.typ.static_size == 1: # SPECIAL (zerocopy)
- ret += f"{'\t'*indent_lvl()}UNMARSHAL_BYTES(ctx, {path.c_str('out->')[:-3]}, {cnt_path.c_str('out->')});\n"
+ ret += f"{'\t'*indent_lvl()}UNMARSHAL_BYTES(ctx, {path.c_str('out->')[:-3]}, {cnt_str});\n"
return idlutil.WalkCmd.KEEP_GOING, pop
ret += f"{'\t'*indent_lvl()}{path.c_str('out->')[:-3]} = extra;\n"
- ret += f"{'\t'*indent_lvl()}extra += sizeof({path.c_str('out->')[:-3]}[0]) * {cnt_path.c_str('out->')};\n"
+ ret += f"{'\t'*indent_lvl()}extra += sizeof({path.c_str('out->')[:-3]}[0]) * {cnt_str};\n"
loopdepth = sum(1 for elem in path.elems if elem.cnt)
loopvar = chr(ord("i") + loopdepth - 1)
- ret += f"{'\t'*indent_lvl()}for ({c9util.typename(child.cnt.typ)} {loopvar} = 0; {loopvar} < {cnt_path.c_str('out->')}; {loopvar}++) {{\n"
+ ret += f"{'\t'*indent_lvl()}for ({cnt_typ} {loopvar} = 0; {loopvar} < {cnt_str}; {loopvar}++) {{\n"
indent_stack.append(IndentLevel(ifdef=False))
if not isinstance(child.typ, idl.Struct):
if child.val:
diff --git a/lib9p/protogen/c_validate.py b/lib9p/protogen/c_validate.py
index e315b60..535a750 100644
--- a/lib9p/protogen/c_validate.py
+++ b/lib9p/protogen/c_validate.py
@@ -24,11 +24,11 @@ def should_save_offset(parent: idl.Struct, child: idl.StructMember) -> bool:
for sibling in parent.members:
if sibling.val:
for tok in sibling.val.tokens:
- if isinstance(tok, idl.ExprSym) and tok.symname == f"&{child.membname}":
+ if isinstance(tok, idl.ExprOff) and tok.membname == child.membname:
return True
if sibling.max:
for tok in sibling.max.tokens:
- if isinstance(tok, idl.ExprSym) and tok.symname == f"&{child.membname}":
+ if isinstance(tok, idl.ExprOff) and tok.membname == child.membname:
return True
return False
@@ -132,21 +132,33 @@ def gen_c_validate(versions: set[str], typs: list[idl.UserType]) -> str:
if should_save_offset(parent, child):
ret += f"{'\t'*indent_lvl()}uint32_t offsetof{''.join('_'+m.membname for m in path.elems)} = net_offset + {incr_buf};\n"
if child.cnt:
- assert child.cnt.typ.static_size
- cnt_path = path.parent().add(child.cnt)
- incr_flush()
+ if isinstance(child.cnt, int):
+ cnt_str = str(child.cnt)
+ cnt_typ = "size_t"
+ else:
+ assert child.cnt.typ.static_size
+ incr_flush()
+ cnt_str = f"LAST_U{child.cnt.typ.static_size*8}LE()"
+ cnt_typ = c9util.typename(child.cnt.typ)
if child.membname == "utf8": # SPECIAL (string)
+ assert child.typ.static_size == 1
# Yes, this is content-validation and "belongs" in
# gen_validate_content(), not here. But it's just
# easier this way.
- ret += f"{'\t'*indent_lvl()}VALIDATE_NET_UTF8(LAST_U{child.cnt.typ.static_size*8}LE());\n"
+ incr_flush()
+ ret += f"{'\t'*indent_lvl()}VALIDATE_NET_UTF8({cnt_str});\n"
return
if child.typ.static_size == 1: # SPECIAL (zerocopy)
- ret += f"{'\t'*indent_lvl()}VALIDATE_NET_BYTES(LAST_U{child.cnt.typ.static_size*8}LE());\n"
+ if isinstance(child.cnt, int):
+ incr_buf += child.cnt
+ return
+ incr_flush()
+ ret += f"{'\t'*indent_lvl()}VALIDATE_NET_BYTES({cnt_str});\n"
return
loopdepth = sum(1 for elem in path.elems if elem.cnt)
loopvar = chr(ord("i") + loopdepth - 1)
- ret += f"{'\t'*indent_lvl()}for ({c9util.typename(child.cnt.typ)} {loopvar} = 0, cnt = LAST_U{child.cnt.typ.static_size*8}LE(); {loopvar} < cnt; {loopvar}++) {{\n"
+ incr_flush()
+ ret += f"{'\t'*indent_lvl()}for ({cnt_typ} {loopvar} = 0, cnt = {cnt_str}; {loopvar} < cnt; {loopvar}++) {{\n"
indent_stack.append(IndentLevel(ifdef=False))
ret += f"{'\t'*indent_lvl()}RESERVE_HOST_BYTES(sizeof({c9util.typename(child.typ)}));\n"
if not isinstance(child.typ, idl.Struct):
diff --git a/lib9p/protogen/h.py b/lib9p/protogen/h.py
index 7785ca1..3b33419 100644
--- a/lib9p/protogen/h.py
+++ b/lib9p/protogen/h.py
@@ -165,6 +165,7 @@ def gen_h(versions: set[str], typs: list[idl.UserType]) -> str:
#include <stdint.h> /* for uint{{n}}_t types */
+#include <libfmt/fmt.h> /* for fmt_formatter */
#include <libhw/generic/net.h> /* for struct iovec */
"""
@@ -206,6 +207,7 @@ enum {c9util.ident('version')} {{
ret += cutil.ifdef_pop(0)
ret += f"\t{c9util.ver_enum('NUM')},\n"
ret += "};\n"
+ ret += f"LO_IMPLEMENTATION_H(fmt_formatter, enum {c9util.ident('version')}, {c9util.ident('version')});\n"
ret += """
/* enum msg_type **************************************************************/
@@ -221,6 +223,7 @@ enum {c9util.ident('version')} {{
ret += f"\t{c9util.Ident(f'TYP_{msg.typname:<{namewidth}}')} = {msg.msgid},\n"
ret += cutil.ifdef_pop(0)
ret += "};\n"
+ ret += f"LO_IMPLEMENTATION_H(fmt_formatter, enum {c9util.ident('msg_type')}, {c9util.ident('msg_type')});\n"
ret += """
/* payload types **************************************************************/
@@ -268,99 +271,11 @@ enum {c9util.ident('version')} {{
match typ:
case idl.Number():
- ret += f"typedef {c9util.typename(typ.prim)} {c9util.typename(typ)};\n"
- prefix = f"{c9util.IDENT(typ.typname)}_"
- namewidth = max(len(name) for name in typ.vals)
- for name, val in typ.vals.items():
- ret += f"#define {prefix}{name:<{namewidth}} (({c9util.typename(typ)})UINT{typ.static_size*8}_C({val}))\n"
+ ret += gen_number(typ)
case idl.Bitfield():
- ret += f"typedef {c9util.typename(typ.prim)} {c9util.typename(typ)};\n"
-
- def bitname(val: idl.Bit | idl.BitAlias) -> str:
- s = val.bitname
- match val:
- case idl.Bit(cat=idl.BitCat.RESERVED):
- s = "_RESERVED_" + s
- case idl.Bit(cat=idl.BitCat.SUBFIELD):
- assert isinstance(typ, idl.Bitfield)
- n = sum(
- 1
- for b in typ.bits[: val.num]
- if b.cat == idl.BitCat.SUBFIELD
- and b.bitname == val.bitname
- )
- s = f"_{s}_{n}"
- case idl.Bit(cat=idl.BitCat.UNUSED):
- return ""
- return c9util.Ident(c9util.add_prefix(typ.typname.upper() + "_", s))
-
- namewidth = max(
- len(bitname(val)) for val in [*typ.bits, *typ.names.values()]
- )
-
- ret += "\n"
- for bit in reversed(typ.bits):
- vers = bit.in_versions
- if bit.cat == idl.BitCat.UNUSED:
- vers = typ.in_versions
- ret += cutil.ifdef_push(2, c9util.ver_ifdef(vers))
-
- # It is important all of the `beg` strings have
- # the same length.
- end = ""
- match bit.cat:
- case (
- idl.BitCat.USED | idl.BitCat.RESERVED | idl.BitCat.SUBFIELD
- ):
- if cutil.ifdef_leaf_is_noop():
- beg = "#define "
- else:
- beg = "# define"
- case idl.BitCat.UNUSED:
- beg = "/* unused"
- end = " */"
-
- c_name = bitname(bit)
- c_val = f"1<<{bit.num}"
- ret += f"{beg} {c_name:<{namewidth}} (({c9util.typename(typ)})({c_val})){end}\n"
- if aliases := [
- alias
- for alias in typ.names.values()
- if isinstance(alias, idl.BitAlias)
- ]:
- ret += "\n"
-
- for alias in aliases:
- ret += cutil.ifdef_push(2, c9util.ver_ifdef(alias.in_versions))
-
- end = ""
- if cutil.ifdef_leaf_is_noop():
- beg = "#define "
- else:
- beg = "# define"
-
- c_name = bitname(alias)
- c_val = alias.val
- ret += f"{beg} {c_name:<{namewidth}} (({c9util.typename(typ)})({c_val})){end}\n"
- ret += cutil.ifdef_pop(1)
- del bitname
+ ret += gen_bitfield(typ)
case idl.Struct(): # and idl.Message():
- ret += c9util.typename(typ) + " {"
- if not typ.members:
- ret += "};\n"
- continue
- ret += "\n"
-
- typewidth = max(len(c9util.typename(m.typ, m)) for m in typ.members)
-
- for member in typ.members:
- if member.val:
- continue
- ret += cutil.ifdef_push(2, c9util.ver_ifdef(member.in_versions))
- ret += f"\t{c9util.typename(member.typ, member):<{typewidth}} {'*' if member.cnt else ' '}{member.membname};\n"
- ret += cutil.ifdef_pop(1)
- ret += "};\n"
- del typ
+ ret += gen_struct(typ)
ret += cutil.ifdef_pop(0)
ret += """
@@ -445,3 +360,176 @@ enum {c9util.ident('version')} {{
ret += "};\n"
return ret
+
+
+def gen_number(typ: idl.Number) -> str:
+ ret = f"typedef {c9util.typename(typ.prim)} {c9util.typename(typ)};\n"
+ ret += f"LO_IMPLEMENTATION_H(fmt_formatter, {c9util.typename(typ)}, {c9util.basename(typ)});\n"
+
+ def lookup_sym(sym: str) -> str:
+ assert False
+
+ def cname(base: str) -> str:
+ prefix = f"{typ.typname}_".upper()
+ return c9util.Ident(c9util.add_prefix(prefix, base))
+
+ namewidth = max(len(cname(name)) for name in typ.vals)
+ for name, val in typ.vals.items():
+ c_name = cname(name)
+ c_val = c9util.idl_expr(val, lookup_sym)
+ ret += f"#define {c_name:<{namewidth}} (({c9util.typename(typ)})({c_val}))\n"
+ return ret
+
+
+def gen_bitfield(typ: idl.Bitfield) -> str:
+ ret = f"typedef {c9util.typename(typ.prim)} {c9util.typename(typ)};\n"
+ ret += f"LO_IMPLEMENTATION_H(fmt_formatter, {c9util.typename(typ)}, {c9util.basename(typ)});\n"
+
+ def lookup_sym(sym: str) -> str:
+ assert False
+
+ # There are 4 parts here: bits, aliases, masks, and numbers.
+
+ # 1. bits
+
+ def bitname(bit: idl.Bit) -> str:
+ prefix = f"{typ.typname}_".upper()
+ base = bit.bitname
+ match bit:
+ case idl.Bit(cat="RESERVED"):
+ base = "_RESERVED_" + base
+ case idl.Bit(cat=idl.BitNum()):
+ base += "_*"
+ case idl.Bit(cat="UNUSED"):
+ base = f"_UNUSED_{bit.num}"
+ return c9util.Ident(c9util.add_prefix(prefix, base))
+
+ namewidth = max(len(bitname(bit)) for bit in typ.bits)
+
+ ret += "/* bits */\n"
+ for bit in reversed(typ.bits):
+ vers = bit.in_versions
+ if bit.cat == "UNUSED":
+ vers = typ.in_versions
+ ret += cutil.ifdef_push(2, c9util.ver_ifdef(vers))
+
+ # It is important all of the `beg` strings have
+ # the same length.
+ end = ""
+ match bit.cat:
+ case "USED" | "RESERVED" | "UNUSED":
+ if cutil.ifdef_leaf_is_noop():
+ beg = "#define "
+ else:
+ beg = "# define"
+ case idl.BitNum():
+ beg = "/* number"
+ end = " */"
+
+ c_name = bitname(bit)
+ c_val = f"UINT{typ.static_size*8}_C(1)<<{bit.num}"
+ ret += (
+ f"{beg} {c_name:<{namewidth}} (({c9util.typename(typ)})({c_val})){end}\n"
+ )
+ ret += cutil.ifdef_pop(1)
+
+ # 2. aliases
+ if typ.aliases:
+
+ def aliasname(alias: idl.BitAlias) -> str:
+ prefix = f"{typ.typname}_".upper()
+ base = alias.bitname
+ return c9util.Ident(c9util.add_prefix(prefix, base))
+
+ ret += "/* aliases */\n"
+ for alias in typ.aliases.values():
+ ret += cutil.ifdef_push(2, c9util.ver_ifdef(alias.in_versions))
+
+ end = ""
+ if cutil.ifdef_leaf_is_noop():
+ beg = "#define "
+ else:
+ beg = "# define"
+
+ c_name = aliasname(alias)
+ c_val = c9util.idl_expr(alias.val, lookup_sym)
+ ret += f"{beg} {c_name:<{namewidth}} (({c9util.typename(typ)})({c_val})){end}\n"
+
+ ret += cutil.ifdef_pop(1)
+
+ # 3. masks
+ if typ.masks:
+
+ def maskname(mask: idl.BitAlias) -> str:
+ prefix = f"{typ.typname}_".upper()
+ base = mask.bitname
+ return c9util.Ident(c9util.add_prefix(prefix, base) + "_MASK")
+
+ ret += "/* masks */\n"
+ for mask in typ.masks.values():
+ ret += cutil.ifdef_push(2, c9util.ver_ifdef(mask.in_versions))
+
+ end = ""
+ if cutil.ifdef_leaf_is_noop():
+ beg = "#define "
+ else:
+ beg = "# define"
+
+ c_name = maskname(mask)
+ c_val = c9util.idl_expr(mask.val, lookup_sym, bitwidth=typ.static_size * 8)
+ ret += f"{beg} {c_name:<{namewidth}} (({c9util.typename(typ)})({c_val})){end}\n"
+
+ ret += cutil.ifdef_pop(1)
+
+ # 4. numbers
+ def numname(num: idl.BitNum, base: str) -> str:
+ prefix = f"{typ.typname}_{num.numname}_".upper()
+ return c9util.Ident(c9util.add_prefix(prefix, base))
+
+ for num in typ.nums.values():
+ namewidth = max(
+ len(numname(num, base))
+ for base in [
+ *[alias.bitname for alias in num.vals.values()],
+ "MASK",
+ ]
+ )
+ ret += f"/* number: {num.numname} */\n"
+ for alias in num.vals.values():
+ ret += cutil.ifdef_push(2, c9util.ver_ifdef(alias.in_versions))
+
+ end = ""
+ if cutil.ifdef_leaf_is_noop():
+ beg = "#define "
+ else:
+ beg = "# define"
+
+ c_name = numname(num, alias.bitname)
+ c_val = c9util.idl_expr(alias.val, lookup_sym)
+ ret += f"{beg} {c_name:<{namewidth}} (({c9util.typename(typ)})({c_val})){end}\n"
+ ret += cutil.ifdef_pop(1)
+ c_name = numname(num, "MASK")
+ c_val = f"{num.mask:#0{typ.static_size*8}b}"
+ ret += (
+ f"{beg} {c_name:<{namewidth}} (({c9util.typename(typ)})({c_val})){end}\n"
+ )
+
+ return ret
+
+
+def gen_struct(typ: idl.Struct) -> str: # and idl.Message
+ ret = c9util.typename(typ) + " {"
+ if typ.members:
+ ret += "\n"
+
+ typewidth = max(len(c9util.typename(m.typ, m)) for m in typ.members)
+
+ for member in typ.members:
+ if member.val:
+ continue
+ ret += cutil.ifdef_push(2, c9util.ver_ifdef(member.in_versions))
+ ret += f"\t{c9util.typename(member.typ, member):<{typewidth}} {'*' if member.cnt else ' '}{member.membname};\n"
+ ret += cutil.ifdef_pop(1)
+ ret += "};\n"
+ ret += f"LO_IMPLEMENTATION_H(fmt_formatter, {c9util.typename(typ)}, {c9util.basename(typ)});\n"
+ return ret