2014-03-10 05:18:05 +00:00
|
|
|
"""
|
|
|
|
Utilities to convert integers and binary strings to binary (number), binary
|
|
|
|
string, number, hexadecimal, etc.
|
|
|
|
"""
|
|
|
|
|
2015-08-15 14:22:29 +00:00
|
|
|
from hachoir_core.endian import BIG_ENDIAN, LITTLE_ENDIAN, MIDDLE_ENDIAN
|
|
|
|
from hachoir_core.compatibility import reversed
|
2014-03-10 05:18:05 +00:00
|
|
|
from itertools import chain, repeat
|
|
|
|
from struct import calcsize, unpack, error as struct_error
|
|
|
|
|
|
|
|
def swap16(value):
|
|
|
|
"""
|
|
|
|
Swap byte between big and little endian of a 16 bits integer.
|
|
|
|
|
|
|
|
>>> "%x" % swap16(0x1234)
|
|
|
|
'3412'
|
|
|
|
"""
|
|
|
|
return (value & 0xFF) << 8 | (value >> 8)
|
|
|
|
|
|
|
|
def swap32(value):
|
|
|
|
"""
|
|
|
|
Swap byte between big and little endian of a 32 bits integer.
|
|
|
|
|
|
|
|
>>> "%x" % swap32(0x12345678)
|
|
|
|
'78563412'
|
|
|
|
"""
|
|
|
|
value = long(value)
|
|
|
|
return ((value & 0x000000FFL) << 24) \
|
|
|
|
| ((value & 0x0000FF00L) << 8) \
|
|
|
|
| ((value & 0x00FF0000L) >> 8) \
|
|
|
|
| ((value & 0xFF000000L) >> 24)
|
|
|
|
|
2015-08-15 14:22:29 +00:00
|
|
|
def arrswapmid(data):
|
|
|
|
r"""
|
|
|
|
Convert an array of characters from middle-endian to big-endian and vice-versa.
|
|
|
|
|
|
|
|
>>> arrswapmid("badcfehg")
|
|
|
|
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
|
|
|
|
"""
|
|
|
|
assert len(data)%2 == 0
|
|
|
|
ret = ['']*len(data)
|
|
|
|
ret[1::2] = data[0::2]
|
|
|
|
ret[0::2] = data[1::2]
|
|
|
|
return ret
|
|
|
|
|
|
|
|
def strswapmid(data):
|
|
|
|
r"""
|
|
|
|
Convert raw data from middle-endian to big-endian and vice-versa.
|
|
|
|
|
|
|
|
>>> strswapmid("badcfehg")
|
|
|
|
'abcdefgh'
|
|
|
|
"""
|
|
|
|
return ''.join(arrswapmid(data))
|
|
|
|
|
2014-03-10 05:18:05 +00:00
|
|
|
def bin2long(text, endian):
|
|
|
|
"""
|
|
|
|
Convert binary number written in a string into an integer.
|
|
|
|
Skip characters differents than "0" and "1".
|
|
|
|
|
|
|
|
>>> bin2long("110", BIG_ENDIAN)
|
|
|
|
6
|
|
|
|
>>> bin2long("110", LITTLE_ENDIAN)
|
|
|
|
3
|
|
|
|
>>> bin2long("11 00", LITTLE_ENDIAN)
|
|
|
|
3
|
|
|
|
"""
|
|
|
|
assert endian in (LITTLE_ENDIAN, BIG_ENDIAN)
|
|
|
|
bits = [ (ord(character)-ord("0")) \
|
|
|
|
for character in text if character in "01" ]
|
|
|
|
if endian is not BIG_ENDIAN:
|
2015-08-15 14:22:29 +00:00
|
|
|
bits = bits[::-1]
|
|
|
|
size = len(bits)
|
|
|
|
assert 0 < size
|
2014-03-10 05:18:05 +00:00
|
|
|
value = 0
|
|
|
|
for bit in bits:
|
|
|
|
value *= 2
|
|
|
|
value += bit
|
|
|
|
return value
|
|
|
|
|
|
|
|
def str2hex(value, prefix="", glue=u"", format="%02X"):
|
|
|
|
r"""
|
|
|
|
Convert binary string in hexadecimal (base 16).
|
|
|
|
|
|
|
|
>>> str2hex("ABC")
|
|
|
|
u'414243'
|
|
|
|
>>> str2hex("\xF0\xAF", glue=" ")
|
|
|
|
u'F0 AF'
|
|
|
|
>>> str2hex("ABC", prefix="0x")
|
|
|
|
u'0x414243'
|
|
|
|
>>> str2hex("ABC", format=r"\x%02X")
|
|
|
|
u'\\x41\\x42\\x43'
|
|
|
|
"""
|
|
|
|
if isinstance(glue, str):
|
|
|
|
glue = unicode(glue)
|
|
|
|
if 0 < len(prefix):
|
|
|
|
text = [prefix]
|
|
|
|
else:
|
|
|
|
text = []
|
|
|
|
for character in value:
|
|
|
|
text.append(format % ord(character))
|
|
|
|
return glue.join(text)
|
|
|
|
|
|
|
|
def countBits(value):
|
|
|
|
"""
|
|
|
|
Count number of bits needed to store a (positive) integer number.
|
|
|
|
|
|
|
|
>>> countBits(0)
|
|
|
|
1
|
|
|
|
>>> countBits(1000)
|
|
|
|
10
|
|
|
|
>>> countBits(44100)
|
|
|
|
16
|
|
|
|
>>> countBits(18446744073709551615)
|
|
|
|
64
|
|
|
|
"""
|
|
|
|
assert 0 <= value
|
|
|
|
count = 1
|
|
|
|
bits = 1
|
|
|
|
while (1 << bits) <= value:
|
|
|
|
count += bits
|
|
|
|
value >>= bits
|
|
|
|
bits <<= 1
|
|
|
|
while 2 <= value:
|
|
|
|
if bits != 1:
|
|
|
|
bits >>= 1
|
|
|
|
else:
|
|
|
|
bits -= 1
|
|
|
|
while (1 << bits) <= value:
|
|
|
|
count += bits
|
|
|
|
value >>= bits
|
|
|
|
return count
|
|
|
|
|
|
|
|
def byte2bin(number, classic_mode=True):
|
|
|
|
"""
|
|
|
|
Convert a byte (integer in 0..255 range) to a binary string.
|
|
|
|
If classic_mode is true (default value), reverse bits.
|
|
|
|
|
|
|
|
>>> byte2bin(10)
|
|
|
|
'00001010'
|
|
|
|
>>> byte2bin(10, False)
|
|
|
|
'01010000'
|
|
|
|
"""
|
|
|
|
text = ""
|
|
|
|
for i in range(0, 8):
|
|
|
|
if classic_mode:
|
|
|
|
mask = 1 << (7-i)
|
|
|
|
else:
|
|
|
|
mask = 1 << i
|
|
|
|
if (number & mask) == mask:
|
|
|
|
text += "1"
|
|
|
|
else:
|
|
|
|
text += "0"
|
|
|
|
return text
|
|
|
|
|
|
|
|
def long2raw(value, endian, size=None):
|
|
|
|
r"""
|
|
|
|
Convert a number (positive and not nul) to a raw string.
|
|
|
|
If size is given, add nul bytes to fill to size bytes.
|
|
|
|
|
|
|
|
>>> long2raw(0x1219, BIG_ENDIAN)
|
|
|
|
'\x12\x19'
|
|
|
|
>>> long2raw(0x1219, BIG_ENDIAN, 4) # 32 bits
|
|
|
|
'\x00\x00\x12\x19'
|
|
|
|
>>> long2raw(0x1219, LITTLE_ENDIAN, 4) # 32 bits
|
|
|
|
'\x19\x12\x00\x00'
|
|
|
|
"""
|
|
|
|
assert (not size and 0 < value) or (0 <= value)
|
2015-08-15 14:22:29 +00:00
|
|
|
assert endian in (LITTLE_ENDIAN, BIG_ENDIAN, MIDDLE_ENDIAN)
|
2014-03-10 05:18:05 +00:00
|
|
|
text = []
|
|
|
|
while (value != 0 or text == ""):
|
|
|
|
byte = value % 256
|
|
|
|
text.append( chr(byte) )
|
|
|
|
value >>= 8
|
|
|
|
if size:
|
|
|
|
need = max(size - len(text), 0)
|
|
|
|
else:
|
|
|
|
need = 0
|
|
|
|
if need:
|
2015-08-15 14:22:29 +00:00
|
|
|
if endian is LITTLE_ENDIAN:
|
2014-03-10 05:18:05 +00:00
|
|
|
text = chain(text, repeat("\0", need))
|
2015-08-15 14:22:29 +00:00
|
|
|
else:
|
|
|
|
text = chain(repeat("\0", need), reversed(text))
|
2014-03-10 05:18:05 +00:00
|
|
|
else:
|
2015-08-15 14:22:29 +00:00
|
|
|
if endian is not LITTLE_ENDIAN:
|
2014-03-10 05:18:05 +00:00
|
|
|
text = reversed(text)
|
2015-08-15 14:22:29 +00:00
|
|
|
if endian is MIDDLE_ENDIAN:
|
|
|
|
text = arrswapmid(text)
|
2014-03-10 05:18:05 +00:00
|
|
|
return "".join(text)
|
|
|
|
|
|
|
|
def long2bin(size, value, endian, classic_mode=False):
|
|
|
|
"""
|
|
|
|
Convert a number into bits (in a string):
|
|
|
|
- size: size in bits of the number
|
|
|
|
- value: positive (or nul) number
|
|
|
|
- endian: BIG_ENDIAN (most important bit first)
|
|
|
|
or LITTLE_ENDIAN (least important bit first)
|
|
|
|
- classic_mode (default: False): reverse each packet of 8 bits
|
|
|
|
|
|
|
|
>>> long2bin(16, 1+4 + (1+8)*256, BIG_ENDIAN)
|
|
|
|
'10100000 10010000'
|
|
|
|
>>> long2bin(16, 1+4 + (1+8)*256, BIG_ENDIAN, True)
|
|
|
|
'00000101 00001001'
|
|
|
|
>>> long2bin(16, 1+4 + (1+8)*256, LITTLE_ENDIAN)
|
|
|
|
'00001001 00000101'
|
|
|
|
>>> long2bin(16, 1+4 + (1+8)*256, LITTLE_ENDIAN, True)
|
|
|
|
'10010000 10100000'
|
|
|
|
"""
|
|
|
|
text = ""
|
|
|
|
assert endian in (LITTLE_ENDIAN, BIG_ENDIAN)
|
|
|
|
assert 0 <= value
|
|
|
|
for index in xrange(size):
|
|
|
|
if (value & 1) == 1:
|
|
|
|
text += "1"
|
|
|
|
else:
|
|
|
|
text += "0"
|
|
|
|
value >>= 1
|
|
|
|
if endian is LITTLE_ENDIAN:
|
|
|
|
text = text[::-1]
|
|
|
|
result = ""
|
|
|
|
while len(text) != 0:
|
|
|
|
if len(result) != 0:
|
|
|
|
result += " "
|
|
|
|
if classic_mode:
|
|
|
|
result += text[7::-1]
|
|
|
|
else:
|
|
|
|
result += text[:8]
|
|
|
|
text = text[8:]
|
|
|
|
return result
|
|
|
|
|
|
|
|
def str2bin(value, classic_mode=True):
|
|
|
|
r"""
|
|
|
|
Convert binary string to binary numbers.
|
|
|
|
If classic_mode is true (default value), reverse bits.
|
|
|
|
|
|
|
|
>>> str2bin("\x03\xFF")
|
|
|
|
'00000011 11111111'
|
|
|
|
>>> str2bin("\x03\xFF", False)
|
|
|
|
'11000000 11111111'
|
|
|
|
"""
|
|
|
|
text = ""
|
|
|
|
for character in value:
|
|
|
|
if text != "":
|
|
|
|
text += " "
|
|
|
|
byte = ord(character)
|
|
|
|
text += byte2bin(byte, classic_mode)
|
|
|
|
return text
|
|
|
|
|
|
|
|
def _createStructFormat():
|
|
|
|
"""
|
|
|
|
Create a dictionnary (endian, size_byte) => struct format used
|
|
|
|
by str2long() to convert raw data to positive integer.
|
|
|
|
"""
|
|
|
|
format = {
|
|
|
|
BIG_ENDIAN: {},
|
|
|
|
LITTLE_ENDIAN: {},
|
|
|
|
}
|
|
|
|
for struct_format in "BHILQ":
|
|
|
|
try:
|
|
|
|
size = calcsize(struct_format)
|
|
|
|
format[BIG_ENDIAN][size] = '>%s' % struct_format
|
|
|
|
format[LITTLE_ENDIAN][size] = '<%s' % struct_format
|
|
|
|
except struct_error:
|
|
|
|
pass
|
|
|
|
return format
|
|
|
|
_struct_format = _createStructFormat()
|
|
|
|
|
|
|
|
def str2long(data, endian):
|
|
|
|
r"""
|
|
|
|
Convert a raw data (type 'str') into a long integer.
|
|
|
|
|
|
|
|
>>> chr(str2long('*', BIG_ENDIAN))
|
|
|
|
'*'
|
|
|
|
>>> str2long("\x00\x01\x02\x03", BIG_ENDIAN) == 0x10203
|
|
|
|
True
|
|
|
|
>>> str2long("\x2a\x10", LITTLE_ENDIAN) == 0x102a
|
|
|
|
True
|
|
|
|
>>> str2long("\xff\x14\x2a\x10", BIG_ENDIAN) == 0xff142a10
|
|
|
|
True
|
|
|
|
>>> str2long("\x00\x01\x02\x03", LITTLE_ENDIAN) == 0x3020100
|
|
|
|
True
|
|
|
|
>>> str2long("\xff\x14\x2a\x10\xab\x00\xd9\x0e", BIG_ENDIAN) == 0xff142a10ab00d90e
|
|
|
|
True
|
|
|
|
>>> str2long("\xff\xff\xff\xff\xff\xff\xff\xff", BIG_ENDIAN) == (2**64-1)
|
|
|
|
True
|
2015-08-15 14:22:29 +00:00
|
|
|
>>> str2long("\x0b\x0a\x0d\x0c", MIDDLE_ENDIAN) == 0x0a0b0c0d
|
|
|
|
True
|
2014-03-10 05:18:05 +00:00
|
|
|
"""
|
|
|
|
assert 1 <= len(data) <= 32 # arbitrary limit: 256 bits
|
|
|
|
try:
|
|
|
|
return unpack(_struct_format[endian][len(data)], data)[0]
|
|
|
|
except KeyError:
|
|
|
|
pass
|
|
|
|
|
2015-08-15 14:22:29 +00:00
|
|
|
assert endian in (BIG_ENDIAN, LITTLE_ENDIAN, MIDDLE_ENDIAN)
|
2014-03-10 05:18:05 +00:00
|
|
|
shift = 0
|
|
|
|
value = 0
|
|
|
|
if endian is BIG_ENDIAN:
|
|
|
|
data = reversed(data)
|
2015-08-15 14:22:29 +00:00
|
|
|
elif endian is MIDDLE_ENDIAN:
|
|
|
|
data = reversed(strswapmid(data))
|
2014-03-10 05:18:05 +00:00
|
|
|
for character in data:
|
|
|
|
byte = ord(character)
|
|
|
|
value += (byte << shift)
|
|
|
|
shift += 8
|
|
|
|
return value
|