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

hugovk <[email protected]>
Newsgroups gmane.comp.python.cvs
Message-ID <[email protected]>
https://github.com/python/cpython/commit/71cb1655b944337f83f68b188c10fd13a3919163
commit: 71cb1655b944337f83f68b188c10fd13a3919163
branch: 3.15
author: Miss Islington (bot) <[email protected]>
committer: hugovk <[email protected]>
date: 2026-08-14T17:54:37+09:00
summary:

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

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 46c355fabff5833b77f72e26eb83f06b0e4d85ff)

Co-authored-by: PhysicistJohn <[email protected]>
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 430f55f2129884..85cecc083f752e 100755
--- a/Lib/test/test_array.py
+++ b/Lib/test/test_array.py
@@ -1638,6 +1638,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 45cdf351cbb6ce..505c44d23735b4 100644
--- a/Modules/arraymodule.c
+++ b/Modules/arraymodule.c
@@ -1661,7 +1661,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.