summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2025-03-23 02:05:13 -0600
committerLuke T. Shumaker <lukeshu@lukeshu.com>2025-03-23 03:05:06 -0600
commitee356d885a984d5e79da0da20ce608787f1426f3 (patch)
tree18599858087855f299ba7e7c3aaa65ef43b77e73
parent77249bb45c44ec88c96cd00da0805e1a58a1bfd6 (diff)
lib9p: protogen: pull idlutil.py out of __init__.py
-rw-r--r--lib9p/protogen/__init__.py156
-rw-r--r--lib9p/protogen/idlutil.py112
2 files changed, 144 insertions, 124 deletions
diff --git a/lib9p/protogen/__init__.py b/lib9p/protogen/__init__.py
index 73542a2..76e80f3 100644
--- a/lib9p/protogen/__init__.py
+++ b/lib9p/protogen/__init__.py
@@ -4,15 +4,13 @@
# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
# SPDX-License-Identifier: AGPL-3.0-or-later
-import enum
-import graphlib
import os.path
import sys
import typing
import idl
-from . import c9util, cutil
+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
@@ -23,100 +21,6 @@ from . import c9util, cutil
__all__ = ["main"]
-# topo_sorted() ################################################################
-
-
-def topo_sorted(typs: list[idl.UserType]) -> typing.Iterable[idl.UserType]:
- ts: graphlib.TopologicalSorter[idl.UserType] = graphlib.TopologicalSorter()
- for typ in typs:
- match typ:
- case idl.Number():
- ts.add(typ)
- case idl.Bitfield():
- ts.add(typ)
- case idl.Struct(): # and idl.Message():
- deps = [
- member.typ
- for member in typ.members
- if not isinstance(member.typ, idl.Primitive)
- ]
- ts.add(typ, *deps)
- return ts.static_order()
-
-
-# walk() #######################################################################
-
-
-class Path:
- root: idl.Type
- elems: list[idl.StructMember]
-
- def __init__(
- self, root: idl.Type, elems: list[idl.StructMember] | None = None
- ) -> None:
- self.root = root
- self.elems = elems if elems is not None else []
-
- def add(self, elem: idl.StructMember) -> "Path":
- return Path(self.root, self.elems + [elem])
-
- def parent(self) -> "Path":
- return Path(self.root, self.elems[:-1])
-
- def c_str(self, base: str, loopdepth: int = 0) -> str:
- ret = base
- for i, elem in enumerate(self.elems):
- if i > 0:
- ret += "."
- ret += elem.membname
- if elem.cnt:
- ret += f"[{chr(ord('i')+loopdepth)}]"
- loopdepth += 1
- return ret
-
- def __str__(self) -> str:
- return self.c_str(self.root.typname + "->")
-
-
-class WalkCmd(enum.Enum):
- KEEP_GOING = 1
- DONT_RECURSE = 2
- ABORT = 3
-
-
-type WalkHandler = typing.Callable[
- [Path], tuple[WalkCmd, typing.Callable[[], None] | None]
-]
-
-
-def _walk(path: Path, handle: WalkHandler) -> WalkCmd:
- typ = path.elems[-1].typ if path.elems else path.root
-
- ret, atexit = handle(path)
-
- if isinstance(typ, idl.Struct):
- match ret:
- case WalkCmd.KEEP_GOING:
- for member in typ.members:
- if _walk(path.add(member), handle) == WalkCmd.ABORT:
- ret = WalkCmd.ABORT
- break
- case WalkCmd.DONT_RECURSE:
- ret = WalkCmd.KEEP_GOING
- case WalkCmd.ABORT:
- ret = WalkCmd.ABORT
- case _:
- assert False, f"invalid cmd: {ret}"
-
- if atexit:
- atexit()
- return ret
-
-
-def walk(typ: idl.Type, handle: WalkHandler) -> None:
- _walk(Path(typ), handle)
-
-
# get_buffer_size() ############################################################
@@ -170,12 +74,12 @@ def _get_buffer_size(typ: idl.Type, version: str) -> TmpBufferSize:
ret.tmp_ends_with_copy = True
return ret
- def handle(path: Path) -> tuple[WalkCmd, None]:
+ def handle(path: idlutil.Path) -> tuple[idlutil.WalkCmd, None]:
nonlocal ret
if path.elems:
child = path.elems[-1]
if version not in child.in_versions:
- return WalkCmd.DONT_RECURSE, None
+ return idlutil.WalkCmd.DONT_RECURSE, None
if child.cnt:
if child.typ.static_size == 1: # SPECIAL (zerocopy)
ret.max_iov += 1
@@ -183,7 +87,7 @@ def _get_buffer_size(typ: idl.Type, version: str) -> TmpBufferSize:
ret.exp_size += 27 if child.membname == "utf8" else 8192
ret.max_size += child.max_cnt
ret.tmp_ends_with_copy = False
- return WalkCmd.DONT_RECURSE, None
+ return idlutil.WalkCmd.DONT_RECURSE, None
sub = _get_buffer_size(child.typ, version)
ret.exp_size += sub.exp_size * 16 # HEURISTIC: MAXWELEM
ret.max_size += sub.max_size * child.max_cnt
@@ -218,7 +122,7 @@ def _get_buffer_size(typ: idl.Type, version: str) -> TmpBufferSize:
# we can merge these
ret.max_iov -= child.max_cnt - 1
ret.tmp_ends_with_copy = sub.tmp_ends_with_copy
- return WalkCmd.DONT_RECURSE, None
+ return idlutil.WalkCmd.DONT_RECURSE, None
if not isinstance(child.typ, idl.Struct):
assert child.typ.static_size
if not ret.tmp_ends_with_copy:
@@ -230,9 +134,9 @@ def _get_buffer_size(typ: idl.Type, version: str) -> TmpBufferSize:
ret.exp_size += child.typ.static_size
ret.max_size += child.typ.static_size
ret.max_copy += child.typ.static_size
- return WalkCmd.KEEP_GOING, None
+ return idlutil.WalkCmd.KEEP_GOING, None
- walk(typ, handle)
+ idlutil.walk(typ, handle)
assert ret.min_size == typ.min_size(version)
assert ret.max_size == typ.max_size(version)
return ret
@@ -343,7 +247,7 @@ enum {c9util.ident('version')} {{
ret += f"/* {c9util.ver_enum(version):<{v_width}}: {line} */\n"
return ret
- for typ in topo_sorted(typs):
+ for typ in idlutil.topo_sorted(typs):
ret += "\n"
ret += cutil.ifdef_push(1, c9util.ver_ifdef(typ.in_versions))
@@ -712,7 +616,7 @@ LM_ALWAYS_INLINE static bool validate_8(struct _validate_ctx *ctx) { return _val
member.max or member.val or any(m.cnt == member for m in typ.members)
)
- for typ in topo_sorted(typs):
+ for typ in idlutil.topo_sorted(typs):
inline = "LM_FLATTEN" if isinstance(typ, idl.Message) else "LM_ALWAYS_INLINE"
argfn = unused if (isinstance(typ, idl.Struct) and not typ.members) else used
ret += "\n"
@@ -841,7 +745,7 @@ LM_ALWAYS_INLINE static void unmarshal_8(struct _unmarshal_ctx *ctx, uint64_t *o
\tctx->net_offset += 8;
}
"""
- for typ in topo_sorted(typs):
+ for typ in idlutil.topo_sorted(typs):
inline = "LM_FLATTEN" if isinstance(typ, idl.Message) else "LM_ALWAYS_INLINE"
argfn = unused if (isinstance(typ, idl.Struct) and not typ.members) else used
ret += "\n"
@@ -943,7 +847,7 @@ LM_ALWAYS_INLINE static void unmarshal_8(struct _unmarshal_ctx *ctx, uint64_t *o
class OffsetExpr:
static: int
cond: dict[frozenset[str], "OffsetExpr"]
- rep: list[tuple[Path, "OffsetExpr"]]
+ rep: list[tuple[idlutil.Path, "OffsetExpr"]]
def __init__(self) -> None:
self.static = 0
@@ -999,7 +903,7 @@ LM_ALWAYS_INLINE static void unmarshal_8(struct _unmarshal_ctx *ctx, uint64_t *o
ret += multiline
return ret
- type OffsetExprRecursion = typing.Callable[[Path], WalkCmd]
+ type OffsetExprRecursion = typing.Callable[[idlutil.Path], idlutil.WalkCmd]
def get_offset_expr(typ: idl.UserType, recurse: OffsetExprRecursion) -> OffsetExpr:
if not isinstance(typ, idl.Struct):
@@ -1008,7 +912,7 @@ LM_ALWAYS_INLINE static void unmarshal_8(struct _unmarshal_ctx *ctx, uint64_t *o
ret.static = typ.static_size
return ret
- stack: list[tuple[Path, OffsetExpr, typing.Callable[[], None]]]
+ stack: list[tuple[idlutil.Path, OffsetExpr, typing.Callable[[], None]]]
def pop_root() -> None:
assert False
@@ -1031,11 +935,13 @@ LM_ALWAYS_INLINE static void unmarshal_8(struct _unmarshal_ctx *ctx, uint64_t *o
stack[-2][1].rep.append((cnt_path, stack[-1][1]))
stack = stack[:-1]
- def handle(path: Path) -> tuple[WalkCmd, typing.Callable[[], None] | None]:
+ def handle(
+ path: idlutil.Path,
+ ) -> tuple[idlutil.WalkCmd, typing.Callable[[], None] | None]:
nonlocal recurse
ret = recurse(path)
- if ret != WalkCmd.KEEP_GOING:
+ if ret != idlutil.WalkCmd.KEEP_GOING:
return ret, None
nonlocal stack
@@ -1059,18 +965,18 @@ LM_ALWAYS_INLINE static void unmarshal_8(struct _unmarshal_ctx *ctx, uint64_t *o
stack[-1][1].static += child.typ.static_size
return ret, pop
- stack = [(Path(typ), OffsetExpr(), pop_root)]
- walk(typ, handle)
+ stack = [(idlutil.Path(typ), OffsetExpr(), pop_root)]
+ idlutil.walk(typ, handle)
return stack[0][1]
- def go_to_end(path: Path) -> WalkCmd:
- return WalkCmd.KEEP_GOING
+ def go_to_end(path: idlutil.Path) -> idlutil.WalkCmd:
+ return idlutil.WalkCmd.KEEP_GOING
- def go_to_tok(name: str) -> typing.Callable[[Path], WalkCmd]:
- def ret(path: Path) -> WalkCmd:
+ def go_to_tok(name: str) -> typing.Callable[[idlutil.Path], idlutil.WalkCmd]:
+ def ret(path: idlutil.Path) -> idlutil.WalkCmd:
if len(path.elems) == 1 and path.elems[0].membname == name:
- return WalkCmd.ABORT
- return WalkCmd.KEEP_GOING
+ return idlutil.WalkCmd.ABORT
+ return idlutil.WalkCmd.KEEP_GOING
return ret
@@ -1107,9 +1013,11 @@ LM_ALWAYS_INLINE static void unmarshal_8(struct _unmarshal_ctx *ctx, uint64_t *o
# Pass 2 - write data
ifdef_depth = 1
- stack: list[tuple[Path, bool]] = [(Path(typ), False)]
+ stack: list[tuple[idlutil.Path, bool]] = [(idlutil.Path(typ), False)]
- def handle(path: Path) -> tuple[WalkCmd, typing.Callable[[], None]]:
+ def handle(
+ path: idlutil.Path,
+ ) -> tuple[idlutil.WalkCmd, typing.Callable[[], None]]:
nonlocal ret
nonlocal ifdef_depth
nonlocal stack
@@ -1185,7 +1093,7 @@ LM_ALWAYS_INLINE static void unmarshal_8(struct _unmarshal_ctx *ctx, uint64_t *o
ret += f"{'\t'*len(stack)}MARSHAL_BYTES(ctx, {path.c_str('val->')[:-3]}, {cnt_path.c_str('val->')});\n"
else:
ret += f"{'\t'*len(stack)}MARSHAL_BYTES_ZEROCOPY(ctx, {path.c_str('val->')[:-3]}, {cnt_path.c_str('val->')});\n"
- return WalkCmd.KEEP_GOING, pop
+ return idlutil.WalkCmd.KEEP_GOING, pop
loopvar = chr(ord("i") + loopdepth - 1)
ret += f"{'\t'*len(stack)}for ({c9util.typename(child.cnt.typ)} {loopvar} = 0; {loopvar} < {cnt_path.c_str('val->')}; {loopvar}++) {{\n"
stack.append((path, False))
@@ -1208,9 +1116,9 @@ LM_ALWAYS_INLINE static void unmarshal_8(struct _unmarshal_ctx *ctx, uint64_t *o
if isinstance(child.typ, idl.Bitfield):
val += f" & {child.typ.typname}_masks[ctx->ctx->version]"
ret += f"{'\t'*len(stack)}MARSHAL_U{child.typ.static_size*8}LE(ctx, {val});\n"
- return WalkCmd.KEEP_GOING, pop
+ return idlutil.WalkCmd.KEEP_GOING, pop
- walk(typ, handle)
+ idlutil.walk(typ, handle)
del handle
del stack
del max_size
diff --git a/lib9p/protogen/idlutil.py b/lib9p/protogen/idlutil.py
new file mode 100644
index 0000000..dc4d012
--- /dev/null
+++ b/lib9p/protogen/idlutil.py
@@ -0,0 +1,112 @@
+# lib9p/protogen/idlutil.py - Utilities for working with the 9P idl package
+#
+# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+import enum
+import graphlib
+import typing
+
+import idl
+
+# pylint: disable=unused-variable
+__all__ = [
+ "topo_sorted",
+ "Path",
+ "WalkCmd",
+ "WalkHandler",
+ "walk",
+]
+
+# topo_sorted() ################################################################
+
+
+def topo_sorted(typs: list[idl.UserType]) -> typing.Iterable[idl.UserType]:
+ ts: graphlib.TopologicalSorter[idl.UserType] = graphlib.TopologicalSorter()
+ for typ in typs:
+ match typ:
+ case idl.Number():
+ ts.add(typ)
+ case idl.Bitfield():
+ ts.add(typ)
+ case idl.Struct(): # and idl.Message():
+ deps = [
+ member.typ
+ for member in typ.members
+ if not isinstance(member.typ, idl.Primitive)
+ ]
+ ts.add(typ, *deps)
+ return ts.static_order()
+
+
+# walk() #######################################################################
+
+
+class Path:
+ root: idl.Type
+ elems: list[idl.StructMember]
+
+ def __init__(
+ self, root: idl.Type, elems: list[idl.StructMember] | None = None
+ ) -> None:
+ self.root = root
+ self.elems = elems if elems is not None else []
+
+ def add(self, elem: idl.StructMember) -> "Path":
+ return Path(self.root, self.elems + [elem])
+
+ def parent(self) -> "Path":
+ return Path(self.root, self.elems[:-1])
+
+ def c_str(self, base: str, loopdepth: int = 0) -> str:
+ ret = base
+ for i, elem in enumerate(self.elems):
+ if i > 0:
+ ret += "."
+ ret += elem.membname
+ if elem.cnt:
+ ret += f"[{chr(ord('i')+loopdepth)}]"
+ loopdepth += 1
+ return ret
+
+ def __str__(self) -> str:
+ return self.c_str(self.root.typname + "->")
+
+
+class WalkCmd(enum.Enum):
+ KEEP_GOING = 1
+ DONT_RECURSE = 2
+ ABORT = 3
+
+
+type WalkHandler = typing.Callable[
+ [Path], tuple[WalkCmd, typing.Callable[[], None] | None]
+]
+
+
+def _walk(path: Path, handle: WalkHandler) -> WalkCmd:
+ typ = path.elems[-1].typ if path.elems else path.root
+
+ ret, atexit = handle(path)
+
+ if isinstance(typ, idl.Struct):
+ match ret:
+ case WalkCmd.KEEP_GOING:
+ for member in typ.members:
+ if _walk(path.add(member), handle) == WalkCmd.ABORT:
+ ret = WalkCmd.ABORT
+ break
+ case WalkCmd.DONT_RECURSE:
+ ret = WalkCmd.KEEP_GOING
+ case WalkCmd.ABORT:
+ ret = WalkCmd.ABORT
+ case _:
+ assert False, f"invalid cmd: {ret}"
+
+ if atexit:
+ atexit()
+ return ret
+
+
+def walk(typ: idl.Type, handle: WalkHandler) -> None:
+ _walk(Path(typ), handle)