From ad306bcb08dc0d1a3c4d201f2d41545d8f5511d4 Mon Sep 17 00:00:00 2001 From: PhysicistJohn <54456354+PhysicistJohn@users.noreply.github.com> Date: Thu, 13 Aug 2026 12:50:50 -0700 Subject: [PATCH] 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 <54456354+PhysicistJohn@users.noreply.github.com> Co-authored-by: Victor Stinner --- Lib/test/test_array.py | 16 ++++++++++++++++ ...7-23-20-00-00.gh-issue-154566.byteswap-zd.rst | 4 ++++ Modules/arraymodule.c | 2 +- 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2026-07-23-20-00-00.gh-issue-154566.byteswap-zd.rst diff --git a/Lib/test/test_array.py b/Lib/test/test_array.py index 430f55f2129884b..85cecc083f752ea 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 000000000000000..e86b9a979946cd1 --- /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 45cdf351cbb6ce8..505c44d23735b49 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];