@@ -30,14 +30,67 @@ class Serializer(ABC, Generic[Serializable]):
3030 def serialize (self , _ : Serializable ) -> bytes :
3131 pass
3232
33- # Unfortunately xdrlib is deprecated in Python 3.11, so we implement the following serialization methods
34- # to be used by descendants of the Serializer class.
35- # See https://datatracker.ietf.org/doc/html/rfc1014 for the XDR specification.
36- def _write_uint32 (self , i : int ) -> bytes :
33+
34+ class Deserializer (ABC , Generic [Serializable ]):
35+ def deserialize_from_bytes (self , payload : bytes ) -> Serializable :
36+ return self .deserialize (io .BytesIO (payload ))
37+
38+ @abstractmethod
39+ def deserialize (self , _ : io .BytesIO ) -> Serializable :
40+ pass
41+
42+
43+ # Unfortunately xdrlib is deprecated in Python 3.11, so we implement the following serialization classes
44+ # See https://datatracker.ietf.org/doc/html/rfc1014 for the XDR specification.
45+
46+
47+ class Int32Serializer (Serializer [int ], Deserializer [int ]):
48+ def serialize (self , i : int ) -> bytes :
49+ return i .to_bytes (length = 4 , byteorder = "big" , signed = True )
50+
51+ def deserialize (self , payload : io .BytesIO ) -> int :
52+ return int .from_bytes (payload .read (4 ), byteorder = "big" , signed = True )
53+
54+
55+ class UInt32Serializer (Serializer [int ], Deserializer [int ]):
56+ def serialize (self , i : int ) -> bytes :
3757 return i .to_bytes (length = 4 , byteorder = "big" , signed = False )
3858
59+ def deserialize (self , payload : io .BytesIO ) -> int :
60+ return int .from_bytes (payload .read (4 ), byteorder = "big" , signed = False )
61+
62+
63+ class OpaqueVarLengthSerializer (Serializer [bytes ], Deserializer [bytes ]):
64+ def serialize (self , body : bytes ) -> bytes :
65+ length = len (body )
66+ result = UInt32Serializer ().serialize (length )
67+ result += body
68+
69+ padding_bytes = (ALIGNMENT - (length % ALIGNMENT )) % ALIGNMENT
70+ return result + b"\x00 " * padding_bytes
71+
72+ def deserialize (self , payload : io .BytesIO ) -> bytes :
73+ length = UInt32Serializer ().deserialize (payload )
74+ result = payload .read (length )
75+ padding_bytes = (ALIGNMENT - (length % ALIGNMENT )) % ALIGNMENT
76+ payload .read (padding_bytes )
77+ return result
78+
79+
80+ class StringSerializer (Serializer [str ], Deserializer [str ]):
81+ def serialize (self , s : str ) -> bytes :
82+ return OpaqueVarLengthSerializer ().serialize (s .encode ("ascii" ))
83+
84+ def deserialize (self , payload : io .BytesIO ) -> str :
85+ return OpaqueVarLengthSerializer ().deserialize (payload ).decode ("ascii" )
86+
87+
88+ class XdrSerializer (Generic [Serializable ], Serializer [Serializable ]):
89+ def _write_uint32 (self , i : int ) -> bytes :
90+ return UInt32Serializer ().serialize (i )
91+
3992 def _write_int32 (self , i : int ) -> bytes :
40- return i . to_bytes ( length = 4 , byteorder = "big" , signed = True )
93+ return Int32Serializer (). serialize ( i )
4194
4295 def _write_uint64 (self , i : int ) -> bytes :
4396 return i .to_bytes (length = 8 , byteorder = "big" , signed = False )
@@ -51,33 +104,18 @@ def _write_var_length(self, elements: list[ElementType], serializer: Serializer[
51104 return result + b"" .join (payload )
52105
53106 def _write_var_length_opaque (self , body : bytes ) -> bytes :
54- length = len (body )
55- result = self ._write_uint32 (length )
56- result += body
57-
58- padding_bytes = (ALIGNMENT - (length % ALIGNMENT )) % ALIGNMENT
59- return result + b"\x00 " * padding_bytes
107+ return OpaqueVarLengthSerializer ().serialize (body )
60108
61109 def _write_string (self , s : str ) -> bytes :
62- return self . _write_var_length_opaque ( s . encode ( "ascii" ) )
110+ return StringSerializer (). serialize ( s )
63111
64112
65- class Deserializer (ABC , Generic [Serializable ]):
66- def deserialize_from_bytes (self , payload : bytes ) -> Serializable :
67- return self .deserialize (io .BytesIO (payload ))
68-
69- @abstractmethod
70- def deserialize (self , _ : io .BytesIO ) -> Serializable :
71- pass
72-
73- # Unfortunately xdrlib is deprecated in Python 3.11, so we implement the following serialization methods
74- # to be used by descendants of the Serializer class.
75- # See https://datatracker.ietf.org/doc/html/rfc1014 for the XDR specification.
113+ class XdrDeserializer (Generic [Serializable ], Deserializer [Serializable ]):
76114 def _read_uint32 (self , payload : io .BytesIO ) -> int :
77- return int . from_bytes ( payload . read ( 4 ), byteorder = "big" , signed = False )
115+ return UInt32Serializer (). deserialize ( payload )
78116
79117 def _read_int32 (self , payload : io .BytesIO ) -> int :
80- return int . from_bytes ( payload . read ( 4 ), byteorder = "big" , signed = True )
118+ return Int32Serializer (). deserialize ( payload )
81119
82120 def _read_uint64 (self , payload : io .BytesIO ) -> int :
83121 return int .from_bytes (payload .read (8 ), byteorder = "big" , signed = False )
@@ -86,19 +124,15 @@ def _read_enum(self, payload: io.BytesIO, enum: EnumType) -> EnumType:
86124 value = self ._read_int32 (payload )
87125 return enum (value )
88126
89- def _read_var_length_opaque (self , payload : io .BytesIO ) -> bytes :
90- length = self ._read_uint32 (payload )
91- result = payload .read (length )
92- padding_bytes = (ALIGNMENT - (length % ALIGNMENT )) % ALIGNMENT
93- payload .read (padding_bytes )
94- return result
95-
96127 def _read_var_length (self , payload : io .BytesIO , deserializer : Deserializer [ElementType ]) -> list [ElementType ]:
97128 length = self ._read_uint32 (payload )
98129 return [deserializer .deserialize (payload ) for _ in range (length )]
99130
131+ def _read_var_length_opaque (self , payload : io .BytesIO ) -> bytes :
132+ return OpaqueVarLengthSerializer ().deserialize (payload )
133+
100134 def _read_string (self , payload : io .BytesIO ) -> str :
101- return self . _read_var_length_opaque ( payload ). decode ( "ascii" )
135+ return StringSerializer (). deserialize ( payload )
102136
103137 def _read_optional (self , payload : io .BytesIO , deserializer : Deserializer [ElementType ]) -> ElementType | None :
104138 has_value = self ._read_enum (payload , sunrpc .Bool )
@@ -107,42 +141,6 @@ def _read_optional(self, payload: io.BytesIO, deserializer: Deserializer[Element
107141 return deserializer .deserialize (payload )
108142
109143
110- # RdJ: A bit clunky having to lift the primitives inside the Serializer/Deserializer class
111- # to enable composition.
112- # Possible design mistake, Alternatively, make serializers functions, since no state is kept.
113- # But most of our stuff is OOP, so it would be inconsistent.
114- class Int32Serializer (Serializer [int ], Deserializer [int ]):
115- def serialize (self , i : int ) -> bytes :
116- return self ._write_int32 (i )
117-
118- def deserialize (self , payload : io .BytesIO ) -> int :
119- return self ._read_int32 (payload )
120-
121-
122- class UInt32Serializer (Serializer [int ], Deserializer [int ]):
123- def serialize (self , i : int ) -> bytes :
124- return self ._write_uint32 (i )
125-
126- def deserialize (self , payload : io .BytesIO ) -> int :
127- return self ._read_uint32 (payload )
128-
129-
130- class StringSerializer (Serializer [str ], Deserializer [str ]):
131- def serialize (self , s : str ) -> bytes :
132- return self ._write_string (s )
133-
134- def deserialize (self , payload : io .BytesIO ) -> str :
135- return self ._read_string (payload )
136-
137-
138- class OpaqueVarLengthSerializer (Serializer [bytes ], Deserializer [bytes ]):
139- def serialize (self , body : bytes ) -> bytes :
140- return self ._write_var_length_opaque (body )
141-
142- def deserialize (self , payload : io .BytesIO ) -> bytes :
143- return self ._read_var_length_opaque (payload )
144-
145-
146144class ReplyStat (Enum ):
147145 MSG_ACCEPTED = 0
148146 MSG_DENIED = 1
@@ -155,7 +153,7 @@ class AuthFlavor(Enum):
155153 AUTH_DES = 3
156154
157155
158- class AuthSerializer (Generic [AuthProtocol ], Serializer [AuthProtocol ], Deserializer [AuthProtocol ]):
156+ class AuthSerializer (Generic [AuthProtocol ], XdrSerializer [AuthProtocol ], XdrDeserializer [AuthProtocol ]):
159157 def serialize (self , protocol : AuthProtocol ) -> bytes :
160158 flavor = self ._flavor ()
161159 result = self ._write_int32 (flavor )
@@ -219,13 +217,13 @@ def _read_body(self, payload: io.BytesIO) -> sunrpc.AuthUnix:
219217
220218class MessageSerializer (
221219 Generic [ProcedureParams , ProcedureResults , Credentials , Verifier ],
222- Serializer [sunrpc .Message [ProcedureParams , ProcedureResults , Credentials , Verifier ]],
223- Deserializer [sunrpc .Message [ProcedureParams , ProcedureResults , Credentials , Verifier ]],
220+ XdrSerializer [sunrpc .Message [ProcedureParams , ProcedureResults , Credentials , Verifier ]],
221+ XdrDeserializer [sunrpc .Message [ProcedureParams , ProcedureResults , Credentials , Verifier ]],
224222):
225223 def __init__ (
226224 self ,
227- paramsSerializer : Serializer [ProcedureParams ],
228- resultsDeserializer : Deserializer [ProcedureResults ],
225+ paramsSerializer : XdrSerializer [ProcedureParams ],
226+ resultsDeserializer : XdrDeserializer [ProcedureResults ],
229227 credentialsSerializer : AuthSerializer [Credentials ],
230228 verifierSerializer : AuthSerializer [Verifier ],
231229 ):
@@ -296,7 +294,7 @@ def _read_mismatch(self, payload: io.BytesIO) -> sunrpc.Mismatch:
296294 return sunrpc .Mismatch (low , high )
297295
298296
299- class PortMappingSerializer (Serializer [sunrpc .PortMapping ]):
297+ class PortMappingSerializer (XdrSerializer [sunrpc .PortMapping ]):
300298 def serialize (self , port_mapping : sunrpc .PortMapping ) -> bytes :
301299 result = self ._write_uint32 (port_mapping .program )
302300 result += self ._write_uint32 (port_mapping .version )
0 commit comments