Skip to content

Commit 8e07fa1

Browse files
committed
Resize the list once in PyListWriter_FromBuffer()
Add _PyListWriter_SET_ITEM_STEAL() static inline function.
1 parent c808cb7 commit 8e07fa1

4 files changed

Lines changed: 71 additions & 22 deletions

File tree

‎Doc/c-api/list.rst‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ On Free Threading, it's more efficient than the ``PyList`` C API to create a
304304
On error, set an exception, leave the writer unchanged, and return ``-1``.
305305
306306
If consuming *iterable* raises an exception, the writer is restored to its
307-
previous state: added items are discarded.
307+
previous state: discard added items.
308308
309309
.. c:function:: int PyListWriter_ExtendArray(PyListWriter *writer, PyObject *const *items, Py_ssize_t nitem)
310310
@@ -330,6 +330,8 @@ On Free Threading, it's more efficient than the ``PyList`` C API to create a
330330
On success, return ``0``.
331331
On error, set an exception, leave the writer unchanged, and return ``-1``.
332332
333+
On error, the writer is restored to its previous state: discard added items.
334+
333335
.. c:function:: int PyListWriter_FromBuffer(PyListWriter *writer, Py_buffer *view)
334336
335337
Add values from a buffer.
@@ -338,3 +340,5 @@ On Free Threading, it's more efficient than the ``PyList`` C API to create a
338340
339341
On success, return ``0``.
340342
On error, set an exception, leave the writer unchanged, and return ``-1``.
343+
344+
On error, the writer is restored to its previous state: discard added items.

‎Include/internal/pycore_list.h‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,37 @@ union _PyStackRef;
7979
PyAPI_FUNC(PyObject *)_PyList_FromStackRefStealOnSuccess(const union _PyStackRef *src, Py_ssize_t n);
8080
PyAPI_FUNC(PyObject *)_PyList_AsTupleAndClear(PyListObject *v);
8181

82+
#ifdef Py_DEBUG
83+
# define _PyListWriter_INIT_ITEMS 1
84+
#else
85+
# define _PyListWriter_INIT_ITEMS 0
86+
#endif
87+
88+
extern int _PyListWriter_Resize(
89+
PyListWriter *writer,
90+
Py_ssize_t new_size);
8291
extern void _PyListWriter_ResetSize(
8392
PyListWriter *writer,
8493
Py_ssize_t size);
8594

95+
96+
// The caller must resize the list so it's big enough to set this item.
97+
// A writer list item must be set exactly once.
98+
static inline void
99+
_PyListWriter_SET_ITEM_STEAL(PyListWriter *writer, Py_ssize_t index,
100+
PyObject *item)
101+
{
102+
assert(item != NULL);
103+
104+
PyListObject *list = (PyListObject*)writer;
105+
assert(0 <= index && index < Py_SIZE(list));
106+
#if _PyListWriter_INIT_ITEMS
107+
assert(list->ob_item[index] == NULL);
108+
#endif
109+
110+
list->ob_item[index] = item;
111+
}
112+
86113
#ifdef __cplusplus
87114
}
88115
#endif

‎Objects/listobject.c‎

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4449,7 +4449,7 @@ PyListWriter_Create(Py_ssize_t size)
44494449
// since the list object is not tracked by the GC, and the writer caller
44504450
// must initialize all items.
44514451

4452-
#ifdef Py_DEBUG
4452+
#if _PyListWriter_INIT_ITEMS
44534453
// In debug mode, set all items to NULL to be able to check that they are
44544454
// non-NULL in PyListWriter_Finish().
44554455
if (1 <= size) {
@@ -4498,7 +4498,7 @@ PyListWriter_Finish(PyListWriter *writer)
44984498
// it's left unchanged on memory allocation failure.
44994499
}
45004500

4501-
#ifdef Py_DEBUG
4501+
#if _PyListWriter_INIT_ITEMS
45024502
// Make sure that all items have been initialized
45034503
Py_ssize_t size = Py_SIZE(list);
45044504
PyObject **list_items = list->ob_item;
@@ -4513,13 +4513,21 @@ PyListWriter_Finish(PyListWriter *writer)
45134513
}
45144514

45154515

4516-
static int
4517-
listwriter_resize(PyListWriter *writer, Py_ssize_t new_size)
4516+
int
4517+
_PyListWriter_Resize(PyListWriter *writer, Py_ssize_t new_size)
45184518
{
45194519
PyListObject *list = _PyList_CAST(writer);
4520+
Py_ssize_t old_size = Py_SIZE(list);
45204521

45214522
// On Free Threading, it can call _PyObject_GC_SET_SHARED() on the list
4522-
return list_resize(list, new_size);
4523+
int res = list_resize(list, new_size);
4524+
#if _PyListWriter_INIT_ITEMS
4525+
if (res == 0 && new_size > old_size) {
4526+
memset(&list->ob_item[old_size], 0,
4527+
(new_size - old_size) * sizeof(PyObject*));
4528+
}
4529+
#endif
4530+
return res;
45234531
}
45244532

