diff options
author | Mark Dickinson <dickinsm@gmail.com> | 2009-09-22 21:47:24 (GMT) |
---|---|---|
committer | Mark Dickinson <dickinsm@gmail.com> | 2009-09-22 21:47:24 (GMT) |
commit | 3e124ae739db8dc6ad43687d6b14466a572a6f8e (patch) | |
tree | c3923f748294a32af0b47a455ff91391c3dffd54 /Objects/rangeobject.c | |
parent | 2df811362213d9e987193772d3bf835a204f2727 (diff) | |
download | cpython-3e124ae739db8dc6ad43687d6b14466a572a6f8e.zip cpython-3e124ae739db8dc6ad43687d6b14466a572a6f8e.tar.gz cpython-3e124ae739db8dc6ad43687d6b14466a572a6f8e.tar.bz2 |
Issue #1766304: Optimize membership testing for ranges: 'n in range(...)'
does an O(1) check, if n is an integer. Non-integers aren't affected.
Thanks Robert Lehmann.
Diffstat (limited to 'Objects/rangeobject.c')
-rw-r--r-- | Objects/rangeobject.c | 57 |
1 files changed, 57 insertions, 0 deletions
diff --git a/Objects/rangeobject.c b/Objects/rangeobject.c index 88ca698..213f3dd 100644 --- a/Objects/rangeobject.c +++ b/Objects/rangeobject.c @@ -273,12 +273,69 @@ range_reduce(rangeobject *r, PyObject *args) r->start, r->stop, r->step); } +static int +range_contains(rangeobject *r, PyObject *ob) { + if (PyLong_Check(ob)) { + int cmp1, cmp2, cmp3; + PyObject *tmp1 = NULL; + PyObject *tmp2 = NULL; + PyObject *zero = NULL; + int result = -1; + + zero = PyLong_FromLong(0); + if (zero == NULL) /* MemoryError in int(0) */ + goto end; + + /* Check if the value can possibly be in the range. */ + + cmp1 = PyObject_RichCompareBool(r->step, zero, Py_GT); + if (cmp1 == -1) + goto end; + if (cmp1 == 1) { /* positive steps: start <= ob < stop */ + cmp2 = PyObject_RichCompareBool(r->start, ob, Py_LE); + cmp3 = PyObject_RichCompareBool(ob, r->stop, Py_LT); + } + else { /* negative steps: stop < ob <= start */ + cmp2 = PyObject_RichCompareBool(ob, r->start, Py_LE); + cmp3 = PyObject_RichCompareBool(r->stop, ob, Py_LT); + } + + if (cmp2 == -1 || cmp3 == -1) /* TypeError */ + goto end; + if (cmp2 == 0 || cmp3 == 0) { /* ob outside of range */ + result = 0; + goto end; + } + + /* Check that the stride does not invalidate ob's membership. */ + tmp1 = PyNumber_Subtract(ob, r->start); + if (tmp1 == NULL) + goto end; + tmp2 = PyNumber_Remainder(tmp1, r->step); + if (tmp2 == NULL) + goto end; + /* result = (int(ob) - start % step) == 0 */ + result = PyObject_RichCompareBool(tmp2, zero, Py_EQ); + end: + Py_XDECREF(tmp1); + Py_XDECREF(tmp2); + Py_XDECREF(zero); + return result; + } + /* Fall back to iterative search. */ + return (int)_PySequence_IterSearch((PyObject*)r, ob, + PY_ITERSEARCH_CONTAINS); +} + static PySequenceMethods range_as_sequence = { (lenfunc)range_length, /* sq_length */ 0, /* sq_concat */ 0, /* sq_repeat */ (ssizeargfunc)range_item, /* sq_item */ 0, /* sq_slice */ + 0, /* sq_ass_item */ + 0, /* sq_ass_slice */ + (objobjproc)range_contains, /* sq_contains */ }; static PyObject * range_iter(PyObject *seq); |