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