]> arthur.barton.de Git - bup.git/blob - lib/bup/vint.py
143e5b6c75647e46bdbb3ed0bcdc3dbef35ef45b
[bup.git] / lib / bup / vint.py
1 """Binary encodings for bup."""
2
3 # Copyright (C) 2010 Rob Browning
4 #
5 # This code is covered under the terms of the GNU Library General
6 # Public License as described in the bup LICENSE file.
7
8 # Variable length integers are encoded as vints -- see lucene.
9
10 from __future__ import absolute_import
11 from io import BytesIO
12 import sys
13
14 from bup import compat
15
16
17 def write_vuint(port, x):
18     write = port.write
19     bytes_from_uint = compat.bytes_from_uint
20     if x < 0:
21         raise Exception("vuints must not be negative")
22     elif x == 0:
23         write(bytes_from_uint(0))
24     else:
25         while True:
26             seven_bits = x & 0x7f
27             x >>= 7
28             if x:
29                 write(bytes_from_uint(0x80 | seven_bits))
30             else:
31                 write(bytes_from_uint(seven_bits))
32                 break
33
34
35 def read_vuint(port):
36     c = port.read(1)
37     if not c:
38         raise EOFError('encountered EOF while reading vuint')
39     assert isinstance(c, bytes)
40     if ord(c) == 0:
41         return 0
42     result = 0
43     offset = 0
44     while True:
45         b = ord(c)
46         if b & 0x80:
47             result |= ((b & 0x7f) << offset)
48             offset += 7
49             c = port.read(1)
50             if not c:
51                 raise EOFError('encountered EOF while reading vuint')
52         else:
53             result |= (b << offset)
54             break
55     return result
56
57
58 def write_vint(port, x):
59     # Sign is handled with the second bit of the first byte.  All else
60     # matches vuint.
61     write = port.write
62     bytes_from_uint = compat.bytes_from_uint
63     if x == 0:
64         write(bytes_from_uint(0))
65     else:
66         if x < 0:
67             x = -x
68             sign_and_six_bits = (x & 0x3f) | 0x40
69         else:
70             sign_and_six_bits = x & 0x3f
71         x >>= 6
72         if x:
73             write(bytes_from_uint(0x80 | sign_and_six_bits))
74             write_vuint(port, x)
75         else:
76             write(bytes_from_uint(sign_and_six_bits))
77
78
79 def read_vint(port):
80     c = port.read(1)
81     if not c:
82         raise EOFError('encountered EOF while reading vint')
83     assert isinstance(c, bytes)
84     negative = False
85     result = 0
86     offset = 0
87     # Handle first byte with sign bit specially.
88     if c:
89         b = ord(c)
90         if b & 0x40:
91             negative = True
92         result |= (b & 0x3f)
93         if b & 0x80:
94             offset += 6
95             c = port.read(1)
96         elif negative:
97             return -result
98         else:
99             return result
100     while True:
101         b = ord(c)
102         if b & 0x80:
103             result |= ((b & 0x7f) << offset)
104             offset += 7
105             c = port.read(1)
106             if not c:
107                 raise EOFError('encountered EOF while reading vint')
108         else:
109             result |= (b << offset)
110             break
111     if negative:
112         return -result
113     else:
114         return result
115
116
117 def write_bvec(port, x):
118     write_vuint(port, len(x))
119     port.write(x)
120
121
122 def read_bvec(port):
123     n = read_vuint(port)
124     return port.read(n)
125
126
127 def skip_bvec(port):
128     port.read(read_vuint(port))
129
130 def send(port, types, *args):
131     if len(types) != len(args):
132         raise Exception('number of arguments does not match format string')
133     for (type, value) in zip(types, args):
134         if type == 'V':
135             write_vuint(port, value)
136         elif type == 'v':
137             write_vint(port, value)
138         elif type == 's':
139             write_bvec(port, value)
140         else:
141             raise Exception('unknown xpack format string item "' + type + '"')
142
143 def recv(port, types):
144     result = []
145     for type in types:
146         if type == 'V':
147             result.append(read_vuint(port))
148         elif type == 'v':
149             result.append(read_vint(port))
150         elif type == 's':
151             result.append(read_bvec(port))
152         else:
153             raise Exception('unknown xunpack format string item "' + type + '"')
154     return result
155
156 def pack(types, *args):
157     port = BytesIO()
158     send(port, types, *args)
159     return port.getvalue()
160
161 def unpack(types, data):
162     port = BytesIO(data)
163     return recv(port, types)