Skip to content

Commit 46c355f

Browse files
gh-154566: Fix array.byteswap() corrupting 'Zd' arrays with more than one element (#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. Co-authored-by: Victor Stinner <vstinner@python.org>
1 parent 5ea3935 commit 46c355f

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
@@ -1583,6 +1583,22 @@ def test_byteswap(self):
15831583
b.byteswap()
15841584
self.assertEqual(a, b)
15851585

1586+
def test_byteswap_single_call_result(self):
1587+
# A single byteswap() must swap each item's two halves (real,
1588+
# imag) independently. test_byteswap above only checks that
1589+
# byteswap() twice round-trips to the original, which passes
1590+
# even if a single call scrambles multi-item arrays.
1591+
a = array.array(self.typecode, self.example)
1592+
original = a.tobytes()
1593+
a.byteswap()
1594+
itemsize = a.itemsize
1595+
half = itemsize // 2
1596+
expected = bytearray()
1597+
for i in range(0, len(original), itemsize):
1598+
item = original[i:i + itemsize]
1599+
expected += item[half - 1::-1] + item[itemsize - 1:half - 1:-1]
1600+
self.assertEqual(a.tobytes(), bytes(expected))
1601+
15861602

15871603
class HalfFloatTest(FPTest, unittest.TestCase):
15881604
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
@@ -1617,7 +1617,7 @@ array_array_byteswap_impl(arrayobject *self)
16171617
break;
16181618
case 16:
16191619
assert(strcmp(self->ob_descr->typecode, "Zd") == 0);
1620-
for (p = self->ob_item, i = Py_SIZE(self); --i >= 0; p += 8) {
1620+
for (p = self->ob_item, i = Py_SIZE(self); --i >= 0; p += 16) {
16211621
char t0 = p[0];
16221622
char t1 = p[1];
16231623
char t2 = p[2];

0 commit comments

Comments
 (0)