# lib9p/protogen/c_unmarshal.py - Generate C unmarshal functions # # Copyright (C) 2024-2025 Luke T. Shumaker # 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