Skip to content

Commit

Permalink
Use byte strings instead of bytearrays in the interface. This breaks …
Browse files Browse the repository at this point in the history
…backwards compatibility.

A byte strings are often used in favour of bytearrays in python. The
main difference is that a byte string is immutable while a bytearray
is mutable. As most users prefer to use byte strings over bytearrays,
the interface is changed to satisfy the users.
  • Loading branch information
eerimoq committed Mar 28, 2016
1 parent 3824712 commit 39b1a5d
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 66 deletions.
42 changes: 21 additions & 21 deletions bitstruct.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import re
import struct

__version__ = "2.1.4"
__version__ = "3.0.0"


def _parse_format(fmt):
Expand Down Expand Up @@ -76,6 +76,7 @@ def _unpack_float(size, bits):

return value


def _unpack_bytearray(size, bits):
value = bytearray()
for i in range(size // 8):
Expand All @@ -87,14 +88,14 @@ def _unpack_bytearray(size, bits):


def pack(fmt, *args):
"""Return a bytearray containing the values v1, v2, ... packed
"""Return a byte string containing the values v1, v2, ... packed
according to the given format. If the total number of bits are not
a multiple of 8, padding will be added at the end of the last
byte.
:param fmt: Bitstruct format string. See format description below.
:param args: Variable argument list of values to pack.
:returns: A bytearray of the packed values.
:returns: A byte string of the packed values.
`fmt` is a string of bitorder-type-length groups. Bitorder may be
omitted.
Expand All @@ -110,7 +111,7 @@ def pack(fmt, *args):
- 's' -- signed integer
- 'f' -- floating point number of 32 or 64 bits
- 'b' -- boolean
- 'r' -- raw, bytearray
- 'r' -- raw, bytes
- 'p' -- padding, ignore
Length is the number of bits to pack the value into.
Expand All @@ -134,7 +135,7 @@ def pack(fmt, *args):
elif _type == 'b':
value_bits = _pack_boolean(size, args[i])
elif _type == 'r':
value_bits = _pack_bytearray(size, args[i])
value_bits = _pack_bytearray(size, bytearray(args[i]))
else:
raise ValueError("bad type '{}' in format".format(_type))

Expand All @@ -150,22 +151,22 @@ def pack(fmt, *args):
if tail != 0:
bits += (8 - tail) * '0'

return bytearray([int(''.join(bits[i:i+8]), 2)
for i in range(0, len(bits), 8)])
return bytes(bytearray([int(''.join(bits[i:i+8]), 2)
for i in range(0, len(bits), 8)]))


def unpack(fmt, data):
"""Unpack the bytearray (presumably packed by pack(fmt, ...))
"""Unpack the byte string (presumably packed by pack(fmt, ...))
according to the given format. The result is a tuple even if it
contains exactly one item.
:param fmt: Bitstruct format string. See pack() for details.
:param data: Bytearray of values to unpack.
:param data: Byte string of values to unpack.
:returns: A tuple of the unpacked values.
"""

bits = ''.join(['{:08b}'.format(b) for b in data])
bits = ''.join(['{:08b}'.format(b) for b in bytearray(data)])
infos = _parse_format(fmt)
res = []
i = 0
Expand All @@ -187,7 +188,7 @@ def unpack(fmt, data):
elif _type == 'b':
value = _unpack_boolean(value_bits)
elif _type == 'r':
value = _unpack_bytearray(size, value_bits)
value = bytes(_unpack_bytearray(size, value_bits))
else:
raise ValueError("bad type '{}' in format".format(_type))
res.append(value)
Expand All @@ -197,8 +198,7 @@ def unpack(fmt, data):


def calcsize(fmt):
"""Return the size of the bitstruct (and hence of the bytearray)
corresponding to the given format.
"""Calculate the number of bits in given format.
:param fmt: Bitstruct format string.
:returns: Number of bits in format string.
Expand All @@ -209,26 +209,26 @@ def calcsize(fmt):


def byteswap(fmt, data, offset = 0):
"""In place swap bytes in `data` according to `fmt`, starting at byte
"""Swap bytes in `data` according to `fmt`, starting at byte
`offset`. `fmt` must be an iterable, iterating over number of
bytes to swap. For example, the format string "24" applied to the
bytearray "\x00\x11\x22\x33\x44\x55" will produce the result
"\x11\x00\x55\x44\x33\x22".
byte string b'\x00\x11\x22\x33\x44\x55' will produce the result
b'\x11\x00\x55\x44\x33\x22'.
:param fmt: Swap format string.
:param data: Bytearray of data to swap.
:param data: Byte string of data to swap.
:param offset: Start offset into `data`.
:returns: Bytearray of swapped bytes.
:returns: Byte string of swapped bytes.
"""

i = offset
data_swapped = b''

for f in fmt:
length = int(f)
value = data[i:i+length]
value.reverse()
data[i:i+length] = value
data_swapped += value[::-1]
i += length

return data
return data_swapped
90 changes: 45 additions & 45 deletions tests/test_bitstruct.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,79 +10,79 @@ def test_pack(self):
"""

packed = pack('u1u1s6u7u9', 0, 0, -2, 65, 22)
self.assertEqual(packed, bytearray(b'\x3e\x82\x16'))
self.assertEqual(packed, b'\x3e\x82\x16')

packed = pack('u1', 1)
self.assertEqual(packed, bytearray(b'\x80'))
self.assertEqual(packed, b'\x80')

packed = pack('u77', 0x100000000001000000)
ref = bytearray(b'\x00\x80\x00\x00\x00\x00\x08\x00\x00\x00')
ref = b'\x00\x80\x00\x00\x00\x00\x08\x00\x00\x00'
self.assertEqual(packed, ref)

packed = pack('p1u1s6u7u9', 0, -2, 65, 22)
self.assertEqual(packed, bytearray(b'\x3e\x82\x16'))
self.assertEqual(packed, b'\x3e\x82\x16')

packed = pack('p1u1s6p7u9', 0, -2, 22)
self.assertEqual(packed, bytearray(b'\x3e\x00\x16'))
self.assertEqual(packed, b'\x3e\x00\x16')

packed = pack('u1s6f32r43', 0, -2, 3.75, bytearray(b'\x00\xff\x00\xff\x00\xff'))
self.assertEqual(packed, bytearray(b'\x7c\x80\xe0\x00\x00\x01\xfe\x01\xfe\x01\xc0'))
packed = pack('u1s6f32r43', 0, -2, 3.75, b'\x00\xff\x00\xff\x00\xff')
self.assertEqual(packed, b'\x7c\x80\xe0\x00\x00\x01\xfe\x01\xfe\x01\xc0')

packed = pack('b1', True)
self.assertEqual(packed, bytearray(b'\x80'))
self.assertEqual(packed, b'\x80')

packed = pack('b1p6b1', True, True)
self.assertEqual(packed, bytearray(b'\x81'))
self.assertEqual(packed, b'\x81')

packed = pack('u5b2u1', -1, False, 1)
self.assertEqual(packed, bytearray(b'\xf9'))
self.assertEqual(packed, b'\xf9')

def test_unpack(self):
"""Unpack values.
"""

unpacked = unpack('u1u1s6u7u9', bytearray(b'\x3e\x82\x16'))
unpacked = unpack('u1u1s6u7u9', b'\x3e\x82\x16')
self.assertEqual(unpacked, (0, 0, -2, 65, 22))

unpacked = unpack('u1', bytearray(b'\x80'))
unpacked = unpack('u1', b'\x80')
self.assertEqual(unpacked, (1,))

packed = bytearray(b'\x00\x80\x00\x00\x00\x00\x08\x00\x00\x00')
packed = b'\x00\x80\x00\x00\x00\x00\x08\x00\x00\x00'
unpacked = unpack('u77', packed)
self.assertEqual(unpacked, (0x100000000001000000,))

packed = bytearray(b'\x3e\x82\x16')
packed = b'\x3e\x82\x16'
unpacked = unpack('p1u1s6u7u9', packed)
self.assertEqual(unpacked, (0, -2, 65, 22))

packed = bytearray(b'\x3e\x82\x16')
packed = b'\x3e\x82\x16'
unpacked = unpack('p1u1s6p7u9', packed)
self.assertEqual(unpacked, (0, -2, 22))

packed = bytearray(b'\x7c\x80\xe0\x00\x00\x01\xfe\x01\xfe\x01\xc0')
packed = b'\x7c\x80\xe0\x00\x00\x01\xfe\x01\xfe\x01\xc0'
unpacked = unpack('u1s6f32r43', packed)
self.assertEqual(unpacked, (0, -2, 3.75, bytearray(b'\x00\xff\x00\xff\x00\xe0')))
self.assertEqual(unpacked, (0, -2, 3.75, b'\x00\xff\x00\xff\x00\xe0'))

packed = bytearray(b'\x80')
packed = b'\x80'
unpacked = unpack('b1', packed)
self.assertEqual(unpacked, (True,))

packed = bytearray(b'\x80')
packed = b'\x80'
unpacked = unpack('b1p6b1', packed)
self.assertEqual(unpacked, (True, False))

packed = bytearray(b'\x06')
packed = b'\x06'
unpacked = unpack('u5b2u1', packed)
self.assertEqual(unpacked, (0, True, 0))

packed = bytearray(b'\x04')
packed = b'\x04'
unpacked = unpack('u5b2u1', packed)
self.assertEqual(unpacked, (0, True, 0))

# bad float size
try:
unpack('f33', bytearray(b'\x00\x00\x00\x00\x00'))
unpack('f33', b'\x00\x00\x00\x00\x00')
self.fail()
except ValueError:
pass
Expand All @@ -100,9 +100,9 @@ def test_unpack(self):
# foo.b = 1;
# foo.c = 0x67;
# foo.d = 0x12345;
unpacked = unpack("s32s8u25u7",
byteswap("414",
bytearray(b'\x01\x00\x00\x00\x01\xe7\xa2\x91\x00')))
unpacked = unpack('s32s8u25u7',
byteswap('414',
b'\x01\x00\x00\x00\x01\xe7\xa2\x91\x00'))
self.assertEqual(unpacked, (1, 1, 0x12345, 0x67))


Expand Down Expand Up @@ -145,8 +145,8 @@ def test_byteswap(self):
"""

res = bytearray(b'\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a')
ref = bytearray(b'\x01\x03\x02\x04\x08\x07\x06\x05\x0a\x09')
res = b'\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a'
ref = b'\x01\x03\x02\x04\x08\x07\x06\x05\x0a\x09'
self.assertEqual(byteswap('12142', ref), res)

packed = pack('u1u5u2u16', 1, 2, 3, 4)
Expand All @@ -159,38 +159,38 @@ def test_endianness(self):
"""

# big endian
ref = b"\x02\x46\x9a\xfe\x00\x00\x00"
packed = pack(">u19s3f32", 0x1234, -2, -1.0)
ref = b'\x02\x46\x9a\xfe\x00\x00\x00'
packed = pack('>u19s3f32', 0x1234, -2, -1.0)
self.assertEqual(packed, ref)
unpacked = unpack(">u19s3f32", packed)
unpacked = unpack('>u19s3f32', packed)
self.assertEqual(unpacked, (0x1234, -2, -1.0))

# little endian
ref = b"\x2c\x48\x0c\x00\x00\x07\xf4"
packed = pack("<u19s3f32", 0x1234, -2, -1.0)
ref = b'\x2c\x48\x0c\x00\x00\x07\xf4'
packed = pack('<u19s3f32', 0x1234, -2, -1.0)
self.assertEqual(packed, ref)
unpacked = unpack("<u19s3f32", packed)
unpacked = unpack('<u19s3f32', packed)
self.assertEqual(unpacked, (0x1234, -2, -1.0))

# mixed endianness
ref = b"\x00\x00\x2f\x3f\xf0\x00\x00\x00\x00\x00\x00\x80"
packed = pack(">u19<s5>f64r3p4", 1, -2, 1.0, bytearray(b"\x80"))
ref = b'\x00\x00\x2f\x3f\xf0\x00\x00\x00\x00\x00\x00\x80'
packed = pack('>u19<s5>f64r3p4', 1, -2, 1.0, b'\x80')
self.assertEqual(packed, ref)
unpacked = unpack(">u19<s5>f64r3p4", packed)
self.assertEqual(unpacked, (1, -2, 1.0, bytearray(b"\x80")))
unpacked = unpack('>u19<s5>f64r3p4', packed)
self.assertEqual(unpacked, (1, -2, 1.0, b'\x80'))

# opposite endianness of the "mixed endianness" test
ref = b"\x80\x00\x1e\x00\x00\x00\x00\x00\x00\x0f\xfc\x20"
packed = pack("<u19>s5<f64r3p4", 1, -2, 1.0, bytearray(b"\x80"))
# opposite endianness of the 'mixed endianness' test
ref = b'\x80\x00\x1e\x00\x00\x00\x00\x00\x00\x0f\xfc\x20'
packed = pack('<u19>s5<f64r3p4', 1, -2, 1.0, b'\x80')
self.assertEqual(packed, ref)
unpacked = unpack("<u19>s5<f64r3p4", packed)
self.assertEqual(unpacked, (1, -2, 1.0, bytearray(b"\x80")))
unpacked = unpack('<u19>s5<f64r3p4', packed)
self.assertEqual(unpacked, (1, -2, 1.0, b'\x80'))

# pack as big endian, unpack as little endian
ref = b"\x40"
packed = pack("u2", 1)
ref = b'\x40'
packed = pack('u2', 1)
self.assertEqual(packed, ref)
unpacked = unpack("<u2", packed)
unpacked = unpack('<u2', packed)
self.assertEqual(unpacked, (2, ))


Expand Down

0 comments on commit 39b1a5d

Please sign in to comment.