1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
'''Data transformation functions.
From bytes to a number, number to bytes, base64-like-encoding, etc.
'''
import math
import types
def bit_size(number):
"""Returns the number of bits required to hold a specific long number"""
if number < 0:
raise ValueError('Only nonnegative numbers possible: %s' % number)
if number == 0:
return 1
return int(math.ceil(math.log(number, 2)))
def byte_size(number):
"""Returns the number of bytes required to hold a specific long number.
The number of bytes is rounded up.
"""
return int(math.ceil(bit_size(number) / 8.0))
def bytes2int(bytes):
"""Converts a list of bytes or an 8-bit string to an integer.
When using unicode strings, encode it to some encoding like UTF8 first.
>>> (((128 * 256) + 64) * 256) + 15
8405007
>>> l = [128, 64, 15]
>>> bytes2int(l) #same as bytes2int('\x80@\x0f')
8405007
"""
if not (type(bytes) is types.ListType or type(bytes) is types.StringType):
raise TypeError("You must pass a string or a list")
# Convert byte stream to integer
integer = 0
for byte in bytes:
integer *= 256
if type(byte) is types.StringType: byte = ord(byte)
integer += byte
return integer
def int2bytes(number, block_size=None):
r'''Converts a number to a string of bytes.
@param number: the number to convert
@param block_size: the number of bytes to output. If the number encoded to
bytes is less than this, the block will be zero-padded. When not given,
the returned block is not padded.
@throws OverflowError when block_size is given and the number takes up more
bytes than fit into the block.
>>> int2bytes(123456789)
'\x07[\xcd\x15'
>>> bytes2int(int2bytes(123456789))
123456789
>>> int2bytes(123456789, 6)
'\x00\x00\x07[\xcd\x15'
>>> bytes2int(int2bytes(123456789, 128))
123456789
>>> int2bytes(123456789, 3)
Traceback (most recent call last):
...
OverflowError: Needed 4 bytes for number, but block size is 3
'''
# Type checking
if type(number) not in (types.LongType, types.IntType):
raise TypeError("You must pass an integer for 'number', not %s" %
number.__class__)
# Do some bounds checking
if block_size is not None:
needed_bytes = byte_size(number)
if needed_bytes > block_size:
raise OverflowError('Needed %i bytes for number, but block size '
'is %i' % (needed_bytes, block_size))
# Convert the number to bytes.
bytes = []
while number > 0:
bytes.insert(0, chr(number & 0xFF))
number >>= 8
# Pad with zeroes to fill the block
if block_size is not None:
padding = (block_size - needed_bytes) * '\x00'
else:
padding = ''
return padding + ''.join(bytes)
def block_op(block_provider, block_size, operation):
r'''Generator, applies the operation on each block and yields the result
Each block is converted to a number, the given operation is applied and then
the resulting number is converted back to a block of data. The resulting
block is yielded.
@param block_provider: an iterable that iterates over the data blocks.
@param block_size: the used block size
@param operation: a function that accepts an integer and returns an integer
>>> blocks = ['\x00\x01\x02', '\x03\x04\x05']
>>> list(block_op(blocks, 3, lambda x: (x + 6)))
['\x00\x01\x08', '\x03\x04\x0b']
'''
for block in block_provider:
number = bytes2int(block)
print 'In : %i (%i bytes)' % (number, byte_size(number))
after_op = operation(number)
print 'Out: %i (%i bytes)' % (after_op, byte_size(after_op))
yield int2bytes(after_op, block_size)
if __name__ == '__main__':
import doctest
doctest.testmod()
|