summaryrefslogtreecommitdiff
path: root/lib9p/protogen/c_unmarshal.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib9p/protogen/c_unmarshal.py')
-rw-r--r--lib9p/protogen/c_unmarshal.py169
1 files changed, 105 insertions, 64 deletions
diff --git a/lib9p/protogen/c_unmarshal.py b/lib9p/protogen/c_unmarshal.py
index e17f456..018d750 100644
--- a/lib9p/protogen/c_unmarshal.py
+++ b/lib9p/protogen/c_unmarshal.py
@@ -3,6 +3,7 @@
# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
# SPDX-License-Identifier: AGPL-3.0-or-later
+import typing
import idl
@@ -21,72 +22,112 @@ def gen_c_unmarshal(versions: set[str], typs: list[idl.UserType]) -> str:
ret = """
/* unmarshal_* ****************************************************************/
-LM_ALWAYS_INLINE static void unmarshal_1(struct _unmarshal_ctx *ctx, uint8_t *out) {
-\t*out = ctx->net_bytes[ctx->net_offset];
-\tctx->net_offset += 1;
-}
-
-LM_ALWAYS_INLINE static void unmarshal_2(struct _unmarshal_ctx *ctx, uint16_t *out) {
-\t*out = uint16le_decode(&ctx->net_bytes[ctx->net_offset]);
-\tctx->net_offset += 2;
-}
-
-LM_ALWAYS_INLINE static void unmarshal_4(struct _unmarshal_ctx *ctx, uint32_t *out) {
-\t*out = uint32le_decode(&ctx->net_bytes[ctx->net_offset]);
-\tctx->net_offset += 4;
-}
-
-LM_ALWAYS_INLINE static void unmarshal_8(struct _unmarshal_ctx *ctx, uint64_t *out) {
-\t*out = uint64le_decode(&ctx->net_bytes[ctx->net_offset]);
-\tctx->net_offset += 8;
-}
"""
- for typ in idlutil.topo_sorted(typs):
- inline = "LM_FLATTEN" if isinstance(typ, idl.Message) else "LM_ALWAYS_INLINE"
- argfn = (
- c9util.arg_unused
- if (isinstance(typ, idl.Struct) and not typ.members)
- else c9util.arg_used
- )
+ ret += cutil.macro(
+ "#define UNMARSHAL_BYTES(ctx, data_lvalue, len)\n"
+ "\tdata_lvalue = (char *)&net_bytes[net_offset];\n"
+ "\tnet_offset += len;\n"
+ )
+ ret += cutil.macro(
+ "#define UNMARSHAL_U8LE(ctx, val_lvalue)\n"
+ "\tval_lvalue = net_bytes[net_offset];\n"
+ "\tnet_offset += 1;\n"
+ )
+ ret += cutil.macro(
+ "#define UNMARSHAL_U16LE(ctx, val_lvalue)\n"
+ "\tval_lvalue = uint16le_decode(&net_bytes[net_offset]);\n"
+ "\tnet_offset += 2;\n"
+ )
+ ret += cutil.macro(
+ "#define UNMARSHAL_U32LE(ctx, val_lvalue)\n"
+ "\tval_lvalue = uint32le_decode(&net_bytes[net_offset]);\n"
+ "\tnet_offset += 4;\n"
+ )
+ ret += cutil.macro(
+ "#define UNMARSHAL_U64LE(ctx, val_lvalue)\n"
+ "\tval_lvalue = uint64le_decode(&net_bytes[net_offset]);\n"
+ "\tnet_offset += 8;\n"
+ )
+
+ class IndentLevel(typing.NamedTuple):
+ ifdef: bool # whether this is both `{` and `#if`, or just `{`
+
+ indent_stack: list[IndentLevel]
+
+ def ifdef_lvl() -> int:
+ return sum(1 if lvl.ifdef else 0 for lvl in indent_stack)
+
+ def indent_lvl() -> int:
+ return len(indent_stack)
+
+ def handle(
+ path: idlutil.Path,
+ ) -> tuple[idlutil.WalkCmd, typing.Callable[[], None]]:
+ nonlocal ret
+ nonlocal indent_stack
+ indent_stack_len = len(indent_stack)
+
+ def pop() -> None:
+ nonlocal ret
+ nonlocal indent_stack
+ nonlocal indent_stack_len
+ while len(indent_stack) > indent_stack_len:
+ if len(indent_stack) == indent_stack_len + 1 and indent_stack[-1].ifdef:
+ break
+ ret += f"{'\t'*(indent_lvl()-1)}}}\n"
+ if indent_stack.pop().ifdef:
+ ret += cutil.ifdef_pop(ifdef_lvl())
+
+ if not path.elems:
+ return idlutil.WalkCmd.KEEP_GOING, pop
+
+ child = path.elems[-1]
+ parent = path.elems[-2].typ if len(path.elems) > 1 else path.root
+ if child.in_versions < parent.in_versions:
+ if line := cutil.ifdef_push(
+ ifdef_lvl() + 1, c9util.ver_ifdef(child.in_versions)
+ ):
+ ret += line
+ ret += (
+ f"{'\t'*indent_lvl()}if ({c9util.ver_cond(child.in_versions)}) {{\n"
+ )
+ indent_stack.append(IndentLevel(ifdef=True))
+ if child.cnt:
+ cnt_path = path.parent().add(child.cnt)
+ 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"
+ 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"
+ 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"
+ indent_stack.append(IndentLevel(ifdef=False))
+ if not isinstance(child.typ, idl.Struct):
+ if child.val:
+ ret += f"{'\t'*indent_lvl()}net_offset += {child.typ.static_size};\n"
+ else:
+ ret += f"{'\t'*indent_lvl()}UNMARSHAL_U{child.typ.static_size*8}LE(ctx, {path.c_str('out->')});\n"
+ return idlutil.WalkCmd.KEEP_GOING, pop
+
+ for typ in typs:
+ if not (
+ isinstance(typ, idl.Message) or typ.typname == "stat"
+ ): # SPECIAL (include stat)
+ continue
+ assert isinstance(typ, idl.Struct)
ret += "\n"
ret += cutil.ifdef_push(1, c9util.ver_ifdef(typ.in_versions))
- ret += f"{inline} static void unmarshal_{typ.typname}(struct _unmarshal_ctx *{argfn('ctx')}, {c9util.typename(typ)} *out) {{\n"
- match typ:
- case idl.Number():
- ret += f"\tunmarshal_{typ.prim.typname}(ctx, ({c9util.typename(typ.prim)} *)out);\n"
- case idl.Bitfield():
- ret += f"\tunmarshal_{typ.prim.typname}(ctx, ({c9util.typename(typ.prim)} *)out);\n"
- case idl.Struct():
- ret += "\tmemset(out, 0, sizeof(*out));\n"
-
- for member in typ.members:
- ret += cutil.ifdef_push(2, c9util.ver_ifdef(member.in_versions))
- if member.val:
- ret += f"\tctx->net_offset += {member.static_size};\n"
- continue
- ret += "\t"
-
- prefix = "\t"
- if member.in_versions != typ.in_versions:
- ret += "if ( " + c9util.ver_cond(member.in_versions) + " ) "
- prefix = "\t\t"
- if member.cnt:
- if member.in_versions != typ.in_versions:
- ret += "{\n"
- ret += prefix
- if member.typ.static_size == 1: # SPECIAL (string, zerocopy)
- ret += f"out->{member.membname} = (char *)&ctx->net_bytes[ctx->net_offset];\n"
- ret += f"{prefix}ctx->net_offset += out->{member.cnt.membname};\n"
- else:
- ret += f"out->{member.membname} = ctx->extra;\n"
- ret += f"{prefix}ctx->extra += sizeof(out->{member.membname}[0]) * out->{member.cnt.membname};\n"
- ret += f"{prefix}for (typeof(out->{member.cnt.membname}) i = 0; i < out->{member.cnt.membname}; i++)\n"
- ret += f"{prefix}\tunmarshal_{member.typ.typname}(ctx, &out->{member.membname}[i]);\n"
- if member.in_versions != typ.in_versions:
- ret += "\t}\n"
- else:
- ret += f"unmarshal_{member.typ.typname}(ctx, &out->{member.membname});\n"
- ret += cutil.ifdef_pop(1)
- ret += "}\n"
+ ret += f"static void unmarshal_{typ.typname}([[gnu::unused]] struct lib9p_ctx *ctx, uint8_t *net_bytes, void *out_buf) {{\n"
+ ret += f"\t{c9util.typename(typ)} *out = out_buf;\n"
+ ret += "\t[[gnu::unused]] void *extra = &out[1];\n"
+ ret += "\tuint32_t net_offset = 0;\n"
+
+ indent_stack = [IndentLevel(ifdef=True)]
+ idlutil.walk(typ, handle)
+ while len(indent_stack) > 0:
+ ret += f"{'\t'*(indent_lvl()-1)}}}\n"
+ if indent_stack.pop().ifdef and indent_stack:
+ ret += cutil.ifdef_pop(ifdef_lvl())
ret += cutil.ifdef_pop(0)
return ret