Skip to content

array.byteswap() corrupts data for 'Zd' (complex double) arrays with more than one element #154566

Description

@PhysicistJohn

Bug description:

array.array('Zd', ...).byteswap() produces incorrect data for arrays
with more than one element. The 16-byte-item loop in array_array_byteswap_impl
(Modules/arraymodule.c) advances the buffer pointer by only 8 bytes per
iteration even though each iteration manually swaps a full 16-byte item
(two independent 8-byte halves, for the real/imaginary double components).
This causes every item after the first to overlap the previous iteration's
byte window and come out scrambled.

Reproducer:

import array
a = array.array('Zd', [1+2j, 3+4j])
raw_before = a.tobytes()
a.byteswap()
raw_after = a.tobytes()

expected = bytearray()
for i in range(0, len(raw_before), 8):
    expected += raw_before[i:i+8][::-1]

print(bytes(expected) == raw_after)  # False -- should be True

A single byteswap() call gives the wrong bytes for the second item
onward. Calling byteswap() twice happens to round-trip back to the
original value (the corruption is self-canceling under double
application), which is why the existing test suite (test_byteswap in
Lib/test/test_array.py, which only checks a double-call round-trip)
doesn't catch it.

The sibling cases in the same switch statement (case 4:, case 8:)
correctly advance the pointer by the item's own size (4, 8 bytes); the
case 16: block is the only one where the increment doesn't match the
16 bytes actually touched in the loop body.

CPython versions tested on:

CPython main branch (3.16.0a0)

Operating systems tested on:

macOS (arm64)

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    extension-modulesC modules in the Modules dirtype-bugAn unexpected behavior, bug, or error

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions