Home | History | Annotate | Download | only in Lib
      1 """Implements (a subset of) Sun XDR -- eXternal Data Representation.
      2 
      3 See: RFC 1014
      4 
      5 """
      6 
      7 import struct
      8 try:
      9     from cStringIO import StringIO as _StringIO
     10 except ImportError:
     11     from StringIO import StringIO as _StringIO
     12 from functools import wraps
     13 
     14 __all__ = ["Error", "Packer", "Unpacker", "ConversionError"]
     15 
     16 # exceptions
     17 class Error(Exception):
     18     """Exception class for this module. Use:
     19 
     20     except xdrlib.Error, var:
     21         # var has the Error instance for the exception
     22 
     23     Public ivars:
     24         msg -- contains the message
     25 
     26     """
     27     def __init__(self, msg):
     28         self.msg = msg
     29     def __repr__(self):
     30         return repr(self.msg)
     31     def __str__(self):
     32         return str(self.msg)
     33 
     34 
     35 class ConversionError(Error):
     36     pass
     37 
     38 def raise_conversion_error(function):
     39     """ Wrap any raised struct.errors in a ConversionError. """
     40 
     41     @wraps(function)
     42     def result(self, value):
     43         try:
     44             return function(self, value)
     45         except struct.error as e:
     46             raise ConversionError(e.args[0])
     47     return result
     48 
     49 
     50 class Packer:
     51     """Pack various data representations into a buffer."""
     52 
     53     def __init__(self):
     54         self.reset()
     55 
     56     def reset(self):
     57         self.__buf = _StringIO()
     58 
     59     def get_buffer(self):
     60         return self.__buf.getvalue()
     61     # backwards compatibility
     62     get_buf = get_buffer
     63 
     64     @raise_conversion_error
     65     def pack_uint(self, x):
     66         self.__buf.write(struct.pack('>L', x))
     67 
     68     @raise_conversion_error
     69     def pack_int(self, x):
     70         self.__buf.write(struct.pack('>l', x))
     71 
     72     pack_enum = pack_int
     73 
     74     def pack_bool(self, x):
     75         if x: self.__buf.write('\0\0\0\1')
     76         else: self.__buf.write('\0\0\0\0')
     77 
     78     def pack_uhyper(self, x):
     79         try:
     80             self.pack_uint(x>>32 & 0xffffffffL)
     81         except (TypeError, struct.error) as e:
     82             raise ConversionError(e.args[0])
     83         try:
     84             self.pack_uint(x & 0xffffffffL)
     85         except (TypeError, struct.error) as e:
     86             raise ConversionError(e.args[0])
     87 
     88     pack_hyper = pack_uhyper
     89 
     90     @raise_conversion_error
     91     def pack_float(self, x):
     92         self.__buf.write(struct.pack('>f', x))
     93 
     94     @raise_conversion_error
     95     def pack_double(self, x):
     96         self.__buf.write(struct.pack('>d', x))
     97 
     98     def pack_fstring(self, n, s):
     99         if n < 0:
    100             raise ValueError, 'fstring size must be nonnegative'
    101         data = s[:n]
    102         n = ((n+3)//4)*4
    103         data = data + (n - len(data)) * '\0'
    104         self.__buf.write(data)
    105 
    106     pack_fopaque = pack_fstring
    107 
    108     def pack_string(self, s):
    109         n = len(s)
    110         self.pack_uint(n)
    111         self.pack_fstring(n, s)
    112 
    113     pack_opaque = pack_string
    114     pack_bytes = pack_string
    115 
    116     def pack_list(self, list, pack_item):
    117         for item in list:
    118             self.pack_uint(1)
    119             pack_item(item)
    120         self.pack_uint(0)
    121 
    122     def pack_farray(self, n, list, pack_item):
    123         if len(list) != n:
    124             raise ValueError, 'wrong array size'
    125         for item in list:
    126             pack_item(item)
    127 
    128     def pack_array(self, list, pack_item):
    129         n = len(list)
    130         self.pack_uint(n)
    131         self.pack_farray(n, list, pack_item)
    132 
    133 
    134 
    135 class Unpacker:
    136     """Unpacks various data representations from the given buffer."""
    137 
    138     def __init__(self, data):
    139         self.reset(data)
    140 
    141     def reset(self, data):
    142         self.__buf = data
    143         self.__pos = 0
    144 
    145     def get_position(self):
    146         return self.__pos
    147 
    148     def set_position(self, position):
    149         self.__pos = position
    150 
    151     def get_buffer(self):
    152         return self.__buf
    153 
    154     def done(self):
    155         if self.__pos < len(self.__buf):
    156             raise Error('unextracted data remains')
    157 
    158     def unpack_uint(self):
    159         i = self.__pos
    160         self.__pos = j = i+4
    161         data = self.__buf[i:j]
    162         if len(data) < 4:
    163             raise EOFError
    164         x = struct.unpack('>L', data)[0]
    165         try:
    166             return int(x)
    167         except OverflowError:
    168             return x
    169 
    170     def unpack_int(self):
    171         i = self.__pos
    172         self.__pos = j = i+4
    173         data = self.__buf[i:j]
    174         if len(data) < 4:
    175             raise EOFError
    176         return struct.unpack('>l', data)[0]
    177 
    178     unpack_enum = unpack_int
    179 
    180     def unpack_bool(self):
    181         return bool(self.unpack_int())
    182 
    183     def unpack_uhyper(self):
    184         hi = self.unpack_uint()
    185         lo = self.unpack_uint()
    186         return long(hi)<<32 | lo
    187 
    188     def unpack_hyper(self):
    189         x = self.unpack_uhyper()
    190         if x >= 0x8000000000000000L:
    191             x = x - 0x10000000000000000L
    192         return x
    193 
    194     def unpack_float(self):
    195         i = self.__pos
    196         self.__pos = j = i+4
    197         data = self.__buf[i:j]
    198         if len(data) < 4:
    199             raise EOFError
    200         return struct.unpack('>f', data)[0]
    201 
    202     def unpack_double(self):
    203         i = self.__pos
    204         self.__pos = j = i+8
    205         data = self.__buf[i:j]
    206         if len(data) < 8:
    207             raise EOFError
    208         return struct.unpack('>d', data)[0]
    209 
    210     def unpack_fstring(self, n):
    211         if n < 0:
    212             raise ValueError, 'fstring size must be nonnegative'
    213         i = self.__pos
    214         j = i + (n+3)//4*4
    215         if j > len(self.__buf):
    216             raise EOFError
    217         self.__pos = j
    218         return self.__buf[i:i+n]
    219 
    220     unpack_fopaque = unpack_fstring
    221 
    222     def unpack_string(self):
    223         n = self.unpack_uint()
    224         return self.unpack_fstring(n)
    225 
    226     unpack_opaque = unpack_string
    227     unpack_bytes = unpack_string
    228 
    229     def unpack_list(self, unpack_item):
    230         list = []
    231         while 1:
    232             x = self.unpack_uint()
    233             if x == 0: break
    234             if x != 1:
    235                 raise ConversionError, '0 or 1 expected, got %r' % (x,)
    236             item = unpack_item()
    237             list.append(item)
    238         return list
    239 
    240     def unpack_farray(self, n, unpack_item):
    241         list = []
    242         for i in range(n):
    243             list.append(unpack_item())
    244         return list
    245 
    246     def unpack_array(self, unpack_item):
    247         n = self.unpack_uint()
    248         return self.unpack_farray(n, unpack_item)
    249