#!/usr/bin/env python #----------------------------------------------------------------------------- # Copyright (c) 2008-2009, David P. D. Moss. All rights reserved. # # Released under the BSD license. See the LICENSE file for details. #----------------------------------------------------------------------------- """ Shared logic for various address types. """ import re as _re #----------------------------------------------------------------------------- def bytes_to_bits(): """ @return: A 256 element list containing 8-bit binary digit strings. The list index value is equivalent to its bit string value. """ lookup = [] bits_per_byte = range(7, -1, -1) for num in range(256): bits = 8 * [None] for i in bits_per_byte: bits[i] = '01'[num & 1] num >>= 1 lookup.append(''.join(bits)) return lookup #: A lookup table of 8-bit integer values to their binary digit bit strings. BYTES_TO_BITS = bytes_to_bits() #----------------------------------------------------------------------------- def valid_words(words, word_size, num_words): """ @param words: A sequence of unsigned integer word values. @param word_size: Width (in bits) of each unsigned integer word value. @param num_words: Number of unsigned integer words expected. @return: C{True} if word sequence is valid for this address type, C{False} otherwise. """ if not hasattr(words, '__iter__'): return False if len(words) != num_words: return False max_word = 2 ** word_size - 1 for i in words: if not 0 <= i <= max_word: return False return True #----------------------------------------------------------------------------- def int_to_words(int_val, word_size, num_words): """ @param int_val: Unsigned integer to be divided into words of equal size. @param word_size: Width (in bits) of each unsigned integer word value. @param num_words: Number of unsigned integer words expected. @return: A tuple contain unsigned integer word values split according to provided arguments. """ max_int = 2 ** (num_words * word_size) - 1 if not 0 <= int_val <= max_int: raise IndexError('integer out of bounds: %r!' % hex(int_val)) max_word = 2 ** word_size - 1 words = [] for _ in range(num_words): word = int_val & max_word words.append(int(word)) int_val >>= word_size return tuple(reversed(words)) #----------------------------------------------------------------------------- def words_to_int(words, word_size, num_words): """ @param words: A sequence of unsigned integer word values. @param word_size: Width (in bits) of each unsigned integer word value. @param num_words: Number of unsigned integer words expected. @return: An unsigned integer that is equivalent to value represented by word sequence. """ if not valid_words(words, word_size, num_words): raise ValueError('invalid integer word sequence: %r!' % words) int_val = 0 for i, num in enumerate(reversed(words)): word = num word = word << word_size * i int_val = int_val | word return int_val #----------------------------------------------------------------------------- def valid_bits(bits, width, word_sep=''): """ @param bits: A network address in a delimited binary string format. @param width: Maximum width (in bits) of a network address (excluding delimiters). @param word_sep: (optional) character or string used to delimit word groups (default: '', no separator). @return: C{True} if network address is valid, C{False} otherwise. """ if not hasattr(bits, 'replace'): return False if word_sep != '': bits = bits.replace(word_sep, '') if len(bits) != width: return False max_int = 2 ** width - 1 try: if 0 <= int(bits, 2) <= max_int: return True except ValueError: pass return False #----------------------------------------------------------------------------- def bits_to_int(bits, width, word_sep=''): """ @param bits: A network address in a delimited binary string format. @param width: Maximum width (in bits) of a network address (excluding delimiters). @param word_sep: (optional) character or string used to delimit word groups (default: '', no separator). @return: An unsigned integer that is equivalent to value represented by network address in readable binary form. """ if not valid_bits(bits, width, word_sep): raise ValueError('invalid readable binary string: %r!' % bits) if word_sep != '': bits = bits.replace(word_sep, '') return int(bits, 2) #----------------------------------------------------------------------------- def int_to_bits(int_val, word_size, num_words, word_sep=''): """ @param int_val: An unsigned integer. @param word_size: Width (in bits) of each unsigned integer word value. @param num_words: Number of unsigned integer words expected. @param word_sep: (optional) character or string used to delimit word groups (default: '', no separator). @return: A network address in a delimited binary string format that is equivalent in value to unsigned integer. """ bit_words = [] for word in int_to_words(int_val, word_size, num_words): bits = [] while word: bits.append(BYTES_TO_BITS[word & 255]) word >>= 8 bits.reverse() bit_str = ''.join(bits) or '0' * word_size bits = ('0' * word_size + bit_str)[-word_size:] bit_words.append(bits) if word_sep is not '': # Check custom separator. if not hasattr(word_sep, 'join'): raise ValueError('word separator is not a string: %r!' % word_sep) return word_sep.join(bit_words) #----------------------------------------------------------------------------- def valid_bin(bin_val, width): """ @param bin_val: A network address in Python's binary representation format ('0bxxx'). @param width: Maximum width (in bits) of a network address (excluding delimiters). @return: C{True} if network address is valid, C{False} otherwise. """ if not hasattr(bin_val, 'startswith'): return False if not bin_val.startswith('0b'): return False bin_val = bin_val.replace('0b', '') if len(bin_val) > width: return False max_int = 2 ** width - 1 try: if 0 <= int(bin_val, 2) <= max_int: return True except ValueError: pass return False #----------------------------------------------------------------------------- def int_to_bin(int_val, width): """ @param int_val: An unsigned integer. @param width: Maximum allowed width (in bits) of a unsigned integer. @return: Equivalent string value in Python's binary representation format ('0bxxx'). """ bin_tokens = [] try: # Python 2.6.x and upwards. bin_val = bin(int_val) except NameError: # Python 2.4.x and 2.5.x i = int_val while i > 0: word = i & 0xff bin_tokens.append(BYTES_TO_BITS[word]) i >>= 8 bin_tokens.reverse() bin_val = '0b' + _re.sub(r'^[0]+([01]+)$', r'\1', ''.join(bin_tokens)) if len(bin_val[2:]) > width: raise IndexError('binary string out of bounds: %s!' % bin_val) return bin_val #----------------------------------------------------------------------------- def bin_to_int(bin_val, width): """ @param bin_val: A string containing an unsigned integer in Python's binary representation format ('0bxxx'). @param width: Maximum allowed width (in bits) of a unsigned integer. @return: An unsigned integer that is equivalent to value represented by Python binary string format. """ if not valid_bin(bin_val, width): raise ValueError('not a valid Python binary string: %r!' % bin_val) return int(bin_val.replace('0b', ''), 2) if __name__ == '__main__': i = 0x100000000 print hex(i) print int_to_bin(i, 32)