fix negative slice indices
authorStefan Behnel <stefan_ml@behnel.de>
Sat, 16 Mar 2013 14:46:46 +0000 (15:46 +0100)
committerStefan Behnel <stefan_ml@behnel.de>
Sat, 16 Mar 2013 14:46:46 +0000 (15:46 +0100)
Cython/Utility/ObjectHandling.c
tests/run/slice2.pyx

index 9f34f7e..1ac5cc5 100644 (file)
@@ -427,15 +427,15 @@ static CYTHON_INLINE int __Pyx_DelItemInt_Fast(PyObject *o, Py_ssize_t i,
     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 (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;
-                }
+            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);
     }
@@ -476,6 +476,25 @@ static CYTHON_INLINE PyObject* __Pyx_PySequence_GetObjectSlice(
             } else
                 cstop = PY_SSIZE_T_MAX;
         }
+        if (unlikely((cstart < 0) | (cstop < 0)) && likely(ms->sq_length)) {
+            Py_ssize_t l = ms->sq_length(obj);
+            if (likely(l >= 0)) {
+                if (cstop < 0) {
+                    cstop += l;
+                    if (cstop < 0) cstop = 0;
+                }
+                if (cstart < 0) {
+                    cstart += l;
+                    if (cstart < 0) cstart = 0;
+                }
+            } else {
+                // if length > max(Py_ssize_t), maybe the object can wrap around itself?
+                if (PyErr_ExceptionMatches(PyExc_OverflowError))
+                    PyErr_Clear();
+                else
+                    return NULL;
+            }
+        }
         return ms->sq_slice(obj, cstart, cstop);
     }
 #endif
index 7c7cd37..e638d1f 100644 (file)
@@ -1,47 +1,74 @@
-def f(obj1, obj2, obj3, obj4):
+def test_full(seq):
     """
     >>> l = [1,2,3,4]
-    >>> f(1, l, 2, 3)
+    >>> test_full(l)
     [1, 2, 3, 4]
-    >>> l == f(1, l, 2, 3)
+    >>> l == test_full(l)
     True
-    >>> l is f(1, l, 2, 3)
+    >>> l is test_full(l)
     False
-    >>> try: f(1, 42, 2, 3)
+    >>> try: test_full(42)
     ... except TypeError: pass
     """
-    obj1 = obj2[:]
-    return obj1
+    obj = seq[:]
+    return obj
 
-def g(obj1, obj2, obj3, obj4):
+def test_start(seq, start):
     """
-    >>> g(1, [1,2,3,4], 2, 3)
+    >>> test_start([1,2,3,4], 2)
     [3, 4]
-    >>> try: g(1, 42, 2, 3)
+    >>> test_start([1,2,3,4], 3)
+    [4]
+    >>> test_start([1,2,3,4], 4)
+    []
+    >>> test_start([1,2,3,4], 8)
+    []
+    >>> test_start([1,2,3,4], -3)
+    [2, 3, 4]
+    >>> test_start([1,2,3,4], -4)
+    [1, 2, 3, 4]
+    >>> test_start([1,2,3,4], -8)
+    [1, 2, 3, 4]
+    >>> test_start([1,2,3,4], 0)
+    [1, 2, 3, 4]
+    >>> try: test_start(42, 2, 3)
     ... except TypeError: pass
     """
-    obj1 = obj2[obj3:]
-    return obj1
+    obj = seq[start:]
+    return obj
 
-def h(obj1, obj2, obj3, obj4):
+def test_stop(seq, stop):
     """
-    >>> h(1, [1,2,3,4], 2, 3)
+    >>> test_stop([1,2,3,4], 3)
     [1, 2, 3]
-    >>> try: h(1, 42, 2, 3)
+    >>> test_stop([1,2,3,4], -1)
+    [1, 2, 3]
+    >>> test_stop([1,2,3,4], -3)
+    [1]
+    >>> test_stop([1,2,3,4], -4)
+    []
+    >>> test_stop([1,2,3,4], -8)
+    []
+    >>> test_stop([1,2,3,4], 0)
+    []
+    >>> try: test_stop(42, 3)
     ... except TypeError: pass
     """
-    obj1 = obj2[:obj4]
-    return obj1
+    obj = seq[:stop]
+    return obj
 
-def j(obj1, obj2, obj3, obj4):
+def test_start_and_stop(seq, start, stop):
     """
-    >>> j(1, [1,2,3,4], 2, 3)
+    >>> l = [1,2,3,4]
+    >>> test_start_and_stop(l, 2, 3)
     [3]
-    >>> try: j(1, 42, 2, 3)
+    >>> test_start_and_stop(l, -3, -1)
+    [2, 3]
+    >>> try: test_start_and_stop(42, 2, 3)
     ... except TypeError: pass
     """
-    obj1 = obj2[obj3:obj4]
-    return obj1
+    obj = seq[start:stop]
+    return obj
 
 class A(object):
     pass