diff options
author | Luke T. Shumaker <lukeshu@lukeshu.com> | 2025-01-11 23:17:53 -0700 |
---|---|---|
committer | Luke T. Shumaker <lukeshu@lukeshu.com> | 2025-01-19 15:43:34 -0700 |
commit | 7d96ae649f38f40ed22fc7d371d2dacfc989eef0 (patch) | |
tree | 89876a2b90307a180bd042575d1a58d1c9cb5b02 /lib9p/idl | |
parent | 7c2c347c7735d3ea96e4a101e6278d68ebff0b5e (diff) |
lib9p: idl.gen: Track min and max object sizes
Diffstat (limited to 'lib9p/idl')
-rw-r--r-- | lib9p/idl/__init__.py | 89 |
1 files changed, 85 insertions, 4 deletions
diff --git a/lib9p/idl/__init__.py b/lib9p/idl/__init__.py index ab45ed0..8fd7e25 100644 --- a/lib9p/idl/__init__.py +++ b/lib9p/idl/__init__.py @@ -43,6 +43,12 @@ class Primitive(enum.Enum): def static_size(self) -> int: return self.value + def min_size(self, version: str) -> int: + return self.value + + def max_size(self, version: str) -> int: + return self.value + class Number: name: str @@ -60,6 +66,12 @@ class Number: def static_size(self) -> int: return self.prim.static_size + def min_size(self, version: str) -> int: + return self.static_size + + def max_size(self, version: str) -> int: + return self.static_size + class BitfieldVal: name: str @@ -88,6 +100,12 @@ class Bitfield: def static_size(self) -> int: return self.prim.static_size + def min_size(self, version: str) -> int: + return self.static_size + + def max_size(self, version: str) -> int: + return self.static_size + def bit_is_valid(self, bit: str | int, ver: str | None = None) -> bool: """Return whether the given bit is valid in the given protocol version. @@ -137,7 +155,7 @@ class Expr: class StructMember: # from left-to-right when parsing - cnt: str | None = None + cnt: "StructMember | None" = None name: str typ: "Type" max: Expr @@ -146,11 +164,58 @@ class StructMember: in_versions: set[str] @property + def min_cnt(self) -> int: + assert self.cnt + if not isinstance(self.cnt.typ, Primitive): + raise ValueError( + f"list count must be an integer type: {repr(self.cnt.name)}" + ) + if self.cnt.val: # TODO: allow this? + raise ValueError(f"list count may not have ,val=: {repr(self.cnt.name)}") + return 0 + + @property + def max_cnt(self) -> int: + assert self.cnt + if not isinstance(self.cnt.typ, Primitive): + raise ValueError( + f"list count must be an integer type: {repr(self.cnt.name)}" + ) + if self.cnt.val: # TODO: allow this? + raise ValueError(f"list count may not have ,val=: {repr(self.cnt.name)}") + if self.cnt.max: + # TODO: be more flexible? + if len(self.cnt.max.tokens) != 1: + raise ValueError( + f"list count ,max= may only have 1 token: {repr(self.cnt.name)}" + ) + match tok := self.cnt.max.tokens[0]: + case ExprLit(): + return tok.val + case ExprSym(name="s32_max"): + return (1 << 31) - 1 + case ExprSym(name="s64_max"): + return (1 << 63) - 1 + case _: + raise ValueError( + f'list count ,max= only allows literal, "s32_max", and "s64_max" tokens: {repr(self.cnt.name)}' + ) + return (1 << (self.cnt.typ.value * 8)) - 1 + + @property def static_size(self) -> int | None: if self.cnt: return None return self.typ.static_size + def min_size(self, version: str) -> int: + cnt = self.min_cnt if self.cnt else 1 + return cnt * self.typ.min_size(version) + + def max_size(self, version: str) -> int: + cnt = self.max_cnt if self.cnt else 1 + return cnt * self.typ.max_size(version) + class Struct: name: str @@ -165,12 +230,28 @@ class Struct: def static_size(self) -> int | None: size = 0 for member in self.members: + if member.in_versions < self.in_versions: + return None msize = member.static_size if msize is None: return None size += msize return size + def min_size(self, version: str) -> int: + return sum( + member.min_size(version) + for member in self.members + if (version in member.in_versions) + ) + + def max_size(self, version: str) -> int: + return sum( + member.max_size(version) + for member in self.members + if (version in member.in_versions) + ) + class Message(Struct): @property @@ -289,9 +370,9 @@ def parse_members(ver: str, env: dict[str, Type], struct: Struct, specs: str) -> if cnt := m.group("cnt"): if len(struct.members) == 0 or struct.members[-1].name != cnt: raise ValueError(f"list count must be previous item: {repr(cnt)}") - if not isinstance(struct.members[-1].typ, Primitive): - raise ValueError(f"list count must be an integer type: {repr(cnt)}") - member.cnt = cnt + cnt_mem = struct.members[-1] + member.cnt = cnt_mem + _ = member.max_cnt # force validation if maxstr := m.group("max"): if (not isinstance(member.typ, Primitive)) or member.cnt: |