diff options
Diffstat (limited to 'lib9p/protogen/c_unmarshal.py')
-rw-r--r-- | lib9p/protogen/c_unmarshal.py | 92 |
1 files changed, 92 insertions, 0 deletions
diff --git a/lib9p/protogen/c_unmarshal.py b/lib9p/protogen/c_unmarshal.py new file mode 100644 index 0000000..e17f456 --- /dev/null +++ b/lib9p/protogen/c_unmarshal.py @@ -0,0 +1,92 @@ +# lib9p/protogen/c_unmarshal.py - Generate C unmarshal 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, idlutil + +# 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_unmarshal"] + + +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 += "\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 += cutil.ifdef_pop(0) + return ret |