diff options
Diffstat (limited to 'Objects/sliceobject.c')
-rw-r--r-- | Objects/sliceobject.c | 191 |
1 files changed, 183 insertions, 8 deletions
diff --git a/Objects/sliceobject.c b/Objects/sliceobject.c index 1593335..52f1c89 100644 --- a/Objects/sliceobject.c +++ b/Objects/sliceobject.c @@ -299,23 +299,198 @@ static PyMemberDef slice_members[] = { {0} }; +/* Helper function to convert a slice argument to a PyLong, and raise TypeError + with a suitable message on failure. */ + static PyObject* -slice_indices(PySliceObject* self, PyObject* len) +evaluate_slice_index(PyObject *v) { - Py_ssize_t ilen, start, stop, step, slicelength; + if (PyIndex_Check(v)) { + return PyNumber_Index(v); + } + else { + PyErr_SetString(PyExc_TypeError, + "slice indices must be integers or " + "None or have an __index__ method"); + return NULL; + } +} - ilen = PyNumber_AsSsize_t(len, PyExc_OverflowError); +/* Compute slice indices given a slice and length. Return -1 on failure. Used + by slice.indices and rangeobject slicing. Assumes that `len` is a + nonnegative instance of PyLong. */ - if (ilen == -1 && PyErr_Occurred()) { - return NULL; +int +_PySlice_GetLongIndices(PySliceObject *self, PyObject *length, + PyObject **start_ptr, PyObject **stop_ptr, + PyObject **step_ptr) +{ + PyObject *start=NULL, *stop=NULL, *step=NULL; + PyObject *upper=NULL, *lower=NULL; + int step_is_negative, cmp_result; + + /* Convert step to an integer; raise for zero step. */ + if (self->step == Py_None) { + step = PyLong_FromLong(1L); + if (step == NULL) + goto error; + step_is_negative = 0; + } + else { + int step_sign; + step = evaluate_slice_index(self->step); + if (step == NULL) + goto error; + step_sign = _PyLong_Sign(step); + if (step_sign == 0) { + PyErr_SetString(PyExc_ValueError, + "slice step cannot be zero"); + goto error; + } + step_is_negative = step_sign < 0; + } + + /* Find lower and upper bounds for start and stop. */ + if (step_is_negative) { + lower = PyLong_FromLong(-1L); + if (lower == NULL) + goto error; + + upper = PyNumber_Add(length, lower); + if (upper == NULL) + goto error; + } + else { + lower = PyLong_FromLong(0L); + if (lower == NULL) + goto error; + + upper = length; + Py_INCREF(upper); } - if (PySlice_GetIndicesEx((PyObject*)self, ilen, &start, &stop, - &step, &slicelength) < 0) { + /* Compute start. */ + if (self->start == Py_None) { + start = step_is_negative ? upper : lower; + Py_INCREF(start); + } + else { + start = evaluate_slice_index(self->start); + if (start == NULL) + goto error; + + if (_PyLong_Sign(start) < 0) { + /* start += length */ + PyObject *tmp = PyNumber_Add(start, length); + Py_DECREF(start); + start = tmp; + if (start == NULL) + goto error; + + cmp_result = PyObject_RichCompareBool(start, lower, Py_LT); + if (cmp_result < 0) + goto error; + if (cmp_result) { + Py_INCREF(lower); + Py_DECREF(start); + start = lower; + } + } + else { + cmp_result = PyObject_RichCompareBool(start, upper, Py_GT); + if (cmp_result < 0) + goto error; + if (cmp_result) { + Py_INCREF(upper); + Py_DECREF(start); + start = upper; + } + } + } + + /* Compute stop. */ + if (self->stop == Py_None) { + stop = step_is_negative ? lower : upper; + Py_INCREF(stop); + } + else { + stop = evaluate_slice_index(self->stop); + if (stop == NULL) + goto error; + + if (_PyLong_Sign(stop) < 0) { + /* stop += length */ + PyObject *tmp = PyNumber_Add(stop, length); + Py_DECREF(stop); + stop = tmp; + if (stop == NULL) + goto error; + + cmp_result = PyObject_RichCompareBool(stop, lower, Py_LT); + if (cmp_result < 0) + goto error; + if (cmp_result) { + Py_INCREF(lower); + Py_DECREF(stop); + stop = lower; + } + } + else { + cmp_result = PyObject_RichCompareBool(stop, upper, Py_GT); + if (cmp_result < 0) + goto error; + if (cmp_result) { + Py_INCREF(upper); + Py_DECREF(stop); + stop = upper; + } + } + } + + *start_ptr = start; + *stop_ptr = stop; + *step_ptr = step; + Py_DECREF(upper); + Py_DECREF(lower); + return 0; + + error: + *start_ptr = *stop_ptr = *step_ptr = NULL; + Py_XDECREF(start); + Py_XDECREF(stop); + Py_XDECREF(step); + Py_XDECREF(upper); + Py_XDECREF(lower); + return -1; +} + +/* Implementation of slice.indices. */ + +static PyObject* +slice_indices(PySliceObject* self, PyObject* len) +{ + PyObject *start, *stop, *step; + PyObject *length; + int error; + + /* Convert length to an integer if necessary; raise for negative length. */ + length = PyNumber_Index(len); + if (length == NULL) + return NULL; + + if (_PyLong_Sign(length) < 0) { + PyErr_SetString(PyExc_ValueError, + "length should not be negative"); + Py_DECREF(length); return NULL; } - return Py_BuildValue("(nnn)", start, stop, step); + error = _PySlice_GetLongIndices(self, length, &start, &stop, &step); + Py_DECREF(length); + if (error == -1) + return NULL; + else + return Py_BuildValue("(NNN)", start, stop, step); } PyDoc_STRVAR(slice_indices_doc, |