1# -----------------------------------------------------------------------------
2# Copyright (c) 2008 by David P. D. Moss. All rights reserved.
3#
4# Released under the BSD license. See the LICENSE file for details.
5# -----------------------------------------------------------------------------
6"""
7Shared logic for various address types.
8"""
9import re as _re
10
11
12def bytes_to_bits():
13 """
14 :return: A 256 element list containing 8-bit binary digit strings. The
15 list index value is equivalent to its bit string value.
16 """
17 lookup = []
18 bits_per_byte = range(7, -1, -1)
19 for num in range(256):
20 bits = 8 * [None]
21 for i in bits_per_byte:
22 bits[i] = '01'[num & 1]
23 num >>= 1
24 lookup.append(''.join(bits))
25 return lookup
26
27
28#: A lookup table of 8-bit integer values to their binary digit bit strings.
29BYTES_TO_BITS = bytes_to_bits()
30
31
32def valid_words(words, word_size, num_words):
33 """
34 :param words: A sequence of unsigned integer word values.
35
36 :param word_size: Width (in bits) of each unsigned integer word value.
37
38 :param num_words: Number of unsigned integer words expected.
39
40 :return: ``True`` if word sequence is valid for this address type,
41 ``False`` otherwise.
42 """
43 if not hasattr(words, '__iter__'):
44 return False
45
46 if len(words) != num_words:
47 return False
48
49 max_word = 2**word_size - 1
50
51 for i in words:
52 if not 0 <= i <= max_word:
53 return False
54
55 return True
56
57
58def int_to_words(int_val, word_size, num_words):
59 """
60 :param int_val: Unsigned integer to be divided into words of equal size.
61
62 :param word_size: Width (in bits) of each unsigned integer word value.
63
64 :param num_words: Number of unsigned integer words expected.
65
66 :return: A tuple contain unsigned integer word values split according
67 to provided arguments.
68 """
69 max_int = 2 ** (num_words * word_size) - 1
70
71 if not 0 <= int_val <= max_int:
72 raise IndexError('integer out of bounds: %r!' % hex(int_val))
73
74 max_word = 2**word_size - 1
75
76 words = []
77 for _ in range(num_words):
78 word = int_val & max_word
79 words.append(int(word))
80 int_val >>= word_size
81
82 return tuple(reversed(words))
83
84
85def words_to_int(words, word_size, num_words):
86 """
87 :param words: A sequence of unsigned integer word values.
88
89 :param word_size: Width (in bits) of each unsigned integer word value.
90
91 :param num_words: Number of unsigned integer words expected.
92
93 :return: An unsigned integer that is equivalent to value represented
94 by word sequence.
95 """
96 if not valid_words(words, word_size, num_words):
97 raise ValueError('invalid integer word sequence: %r!' % (words,))
98
99 int_val = 0
100 for i, num in enumerate(reversed(words)):
101 word = num
102 word = word << word_size * i
103 int_val = int_val | word
104
105 return int_val
106
107
108def valid_bits(bits, width, word_sep=''):
109 """
110 :param bits: A network address in a delimited binary string format.
111
112 :param width: Maximum width (in bits) of a network address (excluding
113 delimiters).
114
115 :param word_sep: (optional) character or string used to delimit word
116 groups (default: '', no separator).
117
118 :return: ``True`` if network address is valid, ``False`` otherwise.
119 """
120 if not isinstance(bits, str):
121 return False
122
123 if word_sep != '':
124 bits = bits.replace(word_sep, '')
125
126 if len(bits) != width:
127 return False
128
129 max_int = 2**width - 1
130
131 try:
132 if 0 <= int(bits, 2) <= max_int:
133 return True
134 except ValueError:
135 pass
136
137 return False
138
139
140def bits_to_int(bits, width, word_sep=''):
141 """
142 :param bits: A network address in a delimited binary string format.
143
144 :param width: Maximum width (in bits) of a network address (excluding
145 delimiters).
146
147 :param word_sep: (optional) character or string used to delimit word
148 groups (default: '', no separator).
149
150 :return: An unsigned integer that is equivalent to value represented
151 by network address in readable binary form.
152 """
153 if not valid_bits(bits, width, word_sep):
154 raise ValueError('invalid readable binary string: %r!' % (bits,))
155
156 if word_sep != '':
157 bits = bits.replace(word_sep, '')
158
159 return int(bits, 2)
160
161
162def int_to_bits(int_val, word_size, num_words, word_sep=''):
163 """
164 :param int_val: An unsigned integer.
165
166 :param word_size: Width (in bits) of each unsigned integer word value.
167
168 :param num_words: Number of unsigned integer words expected.
169
170 :param word_sep: (optional) character or string used to delimit word
171 groups (default: '', no separator).
172
173 :return: A network address in a delimited binary string format that is
174 equivalent in value to unsigned integer.
175 """
176 bit_words = []
177
178 for word in int_to_words(int_val, word_size, num_words):
179 bits = []
180 while word:
181 bits.append(BYTES_TO_BITS[word & 255])
182 word >>= 8
183 bits.reverse()
184 bit_str = ''.join(bits) or '0' * word_size
185 bits = ('0' * word_size + bit_str)[-word_size:]
186 bit_words.append(bits)
187
188 if word_sep != '':
189 # Check custom separator.
190 if not isinstance(word_sep, str):
191 raise ValueError('word separator is not a string: %r!' % (word_sep,))
192
193 return word_sep.join(bit_words)
194
195
196def valid_bin(bin_val, width):
197 """
198 :param bin_val: A network address in Python's binary representation format
199 ('0bxxx').
200
201 :param width: Maximum width (in bits) of a network address (excluding
202 delimiters).
203
204 :return: ``True`` if network address is valid, ``False`` otherwise.
205 """
206 if not isinstance(bin_val, str):
207 return False
208
209 if not bin_val.startswith('0b'):
210 return False
211
212 bin_val = bin_val.replace('0b', '')
213
214 if len(bin_val) > width:
215 return False
216
217 max_int = 2**width - 1
218
219 try:
220 if 0 <= int(bin_val, 2) <= max_int:
221 return True
222 except ValueError:
223 pass
224
225 return False
226
227
228def int_to_bin(int_val, width):
229 """
230 :param int_val: An unsigned integer.
231
232 :param width: Maximum allowed width (in bits) of a unsigned integer.
233
234 :return: Equivalent string value in Python's binary representation format
235 ('0bxxx').
236 """
237 bin_tokens = []
238
239 try:
240 # Python 2.6.x and upwards.
241 bin_val = bin(int_val)
242 except NameError:
243 # Python 2.4.x and 2.5.x
244 i = int_val
245 while i > 0:
246 word = i & 0xFF
247 bin_tokens.append(BYTES_TO_BITS[word])
248 i >>= 8
249
250 bin_tokens.reverse()
251 bin_val = '0b' + _re.sub(r'^[0]+([01]+)$', r'\1', ''.join(bin_tokens))
252
253 if len(bin_val[2:]) > width:
254 raise IndexError('binary string out of bounds: %s!' % (bin_val,))
255
256 return bin_val
257
258
259def bin_to_int(bin_val, width):
260 """
261 :param bin_val: A string containing an unsigned integer in Python's binary
262 representation format ('0bxxx').
263
264 :param width: Maximum allowed width (in bits) of a unsigned integer.
265
266 :return: An unsigned integer that is equivalent to value represented
267 by Python binary string format.
268 """
269 if not valid_bin(bin_val, width):
270 raise ValueError('not a valid Python binary string: %r!' % (bin_val,))
271
272 return int(bin_val.replace('0b', ''), 2)