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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
|
# 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 typing
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_* ****************************************************************/
"""
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:
if len(indent_stack) == indent_stack_len + 1 and indent_stack[-1].ifdef:
break
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:
if line := cutil.ifdef_push(
ifdef_lvl() + 1, c9util.ver_ifdef(child.in_versions)
):
ret += line
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"static void unmarshal_{typ.typname}(struct _unmarshal_ctx *ctx, {c9util.typename(typ)} *out) {{\n"
indent_stack = [IndentLevel(ifdef=True)]
idlutil.walk(typ, handle)
while len(indent_stack) > 0:
ret += f"{'\t'*(indent_lvl()-1)}}}\n"
if indent_stack.pop().ifdef and indent_stack:
ret += cutil.ifdef_pop(ifdef_lvl())
ret += cutil.ifdef_pop(0)
return ret
|