summaryrefslogtreecommitdiff
path: root/lib9p/protogen/c_unmarshal.py
blob: e17f4569cee344e03470bc79c38f31869e9111f5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
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