]> arthur.barton.de Git - bup.git/blob - lib/bup/vint.py
Merge branch 'maint'
[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 cStringIO import StringIO
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         else:
81             return -result if negative else result
82     while c:
83         b = ord(c)
84         if b & 0x80:
85             result |= ((b & 0x7f) << offset)
86             offset += 7
87             c = port.read(1)
88         else:
89             result |= (b << offset)
90             break
91     return -result if negative else result
92
93
94 def write_bvec(port, x):
95     write_vuint(port, len(x))
96     port.write(x)
97
98
99 def read_bvec(port):
100     n = read_vuint(port)
101     return port.read(n)
102
103
104 def skip_bvec(port):
105     port.read(read_vuint(port))
106
107
108 def pack(types, *args):
109     if len(types) != len(args):
110         raise Exception('number of arguments does not match format string')
111     port = StringIO()
112     for (type, value) in zip(types, args):
113         if type == 'V':
114             write_vuint(port, value)
115         elif type == 'v':
116             write_vint(port, value)
117         elif type == 's':
118             write_bvec(port, value)
119         else:
120             raise Exception('unknown xpack format string item "' + type + '"')
121     return port.getvalue()
122
123
124 def unpack(types, data):
125     result = []
126     port = StringIO(data)
127     for type in types:
128         if type == 'V':
129             result.append(read_vuint(port))
130         elif type == 'v':
131             result.append(read_vint(port))
132         elif type == 's':
133             result.append(read_bvec(port))
134         else:
135             raise Exception('unknown xunpack format string item "' + type + '"')
136     return result