diff -r 84beb5cba826 Doc/c-api/slice.rst --- a/Doc/c-api/slice.rst Wed Dec 28 15:43:08 2016 -0800 +++ b/Doc/c-api/slice.rst Fri Dec 30 00:26:58 2016 +0200 @@ -43,16 +43,20 @@ Slice Objects before. -.. c:function:: int PySlice_GetIndicesEx(PyObject *slice, Py_ssize_t length, Py_ssize_t *start, Py_ssize_t *stop, Py_ssize_t *step, Py_ssize_t *slicelength) +.. c:macro:: int PySlice_GetIndicesEx(PyObject *slice, Py_ssize_t length, Py_ssize_t *start, Py_ssize_t *stop, Py_ssize_t *step, Py_ssize_t *slicelength) Usable replacement for :c:func:`PySlice_GetIndices`. Retrieve the start, stop, and step indices from the slice object *slice* assuming a sequence of length *length*, and store the length of the slice in *slicelength*. Out of bounds indices are clipped in a manner consistent with the handling of - normal slices. + normal slices. Caution: arguments *start*, *stop* and *step* are evaluated + more than once. Returns ``0`` on success and ``-1`` on error with exception set. .. versionchanged:: 3.2 The parameter type for the *slice* parameter was ``PySliceObject*`` before. + + .. versionchanged:: 3.5.3 + Implemented as a macro. diff -r 84beb5cba826 Include/sliceobject.h --- a/Include/sliceobject.h Wed Dec 28 15:43:08 2016 -0800 +++ b/Include/sliceobject.h Fri Dec 30 00:26:58 2016 +0200 @@ -41,8 +41,31 @@ PyAPI_FUNC(int) _PySlice_GetLongIndices( PyAPI_FUNC(int) PySlice_GetIndices(PyObject *r, Py_ssize_t length, Py_ssize_t *start, Py_ssize_t *stop, Py_ssize_t *step); PyAPI_FUNC(int) PySlice_GetIndicesEx(PyObject *r, Py_ssize_t length, - Py_ssize_t *start, Py_ssize_t *stop, - Py_ssize_t *step, Py_ssize_t *slicelength); + Py_ssize_t *start, Py_ssize_t *stop, + Py_ssize_t *step, Py_ssize_t *slicelength); +#define PySlice_GetIndicesEx(slice, length, start, stop, step, slicelen) ( \ + _PySlice_Unpack((slice), (start), (stop), (step)) < 0 ? -1 : \ + ((*slicelen = _PySlice_AdjustIndices((length), (start), (stop), *(step))), \ + 0)) + +/* Extract the start, stop and step data members from a slice object as + C integers. Silently reduce values larger than PY_SSIZE_T_MAX to + PY_SSIZE_T_MAX, silently boost the start and stop values less than + -PY_SSIZE_T_MAX-1 to -PY_SSIZE_T_MAX-1, and silently boost the step values + less than -PY_SSIZE_T_MAX to -PY_SSIZE_T_MAX. + Return -1 on error, 0 on success. + */ +PyAPI_FUNC(int) _PySlice_Unpack(PyObject *slice, + Py_ssize_t *start, Py_ssize_t *stop, Py_ssize_t *step); + +/* Adjust start/end slice indices assuming a sequence of specified length. + Out of bounds indices are clipped in a manner consistent with the handling + of normal slices. + Return the length of the slice. Always successful. Doesn't call Python code. + */ +PyAPI_FUNC(Py_ssize_t) _PySlice_AdjustIndices(Py_ssize_t length, + Py_ssize_t *start, Py_ssize_t *stop, + Py_ssize_t step); #ifdef __cplusplus } diff -r 84beb5cba826 Objects/sliceobject.c --- a/Objects/sliceobject.c Wed Dec 28 15:43:08 2016 -0800 +++ b/Objects/sliceobject.c Fri Dec 30 00:26:58 2016 +0200 @@ -191,15 +191,12 @@ PySlice_GetIndices(PyObject *_r, Py_ssiz } int -PySlice_GetIndicesEx(PyObject *_r, Py_ssize_t length, - Py_ssize_t *start, Py_ssize_t *stop, Py_ssize_t *step, - Py_ssize_t *slicelength) +_PySlice_Unpack(PyObject *_r, + Py_ssize_t *start, Py_ssize_t *stop, Py_ssize_t *step) { PySliceObject *r = (PySliceObject*)_r; /* this is harder to get right than you might think */ - Py_ssize_t defstart, defstop; - if (r->step == Py_None) { *step = 1; } @@ -219,42 +216,75 @@ PySlice_GetIndicesEx(PyObject *_r, Py_ss *step = -PY_SSIZE_T_MAX; } - defstart = *step < 0 ? length-1 : 0; - defstop = *step < 0 ? -1 : length; - if (r->start == Py_None) { - *start = defstart; + *start = *step < 0 ? PY_SSIZE_T_MAX-1 : 0;; } else { if (!_PyEval_SliceIndex(r->start, start)) return -1; - if (*start < 0) *start += length; - if (*start < 0) *start = (*step < 0) ? -1 : 0; - if (*start >= length) - *start = (*step < 0) ? length - 1 : length; } if (r->stop == Py_None) { - *stop = defstop; + *stop = *step < 0 ? -PY_SSIZE_T_MAX : PY_SSIZE_T_MAX; } else { if (!_PyEval_SliceIndex(r->stop, stop)) return -1; - if (*stop < 0) *stop += length; - if (*stop < 0) *stop = (*step < 0) ? -1 : 0; - if (*stop >= length) - *stop = (*step < 0) ? length - 1 : length; } - if ((*step < 0 && *stop >= *start) - || (*step > 0 && *start >= *stop)) { - *slicelength = 0; + return 0; +} + +Py_ssize_t +_PySlice_AdjustIndices(Py_ssize_t length, + Py_ssize_t *start, Py_ssize_t *stop, Py_ssize_t step) +{ + /* this is harder to get right than you might think */ + + assert(step != 0); + assert(step >= -PY_SSIZE_T_MAX); + + if (*start < 0) { + *start += length; + if (*start < 0) { + *start = (step < 0) ? -1 : 0; + } } - else if (*step < 0) { - *slicelength = (*stop-*start+1)/(*step)+1; + else if (*start >= length) { + *start = (step < 0) ? length - 1 : length; + } + + if (*stop < 0) { + *stop += length; + if (*stop < 0) { + *stop = (step < 0) ? -1 : 0; + } + } + else if (*stop >= length) { + *stop = (step < 0) ? length - 1 : length; + } + + if (step < 0) { + if (*stop < *start) { + return (*start - *stop - 1) / (-step) + 1; + } } else { - *slicelength = (*stop-*start-1)/(*step)+1; + if (*start < *stop) { + return (*stop - *start - 1) / step + 1; + } } + return 0; +} +#undef PySlice_GetIndicesEx + +int +PySlice_GetIndicesEx(PyObject *_r, Py_ssize_t length, + Py_ssize_t *start, Py_ssize_t *stop, Py_ssize_t *step, + Py_ssize_t *slicelength) +{ + if (_PySlice_Unpack(_r, start, stop, step) < 0) + return -1; + *slicelength = _PySlice_AdjustIndices(length, start, stop, *step); return 0; } diff -r 84beb5cba826 PC/python3.def --- a/PC/python3.def Wed Dec 28 15:43:08 2016 -0800 +++ b/PC/python3.def Fri Dec 30 00:26:58 2016 +0200 @@ -757,6 +757,8 @@ EXPORTS _PyObject_GC_Resize=python35._PyObject_GC_Resize _PyObject_New=python35._PyObject_New _PyObject_NewVar=python35._PyObject_NewVar + _PySlice_AdjustIndices=python35._PySlice_AdjustIndices + _PySlice_Unpack=python35._PySlice_Unpack _PyState_AddModule=python35._PyState_AddModule _PyThreadState_Init=python35._PyThreadState_Init _PyThreadState_Prealloc=python35._PyThreadState_Prealloc