Skip to content

Commit

Permalink
Big simplification to byteswap().
Browse files Browse the repository at this point in the history
Not sure anyone needed all those options?
  • Loading branch information
scott-griffiths committed Jun 22, 2024
1 parent 2705585 commit 87f3ebd
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 104 deletions.
68 changes: 22 additions & 46 deletions bitformat/bits.py
Original file line number Diff line number Diff line change
Expand Up @@ -1091,54 +1091,30 @@ def rol(self, n: int, /, start: int | None = None, end: int | None = None) -> TB
n %= (end - start)
return self[:start] + self[start + n: end] + self[start: start + n] + self[end:]

def byteswap(self, fmt: int | Iterable[int] | str = None, start: int | None = None,
end: int | None = None, repeat: bool = True) -> TBits:
"""Change the endianness in-place. Return number of repeats of fmt done.
fmt -- An integer number of bytes or
an iterable of integers. Defaults to 0, which byte reverses the
whole Bits.
start -- Start bit position, defaults to 0.
end -- End bit position, defaults to len(self).
repeat -- If True (the default) the byte swapping pattern is repeated
as much as possible.
def byteswap(self, bytelength: int | None = None, /) -> TBits:
"""Change the byte endianness. Return new Bits.
"""
start_v, end_v = self._validate_slice(start, end)
if fmt is None or fmt == 0:
# reverse all of the whole bytes.
bytesizes = [(end_v - start_v) // 8]
elif isinstance(fmt, numbers.Integral):
if fmt < 0:
raise ValueError(f"Improper byte length {fmt}.")
bytesizes = [fmt]
elif isinstance(fmt, abc.Iterable):
bytesizes = fmt
for bytesize in bytesizes:
if not isinstance(bytesize, numbers.Integral) or bytesize < 0:
raise ValueError(f"Improper byte length {bytesize}.")
else:
raise TypeError("Format must be an integer, string or iterable.")
bytelength: An int giving the number of bytes to swap.
s = self._copy()
repeats = 0
totalbitsize = 8 * sum(bytesizes)
if totalbitsize == 0:
return s
if repeat:
# Try to repeat up to the end of the Bits.
finalbit = end_v
else:
# Just try one (set of) byteswap(s).
finalbit = start_v + totalbitsize
for patternend in range(start_v + totalbitsize, finalbit + 1, totalbitsize):
bytestart = patternend - totalbitsize
for bytesize in bytesizes:
byteend = bytestart + bytesize * 8
s._reversebytes(bytestart, byteend)
bytestart += bytesize * 8
repeats += 1
return s
The whole of the Bits will be byte-swapped. It must be a multiple
of bytelength long.
"""
if len(self) % 8 != 0:
raise ValueError(f"Bit length must be an multiple of 8 to use byteswap.")
if bytelength is None:
bytelength = len(self) // 8
if bytelength == 0:
return Bits()
if bytelength < 0:
raise ValueError(f"Negative bytelength given: {bytelength}.")
if len(self) % (bytelength * 8) != 0:
raise ValueError(f"The bits should be a whole number of bytelength bytes long.")
chunks = []
for startbit in range(0, len(self), bytelength *8):
x = self[startbit:startbit + bytelength * 8].to_bytes()
chunks.append(Bits.from_bytes(x[::-1]))
return Bits.join(chunks)

def replace(self, old: BitsType, new: BitsType, /, start: int | None = None, end: int | None = None,
count: int | None = None, bytealigned: bool | None = None) -> TBits:
Expand Down
69 changes: 11 additions & 58 deletions tests/test_bitstream.py
Original file line number Diff line number Diff line change
Expand Up @@ -946,37 +946,15 @@ def test_reverse_bytes(self):
a = a.byteswap()
assert a == '0x563412'
b = a + '0b1'
b = b.byteswap()
assert '0x123456, 0b1' == b
with pytest.raises(ValueError):
_ = b.byteswap()
a = Bits('0x54')
a = a.byteswap()
assert a == '0x54'
a = Bits()
a = a.byteswap()
assert not a

def test_reverse_bytes2(self):
a = Bits()
a = a.byteswap()
assert a == Bits()
a = Bits('0x00112233')
a = a.byteswap(0, 0, 16)
assert a == '0x11002233'
a = a.byteswap(0, 4, 28)
assert a == '0x12302103'
a = a.byteswap(start=0, end=18)
assert a == '0x30122103'
with pytest.raises(ValueError):
_ = a.byteswap(0, 10, 2)
with pytest.raises(ValueError):
_ = a.byteswap(0, -4, 4)
with pytest.raises(ValueError):
_ = a.byteswap(0, 24, 48)
a = a.byteswap(0, 24)
assert a == '0x30122103'
a = a.byteswap(0, 11, 11)
assert a == '0x30122103'

def test_startswith(self):
a = Bits()
assert a.starts_with(Bits())
Expand Down Expand Up @@ -1372,11 +1350,8 @@ def test_function_negative_indices(self):
t = t.reverse(-12, -4)
assert t == '0x778899abc7bf'

# reversebytes
t = t.byteswap(0, -40, -16)
assert t == '0x77ab9988c7bf'

# overwrite
t = Bits('0x77ab9988c7bf')
t = t.overwrite('0x666', -20)
assert t == '0x77ab998666bf'

Expand Down Expand Up @@ -1445,46 +1420,24 @@ def test_rotate_start_and_end(self):

def test_byte_swap_int(self):
s = Bits('0xf234567f')
s = s.byteswap(1, start=4)
s = s.byteswap(1)
assert s == '0xf234567f'
s = s.byteswap(2, start=4)
assert s == '0xf452367f'
s = s.byteswap(2, start=4, end=-4)
s = s.byteswap(2)
assert s == '0x34f27f56'
s = s.byteswap(2)
assert s == '0xf234567f'
s = s.byteswap(3)
assert s == '0x5634f27f'
s = s.byteswap(2, repeat=False)
assert s == '0x3456f27f'

def test_byte_swap_pack_code(self):
s = Bits('0x0011223344556677')
s = s.byteswap(1)
assert s == '0x0011223344556677'

def test_byte_swap_iterable(self):
s = Bits('0x0011223344556677')
s = s.byteswap(range(1, 4), repeat=False)
assert s == '0x0022115544336677'
s = s.byteswap([2], start=8)
assert s == '0x0011224455663377'
s = s.byteswap([2, 3], start=4)
assert s == '0x0120156452463377'
with pytest.raises(ValueError):
_ = s.byteswap(3)

def test_byte_swap_errors(self):
s = Bits('0x0011223344556677')
with pytest.raises(ValueError):
with pytest.raises(TypeError):
s.byteswap('z')
with pytest.raises(ValueError):
s.byteswap(-1)
with pytest.raises(ValueError):
with pytest.raises(TypeError):
s.byteswap([-1])
with pytest.raises(ValueError):
s.byteswap([1, 'e'])
with pytest.raises(ValueError):
s.byteswap('!h')
with pytest.raises(ValueError):
s.byteswap(2, start=-1000)
with pytest.raises(TypeError):
s.byteswap(5.4)

def test_unicode(self):
Expand Down

0 comments on commit 87f3ebd

Please sign in to comment.