#!/usr/bin/env python
import struct
import unittest
#-----------------------------------------------------------------------------
#: enable verbose print statements.
DEBUG = True
#: struct format lookup for specific word sizes.
STRUCT_FMT = {
8 : 'B', # unsigned char
16 : 'H', # unsigned short
32 : 'I', # unsigned int
}
#-----------------------------------------------------------------------------
def int_to_words(int_val, num_words=4, word_size=32):
"""
@param int_val: an arbitrary length Python integer to be split up.
Network byte order is assumed. Raises an IndexError if width of
integer (in bits) exceeds word_size * num_words.
@param num_words: number of words expected in return value tuple.
@param word_size: size/width of individual words (in bits).
@return: a list of fixed width words based on provided parameters.
"""
max_int = 2 ** (word_size*num_words) - 1
max_word_size = 2 ** word_size - 1
if not 0 <= int_val <= max_int:
raise IndexError('integer %r is out of bounds!' % hex(int_val))
words = []
for _ in range(num_words):
word = int_val & max_word_size
words.append(int(word))
int_val >>= word_size
words.reverse()
return words
#-----------------------------------------------------------------------------
def int_to_packed(int_val, width=128, word_size=32):
"""
@param int_val: an arbitrary sized Python integer to be packed.
@param width: expected maximum with of an integer. Can be any size but
should be divide by word_size without a remainder.
@param word_size: size/width of individual words (in bits).
Valid sizes are 8, 16 and 32 bits.
@return: a (network byte order) packed string equivalent to integer value.
"""
num_words = width / word_size
words = int_to_words(int_val, num_words, word_size)
try:
fmt = '>%d%s' % (num_words, STRUCT_FMT[word_size])
#DEBUG: print 'format:', fmt
except KeyError:
raise ValueError('unsupported word size: %d!' % word_size)
return struct.pack(fmt, *words)
#-----------------------------------------------------------------------------
def packed_to_int(packed_int, width=128, word_size=32):
"""
@param packed_int: a packed string to be converted to an abritrary size
Python integer. Network byte order is assumed.
@param width: expected maximum width of return value integer. Can be any
size but should divide by word_size equally without remainder.
@param word_size: size/width of individual words (in bits).
Valid sizes are 8, 16 and 32 bits.
@return: an arbitrary sized Python integer.
"""
num_words = width / word_size
try:
fmt = '>%d%s' % (num_words, STRUCT_FMT[word_size])
#DEBUG: print 'format:', fmt
except KeyError:
raise ValueError('unsupported word size: %d!' % word_size)
words = list(struct.unpack(fmt, packed_int))
words.reverse()
int_val = 0
for i, num in enumerate(words):
word = num
word = word << word_size * i
int_val = int_val | word
return int_val
#-----------------------------------------------------------------------------
class NetworkAddressTests(unittest.TestCase):
"""Example test case using various network address types"""
def debug(self, val, expect_val_packed, actual_val_packed, new_val):
print 'original int :', hex(val)
print 'packed int (expected) : %r' % expect_val_packed
print 'packed int (actual) : %r' % actual_val_packed
print 'unpacked int :', hex(new_val)
print
def testIPv4(self):
"""IP version 4 address test"""
val = 0xfffefffe
expect_val_packed = '\xff\xfe\xff\xfe'
actual_val_packed = int_to_packed(val, width=32, word_size=8)
new_val = packed_to_int(actual_val_packed, width=32, word_size=8)
self.assertEqual(val, new_val)
self.assertEqual(expect_val_packed, actual_val_packed)
if DEBUG:
print 'IPv4'
self.debug(val, expect_val_packed, actual_val_packed, new_val)
def testMAC(self):
"""MAC address test"""
val = 0xfffefffefffe
expect_val_packed = '\xff\xfe\xff\xfe\xff\xfe'
actual_val_packed = int_to_packed(val, width=48, word_size=8)
new_val = packed_to_int(actual_val_packed, width=48, word_size=8)
self.assertEqual(val, new_val)
self.assertEqual(expect_val_packed, actual_val_packed)
if DEBUG:
print 'MAC'
self.debug(val, expect_val_packed, actual_val_packed, new_val)
def testIPv6(self):
"""IP version 6 address test"""
val = 0xfffefffefffefffefffefffefffefffe
expect_val_packed = '\xff\xfe\xff\xfe\xff\xfe\xff\xfe' \
'\xff\xfe\xff\xfe\xff\xfe\xff\xfe'
actual_val_packed = int_to_packed(val, width=128, word_size=32)
new_val = packed_to_int(actual_val_packed, width=128, word_size=32)
self.assertEqual(val, new_val)
self.assertEqual(expect_val_packed, actual_val_packed)
if DEBUG:
print 'IPv6'
self.debug(val, expect_val_packed, actual_val_packed, new_val)
#-----------------------------------------------------------------------------
if __name__ == '__main__':
unittest.main()