]> arthur.barton.de Git - bup.git/blob - lib/bup/vint.py
Fix hashsplit for python 3; add compat.buffer
[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     if ord(c) == 0:
40         return 0
41     result = 0
42     offset = 0
43     while True:
44         b = ord(c)
45         if b & 0x80:
46             result |= ((b & 0x7f) << offset)
47             offset += 7
48             c = port.read(1)
49             if not c:
50                 raise EOFError('encountered EOF while reading vuint')
51         else:
52             result |= (b << offset)
53             break
54     return result
55
56
57 def write_vint(port, x):
58     # Sign is handled with the second bit of the first byte.  All else
59     # matches vuint.
60     write = port.write
61     bytes_from_uint = compat.bytes_from_uint
62     if x == 0:
63         write(bytes_from_uint(0))
64     else:
65         if x < 0:
66             x = -x
67             sign_and_six_bits = (x & 0x3f) | 0x40
68         else:
69             sign_and_six_bits = x & 0x3f
70         x >>= 6
71         if x:
72             write(bytes_from_uint(0x80 | sign_and_six_bits))
73             write_vuint(port, x)
74         else:
75             write(bytes_from_uint(sign_and_six_bits))
76
77
78 def read_vint(port):
79     c = port.read(1)
80     if not c:
81         raise EOFError('encountered EOF while reading vint')
82     negative = False
83     result = 0
84     offset = 0
85     # Handle first byte with sign bit specially.
86     if c:
87         b = ord(c)
88         if b & 0x40:
89             negative = True
90         result |= (b & 0x3f)
91         if b & 0x80:
92             offset += 6
93             c = port.read(1)
94         elif negative:
95             return -result
96         else:
97             return result
98     while True:
99         b = ord(c)
100         if b & 0x80:
101             result |= ((b & 0x7f) << offset)
102             offset += 7
103             c = port.read(1)
104             if not c:
105                 raise EOFError('encountered EOF while reading vint')
106         else:
107             result |= (b << offset)
108             break
109     if negative:
110         return -result
111     else:
112         return result
113
114
115 def write_bvec(port, x):
116     write_vuint(port, len(x))
117     port.write(x)
118
119
120 def read_bvec(port):
121     n = read_vuint(port)
122     return port.read(n)
123
124
125 def skip_bvec(port):
126     port.read(read_vuint(port))
127
128 def send(port, types, *args):
129     if len(types) != len(args):
130         raise Exception('number of arguments does not match format string')
131     for (type, value) in zip(types, args):
132         if type == 'V':
133             write_vuint(port, value)
134         elif type == 'v':
135             write_vint(port, value)
136         elif type == 's':
137             write_bvec(port, value)
138         else:
139             raise Exception('unknown xpack format string item "' + type + '"')
140
141 def recv(port, types):
142     result = []
143     for type in types:
144         if type == 'V':
145             result.append(read_vuint(port))
146         elif type == 'v':
147             result.append(read_vint(port))
148         elif type == 's':
149             result.append(read_bvec(port))
150         else:
151             raise Exception('unknown xunpack format string item "' + type + '"')
152     return result
153
154 def pack(types, *args):
155     port = BytesIO()
156     send(port, types, *args)
157     return port.getvalue()
158
159 def unpack(types, data):
160     port = BytesIO(data)
161     return recv(port, types)