Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions Lib/test/test_buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -4447,6 +4447,41 @@ def test_bytearray_release_buffer_read_flag(self):
with self.assertRaises(SystemError):
obj.__buffer__(inspect.BufferFlags.WRITE)

@support.cpython_only
@unittest.skipIf(_testcapi is None, "requires _testcapi")
def test_bytearray_alignment(self):
# gh-140557: pointer alignment of buffers including empty allocation
# should be at least to `size_t`.
align = struct.calcsize("N")
cases = [
bytearray(),
bytearray(1),
bytearray(b"0123456789abcdef"),
bytearray(16),
]
ptrs = [_testcapi.buffer_pointer_as_int(array) for array in cases]
self.assertEqual([ptr % align for ptr in ptrs], [0]*len(ptrs))

@support.cpython_only
@unittest.skipIf(_testcapi is None, "requires _testcapi")
def test_array_alignment(self):
# gh-140557: pointer alignment of buffers including empty allocation
# should match the maximum array alignment.
align = max(struct.calcsize(fmt) for fmt in ARRAY)
cases = [array.array(fmt) for fmt in ARRAY]
# Empty arrays
self.assertEqual(
[_testcapi.buffer_pointer_as_int(case) % align for case in cases],
[0] * len(cases),
)
for case in cases:
case.append(0)
# Allocated arrays
self.assertEqual(
[_testcapi.buffer_pointer_as_int(case) % align for case in cases],
[0] * len(cases),
)

@support.cpython_only
def test_pybuffer_size_from_format(self):
# basic tests
Expand Down
1 change: 1 addition & 0 deletions Misc/ACKS
Original file line number Diff line number Diff line change
Expand Up @@ -1151,6 +1151,7 @@ Per Lindqvist
Eric Lindvall
Gregor Lingl
Everett Lipman
Jake Lishman
Mirko Liss
Alexander Liu
Hui Liu
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
:class:`bytearray` buffers now have the same alignment
when empty as when allocated. Unaligned buffers can still be created by slicing.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
:class:`array.array` buffers now have the same alignment when empty as when
allocated. Unaligned buffers can still be created by slicing.
24 changes: 24 additions & 0 deletions Modules/_testcapi/buffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,27 @@ static PyTypeObject testBufType = {
.tp_members = testbuf_members
};

/* Get the pointer from a buffer-supporting object as a PyLong.
*
* Used to test alignment properties. */
static PyObject *
buffer_pointer_as_int(PyObject *Py_UNUSED(module), PyObject *obj)
{
PyObject *out;
Py_buffer view;
if (PyObject_GetBuffer(obj, &view, PyBUF_SIMPLE) != 0) {
return NULL;
}
out = PyLong_FromVoidPtr(view.buf);
PyBuffer_Release(&view);
return out;
}

static PyMethodDef test_methods[] = {
{"buffer_pointer_as_int", buffer_pointer_as_int, METH_O},
{NULL},
};

int
_PyTestCapi_Init_Buffer(PyObject *m) {
if (PyType_Ready(&testBufType) < 0) {
Expand All @@ -106,6 +127,9 @@ _PyTestCapi_Init_Buffer(PyObject *m) {
if (PyModule_AddObjectRef(m, "testBuf", (PyObject *)&testBufType)) {
return -1;
}
if (PyModule_AddFunctions(m, test_methods) < 0) {
return -1;
}

return 0;
}
2 changes: 1 addition & 1 deletion Modules/arraymodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -2655,7 +2655,7 @@ array_ass_subscr(PyObject *op, PyObject *item, PyObject *value)
}
}

static const void *emptybuf = "";
static const _Py_ALIGNED_DEF(ALIGNOF_MAX_ALIGN_T, char) emptybuf[] = "";


static int
Expand Down
Loading