summaryrefslogtreecommitdiff
path: root/lib9p/idl.gen
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2025-01-13 23:20:13 -0700
committerLuke T. Shumaker <lukeshu@lukeshu.com>2025-02-12 20:55:19 -0700
commita24a60232204702fe245c312edb0c2c8041b17a8 (patch)
tree01cc4a46cad37087aa419b2b13c9aa27c568eea2 /lib9p/idl.gen
parent1793bda7f3e445c5ad81cf108589f8b1edcda4eb (diff)
lib9p: Rewrite the marshalers to support zero-copy for data
Diffstat (limited to 'lib9p/idl.gen')
-rwxr-xr-xlib9p/idl.gen493
1 files changed, 370 insertions, 123 deletions
diff --git a/lib9p/idl.gen b/lib9p/idl.gen
index bf0935c..9a8260a 100755
--- a/lib9p/idl.gen
+++ b/lib9p/idl.gen
@@ -183,6 +183,23 @@ class Path:
def add(self, elem: idl.StructMember) -> "Path":
return Path(self.root, self.elems + [elem])
+ def parent(self) -> "Path":
+ return Path(self.root, self.elems[:-1])
+
+ def c_str(self, base: str, loopdepth: int = 0) -> str:
+ ret = base
+ for i, elem in enumerate(self.elems):
+ if i > 0:
+ ret += "."
+ ret += elem.name
+ if elem.cnt:
+ ret += f"[{chr(ord('i')+loopdepth)}]"
+ loopdepth += 1
+ return ret
+
+ def __str__(self) -> str:
+ return self.c_str(self.root.name + "->")
+
class WalkCmd(enum.Enum):
KEEP_GOING = 1
@@ -190,10 +207,15 @@ class WalkCmd(enum.Enum):
ABORT = 3
-def _walk(path: Path, handle: typing.Callable[[Path], WalkCmd]) -> WalkCmd:
+type WalkHandler = typing.Callable[
+ [Path], tuple[WalkCmd, typing.Callable[[], None] | None]
+]
+
+
+def _walk(path: Path, handle: WalkHandler) -> WalkCmd:
typ = path.elems[-1].typ if path.elems else path.root
- ret = handle(path)
+ ret, atexit = handle(path)
if isinstance(typ, idl.Struct):
match ret:
@@ -209,10 +231,12 @@ def _walk(path: Path, handle: typing.Callable[[Path], WalkCmd]) -> WalkCmd:
case _:
assert False, f"invalid cmd: {ret}"
+ if atexit:
+ atexit()
return ret
-def walk(typ: idl.Type, handle: typing.Callable[[Path], WalkCmd]) -> None:
+def walk(typ: idl.Type, handle: WalkHandler) -> None:
_walk(Path(typ), handle)
@@ -258,12 +282,12 @@ def get_buffer_size(typ: idl.Type, version: str) -> BufferSize:
ret._ends_with_copy = True
return ret
- def handle(path: Path) -> WalkCmd:
+ def handle(path: Path) -> tuple[WalkCmd, None]:
nonlocal ret
if path.elems:
child = path.elems[-1]
if version not in child.in_versions:
- return WalkCmd.DONT_RECURSE
+ return WalkCmd.DONT_RECURSE, None
if child.cnt:
if child.typ.static_size == 1: # SPECIAL (zerocopy)
ret.max_iov += 1
@@ -271,7 +295,7 @@ def get_buffer_size(typ: idl.Type, version: str) -> BufferSize:
ret.exp_size += 27 if child.name == "utf8" else 8192
ret.max_size += child.max_cnt
ret._ends_with_copy = False
- return WalkCmd.DONT_RECURSE
+ return WalkCmd.DONT_RECURSE, None
sub = get_buffer_size(child.typ, version)
ret.exp_size += sub.exp_size * 16 # HEURISTIC: MAXWELEM
ret.max_size += sub.max_size * child.max_cnt
@@ -306,7 +330,7 @@ def get_buffer_size(typ: idl.Type, version: str) -> BufferSize:
# we can merge these
ret.max_iov -= child.max_cnt - 1
ret._ends_with_copy = sub._ends_with_copy
- return WalkCmd.DONT_RECURSE
+ return WalkCmd.DONT_RECURSE, None
elif not isinstance(child.typ, idl.Struct):
assert child.typ.static_size
if not ret._ends_with_copy:
@@ -318,7 +342,7 @@ def get_buffer_size(typ: idl.Type, version: str) -> BufferSize:
ret.exp_size += child.typ.static_size
ret.max_size += child.typ.static_size
ret.max_copy += child.typ.static_size
- return WalkCmd.KEEP_GOING
+ return WalkCmd.KEEP_GOING, None
walk(typ, handle)
assert ret.min_size == typ.min_size(version)
@@ -340,6 +364,8 @@ def gen_h(versions: set[str], typs: list[idl.Type]) -> str:
#endif
#include <stdint.h> /* for uint{{n}}_t types */
+
+#include <libhw/generic/net.h> /* for struct iovec */
"""
id2typ: dict[int, idl.Message] = {}
@@ -507,7 +533,7 @@ enum {idprefix}version {{
ret += ifdef_pop(0)
ret += """
-/* sizes **********************************************************************/
+/* containers *****************************************************************/
"""
ret += "\n"
ret += f"#define _{idprefix.upper()}MAX(a, b) ((a) > (b)) ? (a) : (b)\n"
@@ -573,6 +599,20 @@ enum {idprefix}version {{
directive = "elif"
ret += "#endif\n"
+ ret += "\n"
+ ret += f"struct {idprefix}Tmsg_send_buf {{\n"
+ ret += f"\tsize_t iov_cnt;\n"
+ ret += f"\tstruct iovec iov[{idprefix.upper()}TMSG_MAX_IOV];\n"
+ ret += f"\tuint8_t copied[{idprefix.upper()}TMSG_MAX_COPY];\n"
+ ret += "};\n"
+
+ ret += "\n"
+ ret += f"struct {idprefix}Rmsg_send_buf {{\n"
+ ret += f"\tsize_t iov_cnt;\n"
+ ret += f"\tstruct iovec iov[{idprefix.upper()}RMSG_MAX_IOV];\n"
+ ret += f"\tuint8_t copied[{idprefix.upper()}RMSG_MAX_COPY];\n"
+ ret += "};\n"
+
return ret
@@ -917,139 +957,346 @@ LM_ALWAYS_INLINE static void unmarshal_8(struct _unmarshal_ctx *ctx, uint64_t *o
ret += """
/* marshal_* ******************************************************************/
-LM_ALWAYS_INLINE static bool _marshal_too_large(struct _marshal_ctx *ctx) {
-\tlib9p_errorf(ctx->ctx, LINUX_ERANGE, "%s too large to marshal into %s limit (limit=%"PRIu32")",
-\t\t(ctx->net_bytes[4] % 2 == 0) ? "T-message" : "R-message",
-\t\tctx->ctx->version ? "negotiated" : ((ctx->net_bytes[4] % 2 == 0) ? "client" : "server"),
-\t\tctx->ctx->max_msg_size);
-\treturn true;
-}
+"""
+ ret += c_macro(
+ "#define MARSHAL_BYTES_ZEROCOPY(ctx, data, len)\n"
+ "\tif (ctx->net_iov[ctx->net_iov_cnt-1].iov_len)\n"
+ "\t\tctx->net_iov_cnt++;\n"
+ "\tctx->net_iov[ctx->net_iov_cnt-1].iov_base = data;\n"
+ "\tctx->net_iov[ctx->net_iov_cnt-1].iov_len = len;\n"
+ "\tctx->net_iov_cnt++;\n"
+ )
+ ret += c_macro(
+ "#define MARSHAL_BYTES(ctx, data, len)\n"
+ "\tif (!ctx->net_iov[ctx->net_iov_cnt-1].iov_base)\n"
+ "\t\tctx->net_iov[ctx->net_iov_cnt-1].iov_base = &ctx->net_copied[ctx->net_copied_size];\n"
+ "\tmemcpy(&ctx->net_copied[ctx->net_copied_size], data, len);\n"
+ "\tctx->net_copied_size += len;\n"
+ "\tctx->net_iov[ctx->net_iov_cnt-1].iov_len += len;\n"
+ )
+ ret += c_macro(
+ "#define MARSHAL_U8LE(ctx, val)\n"
+ "\tif (!ctx->net_iov[ctx->net_iov_cnt-1].iov_base)\n"
+ "\t\tctx->net_iov[ctx->net_iov_cnt-1].iov_base = &ctx->net_copied[ctx->net_copied_size];\n"
+ "\tctx->net_copied[ctx->net_copied_size] = val;\n"
+ "\tctx->net_copied_size += 1;\n"
+ "\tctx->net_iov[ctx->net_iov_cnt-1].iov_len += 1;\n"
+ )
+ ret += c_macro(
+ "#define MARSHAL_U16LE(ctx, val)\n"
+ "\tif (!ctx->net_iov[ctx->net_iov_cnt-1].iov_base)\n"
+ "\t\tctx->net_iov[ctx->net_iov_cnt-1].iov_base = &ctx->net_copied[ctx->net_copied_size];\n"
+ "\tuint16le_encode(&ctx->net_copied[ctx->net_copied_size], val);\n"
+ "\tctx->net_copied_size += 2;\n"
+ "\tctx->net_iov[ctx->net_iov_cnt-1].iov_len += 2;\n"
+ )
+ ret += c_macro(
+ "#define MARSHAL_U32LE(ctx, val)\n"
+ "\tif (!ctx->net_iov[ctx->net_iov_cnt-1].iov_base)\n"
+ "\t\tctx->net_iov[ctx->net_iov_cnt-1].iov_base = &ctx->net_copied[ctx->net_copied_size];\n"
+ "\tuint32le_encode(&ctx->net_copied[ctx->net_copied_size], val);\n"
+ "\tctx->net_copied_size += 4;\n"
+ "\tctx->net_iov[ctx->net_iov_cnt-1].iov_len += 4;\n"
+ )
+ ret += c_macro(
+ "#define MARSHAL_U64LE(ctx, val)\n"
+ "\tif (!ctx->net_iov[ctx->net_iov_cnt-1].iov_base)\n"
+ "\t\tctx->net_iov[ctx->net_iov_cnt-1].iov_base = &ctx->net_copied[ctx->net_copied_size];\n"
+ "\tuint64le_encode(&ctx->net_copied[ctx->net_copied_size], val);\n"
+ "\tctx->net_copied_size += 8;\n"
+ "\tctx->net_iov[ctx->net_iov_cnt-1].iov_len += 8;\n"
+ )
-LM_ALWAYS_INLINE static bool marshal_1(struct _marshal_ctx *ctx, uint8_t *val) {
-\tif (ctx->net_offset + 1 > ctx->ctx->max_msg_size)
-\t\treturn _marshal_too_large(ctx);
-\tctx->net_bytes[ctx->net_offset] = *val;
-\tctx->net_offset += 1;
-\treturn false;
-}
+ class OffsetExpr:
+ static: int
+ cond: dict[frozenset[str], "OffsetExpr"]
+ rep: list[tuple[Path, "OffsetExpr"]]
+
+ def __init__(self) -> None:
+ self.static = 0
+ self.rep = []
+ self.cond = {}
+
+ def add(self, other: "OffsetExpr") -> None:
+ self.static += other.static
+ self.rep += other.rep
+ for k, v in other.cond.items():
+ if k in self.cond:
+ self.cond[k].add(v)
+ else:
+ self.cond[k] = v
+
+ def gen_c(
+ self,
+ dsttyp: str,
+ dstvar: str,
+ root: str,
+ indent_depth: int,
+ loop_depth: int,
+ ) -> str:
+ oneline: list[str] = []
+ multiline = ""
+ if self.static:
+ oneline.append(str(self.static))
+ for cnt, sub in self.rep:
+ if not sub.cond and not sub.rep:
+ if sub.static == 1:
+ oneline.append(cnt.c_str(root))
+ else:
+ oneline.append(f"({cnt.c_str(root)})*{sub.static}")
+ continue
+ loopvar = chr(ord("i") + loop_depth)
+ multiline += f"{'\t'*indent_depth}for ({c_typename(cnt.elems[-1].typ)} {loopvar} = 0; {loopvar} < {cnt.c_str(root)}; {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():
+ multiline += ifdef_push(indent_depth + 1, c_ver_ifdef(vers))
+ multiline += f"{'\t'*indent_depth}if {c_ver_cond(vers)} {{\n"
+ multiline += sub.gen_c("", dstvar, root, indent_depth + 1, loop_depth)
+ multiline += f"{'\t'*indent_depth}}}\n"
+ multiline += ifdef_pop(indent_depth)
+ if dsttyp:
+ if not oneline:
+ oneline.append("0")
+ ret = f"{'\t'*indent_depth}{dsttyp} {dstvar} = {' + '.join(oneline)};\n"
+ elif oneline:
+ ret = f"{'\t'*indent_depth}{dstvar} += {' + '.join(oneline)};\n"
+ ret += multiline
+ return ret
-LM_ALWAYS_INLINE static bool marshal_2(struct _marshal_ctx *ctx, uint16_t *val) {
-\tif (ctx->net_offset + 2 > ctx->ctx->max_msg_size)
-\t\treturn _marshal_too_large(ctx);
-\tuint16le_encode(&ctx->net_bytes[ctx->net_offset], *val);
-\tctx->net_offset += 2;
-\treturn false;
-}
+ type OffsetExprRecursion = typing.Callable[[Path], WalkCmd]
-LM_ALWAYS_INLINE static bool marshal_4(struct _marshal_ctx *ctx, uint32_t *val) {
-\tif (ctx->net_offset + 4 > ctx->ctx->max_msg_size)
-\t\treturn true;
-\tuint32le_encode(&ctx->net_bytes[ctx->net_offset], *val);
-\tctx->net_offset += 4;
-\treturn false;
-}
+ def get_offset_expr(typ: idl.Type, recurse: OffsetExprRecursion) -> OffsetExpr:
+ if not isinstance(typ, idl.Struct):
+ assert typ.static_size
+ ret = OffsetExpr()
+ ret.static = typ.static_size
+ return ret
-LM_ALWAYS_INLINE static bool marshal_8(struct _marshal_ctx *ctx, uint64_t *val) {
-\tif (ctx->net_offset + 8 > ctx->ctx->max_msg_size)
-\t\treturn true;
-\tuint64le_encode(&ctx->net_bytes[ctx->net_offset], *val);
-\tctx->net_offset += 8;
-\treturn false;
-}
-"""
- for typ in topo_sorted(typs):
- inline = "LM_FLATTEN" if isinstance(typ, idl.Message) else "LM_ALWAYS_INLINE"
- argfn = unused if (isinstance(typ, idl.Struct) and not typ.members) else used
+ stack: list[tuple[Path, OffsetExpr, typing.Callable[[], None]]]
+
+ def pop_root() -> None:
+ assert False
+
+ def pop_cond() -> None:
+ nonlocal stack
+ key = frozenset(stack[-1][0].elems[-1].in_versions)
+ if key in stack[-2][1].cond:
+ stack[-2][1].cond[key].add(stack[-1][1])
+ else:
+ stack[-2][1].cond[key] = stack[-1][1]
+ stack = stack[:-1]
+
+ def pop_rep() -> None:
+ nonlocal stack
+ member_path = stack[-1][0]
+ member = member_path.elems[-1]
+ assert member.cnt
+ cnt_path = member_path.parent().add(member.cnt)
+ stack[-2][1].rep.append((cnt_path, stack[-1][1]))
+ stack = stack[:-1]
+
+ def handle(path: Path) -> tuple[WalkCmd, typing.Callable[[], None] | None]:
+ nonlocal recurse
+
+ ret = recurse(path)
+ if ret != WalkCmd.KEEP_GOING:
+ return ret, None
+
+ nonlocal stack
+ stack_len = len(stack)
+
+ def pop() -> None:
+ nonlocal stack
+ nonlocal stack_len
+ while len(stack) > stack_len:
+ stack[-1][2]()
+
+ if path.elems:
+ child = path.elems[-1]
+ parent = path.elems[-2].typ if len(path.elems) > 1 else path.root
+ if child.in_versions < parent.in_versions:
+ stack.append((path, OffsetExpr(), pop_cond))
+ if child.cnt:
+ stack.append((path, OffsetExpr(), pop_rep))
+ if not isinstance(child.typ, idl.Struct):
+ assert child.typ.static_size
+ stack[-1][1].static += child.typ.static_size
+ return ret, pop
+
+ stack = [(Path(typ), OffsetExpr(), pop_root)]
+ walk(typ, handle)
+ return stack[0][1]
+
+ def go_to_end(path: Path) -> WalkCmd:
+ return WalkCmd.KEEP_GOING
+
+ def go_to_tok(name: str) -> typing.Callable[[Path], WalkCmd]:
+ def ret(path: Path) -> WalkCmd:
+ if len(path.elems) == 1 and path.elems[0].name == name:
+ return WalkCmd.ABORT
+ return WalkCmd.KEEP_GOING
+
+ return ret
+
+ for typ in typs:
+ if not (
+ isinstance(typ, idl.Message) or typ.name == "stat"
+ ): # SPECIAL (include stat)
+ continue
+ assert isinstance(typ, idl.Struct)
ret += "\n"
ret += ifdef_push(1, c_ver_ifdef(typ.in_versions))
- ret += f"{inline} static bool marshal_{typ.name}(struct _marshal_ctx *{argfn('ctx')}, {c_typename(typ)} *{argfn('val')}) {{\n"
- match typ:
- case idl.Number():
- ret += f"\treturn marshal_{typ.prim.name}(ctx, ({c_typename(typ.prim)} *)val);\n"
- case idl.Bitfield():
- ret += f"\t{c_typename(typ)} masked_val = *val & {typ.name}_masks[ctx->ctx->version];\n"
- ret += f"\treturn marshal_{typ.prim.name}(ctx, ({c_typename(typ.prim)} *)&masked_val);\n"
- case idl.Struct():
- if len(typ.members) == 0:
- ret += "\treturn false;\n"
- ret += "}\n"
- continue
+ ret += f"static bool marshal_{typ.name}(struct _marshal_ctx *ctx, {c_typename(typ)} *val) {{\n"
- # Pass 1 - declare offset variables
- mark_offset = set()
- for member in typ.members:
- if member.val:
- if member.name not in mark_offset:
- ret += f"\tuint32_t _{member.name}_offset;\n"
- mark_offset.add(member.name)
- for tok in member.val.tokens:
- if isinstance(tok, idl.ExprSym) and tok.name.startswith("&"):
- if tok.name[1:] not in mark_offset:
- ret += f"\tuint32_t _{tok.name[1:]}_offset;\n"
- mark_offset.add(tok.name[1:])
+ # Pass 1 - check size
+ max_size = max(typ.max_size(v) for v in typ.in_versions)
- # Pass 2 - main pass
- ret += "\treturn false\n"
- for member in typ.members:
- ret += ifdef_push(2, c_ver_ifdef(member.in_versions))
- ret += "\t || "
- if member.in_versions != typ.in_versions:
- ret += "( " + c_ver_cond(member.in_versions) + " && "
- if member.name in mark_offset:
- ret += f"({{ _{member.name}_offset = ctx->net_offset; "
- if member.cnt:
- ret += "({ bool err = false;\n"
- ret += f"\t for (typeof(val->{member.cnt.name}) i = 0; i < val->{member.cnt.name} && !err; i++)\n"
- ret += "\t \terr = "
- if member.typ.static_size == 1: # SPECIAL (string)
- # Special-case is that we cast from `char` to `uint8_t`.
- ret += f"marshal_{member.typ.name}(ctx, (uint8_t *)&val->{member.name}[i]);\n"
- else:
- ret += f"marshal_{member.typ.name}(ctx, &val->{member.name}[i]);\n"
- ret += f"\t err; }})"
- elif member.val:
- # Just increment net_offset, don't actually marshal anything (yet).
- assert member.static_size
- ret += (
- f"({{ ctx->net_offset += {member.static_size}; false; }})"
- )
+ if max_size > u32max: # SPECIAL (9P2000.e)
+ ret += get_offset_expr(typ, go_to_end).gen_c(
+ "uint64_t", "needed_size", "val->", 1, 0
+ )
+ ret += "\tif (needed_size > (uint64_t)(ctx->ctx->max_msg_size)) {\n"
+ else:
+ ret += get_offset_expr(typ, go_to_end).gen_c(
+ "uint32_t", "needed_size", "val->", 1, 0
+ )
+ ret += "\tif (needed_size > ctx->ctx->max_msg_size) {\n"
+ if isinstance(typ, idl.Message): # SPECIAL (disable for stat)
+ ret += f'\t\tlib9p_errorf(ctx->ctx, LINUX_ERANGE, "%s message too large to marshal into %s limit (limit=%PRIu32)",\n'
+ ret += f'\t\t\t"{typ.name}",\n'
+ ret += f'\t\t\tctx->ctx->version ? "negotiated" : "{'client' if typ.msgid % 2 == 0 else 'server'}",\n'
+ ret += "\t\t\tctx->ctx->max_msg_size);\n"
+ ret += "\t\treturn true;\n"
+ ret += "\t}\n"
+
+ # Pass 2 - write data
+ ifdef_depth = 1
+ stack: list[tuple[Path, bool]] = [(Path(typ), False)]
+
+ def handle(path: Path) -> tuple[WalkCmd, typing.Callable[[], None]]:
+ nonlocal ret
+ nonlocal ifdef_depth
+ nonlocal stack
+ stack_len = len(stack)
+
+ def pop() -> None:
+ nonlocal ret
+ nonlocal ifdef_depth
+ nonlocal stack
+ nonlocal stack_len
+ while len(stack) > stack_len:
+ ret += f"{'\t'*(len(stack)-1)}}}\n"
+ if stack[-1][1]:
+ ifdef_depth -= 1
+ ret += ifdef_pop(ifdef_depth)
+ stack = stack[:-1]
+
+ loopdepth = sum(1 for elem in path.elems if elem.cnt)
+ struct = path.elems[-1].typ if path.elems else path.root
+ if isinstance(struct, idl.Struct):
+ offsets: list[str] = []
+ for member in struct.members:
+ if not member.val:
+ continue
+ for tok in member.val.tokens:
+ if not isinstance(tok, idl.ExprSym):
+ continue
+ if tok.name == "end" or tok.name.startswith("&"):
+ if tok.name not in offsets:
+ offsets.append(tok.name)
+ for name in offsets:
+ name_prefix = "offsetof_" + "".join(
+ m.name + "_" for m in path.elems
+ )
+ if name == "end":
+ if not path.elems:
+ nonlocal max_size
+ if max_size > u32max:
+ ret += f"{'\t'*len(stack)}uint32_t {name_prefix}end = (uint32_t)needed_size;\n"
+ else:
+ ret += f"{'\t'*len(stack)}uint32_t {name_prefix}end = needed_size;\n"
+ continue
+ recurse: OffsetExprRecursion = go_to_end
else:
- ret += f"marshal_{member.typ.name}(ctx, &val->{member.name})"
- if member.name in mark_offset:
- ret += "; })"
- if member.in_versions != typ.in_versions:
- ret += " )"
- ret += "\n"
-
- # Pass 3 - marshal ,val= members
- for member in typ.members:
- if member.val:
- assert member.static_size
- ret += ifdef_push(2, c_ver_ifdef(member.in_versions))
+ assert name.startswith("&")
+ name = name[1:]
+ recurse = go_to_tok(name)
+ expr = get_offset_expr(struct, recurse)
+ expr_prefix = path.c_str("val->", loopdepth)
+ if not expr_prefix.endswith(">"):
+ expr_prefix += "."
+ ret += expr.gen_c(
+ "uint32_t",
+ name_prefix + name,
+ expr_prefix,
+ len(stack),
+ loopdepth,
+ )
+ if path.elems:
+ child = path.elems[-1]
+ parent = path.elems[-2].typ if len(path.elems) > 1 else path.root
+ if child.in_versions < parent.in_versions:
+ ret += ifdef_push(ifdef_depth + 1, c_ver_ifdef(child.in_versions))
+ ifdef_depth += 1
+ ret += f"{'\t'*len(stack)}if ({c_ver_cond(child.in_versions)}) {{\n"
+ stack.append((path, True))
+ if child.cnt:
+ cnt_path = path.parent().add(child.cnt)
+ if child.typ.static_size == 1: # SPECIAL (zerocopy)
+ if path.root.name == "stat": # SPECIAL (stat)
+ ret += f"{'\t'*len(stack)}MARSHAL_BYTES(ctx, {path.c_str('val->')[:-3]}, {cnt_path.c_str('val->')});\n"
+ else:
+ ret += f"{'\t'*len(stack)}MARSHAL_BYTES_ZEROCOPY(ctx, {path.c_str('val->')[:-3]}, {cnt_path.c_str('val->')});\n"
+ return WalkCmd.KEEP_GOING, pop
+ loopvar = chr(ord("i") + loopdepth - 1)
+ ret += f"{'\t'*len(stack)}for ({c_typename(child.cnt.typ)} {loopvar} = 0; {loopvar} < {cnt_path.c_str('val->')}; {loopvar}++) {{\n"
+ stack.append((path, False))
+ if not isinstance(child.typ, idl.Struct):
+ if child.val:
def lookup_sym(sym: str) -> str:
- match sym:
- case "end":
- return "ctx->net_offset"
- case _:
- assert sym.startswith("&")
- return f"_{sym[1:]}_offset"
-
- if member.static_size == 1:
- ret += f"\t || ({{ ctx->net_bytes[_{member.name}_offset] = {c_expr(member.val, lookup_sym)}; false; }})\n"
- else:
- ret += f"\t || ({{ uint{member.static_size*8}le_encode(&ctx->net_bytes[_{member.name}_offset], {c_expr(member.val, lookup_sym)}); false; }})\n"
+ nonlocal path
+ if sym.startswith("&"):
+ sym = sym[1:]
+ return (
+ "offsetof_"
+ + "".join(m.name + "_" for m in path.elems[:-1])
+ + sym
+ )
+
+ val = c_expr(child.val, lookup_sym)
+ else:
+ val = path.c_str("val->")
+ if isinstance(child.typ, idl.Bitfield):
+ val += f" & {child.typ.name}_masks[ctx->ctx->version]"
+ ret += f"{'\t'*len(stack)}MARSHAL_U{child.typ.static_size*8}LE(ctx, {val});\n"
+ return WalkCmd.KEEP_GOING, pop
- ret += ifdef_pop(1)
- ret += "\t ;\n"
+ walk(typ, handle)
+
+ ret += "\treturn false;\n"
ret += "}\n"
ret += ifdef_pop(0)
# function tables ##########################################################
ret += """
/* function tables ************************************************************/
-
"""
+
+ ret += "\n"
+ ret += f"const uint32_t _{idprefix}table_msg_min_size[{c_ver_enum('NUM')}] = {{\n"
+ rerror = next(typ for typ in typs if typ.name == "Rerror")
+ ret += f"\t[{c_ver_enum('unknown')}] = {rerror.min_size('9P2000')},\n" # SPECIAL (initialization)
+ for ver in sorted(versions):
+ ret += ifdef_push(1, c_ver_ifdef({ver}))
+ ret += f"\t[{c_ver_enum(ver)}] = {rerror.min_size(ver)},\n"
+ ret += ifdef_pop(0)
+ ret += "};\n"
+
+ ret += "\n"
ret += c_macro(
f"#define _MSG_RECV(typ) [{idprefix.upper()}TYP_##typ/2] = {{\n"
f"\t\t.basesize = sizeof(struct {idprefix}msg_##typ),\n"