hgext/fsmonitor/pywatchman/pybser.py
changeset 30656 16f4b341288d
parent 28432 2377c4ac4eec
child 41365 876494fd967d
equal deleted inserted replaced
30655:f35397fe0c04 30656:16f4b341288d
    24 # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
    24 # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
    25 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
    25 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
    26 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    26 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    28 
    28 
       
    29 from __future__ import absolute_import
       
    30 from __future__ import division
       
    31 from __future__ import print_function
       
    32 # no unicode literals
       
    33 
       
    34 import binascii
    29 import collections
    35 import collections
    30 import ctypes
    36 import ctypes
    31 import struct
    37 import struct
    32 import sys
    38 import sys
    33 
    39 
    34 BSER_ARRAY = '\x00'
    40 from . import (
    35 BSER_OBJECT = '\x01'
    41     compat,
    36 BSER_STRING = '\x02'
    42 )
    37 BSER_INT8 = '\x03'
    43 
    38 BSER_INT16 = '\x04'
    44 BSER_ARRAY = b'\x00'
    39 BSER_INT32 = '\x05'
    45 BSER_OBJECT = b'\x01'
    40 BSER_INT64 = '\x06'
    46 BSER_BYTESTRING = b'\x02'
    41 BSER_REAL = '\x07'
    47 BSER_INT8 = b'\x03'
    42 BSER_TRUE = '\x08'
    48 BSER_INT16 = b'\x04'
    43 BSER_FALSE = '\x09'
    49 BSER_INT32 = b'\x05'
    44 BSER_NULL = '\x0a'
    50 BSER_INT64 = b'\x06'
    45 BSER_TEMPLATE = '\x0b'
    51 BSER_REAL = b'\x07'
    46 BSER_SKIP = '\x0c'
    52 BSER_TRUE = b'\x08'
       
    53 BSER_FALSE = b'\x09'
       
    54 BSER_NULL = b'\x0a'
       
    55 BSER_TEMPLATE = b'\x0b'
       
    56 BSER_SKIP = b'\x0c'
       
    57 BSER_UTF8STRING = b'\x0d'
       
    58 
       
    59 if compat.PYTHON3:
       
    60     STRING_TYPES = (str, bytes)
       
    61     unicode = str
       
    62     def tobytes(i):
       
    63         return str(i).encode('ascii')
       
    64     long = int
       
    65 else:
       
    66     STRING_TYPES = (unicode, str)
       
    67     tobytes = bytes
    47 
    68 
    48 # Leave room for the serialization header, which includes
    69 # Leave room for the serialization header, which includes
    49 # our overall length.  To make things simpler, we'll use an
    70 # our overall length.  To make things simpler, we'll use an
    50 # int32 for the header
    71 # int32 for the header
    51 EMPTY_HEADER = "\x00\x01\x05\x00\x00\x00\x00"
    72 EMPTY_HEADER = b"\x00\x01\x05\x00\x00\x00\x00"
    52 
    73 EMPTY_HEADER_V2 = b"\x00\x02\x00\x00\x00\x00\x05\x00\x00\x00\x00"
    53 # Python 3 conditional for supporting Python 2's int/long types
       
    54 if sys.version_info > (3,):
       
    55     long = int
       
    56 
    74 
    57 def _int_size(x):
    75 def _int_size(x):
    58     """Return the smallest size int that can store the value"""
    76     """Return the smallest size int that can store the value"""
    59     if -0x80 <= x <= 0x7F:
    77     if -0x80 <= x <= 0x7F:
    60         return 1
    78         return 1
    65     elif long(-0x8000000000000000) <= x <= long(0x7FFFFFFFFFFFFFFF):
    83     elif long(-0x8000000000000000) <= x <= long(0x7FFFFFFFFFFFFFFF):
    66         return 8
    84         return 8
    67     else:
    85     else:
    68         raise RuntimeError('Cannot represent value: ' + str(x))
    86         raise RuntimeError('Cannot represent value: ' + str(x))
    69 
    87 
       
    88 def _buf_pos(buf, pos):
       
    89     ret = buf[pos]
       
    90     # In Python 2, buf is a str array so buf[pos] is a string. In Python 3, buf
       
    91     # is a bytes array and buf[pos] is an integer.
       
    92     if compat.PYTHON3:
       
    93         ret = bytes((ret,))
       
    94     return ret
    70 
    95 
    71 class _bser_buffer(object):
    96 class _bser_buffer(object):
    72 
    97 
    73     def __init__(self):
    98     def __init__(self, version):
       
    99         self.bser_version = version
    74         self.buf = ctypes.create_string_buffer(8192)
   100         self.buf = ctypes.create_string_buffer(8192)
    75         struct.pack_into(str(len(EMPTY_HEADER)) + 's', self.buf, 0, EMPTY_HEADER)
   101         if self.bser_version == 1:
    76         self.wpos = len(EMPTY_HEADER)
   102             struct.pack_into(tobytes(len(EMPTY_HEADER)) + b's', self.buf, 0,
       
   103                              EMPTY_HEADER)
       
   104             self.wpos = len(EMPTY_HEADER)
       
   105         else:
       
   106             assert self.bser_version == 2
       
   107             struct.pack_into(tobytes(len(EMPTY_HEADER_V2)) + b's', self.buf, 0,
       
   108                              EMPTY_HEADER_V2)
       
   109             self.wpos = len(EMPTY_HEADER_V2)
    77 
   110 
    78     def ensure_size(self, size):
   111     def ensure_size(self, size):
    79         while ctypes.sizeof(self.buf) - self.wpos < size:
   112         while ctypes.sizeof(self.buf) - self.wpos < size:
    80             ctypes.resize(self.buf, ctypes.sizeof(self.buf) * 2)
   113             ctypes.resize(self.buf, ctypes.sizeof(self.buf) * 2)
    81 
   114 
    82     def append_long(self, val):
   115     def append_long(self, val):
    83         size = _int_size(val)
   116         size = _int_size(val)
    84         to_write = size + 1
   117         to_write = size + 1
    85         self.ensure_size(to_write)
   118         self.ensure_size(to_write)
    86         if size == 1:
   119         if size == 1:
    87             struct.pack_into('=cb', self.buf, self.wpos, BSER_INT8, val)
   120             struct.pack_into(b'=cb', self.buf, self.wpos, BSER_INT8, val)
    88         elif size == 2:
   121         elif size == 2:
    89             struct.pack_into('=ch', self.buf, self.wpos, BSER_INT16, val)
   122             struct.pack_into(b'=ch', self.buf, self.wpos, BSER_INT16, val)
    90         elif size == 4:
   123         elif size == 4:
    91             struct.pack_into('=ci', self.buf, self.wpos, BSER_INT32, val)
   124             struct.pack_into(b'=ci', self.buf, self.wpos, BSER_INT32, val)
    92         elif size == 8:
   125         elif size == 8:
    93             struct.pack_into('=cq', self.buf, self.wpos, BSER_INT64, val)
   126             struct.pack_into(b'=cq', self.buf, self.wpos, BSER_INT64, val)
    94         else:
   127         else:
    95             raise RuntimeError('Cannot represent this long value')
   128             raise RuntimeError('Cannot represent this long value')
    96         self.wpos += to_write
   129         self.wpos += to_write
    97 
   130 
    98 
   131 
   102         s_len = len(s)
   135         s_len = len(s)
   103         size = _int_size(s_len)
   136         size = _int_size(s_len)
   104         to_write = 2 + size + s_len
   137         to_write = 2 + size + s_len
   105         self.ensure_size(to_write)
   138         self.ensure_size(to_write)
   106         if size == 1:
   139         if size == 1:
   107             struct.pack_into('=ccb' + str(s_len) + 's', self.buf, self.wpos, BSER_STRING, BSER_INT8, s_len, s)
   140             struct.pack_into(b'=ccb' + tobytes(s_len) + b's', self.buf,
       
   141                 self.wpos, BSER_BYTESTRING, BSER_INT8, s_len, s)
   108         elif size == 2:
   142         elif size == 2:
   109             struct.pack_into('=cch' + str(s_len) + 's', self.buf, self.wpos, BSER_STRING, BSER_INT16, s_len, s)
   143             struct.pack_into(b'=cch' + tobytes(s_len) + b's', self.buf,
       
   144                 self.wpos, BSER_BYTESTRING, BSER_INT16, s_len, s)
   110         elif size == 4:
   145         elif size == 4:
   111             struct.pack_into('=cci' + str(s_len) + 's', self.buf, self.wpos, BSER_STRING, BSER_INT32, s_len, s)
   146             struct.pack_into(b'=cci' + tobytes(s_len) + b's', self.buf,
       
   147                 self.wpos, BSER_BYTESTRING, BSER_INT32, s_len, s)
   112         elif size == 8:
   148         elif size == 8:
   113             struct.pack_into('=ccq' + str(s_len) + 's', self.buf, self.wpos, BSER_STRING, BSER_INT64, s_len, s)
   149             struct.pack_into(b'=ccq' + tobytes(s_len) + b's', self.buf,
       
   150                 self.wpos, BSER_BYTESTRING, BSER_INT64, s_len, s)
   114         else:
   151         else:
   115             raise RuntimeError('Cannot represent this string value')
   152             raise RuntimeError('Cannot represent this string value')
   116         self.wpos += to_write
   153         self.wpos += to_write
   117 
   154 
   118 
   155 
   122             self.ensure_size(needed)
   159             self.ensure_size(needed)
   123             if val:
   160             if val:
   124                 to_encode = BSER_TRUE
   161                 to_encode = BSER_TRUE
   125             else:
   162             else:
   126                 to_encode = BSER_FALSE
   163                 to_encode = BSER_FALSE
   127             struct.pack_into('=c', self.buf, self.wpos, to_encode)
   164             struct.pack_into(b'=c', self.buf, self.wpos, to_encode)
   128             self.wpos += needed
   165             self.wpos += needed
   129         elif val is None:
   166         elif val is None:
   130             needed = 1
   167             needed = 1
   131             self.ensure_size(needed)
   168             self.ensure_size(needed)
   132             struct.pack_into('=c', self.buf, self.wpos, BSER_NULL)
   169             struct.pack_into(b'=c', self.buf, self.wpos, BSER_NULL)
   133             self.wpos += needed
   170             self.wpos += needed
   134         elif isinstance(val, (int, long)):
   171         elif isinstance(val, (int, long)):
   135             self.append_long(val)
   172             self.append_long(val)
   136         elif isinstance(val, (str, unicode)):
   173         elif isinstance(val, STRING_TYPES):
   137             self.append_string(val)
   174             self.append_string(val)
   138         elif isinstance(val, float):
   175         elif isinstance(val, float):
   139             needed = 9
   176             needed = 9
   140             self.ensure_size(needed)
   177             self.ensure_size(needed)
   141             struct.pack_into('=cd', self.buf, self.wpos, BSER_REAL, val)
   178             struct.pack_into(b'=cd', self.buf, self.wpos, BSER_REAL, val)
   142             self.wpos += needed
   179             self.wpos += needed
   143         elif isinstance(val, collections.Mapping) and isinstance(val, collections.Sized):
   180         elif isinstance(val, collections.Mapping) and \
       
   181             isinstance(val, collections.Sized):
   144             val_len = len(val)
   182             val_len = len(val)
   145             size = _int_size(val_len)
   183             size = _int_size(val_len)
   146             needed = 2 + size
   184             needed = 2 + size
   147             self.ensure_size(needed)
   185             self.ensure_size(needed)
   148             if size == 1:
   186             if size == 1:
   149                 struct.pack_into('=ccb', self.buf, self.wpos, BSER_OBJECT, BSER_INT8, val_len)
   187                 struct.pack_into(b'=ccb', self.buf, self.wpos, BSER_OBJECT,
       
   188                     BSER_INT8, val_len)
   150             elif size == 2:
   189             elif size == 2:
   151                 struct.pack_into('=cch', self.buf, self.wpos, BSER_OBJECT, BSER_INT16, val_len)
   190                 struct.pack_into(b'=cch', self.buf, self.wpos, BSER_OBJECT,
       
   191                     BSER_INT16, val_len)
   152             elif size == 4:
   192             elif size == 4:
   153                 struct.pack_into('=cci', self.buf, self.wpos, BSER_OBJECT, BSER_INT32, val_len)
   193                 struct.pack_into(b'=cci', self.buf, self.wpos, BSER_OBJECT,
       
   194                     BSER_INT32, val_len)
   154             elif size == 8:
   195             elif size == 8:
   155                 struct.pack_into('=ccq', self.buf, self.wpos, BSER_OBJECT, BSER_INT64, val_len)
   196                 struct.pack_into(b'=ccq', self.buf, self.wpos, BSER_OBJECT,
       
   197                     BSER_INT64, val_len)
   156             else:
   198             else:
   157                 raise RuntimeError('Cannot represent this mapping value')
   199                 raise RuntimeError('Cannot represent this mapping value')
   158             self.wpos += needed
   200             self.wpos += needed
   159             for k, v in val.iteritems():
   201             if compat.PYTHON3:
       
   202                 iteritems = val.items()
       
   203             else:
       
   204                 iteritems = val.iteritems()
       
   205             for k, v in iteritems:
   160                 self.append_string(k)
   206                 self.append_string(k)
   161                 self.append_recursive(v)
   207                 self.append_recursive(v)
   162         elif isinstance(val, collections.Iterable) and isinstance(val, collections.Sized):
   208         elif isinstance(val, collections.Iterable) and \
       
   209             isinstance(val, collections.Sized):
   163             val_len = len(val)
   210             val_len = len(val)
   164             size = _int_size(val_len)
   211             size = _int_size(val_len)
   165             needed = 2 + size
   212             needed = 2 + size
   166             self.ensure_size(needed)
   213             self.ensure_size(needed)
   167             if size == 1:
   214             if size == 1:
   168                 struct.pack_into('=ccb', self.buf, self.wpos, BSER_ARRAY, BSER_INT8, val_len)
   215                 struct.pack_into(b'=ccb', self.buf, self.wpos, BSER_ARRAY,
       
   216                     BSER_INT8, val_len)
   169             elif size == 2:
   217             elif size == 2:
   170                 struct.pack_into('=cch', self.buf, self.wpos, BSER_ARRAY, BSER_INT16, val_len)
   218                 struct.pack_into(b'=cch', self.buf, self.wpos, BSER_ARRAY,
       
   219                     BSER_INT16, val_len)
   171             elif size == 4:
   220             elif size == 4:
   172                 struct.pack_into('=cci', self.buf, self.wpos, BSER_ARRAY, BSER_INT32, val_len)
   221                 struct.pack_into(b'=cci', self.buf, self.wpos, BSER_ARRAY,
       
   222                     BSER_INT32, val_len)
   173             elif size == 8:
   223             elif size == 8:
   174                 struct.pack_into('=ccq', self.buf, self.wpos, BSER_ARRAY, BSER_INT64, val_len)
   224                 struct.pack_into(b'=ccq', self.buf, self.wpos, BSER_ARRAY,
       
   225                     BSER_INT64, val_len)
   175             else:
   226             else:
   176                 raise RuntimeError('Cannot represent this sequence value')
   227                 raise RuntimeError('Cannot represent this sequence value')
   177             self.wpos += needed
   228             self.wpos += needed
   178             for v in val:
   229             for v in val:
   179                 self.append_recursive(v)
   230                 self.append_recursive(v)
   180         else:
   231         else:
   181             raise RuntimeError('Cannot represent unknown value type')
   232             raise RuntimeError('Cannot represent unknown value type')
   182 
   233 
   183 
   234 
   184 def dumps(obj):
   235 def dumps(obj, version=1, capabilities=0):
   185     bser_buf = _bser_buffer()
   236     bser_buf = _bser_buffer(version=version)
   186     bser_buf.append_recursive(obj)
   237     bser_buf.append_recursive(obj)
   187     # Now fill in the overall length
   238     # Now fill in the overall length
   188     obj_len = bser_buf.wpos - len(EMPTY_HEADER)
   239     if version == 1:
   189     struct.pack_into('=i', bser_buf.buf, 3, obj_len)
   240         obj_len = bser_buf.wpos - len(EMPTY_HEADER)
       
   241         struct.pack_into(b'=i', bser_buf.buf, 3, obj_len)
       
   242     else:
       
   243         obj_len = bser_buf.wpos - len(EMPTY_HEADER_V2)
       
   244         struct.pack_into(b'=i', bser_buf.buf, 2, capabilities)
       
   245         struct.pack_into(b'=i', bser_buf.buf, 7, obj_len)
   190     return bser_buf.buf.raw[:bser_buf.wpos]
   246     return bser_buf.buf.raw[:bser_buf.wpos]
   191 
       
   192 
       
   193 def _bunser_int(buf, pos):
       
   194     try:
       
   195         int_type = buf[pos]
       
   196     except IndexError:
       
   197         raise ValueError('Invalid bser int encoding, pos out of range')
       
   198     if int_type == BSER_INT8:
       
   199         needed = 2
       
   200         fmt = '=b'
       
   201     elif int_type == BSER_INT16:
       
   202         needed = 3
       
   203         fmt = '=h'
       
   204     elif int_type == BSER_INT32:
       
   205         needed = 5
       
   206         fmt = '=i'
       
   207     elif int_type == BSER_INT64:
       
   208         needed = 9
       
   209         fmt = '=q'
       
   210     else:
       
   211         raise ValueError('Invalid bser int encoding 0x%02x' % int(int_type))
       
   212     int_val = struct.unpack_from(fmt, buf, pos + 1)[0]
       
   213     return (int_val, pos + needed)
       
   214 
       
   215 
       
   216 def _bunser_string(buf, pos):
       
   217     str_len, pos = _bunser_int(buf, pos + 1)
       
   218     str_val = struct.unpack_from(str(str_len) + 's', buf, pos)[0]
       
   219     return (str_val, pos + str_len)
       
   220 
       
   221 
       
   222 def _bunser_array(buf, pos, mutable=True):
       
   223     arr_len, pos = _bunser_int(buf, pos + 1)
       
   224     arr = []
       
   225     for i in range(arr_len):
       
   226         arr_item, pos = _bser_loads_recursive(buf, pos, mutable)
       
   227         arr.append(arr_item)
       
   228 
       
   229     if not mutable:
       
   230       arr = tuple(arr)
       
   231 
       
   232     return arr, pos
       
   233 
       
   234 
   247 
   235 # This is a quack-alike with the bserObjectType in bser.c
   248 # This is a quack-alike with the bserObjectType in bser.c
   236 # It provides by getattr accessors and getitem for both index
   249 # It provides by getattr accessors and getitem for both index
   237 # and name.
   250 # and name.
   238 class _BunserDict(object):
   251 class _BunserDict(object):
   258             raise KeyError('_BunserDict has no key %s' % key)
   271             raise KeyError('_BunserDict has no key %s' % key)
   259 
   272 
   260     def __len__(self):
   273     def __len__(self):
   261         return len(self._keys)
   274         return len(self._keys)
   262 
   275 
   263 def _bunser_object(buf, pos, mutable=True):
   276 class Bunser(object):
   264     obj_len, pos = _bunser_int(buf, pos + 1)
   277     def __init__(self, mutable=True, value_encoding=None, value_errors=None):
   265     if mutable:
   278         self.mutable = mutable
   266         obj = {}
   279         self.value_encoding = value_encoding
       
   280 
       
   281         if value_encoding is None:
       
   282             self.value_errors = None
       
   283         elif value_errors is None:
       
   284             self.value_errors = 'strict'
       
   285         else:
       
   286             self.value_errors = value_errors
       
   287 
       
   288     @staticmethod
       
   289     def unser_int(buf, pos):
       
   290         try:
       
   291             int_type = _buf_pos(buf, pos)
       
   292         except IndexError:
       
   293             raise ValueError('Invalid bser int encoding, pos out of range')
       
   294         if int_type == BSER_INT8:
       
   295             needed = 2
       
   296             fmt = b'=b'
       
   297         elif int_type == BSER_INT16:
       
   298             needed = 3
       
   299             fmt = b'=h'
       
   300         elif int_type == BSER_INT32:
       
   301             needed = 5
       
   302             fmt = b'=i'
       
   303         elif int_type == BSER_INT64:
       
   304             needed = 9
       
   305             fmt = b'=q'
       
   306         else:
       
   307             raise ValueError('Invalid bser int encoding 0x%s' %
       
   308                              binascii.hexlify(int_type).decode('ascii'))
       
   309         int_val = struct.unpack_from(fmt, buf, pos + 1)[0]
       
   310         return (int_val, pos + needed)
       
   311 
       
   312     def unser_utf8_string(self, buf, pos):
       
   313         str_len, pos = self.unser_int(buf, pos + 1)
       
   314         str_val = struct.unpack_from(tobytes(str_len) + b's', buf, pos)[0]
       
   315         return (str_val.decode('utf-8'), pos + str_len)
       
   316 
       
   317     def unser_bytestring(self, buf, pos):
       
   318         str_len, pos = self.unser_int(buf, pos + 1)
       
   319         str_val = struct.unpack_from(tobytes(str_len) + b's', buf, pos)[0]
       
   320         if self.value_encoding is not None:
       
   321             str_val = str_val.decode(self.value_encoding, self.value_errors)
       
   322             # str_len stays the same because that's the length in bytes
       
   323         return (str_val, pos + str_len)
       
   324 
       
   325     def unser_array(self, buf, pos):
       
   326         arr_len, pos = self.unser_int(buf, pos + 1)
       
   327         arr = []
       
   328         for i in range(arr_len):
       
   329             arr_item, pos = self.loads_recursive(buf, pos)
       
   330             arr.append(arr_item)
       
   331 
       
   332         if not self.mutable:
       
   333           arr = tuple(arr)
       
   334 
       
   335         return arr, pos
       
   336 
       
   337     def unser_object(self, buf, pos):
       
   338         obj_len, pos = self.unser_int(buf, pos + 1)
       
   339         if self.mutable:
       
   340             obj = {}
       
   341         else:
       
   342             keys = []
       
   343             vals = []
       
   344 
       
   345         for i in range(obj_len):
       
   346             key, pos = self.unser_utf8_string(buf, pos)
       
   347             val, pos = self.loads_recursive(buf, pos)
       
   348             if self.mutable:
       
   349                 obj[key] = val
       
   350             else:
       
   351                 keys.append(key)
       
   352                 vals.append(val)
       
   353 
       
   354         if not self.mutable:
       
   355             obj = _BunserDict(keys, vals)
       
   356 
       
   357         return obj, pos
       
   358 
       
   359     def unser_template(self, buf, pos):
       
   360         val_type = _buf_pos(buf, pos + 1)
       
   361         if val_type != BSER_ARRAY:
       
   362             raise RuntimeError('Expect ARRAY to follow TEMPLATE')
       
   363         # force UTF-8 on keys
       
   364         keys_bunser = Bunser(mutable=self.mutable, value_encoding='utf-8')
       
   365         keys, pos = keys_bunser.unser_array(buf, pos + 1)
       
   366         nitems, pos = self.unser_int(buf, pos)
       
   367         arr = []
       
   368         for i in range(nitems):
       
   369             if self.mutable:
       
   370                 obj = {}
       
   371             else:
       
   372                 vals = []
       
   373 
       
   374             for keyidx in range(len(keys)):
       
   375                 if _buf_pos(buf, pos) == BSER_SKIP:
       
   376                     pos += 1
       
   377                     ele = None
       
   378                 else:
       
   379                     ele, pos = self.loads_recursive(buf, pos)
       
   380 
       
   381                 if self.mutable:
       
   382                     key = keys[keyidx]
       
   383                     obj[key] = ele
       
   384                 else:
       
   385                     vals.append(ele)
       
   386 
       
   387             if not self.mutable:
       
   388                 obj = _BunserDict(keys, vals)
       
   389 
       
   390             arr.append(obj)
       
   391         return arr, pos
       
   392 
       
   393     def loads_recursive(self, buf, pos):
       
   394         val_type = _buf_pos(buf, pos)
       
   395         if (val_type == BSER_INT8 or val_type == BSER_INT16 or
       
   396             val_type == BSER_INT32 or val_type == BSER_INT64):
       
   397             return self.unser_int(buf, pos)
       
   398         elif val_type == BSER_REAL:
       
   399             val = struct.unpack_from(b'=d', buf, pos + 1)[0]
       
   400             return (val, pos + 9)
       
   401         elif val_type == BSER_TRUE:
       
   402             return (True, pos + 1)
       
   403         elif val_type == BSER_FALSE:
       
   404             return (False, pos + 1)
       
   405         elif val_type == BSER_NULL:
       
   406             return (None, pos + 1)
       
   407         elif val_type == BSER_BYTESTRING:
       
   408             return self.unser_bytestring(buf, pos)
       
   409         elif val_type == BSER_UTF8STRING:
       
   410             return self.unser_utf8_string(buf, pos)
       
   411         elif val_type == BSER_ARRAY:
       
   412             return self.unser_array(buf, pos)
       
   413         elif val_type == BSER_OBJECT:
       
   414             return self.unser_object(buf, pos)
       
   415         elif val_type == BSER_TEMPLATE:
       
   416             return self.unser_template(buf, pos)
       
   417         else:
       
   418             raise ValueError('unhandled bser opcode 0x%s' %
       
   419                              binascii.hexlify(val_type).decode('ascii'))
       
   420 
       
   421 
       
   422 def _pdu_info_helper(buf):
       
   423     bser_version = -1
       
   424     if buf[0:2] == EMPTY_HEADER[0:2]:
       
   425         bser_version = 1
       
   426         bser_capabilities = 0
       
   427         expected_len, pos2 = Bunser.unser_int(buf, 2)
       
   428     elif buf[0:2] == EMPTY_HEADER_V2[0:2]:
       
   429         if len(buf) < 8:
       
   430             raise ValueError('Invalid BSER header')
       
   431         bser_version = 2
       
   432         bser_capabilities = struct.unpack_from("I", buf, 2)[0]
       
   433         expected_len, pos2 = Bunser.unser_int(buf, 6)
   267     else:
   434     else:
   268         keys = []
   435         raise ValueError('Invalid BSER header')
   269         vals = []
   436 
   270 
   437     return bser_version, bser_capabilities, expected_len, pos2
   271     for i in range(obj_len):
   438 
   272         key, pos = _bunser_string(buf, pos)
   439 
   273         val, pos = _bser_loads_recursive(buf, pos, mutable)
   440 def pdu_info(buf):
   274         if mutable:
   441     info = _pdu_info_helper(buf)
   275             obj[key] = val
   442     return info[0], info[1], info[2] + info[3]
   276         else:
       
   277             keys.append(key)
       
   278             vals.append(val)
       
   279 
       
   280     if not mutable:
       
   281         obj = _BunserDict(keys, vals)
       
   282 
       
   283     return obj, pos
       
   284 
       
   285 
       
   286 def _bunser_template(buf, pos, mutable=True):
       
   287     if buf[pos + 1] != BSER_ARRAY:
       
   288         raise RuntimeError('Expect ARRAY to follow TEMPLATE')
       
   289     keys, pos = _bunser_array(buf, pos + 1)
       
   290     nitems, pos = _bunser_int(buf, pos)
       
   291     arr = []
       
   292     for i in range(nitems):
       
   293         if mutable:
       
   294             obj = {}
       
   295         else:
       
   296             vals = []
       
   297 
       
   298         for keyidx in range(len(keys)):
       
   299             if buf[pos] == BSER_SKIP:
       
   300                 pos += 1
       
   301                 ele = None
       
   302             else:
       
   303                 ele, pos = _bser_loads_recursive(buf, pos, mutable)
       
   304 
       
   305             if mutable:
       
   306                 key = keys[keyidx]
       
   307                 obj[key] = ele
       
   308             else:
       
   309                 vals.append(ele)
       
   310 
       
   311         if not mutable:
       
   312             obj = _BunserDict(keys, vals)
       
   313 
       
   314         arr.append(obj)
       
   315     return arr, pos
       
   316 
       
   317 
       
   318 def _bser_loads_recursive(buf, pos, mutable=True):
       
   319     val_type = buf[pos]
       
   320     if (val_type == BSER_INT8 or val_type == BSER_INT16 or
       
   321         val_type == BSER_INT32 or val_type == BSER_INT64):
       
   322         return _bunser_int(buf, pos)
       
   323     elif val_type == BSER_REAL:
       
   324         val = struct.unpack_from('=d', buf, pos + 1)[0]
       
   325         return (val, pos + 9)
       
   326     elif val_type == BSER_TRUE:
       
   327         return (True, pos + 1)
       
   328     elif val_type == BSER_FALSE:
       
   329         return (False, pos + 1)
       
   330     elif val_type == BSER_NULL:
       
   331         return (None, pos + 1)
       
   332     elif val_type == BSER_STRING:
       
   333         return _bunser_string(buf, pos)
       
   334     elif val_type == BSER_ARRAY:
       
   335         return _bunser_array(buf, pos, mutable)
       
   336     elif val_type == BSER_OBJECT:
       
   337         return _bunser_object(buf, pos, mutable)
       
   338     elif val_type == BSER_TEMPLATE:
       
   339         return _bunser_template(buf, pos, mutable)
       
   340     else:
       
   341         raise RuntimeError('unhandled bser opcode 0x%02x' % (val_type,))
       
   342 
   443 
   343 
   444 
   344 def pdu_len(buf):
   445 def pdu_len(buf):
   345     if buf[0:2] != EMPTY_HEADER[0:2]:
   446     info = _pdu_info_helper(buf)
   346         raise RuntimeError('Invalid BSER header')
   447     return info[2] + info[3]
   347     expected_len, pos = _bunser_int(buf, 2)
   448 
   348     return expected_len + pos
   449 
   349 
   450 def loads(buf, mutable=True, value_encoding=None, value_errors=None):
   350 
   451     """Deserialize a BSER-encoded blob.
   351 def loads(buf, mutable=True):
   452 
   352     if buf[0:2] != EMPTY_HEADER[0:2]:
   453     @param buf: The buffer to deserialize.
   353         raise RuntimeError('Invalid BSER header')
   454     @type buf: bytes
   354     expected_len, pos = _bunser_int(buf, 2)
   455 
       
   456     @param mutable: Whether to return mutable results.
       
   457     @type mutable: bool
       
   458 
       
   459     @param value_encoding: Optional codec to use to decode values. If
       
   460                            unspecified or None, return values as bytestrings.
       
   461     @type value_encoding: str
       
   462 
       
   463     @param value_errors: Optional error handler for codec. 'strict' by default.
       
   464                          The other most common argument is 'surrogateescape' on
       
   465                          Python 3. If value_encoding is None, this is ignored.
       
   466     @type value_errors: str
       
   467     """
       
   468 
       
   469     info = _pdu_info_helper(buf)
       
   470     expected_len = info[2]
       
   471     pos = info[3]
       
   472 
   355     if len(buf) != expected_len + pos:
   473     if len(buf) != expected_len + pos:
   356         raise RuntimeError('bser data len != header len')
   474         raise ValueError('bser data len != header len')
   357     return _bser_loads_recursive(buf, pos, mutable)[0]
   475 
   358 
   476     bunser = Bunser(mutable=mutable, value_encoding=value_encoding,
   359 # no-check-code -- this is a 3rd party library
   477                     value_errors=value_errors)
       
   478 
       
   479     return bunser.loads_recursive(buf, pos)[0]
       
   480 
       
   481 
       
   482 def load(fp, mutable=True, value_encoding=None, value_errors=None):
       
   483     from . import load
       
   484     return load.load(fp, mutable, value_encoding, value_errors)