Skip to content

Commit ad306bc

Browse files
PhysicistJohnvstinner
authored andcommitted
gh-154566: Fix array.byteswap() corrupting 'Zd' arrays with more than one element (GH-154567)
Fix array.array.byteswap() corrupting data for 'Zd' (complex double) arrays with more than one element: the 16-byte item loop advanced the buffer pointer by only 8 bytes per iteration, causing items after the first to be scrambled. (cherry picked from commit 46c355f) Co-authored-by: PhysicistJohn <54456354+PhysicistJohn@users.noreply.github.com> Co-authored-by: Victor Stinner <vstinner@python.org>
1 parent ed38a08 commit ad306bc

3 files changed

Lines changed: 21 additions & 1 deletion

File tree

Lib/test/test_array.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1638,6 +1638,22 @@ def test_byteswap(self):
16381638
b.byteswap()
16391639
self.assertEqual(a, b)
16401640

1641+
def test_byteswap_single_call_result(self):
1642+
# A single byteswap() must swap each item's two halves (real,
1643+
# imag) independently. test_byteswap above only checks that
1644+
# byteswap() twice round-trips to the original, which passes
1645+
# even if a single call scrambles multi-item arrays.
1646+
a = array.array(self.typecode, self.example)
1647+
original = a.tobytes()
1648+
a.byteswap()
1649+
itemsize = a.itemsize
1650+
half = itemsize // 2
1651+
expected = bytearray()
1652+
for i in range(0, len(original), itemsize):
1653+
item = original[i:i + itemsize]
1654+
expected += item[half - 1::-1] + item[itemsize - 1:half - 1:-1]
1655+
self.assertEqual(a.tobytes(), bytes(expected))
1656+
16411657

16421658
class HalfFloatTest(FPTest, unittest.TestCase):
16431659
example = [-42.0, 0, 42, 1e2, -1e4]
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Fix :meth:`array.array.byteswap` corrupting data for ``'Zd'`` (complex
2+
double) arrays with more than one element: the 16-byte item loop advanced
3+
the buffer pointer by only 8 bytes per iteration, causing items after the
4+
first to be scrambled.

Modules/arraymodule.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1661,7 +1661,7 @@ array_array_byteswap_impl(arrayobject *self)
16611661
break;
16621662
case 16:
16631663
assert(strcmp(self->ob_descr->typecode, "Zd") == 0);
1664-
for (p = self->ob_item, i = Py_SIZE(self); --i >= 0; p += 8) {
1664+
for (p = self->ob_item, i = Py_SIZE(self); --i >= 0; p += 16) {
16651665
char t0 = p[0];
16661666
char t1 = p[1];
16671667
char t2 = p[2];

0 commit comments

Comments
 (0)