summaryrefslogtreecommitdiff
path: root/lib9p/idl
diff options
context:
space:
mode:
authorLuke T. Shumaker <lukeshu@lukeshu.com>2025-01-19 15:53:46 -0700
committerLuke T. Shumaker <lukeshu@lukeshu.com>2025-01-19 15:53:46 -0700
commit104ea21b497171f5a1c4ba80d82337da3f7c2632 (patch)
tree9b5a167833b9caa4f8f829c9bc7a3711a1cd837a /lib9p/idl
parenta35db3be439c9a27f0763036cf3d4992ccf893eb (diff)
parent0ab9da9bc3c6cdaef00b7202ba03eff917b44c95 (diff)
Merge branch 'lukeshu/9p-tidy'
Diffstat (limited to 'lib9p/idl')
-rw-r--r--lib9p/idl/2012-9P2000.e.9p9
-rw-r--r--lib9p/idl/__init__.py89
2 files changed, 91 insertions, 7 deletions
diff --git a/lib9p/idl/2012-9P2000.e.9p b/lib9p/idl/2012-9P2000.e.9p
index c113618..aba4e66 100644
--- a/lib9p/idl/2012-9P2000.e.9p
+++ b/lib9p/idl/2012-9P2000.e.9p
@@ -1,6 +1,6 @@
# lib9p/idl/2012-9P2000.e.9p - Definitions of 9P2000.e messages
#
-# Copyright (C) 2024 Luke T. Shumaker <lukeshu@lukeshu.com>
+# Copyright (C) 2024-2025 Luke T. Shumaker <lukeshu@lukeshu.com>
# SPDX-License-Identifier: AGPL-3.0-or-later
# "9P2000.e" Erlang extension
@@ -10,9 +10,12 @@ version "9P2000.e"
from ./2002-9P2000.9p import *
+# like 9P2000 `d`, but without the s32_max limit
+struct d_e = "len[4] len*(dat[1])"
+
msg Tsession = "size[4,val=end-&size] typ[1,val=150] tag[tag] key[8]"
msg Rsession = "size[4,val=end-&size] typ[1,val=151] tag[tag]"
msg Tsread = "size[4,val=end-&size] typ[1,val=152] tag[tag] fid[4] nwname[2] nwname*(wname[s])"
-msg Rsread = "size[4,val=end-&size] typ[1,val=153] tag[tag] data[d]"
-msg Tswrite = "size[4,val=end-&size] typ[1,val=154] tag[tag] fid[4] nwname[2] nwname*(wname[s]) data[d]"
+msg Rsread = "size[4,val=end-&size] typ[1,val=153] tag[tag] data[d_e]"
+msg Tswrite = "size[4,val=end-&size] typ[1,val=154] tag[tag] fid[4] nwname[2] nwname*(wname[s]) data[d_e]"
msg Rswrite = "size[4,val=end-&size] typ[1,val=155] tag[tag] count[4]"
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: