gh-154566: Fix array.byteswap() corrupting 'Zd' arrays with more than one element (#154567)

vstinner <[email protected]>
Newsgroups gmane.comp.python.cvs
Message-ID <[email protected]>
https://github.com/python/cpython/commit/46c355fabff5833b77f72e26eb83f06b0e4d85ff
commit: 46c355fabff5833b77f72e26eb83f06b0e4d85ff
branch: main
author: PhysicistJohn <[email protected]>
committer: vstinner <[email protected]>
date: 2026-08-13T21:50:50+02:00
summary:

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 <[email protected]>

files:
A Misc/NEWS.d/next/Library/2026-07-23-20-00-00.gh-issue-154566.byteswap-zd.rst
M Lib/test/test_array.py
M Modules/arraymodule.c

diff --git a/Lib/test/test_array.py b/Lib/test/test_array.py
index d9b608fb23d0c6..c931be6df5fdaa 100755
--- a/Lib/test/test_array.py
+++ b/Lib/test/test_array.py
@@ -1583,6 +1583,22 @@ def test_byteswap(self):
             b.byteswap()
             self.assertEqual(a, b)
 
+    def test_byteswap_single_call_result(self):
+        # A single byteswap() must swap each item's two halves (real,
+        # imag) independently. test_byteswap above only checks that
+        # byteswap() twice round-trips to the original, which passes
+        # even if a single call scrambles multi-item arrays.
+        a = array.array(self.typecode, self.example)
+        original = a.tobytes()
+        a.byteswap()
+        itemsize = a.itemsize
+        half = itemsize // 2
+        expected = bytearray()
+        for i in range(0, len(original), itemsize):
+            item = original[i:i + itemsize]
+            expected += item[half - 1::-1] + item[itemsize - 1:half - 1:-1]
+        self.assertEqual(a.tobytes(), bytes(expected))
+
 
 class HalfFloatTest(FPTest, unittest.TestCase):
     example = [-42.0, 0, 42, 1e2, -1e4]
diff --git a/Misc/NEWS.d/next/Library/2026-07-23-20-00-00.gh-issue-154566.byteswap-zd.rst b/Misc/NEWS.d/next/Library/2026-07-23-20-00-00.gh-issue-154566.byteswap-zd.rst
new file mode 100644
index 00000000000000..e86b9a979946cd
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-07-23-20-00-00.gh-issue-154566.byteswap-zd.rst
@@ -0,0 +1,4 @@
+Fix :meth:`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.
diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c
index 39a399d7a49cf5..a0181c083a6036 100644
--- a/Modules/arraymodule.c
+++ b/Modules/arraymodule.c
@@ -1617,7 +1617,7 @@ array_array_byteswap_impl(arrayobject *self)
         break;
     case 16:
         assert(strcmp(self->ob_descr->typecode, "Zd") == 0);
-        for (p = self->ob_item, i = Py_SIZE(self); --i >= 0; p += 8) {
+        for (p = self->ob_item, i = Py_SIZE(self); --i >= 0; p += 16) {
             char t0 = p[0];
             char t1 = p[1];
             char t2 = p[2];

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.