Index: Objects/rangeobject.c =================================================================== --- Objects/rangeobject.c (revision 62506) +++ Objects/rangeobject.c (working copy) @@ -14,6 +14,7 @@ PyObject *start; PyObject *stop; PyObject *step; + Py_ssize_t length; } rangeobject; /* Helper function for validating step. Always returns a new reference or @@ -43,6 +44,9 @@ return step; } +static Py_ssize_t range_compute_length(PyObject *start, + PyObject *stop, PyObject *step); + /* XXX(nnorwitz): should we error check if the user passes any empty ranges? range(-10) range(0, -5) @@ -53,6 +57,8 @@ { rangeobject *obj = NULL; PyObject *start = NULL, *stop = NULL, *step = NULL; + PyObject *len_obj = NULL, *tmp = NULL; + Py_ssize_t length; if (!_PyArg_NoKeywords("range()", kw)) return NULL; @@ -81,18 +87,39 @@ goto Fail; } + length = range_compute_length(start, stop, step); + if (length < 0) + goto Fail; + + /* Normalize stop */ + len_obj = PyLong_FromSsize_t(length); + if (len_obj == NULL) + goto Fail; + tmp = PyNumber_Multiply(len_obj, step); + Py_DECREF(len_obj); + if (tmp == NULL) + goto Fail; + Py_DECREF(tmp); + Py_DECREF(stop); + stop = PyNumber_Add(start, tmp); + if (stop == NULL) + goto Fail; + obj = PyObject_New(rangeobject, &PyRange_Type); if (obj == NULL) goto Fail; obj->start = start; obj->stop = stop; obj->step = step; + obj->length = length; return (PyObject *) obj; Fail: Py_XDECREF(start); Py_XDECREF(stop); Py_XDECREF(step); + Py_XDECREF(len_obj); + Py_XDECREF(tmp); return NULL; } @@ -116,16 +143,16 @@ * Arguments MUST return 1 with either PyLong_Check() or * PyLong_Check(). Return -1 when there is an error. */ -static PyObject* -range_length_obj(rangeobject *r) +static Py_ssize_t +range_compute_length(PyObject *start, PyObject *stop, PyObject *step) { /* ------------------------------------------------------------- Algorithm is equal to that of get_len_of_range(), but it operates on PyObjects (which are assumed to be PyLong or PyInt objects). ---------------------------------------------------------------*/ + Py_ssize_t length; int cmp_result, cmp_call; PyObject *lo, *hi; - PyObject *step = NULL; PyObject *diff = NULL; PyObject *one = NULL; PyObject *tmp1 = NULL, *tmp2 = NULL, *result; @@ -133,30 +160,29 @@ PyObject *zero = PyLong_FromLong(0); if (zero == NULL) - return NULL; - cmp_call = PyObject_Cmp(r->step, zero, &cmp_result); + return -1; + cmp_call = PyObject_Cmp(step, zero, &cmp_result); Py_DECREF(zero); if (cmp_call == -1) - return NULL; + return -1; assert(cmp_result != 0); if (cmp_result > 0) { - lo = r->start; - hi = r->stop; - step = r->step; + lo = start; + hi = stop; Py_INCREF(step); } else { - lo = r->stop; - hi = r->start; - step = PyNumber_Negative(r->step); + lo = stop; + hi = start; + step = PyNumber_Negative(step); if (!step) - return NULL; + return -1; } /* if (lo >= hi), return length of 0. */ if (PyObject_Compare(lo, hi) >= 0) { Py_XDECREF(step); - return PyLong_FromLong(0); + return 0; } if ((one = PyLong_FromLong(1L)) == NULL) @@ -179,7 +205,12 @@ Py_DECREF(step); Py_DECREF(tmp1); Py_DECREF(one); - return result; + length = PyLong_AsSsize_t(result); + if (length == -1 && PyErr_ExceptionMatches(PyExc_OverflowError)) + PyErr_SetString(PyExc_OverflowError, + "range length exceded sys.maxsize"); + Py_DECREF(result); + return length; Fail: Py_XDECREF(tmp2); @@ -187,19 +218,13 @@ Py_XDECREF(step); Py_XDECREF(tmp1); Py_XDECREF(one); - return NULL; + return -1; } static Py_ssize_t range_length(rangeobject *r) { - PyObject *len = range_length_obj(r); - Py_ssize_t result = -1; - if (len) { - result = PyLong_AsSsize_t(len); - Py_DECREF(len); - } - return result; + return r->length; } /* range(...)[x] is necessary for: seq[:] = range(...) */ @@ -252,6 +277,61 @@ r->start, r->stop, r->step); } +static long +range_hash(rangeobject *v) +{ + Py_ssize_t len, start, step; + + len = range_length(v); + if (len == -1) + return -1; + start = PyObject_Hash(v->start); + if (start == -1) + return -1; + step = PyObject_Hash(v->step); + if (step == -1) + return -1; + + return (long)(start*1000003L+len)*1630457L+step; +} + +static PyObject * +range_richcompare(rangeobject *v, rangeobject *w, int op) +{ + int is_equal = 0; + + if (!PyRange_Check(v) || !PyRange_Check(w) || + (op != Py_EQ && op != Py_NE)) { + Py_INCREF(Py_NotImplemented); + return Py_NotImplemented; + } + + is_equal = PyObject_RichCompareBool(v->start, w->start, Py_EQ); + if (is_equal == -1) + return NULL; + if (!is_equal) + goto find_result; + + is_equal = PyObject_RichCompareBool(v->stop, w->stop, Py_EQ); + if (is_equal == -1) + return NULL; + if (!is_equal) { + goto find_result; + } + + is_equal = PyObject_RichCompareBool(v->step, w->step, Py_EQ); + if (is_equal == -1) + return NULL; + +find_result: + if ((is_equal && op == Py_EQ) || (!is_equal && op == Py_NE)) { + Py_RETURN_TRUE; + } + else { + Py_RETURN_FALSE; + } +} + static PySequenceMethods range_as_sequence = { (lenfunc)range_length, /* sq_length */ 0, /* sq_concat */ @@ -274,43 +354,43 @@ PyTypeObject PyRange_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) - "range", /* Name of this type */ - sizeof(rangeobject), /* Basic object size */ - 0, /* Item size for varobject */ - (destructor)range_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - (reprfunc)range_repr, /* tp_repr */ - 0, /* tp_as_number */ - &range_as_sequence, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - range_doc, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - range_iter, /* tp_iter */ - 0, /* tp_iternext */ - range_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - range_new, /* tp_new */ + "range", /* Name of this type */ + sizeof(rangeobject), /* Basic object size */ + 0, /* Item size for varobject */ + (destructor)range_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + (reprfunc)range_repr, /* tp_repr */ + 0, /* tp_as_number */ + &range_as_sequence, /* tp_as_sequence */ + 0, /* tp_as_mapping */ + (hashfunc)range_hash, /* tp_hash */ + 0, /* tp_call */ + 0, /* tp_str */ + PyObject_GenericGetAttr, /* tp_getattro */ + 0, /* tp_setattro */ + 0, /* tp_as_buffer */ + Py_TPFLAGS_DEFAULT, /* tp_flags */ + range_doc, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + (richcmpfunc)range_richcompare, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + range_iter, /* tp_iter */ + 0, /* tp_iternext */ + range_methods, /* tp_methods */ + 0, /* tp_members */ + 0, /* tp_getset */ + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ + 0, /* tp_init */ + 0, /* tp_alloc */ + range_new, /* tp_new */ }; /*********************** range Iterator **************************/ @@ -647,7 +727,7 @@ return NULL; /* start + (len - 1) * step */ - len = range_length_obj(range); + len = PyLong_FromSsize_t(range->length); if (!len) goto create_failure; Index: Lib/test/test_builtin.py =================================================================== --- Lib/test/test_builtin.py (revision 62506) +++ Lib/test/test_builtin.py (working copy) @@ -1590,11 +1590,25 @@ self.assertRaises(TypeError, range, 0, "spam") self.assertRaises(TypeError, range, 0, 42, "spam") - #NEAL self.assertRaises(OverflowError, range, -sys.maxsize, sys.maxsize) - #NEAL self.assertRaises(OverflowError, range, 0, 2*sys.maxsize) + self.assertRaises(OverflowError, range, -sys.maxsize, sys.maxsize) + self.assertRaises(OverflowError, range, 0, 2*sys.maxsize) - self.assertRaises(OverflowError, len, range(0, sys.maxsize**10)) + # self.assertRaises(OverflowError, len, range(0, sys.maxsize**10)) + self.assertEquals(range(1234), range(1234)) + self.assertEquals(range(4, 3, 2), range(4, 3, 2)) + self.assertEquals(range(2222, 3333), range(2222, 3333)) + self.assertNotEquals(range(1, 15, 3), range(1, 20, 3)) + # The sequence of numbers can be the same + self.assertEquals(range(0, 9, 2), range(0, 10, 2)) + + self.assertEquals(hash(range(1234)), hash(range(1234))) + self.assertEquals(hash(range(1, 20, 3)), hash(range(1, 20, 3))) + self.assertNotEquals(hash(range(5, 10)), hash(range(6, 10))) + self.assertNotEquals(hash(range(0, 10, 2)), hash(range(0, 11, 2))) + self.assertNotEquals(hash(range(1)), hash(range(1, 2, 3))) + self.assertEquals(hash(range(0, 9, 2)), hash(range(0, 10, 2))) + def test_input(self): self.write_testfile() fp = open(TESTFN, 'r')