blob: fb1e72da20d96d33594b8cc760030a8ae44c1ece [file] [log] [blame]
Guido van Rossum731630b1996-08-19 22:26:43 +00001"""Implements (a subset of) Sun XDR -- eXternal Data Representation.
2
3See: RFC 1014
4
5This module will conditionally use the _xdrmodule.so module to get
6support for those representations we can't do much with from Python.
7
8"""
9
10import struct
11from types import LongType
12
Guido van Rossum731630b1996-08-19 22:26:43 +000013# use C layer XDR libraries for some data types if available
14try:
15 import _xdr
16except ImportError:
17 _xdr = None
18
19# this test is done to see if machine representation is the same as
20# network representation. if so, we can use module struct for packing
21# some data types
Guido van Rossuma9b60d91996-08-26 22:34:57 +000022_USE_MACHINE_REP = (struct.pack('l', 1) == '\0\0\0\1')
Guido van Rossum731630b1996-08-19 22:26:43 +000023
24# exceptions
25class Error:
26 """Exception class for this module. Use:
27
28 except xdrlib.Error, var:
29 # var has the Error instance for the exception
30
31 Public ivars:
32 msg -- contains the message
33
34 """
35 def __init__(self, msg):
36 self.msg = msg
37 def __repr__(self):
38 return repr(self.msg)
39 def __str__(self):
40 return str(self.msg)
41
42
43class ConversionError(Error):
44 pass
45
46
47
48class Packer:
49 """Pack various data representations into a buffer."""
50
51 def __init__(self):
52 self.reset()
53
54 def reset(self):
55 self.__buf = ''
56
57 def get_buffer(self):
58 return self.__buf
59 # backwards compatibility
60 get_buf = get_buffer
61
62 def pack_uint(self, x):
63 self.__buf = self.__buf + \
64 (chr(int(x>>24 & 0xff)) + chr(int(x>>16 & 0xff)) + \
65 chr(int(x>>8 & 0xff)) + chr(int(x & 0xff)))
Guido van Rossuma9b60d91996-08-26 22:34:57 +000066 if _USE_MACHINE_REP:
Guido van Rossum731630b1996-08-19 22:26:43 +000067 def pack_uint(self, x):
68 if type(x) == LongType:
69 x = int((x + 0x80000000L) % 0x100000000L - 0x80000000L)
70 self.__buf = self.__buf + struct.pack('l', x)
71
72 pack_int = pack_uint
73 pack_enum = pack_int
74
75 def pack_bool(self, x):
76 if x: self.__buf = self.__buf + '\0\0\0\1'
77 else: self.__buf = self.__buf + '\0\0\0\0'
78
79 def pack_uhyper(self, x):
80 self.pack_uint(int(x>>32 & 0xffffffff))
81 self.pack_uint(int(x & 0xffffffff))
82
83 pack_hyper = pack_uhyper
84
85 def pack_float(self, x):
86 raise ConversionError('Not supported')
87 def pack_double(self, x):
88 raise ConversionError('Not supported')
89 # get these from the C layer if available
90 if _xdr:
91 def pack_float(self, x):
92 try: self.__buf = self.__buf + _xdr.pack_float(x)
93 except _xdr.error, msg:
94 raise ConversionError(msg)
95 def pack_double(self, x):
96 try: self.__buf = self.__buf + _xdr.pack_double(x)
97 except _xdr.error, msg:
98 raise ConversionError(msg)
99
100 def pack_fstring(self, n, s):
101 if n < 0:
102 raise ValueError, 'fstring size must be nonnegative'
103 n = ((n+3)/4)*4
104 data = s[:n]
105 data = data + (n - len(data)) * '\0'
106 self.__buf = self.__buf + data
107
108 pack_fopaque = pack_fstring
109
110 def pack_string(self, s):
111 n = len(s)
112 self.pack_uint(n)
113 self.pack_fstring(n, s)
114
115 pack_opaque = pack_string
116 pack_bytes = pack_string
117
118 def pack_list(self, list, pack_item):
119 for item in list:
120 self.pack_uint(1)
121 pack_item(item)
122 self.pack_uint(0)
123
124 def pack_farray(self, n, list, pack_item):
125 if len(list) <> n:
126 raise ValueError, 'wrong array size'
127 for item in list:
128 pack_item(item)
129
130 def pack_array(self, list, pack_item):
131 n = len(list)
132 self.pack_uint(n)
133 self.pack_farray(n, list, pack_item)
134
135
136
137class Unpacker:
138 """Unpacks various data representations from the given buffer."""
139
140 def __init__(self, data):
141 self.reset(data)
142
143 def reset(self, data):
144 self.__buf = data
145 self.__pos = 0
146
147 def get_position(self):
148 return self.__pos
149
150 def set_position(self, position):
151 self.__pos = position
152
Barry Warsaw75eccc51996-12-04 22:04:39 +0000153 def get_buffer(self):
154 return self.__buf
155
Guido van Rossum731630b1996-08-19 22:26:43 +0000156 def done(self):
157 if self.__pos < len(self.__buf):
158 raise Error('unextracted data remains')
159
160 def unpack_uint(self):
161 i = self.__pos
162 self.__pos = j = i+4
163 data = self.__buf[i:j]
164 if len(data) < 4:
165 raise EOFError
166 x = long(ord(data[0]))<<24 | ord(data[1])<<16 | \
167 ord(data[2])<<8 | ord(data[3])
168 # Return a Python long only if the value is not representable
169 # as a nonnegative Python int
170 if x < 0x80000000L:
171 x = int(x)
172 return x
Guido van Rossuma9b60d91996-08-26 22:34:57 +0000173 if _USE_MACHINE_REP:
Guido van Rossum731630b1996-08-19 22:26:43 +0000174 def unpack_uint(self):
175 i = self.__pos
176 self.__pos = j = i+4
177 data = self.__buf[i:j]
178 if len(data) < 4:
179 raise EOFError
180 return struct.unpack('l', data)[0]
181
182 def unpack_int(self):
183 x = self.unpack_uint()
184 if x >= 0x80000000L:
185 x = x - 0x100000000L
186 return int(x)
187
188 unpack_enum = unpack_int
189 unpack_bool = unpack_int
190
191 def unpack_uhyper(self):
192 hi = self.unpack_uint()
193 lo = self.unpack_uint()
194 return long(hi)<<32 | lo
195
196 def unpack_hyper(self):
197 x = self.unpack_uhyper()
198 if x >= 0x8000000000000000L:
199 x = x - 0x10000000000000000L
200 return x
201
202 def unpack_float(self):
203 raise ConversionError('Not supported')
204 def unpack_double(self):
205 raise ConversionError('Not supported')
206 # get these from the C layer if available
207 if _xdr:
208 def unpack_float(self):
209 i = self.__pos
210 self.__pos = j = i+4
211 data = self.__buf[i:j]
212 if len(data) < 4:
213 raise EOFError
214 try: return _xdr.unpack_float(data)
215 except _xdr.error, msg:
216 raise ConversionError(msg)
217
218 def unpack_double(self):
219 i = self.__pos
220 self.__pos = j = i+8
221 data = self.__buf[i:j]
222 if len(data) < 8:
223 raise EOFError
224 try: return _xdr.unpack_double(data)
225 except _xdr.error, msg:
226 raise ConversionError(msg)
227
228 def unpack_fstring(self, n):
229 if n < 0:
230 raise ValueError, 'fstring size must be nonnegative'
231 i = self.__pos
232 j = i + (n+3)/4*4
233 if j > len(self.__buf):
234 raise EOFError
235 self.__pos = j
236 return self.__buf[i:i+n]
237
238 unpack_fopaque = unpack_fstring
239
240 def unpack_string(self):
241 n = self.unpack_uint()
242 return self.unpack_fstring(n)
243
244 unpack_opaque = unpack_string
245 unpack_bytes = unpack_string
246
247 def unpack_list(self, unpack_item):
248 list = []
249 while 1:
250 x = self.unpack_uint()
251 if x == 0: break
252 if x <> 1:
253 raise ConversionError('0 or 1 expected, got ' + `x`)
254 item = unpack_item()
255 list.append(item)
256 return list
257
258 def unpack_farray(self, n, unpack_item):
259 list = []
260 for i in range(n):
261 list.append(unpack_item())
262 return list
263
264 def unpack_array(self, unpack_item):
265 n = self.unpack_uint()
266 return self.unpack_farray(n, unpack_item)
267
268
269# test suite
Guido van Rossum72fba791996-08-19 22:49:35 +0000270def _test():
Guido van Rossum731630b1996-08-19 22:26:43 +0000271 p = Packer()
272 packtest = [
273 (p.pack_uint, (9,)),
274 (p.pack_bool, (None,)),
275 (p.pack_bool, ('hello',)),
276 (p.pack_uhyper, (45L,)),
277 (p.pack_float, (1.9,)),
278 (p.pack_double, (1.9,)),
279 (p.pack_string, ('hello world',)),
280 (p.pack_list, (range(5), p.pack_uint)),
281 (p.pack_array, (['what', 'is', 'hapnin', 'doctor'], p.pack_string)),
282 ]
283 succeedlist = [1] * len(packtest)
284 count = 0
285 for method, args in packtest:
286 print 'pack test', count,
287 try:
288 apply(method, args)
289 print 'succeeded'
290 except ConversionError, var:
291 print 'ConversionError:', var.msg
292 succeedlist[count] = 0
293 count = count + 1
294 data = p.get_buffer()
295 # now verify
296 up = Unpacker(data)
297 unpacktest = [
298 (up.unpack_uint, (), lambda x: x == 9),
299 (up.unpack_bool, (), lambda x: not x),
300 (up.unpack_bool, (), lambda x: x),
301 (up.unpack_uhyper, (), lambda x: x == 45L),
302 (up.unpack_float, (), lambda x: 1.89 < x < 1.91),
303 (up.unpack_double, (), lambda x: 1.89 < x < 1.91),
304 (up.unpack_string, (), lambda x: x == 'hello world'),
305 (up.unpack_list, (up.unpack_uint,), lambda x: x == range(5)),
306 (up.unpack_array, (up.unpack_string,),
307 lambda x: x == ['what', 'is', 'hapnin', 'doctor']),
308 ]
309 count = 0
310 for method, args, pred in unpacktest:
311 print 'unpack test', count,
312 try:
313 if succeedlist[count]:
314 x = apply(method, args)
315 print pred(x) and 'succeeded' or 'failed', ':', x
316 else:
317 print 'skipping'
318 except ConversionError, var:
319 print 'ConversionError:', var.msg
320 count = count + 1
321
322if __name__ == '__main__':
Guido van Rossum72fba791996-08-19 22:49:35 +0000323 _test()