clean up (Set|Del)ItemInt code and fix negative indexing of objects with length ...
authorStefan Behnel <stefan_ml@behnel.de>
Sat, 16 Mar 2013 10:08:31 +0000 (11:08 +0100)
committerStefan Behnel <stefan_ml@behnel.de>
Sat, 16 Mar 2013 10:08:31 +0000 (11:08 +0100)
Cython/Utility/ObjectHandling.c
tests/run/index.pyx

index 7128030..c97d749 100644 (file)
@@ -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);
     }
index c24d114..6f068da 100644 (file)
@@ -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