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