From f1370b3f07004374f505e3df2d33919e01458110 Mon Sep 17 00:00:00 2001 From: Stefan Behnel Date: Sat, 16 Mar 2013 11:08:31 +0100 Subject: [PATCH] clean up (Set|Del)ItemInt code and fix negative indexing of objects with length > max(Py_ssize_t) --- Cython/Utility/ObjectHandling.c | 42 ++++++++++++++++++++++++++++++++------- tests/run/index.pyx | 44 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 78 insertions(+), 8 deletions(-) diff --git a/Cython/Utility/ObjectHandling.c b/Cython/Utility/ObjectHandling.c index 7128030..c97d749 100644 --- a/Cython/Utility/ObjectHandling.c +++ b/Cython/Utility/ObjectHandling.c @@ -302,7 +302,8 @@ static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Fast(PyObject *o, Py_ssize_t i, Py_INCREF(r); return r; } - } else { /* inlined PySequence_GetItem() + special cased length overflow */ + } else { + // inlined PySequence_GetItem() + special cased length overflow PySequenceMethods *m = Py_TYPE(o)->tp_as_sequence; if (likely(m && m->sq_item)) { if (wraparound && unlikely(i < 0) && likely(m->sq_length)) { @@ -335,6 +336,12 @@ static CYTHON_INLINE PyObject *__Pyx_GetItemInt_Fast(PyObject *o, Py_ssize_t i, __Pyx_SetItemInt_Fast(o, i, v, is_list, wraparound, boundscheck) : \ __Pyx_SetItemInt_Generic(o, to_py_func(i), v)) +static CYTHON_INLINE int __Pyx_SetItemInt_Generic(PyObject *o, PyObject *j, PyObject *v); +static CYTHON_INLINE int __Pyx_SetItemInt_Fast(PyObject *o, Py_ssize_t i, PyObject *v, + int is_list, int wraparound, int boundscheck); + +/////////////// SetItemInt /////////////// + static CYTHON_INLINE int __Pyx_SetItemInt_Generic(PyObject *o, PyObject *j, PyObject *v) { int r; if (!j) return -1; @@ -355,13 +362,21 @@ static CYTHON_INLINE int __Pyx_SetItemInt_Fast(PyObject *o, Py_ssize_t i, PyObje Py_DECREF(old); return 1; } - } else { /* inlined PySequence_SetItem() */ + } else { + // inlined PySequence_SetItem() + special cased length overflow PySequenceMethods *m = Py_TYPE(o)->tp_as_sequence; if (likely(m && m->sq_ass_item)) { if (wraparound && unlikely(i < 0) && likely(m->sq_length)) { Py_ssize_t l = m->sq_length(o); - if (unlikely(l < 0)) return -1; - i += l; + if (likely(l >= 0)) { + i += l; + } else { + // if length > max(Py_ssize_t), maybe the object can wrap around itself? + if (PyErr_ExceptionMatches(PyExc_OverflowError)) + PyErr_Clear(); + else + return -1; + } } return m->sq_ass_item(o, i, v); } @@ -386,6 +401,12 @@ static CYTHON_INLINE int __Pyx_SetItemInt_Fast(PyObject *o, Py_ssize_t i, PyObje __Pyx_DelItemInt_Fast(o, i, is_list, wraparound) : \ __Pyx_DelItem_Generic(o, to_py_func(i))) +static CYTHON_INLINE int __Pyx_DelItem_Generic(PyObject *o, PyObject *j); +static CYTHON_INLINE int __Pyx_DelItemInt_Fast(PyObject *o, Py_ssize_t i, + CYTHON_UNUSED int is_list, int wraparound); + +/////////////// DelItemInt /////////////// + static CYTHON_INLINE int __Pyx_DelItem_Generic(PyObject *o, PyObject *j) { int r; if (!j) return -1; @@ -401,13 +422,20 @@ static CYTHON_INLINE int __Pyx_DelItemInt_Fast(PyObject *o, Py_ssize_t i, return PySequence_DelItem(o, i); } #else - /* inlined PySequence_DelItem() */ + // inlined PySequence_DelItem() + special cased length overflow PySequenceMethods *m = Py_TYPE(o)->tp_as_sequence; if (likely(m && m->sq_ass_item)) { if (wraparound && unlikely(i < 0) && likely(m->sq_length)) { Py_ssize_t l = m->sq_length(o); - if (unlikely(l < 0)) return -1; - i += l; + if (likely(l >= 0)) { + i += l; + } else { + // if length > max(Py_ssize_t), maybe the object can wrap around itself? + if (PyErr_ExceptionMatches(PyExc_OverflowError)) + PyErr_Clear(); + else + return -1; + } } return m->sq_ass_item(o, i, (PyObject *)NULL); } diff --git a/tests/run/index.pyx b/tests/run/index.pyx index c24d114..6f068da 100644 --- a/tests/run/index.pyx +++ b/tests/run/index.pyx @@ -5,11 +5,15 @@ __doc__ = u""" TypeError: 'int' object ... """ +cdef Py_ssize_t maxsize + import sys if sys.version_info < (2,5): __doc__ = __doc__.replace(u"'int' object ...", u'unsubscriptable object') + maxsize = min(sys.maxint, 2**31-1) +else: + maxsize = getattr(sys, 'maxsize', getattr(sys, 'maxint', None)) -cdef Py_ssize_t maxsize = getattr(sys, 'maxsize', getattr(sys, 'maxint', None)) py_maxsize = maxsize import cython @@ -166,12 +170,22 @@ def large_literal_index(object o): class LargeIndexable(object): + expected = None + def __len__(self): raise OverflowError def __getitem__(self, index): return index + def __setitem__(self, index, value): + assert index == value == self.expected + self.expected = None + + def __delitem__(self, index): + assert self.expected == index + self.expected = None + def test_large_indexing(obj): """ @@ -199,3 +213,31 @@ def test_large_indexing(obj): obj[maxsize], obj[-maxsize], #obj[maxsize*2], obj[-maxsize*2] # FIXME! ) + + +def del_large_index(obj, Py_ssize_t index): + """ + >>> obj = LargeIndexable() + >>> del_large_index(obj, 0) + >>> del_large_index(obj, 1) + >>> del_large_index(obj, -1) + >>> del_large_index(obj, py_maxsize) + >>> del_large_index(obj, -py_maxsize) + """ + obj.expected = index + del obj[index] + assert obj.expected is None + + +def set_large_index(obj, Py_ssize_t index): + """ + >>> obj = LargeIndexable() + >>> set_large_index(obj, 0) + >>> set_large_index(obj, 1) + >>> set_large_index(obj, -1) + >>> set_large_index(obj, py_maxsize) + >>> set_large_index(obj, -py_maxsize) + """ + obj.expected = index + obj[index] = index + assert obj.expected is None -- 2.7.4