45254533

@@ -4529,19 +4537,19 @@ PyListWriter_AppendSteal(PyListWriter *writer, PyObject *item)
45294537
assert(listwriter_check_consistency(writer));
45304538
PyListObject *list = _PyList_CAST(writer);
45314539

4532-
Py_ssize_t len = Py_SIZE(list);
4540+
Py_ssize_t index = Py_SIZE(list);
45334541
Py_ssize_t allocated = list->allocated;
4534-
if (len < allocated) {
4535-
list->ob_item[len] = item;
4536-
Py_SET_SIZE(list, len + 1);
4542+
if (index < allocated) {
4543+
Py_SET_SIZE(list, index + 1);
4544+
list->ob_item[index] = item;
45374545
return 0;
45384546
}
45394547

4540-
if (listwriter_resize(writer, len + 1) < 0) {
4548+
if (_PyListWriter_Resize(writer, index + 1) < 0) {
45414549
Py_DECREF(item);
45424550
return -1;
45434551
}
4544-
list->ob_item[len] = item;
4552+
_PyListWriter_SET_ITEM_STEAL(writer, index, item);
45454553
return 0;
45464554
}
45474555

@@ -4568,7 +4576,7 @@ _PyListWriter_ResetSize(PyListWriter *writer, Py_ssize_t size)
45684576
PyObject **items = list->ob_item;
45694577
for (Py_ssize_t i = size; i < new_size; i++) {
45704578
Py_DECREF(items[i]);
4571-
#ifdef Py_DEBUG
4579+
#if _PyListWriter_INIT_ITEMS
45724580
items[i] = NULL;
45734581
#endif
45744582
}
@@ -4652,10 +4660,9 @@ listwriter_extend_array(PyListWriter *writer,
46524660

46534661
PyListObject *list = _PyList_CAST(writer);
46544662
Py_ssize_t size = Py_SIZE(list);
4655-
Py_ssize_t allocated = list->allocated;
46564663
Py_ssize_t new_size = size + nitem;
4657-
if (new_size > allocated) {
4658-
if (listwriter_resize(writer, new_size) < 0) {
4664+
if (new_size > list->allocated) {
4665+
if (_PyListWriter_Resize(writer, new_size) < 0) {
46594666
if (steal) {
46604667
for (Py_ssize_t i = 0; i < nitem; i++) {
46614668
Py_DECREF(items[i]);

‎Objects/memoryobject.c‎

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2401,30 +2401,41 @@ PyListWriter_FromBuffer(PyListWriter *writer, const Py_buffer *view)
24012401
}
24022402

24032403
PyListObject *list = _PyList_CAST(writer);
2404-
Py_ssize_t old_writer_size = Py_SIZE(list);
2404+
Py_ssize_t old_size = Py_SIZE(list);
24052405

24062406
const char *ptr = view->buf;
24072407
const Py_ssize_t shape = view->shape ? view->shape[0] : view->len;
24082408
const Py_ssize_t strides = view->strides ? view->strides[0] : view->itemsize;
24092409
const Py_ssize_t *suboffsets = view->suboffsets;
24102410

2411+
Py_ssize_t new_size = old_size + shape;
2412+
if (new_size > list->allocated) {
2413+
if (_PyListWriter_Resize(writer, new_size) < 0) {
2414+
return -1;
2415+
}
2416+
}
2417+
else {
2418+
Py_SET_SIZE(list, new_size);
2419+
}
2420+
24112421
for (Py_ssize_t i = 0; i < shape; ptr += strides, i++) {
24122422
const char *xptr = ADJUST_PTR(ptr, suboffsets, 0);
2423+
24132424
PyObject *item = unpack_single(NULL, xptr, fmt);
24142425
if (item == NULL) {
2426+
Py_SET_SIZE(list, old_size + i);
24152427
goto error;
24162428
}
2417-
if (PyListWriter_AppendSteal(writer, item) < 0) {
2418-
goto error;
2419-
}
2429+
2430+
_PyListWriter_SET_ITEM_STEAL(writer, old_size + i, item);
24202431
}
24212432
return 0;
24222433

24232434
error:
2424-
if (Py_SIZE(list) != old_writer_size) {
2435+
if (Py_SIZE(list) != old_size) {
24252436
// On error, reset the writer to its previous state:
24262437
// discard added items
2427-
_PyListWriter_ResetSize(writer, old_writer_size);
2438+
_PyListWriter_ResetSize(writer, old_size);
24282439
}
24292440
return -1;
24302441
}

0 commit comments

Comments
 (0)