diff options
author | Eli Bendersky <eliben@gmail.com> | 2012-03-09 11:38:15 (GMT) |
---|---|---|
committer | Eli Bendersky <eliben@gmail.com> | 2012-03-09 11:38:15 (GMT) |
commit | 865756a94cf4f34ea27ce70b5160fd8ff3199a73 (patch) | |
tree | 08c404f53697463f429de5ca1e4dee8822c51d46 /Modules/_elementtree.c | |
parent | f5a1d76b48a9c0b5c9bbd0a59088f3df1c21f426 (diff) | |
download | cpython-865756a94cf4f34ea27ce70b5160fd8ff3199a73.zip cpython-865756a94cf4f34ea27ce70b5160fd8ff3199a73.tar.gz cpython-865756a94cf4f34ea27ce70b5160fd8ff3199a73.tar.bz2 |
Issue #14178: Problem deleting slices with steps != +1 in the _elementtree module.
Fixed the problem and added some tests. Closes #14178
Diffstat (limited to 'Modules/_elementtree.c')
-rw-r--r-- | Modules/_elementtree.c | 70 |
1 files changed, 67 insertions, 3 deletions
diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c index 348fefc..ba37cd7 100644 --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -1343,9 +1343,74 @@ element_ass_subscr(PyObject* self_, PyObject* item, PyObject* value) return -1; } - if (value == NULL) - newlen = 0; + if (value == NULL) { + /* Delete slice */ + size_t cur; + Py_ssize_t i; + + if (slicelen <= 0) + return 0; + + /* Since we're deleting, the direction of the range doesn't matter, + * so for simplicity make it always ascending. + */ + if (step < 0) { + stop = start + 1; + start = stop + step * (slicelen - 1) - 1; + step = -step; + } + + assert((size_t)slicelen <= PY_SIZE_MAX / sizeof(PyObject *)); + + /* recycle is a list that will contain all the children + * scheduled for removal. + */ + if (!(recycle = PyList_New(slicelen))) { + PyErr_NoMemory(); + return -1; + } + + /* This loop walks over all the children that have to be deleted, + * with cur pointing at them. num_moved is the amount of children + * until the next deleted child that have to be "shifted down" to + * occupy the deleted's places. + * Note that in the ith iteration, shifting is done i+i places down + * because i children were already removed. + */ + for (cur = start, i = 0; cur < (size_t)stop; cur += step, ++i) { + /* Compute how many children have to be moved, clipping at the + * list end. + */ + Py_ssize_t num_moved = step - 1; + if (cur + step >= (size_t)self->extra->length) { + num_moved = self->extra->length - cur - 1; + } + + PyList_SET_ITEM(recycle, i, self->extra->children[cur]); + + memmove( + self->extra->children + cur - i, + self->extra->children + cur + 1, + num_moved * sizeof(PyObject *)); + } + + /* Leftover "tail" after the last removed child */ + cur = start + (size_t)slicelen * step; + if (cur < (size_t)self->extra->length) { + memmove( + self->extra->children + cur - slicelen, + self->extra->children + cur, + (self->extra->length - cur) * sizeof(PyObject *)); + } + + self->extra->length -= slicelen; + + /* Discard the recycle list with all the deleted sub-elements */ + Py_XDECREF(recycle); + return 0; + } else { + /* A new slice is actually being assigned */ seq = PySequence_Fast(value, ""); if (!seq) { PyErr_Format( @@ -1367,7 +1432,6 @@ element_ass_subscr(PyObject* self_, PyObject* item, PyObject* value) return -1; } - /* Resize before creating the recycle bin, to prevent refleaks. */ if (newlen > slicelen) { if (element_resize(self, newlen - slicelen) < 0) { |