diff options
Diffstat (limited to 'lib9p/protogen/c_unmarshal.py')
-rw-r--r-- | lib9p/protogen/c_unmarshal.py | 159 |
1 files changed, 96 insertions, 63 deletions
diff --git a/lib9p/protogen/c_unmarshal.py b/lib9p/protogen/c_unmarshal.py index e17f456..d076352 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,104 @@ 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 *)&ctx->net_bytes[ctx->net_offset];\n" + "\tctx->net_offset += len;\n" + ) + ret += cutil.macro( + "#define UNMARSHAL_U8LE(ctx, val_lvalue)\n" + "\tval_lvalue = ctx->net_bytes[ctx->net_offset];\n" + "\tctx->net_offset += 1;\n" + ) + ret += cutil.macro( + "#define UNMARSHAL_U16LE(ctx, val_lvalue)\n" + "\tval_lvalue = uint16le_decode(&ctx->net_bytes[ctx->net_offset]);\n" + "\tctx->net_offset += 2;\n" + ) + ret += cutil.macro( + "#define UNMARSHAL_U32LE(ctx, val_lvalue)\n" + "\tval_lvalue = uint32le_decode(&ctx->net_bytes[ctx->net_offset]);\n" + "\tctx->net_offset += 4;\n" + ) + ret += cutil.macro( + "#define UNMARSHAL_U64LE(ctx, val_lvalue)\n" + "\tval_lvalue = uint64le_decode(&ctx->net_bytes[ctx->net_offset]);\n" + "\tctx->net_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: + 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: + ret += cutil.ifdef_push( + ifdef_lvl() + 1, c9util.ver_ifdef(child.in_versions) + ) + 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]} = ctx->extra;\n" + ret += f"{'\t'*indent_lvl()}ctx->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()}ctx->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 += f"static void unmarshal_{typ.typname}(struct _unmarshal_ctx *ctx, {c9util.typename(typ)} *out) {{\n" + + indent_stack = [IndentLevel(ifdef=True)] + idlutil.walk(typ, handle) + ret += "}\n" ret += cutil.ifdef_pop(0) return ret |