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.py92
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