diff options
author | Guido van Rossum <guido@python.org> | 2007-05-07 22:24:25 (GMT) |
---|---|---|
committer | Guido van Rossum <guido@python.org> | 2007-05-07 22:24:25 (GMT) |
commit | 805365ee39298f93e433e19ae0dd87c6f782145b (patch) | |
tree | ae8f8a3c315b49cfb2e7926d4b7e56f64c68b21c /Objects/rangeobject.c | |
parent | 598d98a7e8981e650e803e41e884ffc905b2311e (diff) | |
download | cpython-805365ee39298f93e433e19ae0dd87c6f782145b.zip cpython-805365ee39298f93e433e19ae0dd87c6f782145b.tar.gz cpython-805365ee39298f93e433e19ae0dd87c6f782145b.tar.bz2 |
Merged revisions 55007-55179 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/branches/p3yk
........
r55077 | guido.van.rossum | 2007-05-02 11:54:37 -0700 (Wed, 02 May 2007) | 2 lines
Use the new print syntax, at least.
........
r55142 | fred.drake | 2007-05-04 21:27:30 -0700 (Fri, 04 May 2007) | 1 line
remove old cruftiness
........
r55143 | fred.drake | 2007-05-04 21:52:16 -0700 (Fri, 04 May 2007) | 1 line
make this work with the new Python
........
r55162 | neal.norwitz | 2007-05-06 22:29:18 -0700 (Sun, 06 May 2007) | 1 line
Get asdl code gen working with Python 2.3. Should continue to work with 3.0
........
r55164 | neal.norwitz | 2007-05-07 00:00:38 -0700 (Mon, 07 May 2007) | 1 line
Verify checkins to p3yk (sic) branch go to 3000 list.
........
r55166 | neal.norwitz | 2007-05-07 00:12:35 -0700 (Mon, 07 May 2007) | 1 line
Fix this test so it runs again by importing warnings_test properly.
........
r55167 | neal.norwitz | 2007-05-07 01:03:22 -0700 (Mon, 07 May 2007) | 8 lines
So long xrange. range() now supports values that are outside
-sys.maxint to sys.maxint. floats raise a TypeError.
This has been sitting for a long time. It probably has some problems and
needs cleanup. Objects/rangeobject.c now uses 4-space indents since
it is almost completely new.
........
r55171 | guido.van.rossum | 2007-05-07 10:21:26 -0700 (Mon, 07 May 2007) | 4 lines
Fix two tests that were previously depending on significant spaces
at the end of a line (and before that on Python 2.x print behavior
that has no exact equivalent in 3.0).
........
Diffstat (limited to 'Objects/rangeobject.c')
-rw-r--r-- | Objects/rangeobject.c | 709 |
1 files changed, 557 insertions, 152 deletions
diff --git a/Objects/rangeobject.c b/Objects/rangeobject.c index c48bee0..f38935b 100644 --- a/Objects/rangeobject.c +++ b/Objects/rangeobject.c @@ -2,139 +2,292 @@ #include "Python.h" +/* Support objects whose length is > PY_SSIZE_T_MAX. + + This could be sped up for small PyLongs if they fit in an Py_ssize_t. + This only matters on Win64. Though we could use PY_LONG_LONG which + would presumably help perf. +*/ + typedef struct { - PyObject_HEAD - long start; - long step; - long len; + PyObject_HEAD + PyObject *start; + PyObject *stop; + PyObject *step; } rangeobject; -/* Return number of items in range/xrange (lo, hi, step). step > 0 - * required. Return a value < 0 if & only if the true value is too - * large to fit in a signed long. - */ -static long -get_len_of_range(long lo, long hi, long step) +/* Helper function for validating step. Always returns a new reference or + NULL on error. +*/ +static PyObject * +validate_step(PyObject *step) { - /* ------------------------------------------------------------- - If lo >= hi, the range is empty. - Else if n values are in the range, the last one is - lo + (n-1)*step, which must be <= hi-1. Rearranging, - n <= (hi - lo - 1)/step + 1, so taking the floor of the RHS gives - the proper value. Since lo < hi in this case, hi-lo-1 >= 0, so - the RHS is non-negative and so truncation is the same as the - floor. Letting M be the largest positive long, the worst case - for the RHS numerator is hi=M, lo=-M-1, and then - hi-lo-1 = M-(-M-1)-1 = 2*M. Therefore unsigned long has enough - precision to compute the RHS exactly. - ---------------------------------------------------------------*/ - long n = 0; - if (lo < hi) { - unsigned long uhi = (unsigned long)hi; - unsigned long ulo = (unsigned long)lo; - unsigned long diff = uhi - ulo - 1; - n = (long)(diff / (unsigned long)step + 1); - } - return n; + /* No step specified, use a step of 1. */ + if (!step) + return PyInt_FromLong(1); + + step = PyNumber_Index(step); + if (step) { + Py_ssize_t istep = PyNumber_AsSsize_t(step, NULL); + if (istep == -1 && PyErr_Occurred()) { + /* Ignore OverflowError, we know the value isn't 0. */ + PyErr_Clear(); + } + else if (istep == 0) { + PyErr_SetString(PyExc_ValueError, + "range() arg 3 must not be zero"); + Py_CLEAR(step); + } + } + + return step; } +/* XXX(nnorwitz): should we error check if the user passes any empty ranges? + range(-10) + range(0, -5) + range(0, 5, -1) +*/ static PyObject * range_new(PyTypeObject *type, PyObject *args, PyObject *kw) { - rangeobject *obj; - long ilow = 0, ihigh = 0, istep = 1; - long n; - - if (!_PyArg_NoKeywords("xrange()", kw)) - return NULL; - - if (PyTuple_Size(args) <= 1) { - if (!PyArg_ParseTuple(args, - "l;xrange() requires 1-3 int arguments", - &ihigh)) - return NULL; - } - else { - if (!PyArg_ParseTuple(args, - "ll|l;xrange() requires 1-3 int arguments", - &ilow, &ihigh, &istep)) - return NULL; - } - if (istep == 0) { - PyErr_SetString(PyExc_ValueError, "xrange() arg 3 must not be zero"); - return NULL; - } - if (istep > 0) - n = get_len_of_range(ilow, ihigh, istep); - else - n = get_len_of_range(ihigh, ilow, -istep); - if (n < 0) { - PyErr_SetString(PyExc_OverflowError, - "xrange() result has too many items"); - return NULL; - } - - obj = PyObject_New(rangeobject, &PyRange_Type); - if (obj == NULL) - return NULL; - obj->start = ilow; - obj->len = n; - obj->step = istep; - return (PyObject *) obj; + rangeobject *obj = NULL; + PyObject *start = NULL, *stop = NULL, *step = NULL; + + if (!_PyArg_NoKeywords("range()", kw)) + return NULL; + + if (PyTuple_Size(args) <= 1) { + if (!PyArg_UnpackTuple(args, "range", 1, 1, &stop)) + goto Fail; + stop = PyNumber_Index(stop); + if (!stop) + goto Fail; + start = PyInt_FromLong(0); + step = PyInt_FromLong(1); + if (!start || !step) + goto Fail; + } + else { + if (!PyArg_UnpackTuple(args, "range", 2, 3, + &start, &stop, &step)) + goto Fail; + + /* Convert borrowed refs to owned refs */ + start = PyNumber_Index(start); + stop = PyNumber_Index(stop); + step = validate_step(step); + if (!start || !stop || !step) + goto Fail; + } + + obj = PyObject_New(rangeobject, &PyRange_Type); + if (obj == NULL) + goto Fail; + obj->start = start; + obj->stop = stop; + obj->step = step; + return (PyObject *) obj; + +Fail: + Py_XDECREF(start); + Py_XDECREF(stop); + Py_XDECREF(step); + return NULL; } PyDoc_STRVAR(range_doc, -"xrange([start,] stop[, step]) -> xrange object\n\ +"range([start,] stop[, step]) -> range object\n\ \n\ -Like range(), but instead of returning a list, returns an object that\n\ -generates the numbers in the range on demand. For looping, this is \n\ -slightly faster than range() and more memory efficient."); +Returns an iterator that generates the numbers in the range on demand."); -static PyObject * -range_item(rangeobject *r, Py_ssize_t i) +static void +range_dealloc(rangeobject *r) { - if (i < 0 || i >= r->len) { - PyErr_SetString(PyExc_IndexError, - "xrange object index out of range"); - return NULL; - } - return PyInt_FromSsize_t(r->start + (i % r->len) * r->step); + Py_DECREF(r->start); + Py_DECREF(r->stop); + Py_DECREF(r->step); +} + +/* Return number of items in range (lo, hi, step), when arguments are + * PyInt or PyLong objects. step > 0 required. Return a value < 0 if + * & only if the true value is too large to fit in a signed long. + * Arguments MUST return 1 with either PyInt_Check() or + * PyLong_Check(). Return -1 when there is an error. + */ +static PyObject* +range_length_obj(rangeobject *r) +{ + /* ------------------------------------------------------------- + Algorithm is equal to that of get_len_of_range(), but it operates + on PyObjects (which are assumed to be PyLong or PyInt objects). + ---------------------------------------------------------------*/ + int cmp_result, cmp_call; + PyObject *lo, *hi; + PyObject *step = NULL; + PyObject *diff = NULL; + PyObject *one = NULL; + PyObject *tmp1 = NULL, *tmp2 = NULL, *result; + /* holds sub-expression evaluations */ + + PyObject *zero = PyLong_FromLong(0); + if (zero == NULL) + return NULL; + cmp_call = PyObject_Cmp(r->step, zero, &cmp_result); + Py_DECREF(zero); + if (cmp_call == -1) + return NULL; + + assert(cmp_result != 0); + if (cmp_result > 0) { + lo = r->start; + hi = r->stop; + step = r->step; + Py_INCREF(step); + } else { + lo = r->stop; + hi = r->start; + step = PyNumber_Negative(r->step); + if (!step) + return NULL; + } + + /* if (lo >= hi), return length of 0. */ + if (PyObject_Compare(lo, hi) >= 0) { + Py_XDECREF(step); + return PyLong_FromLong(0); + } + + if ((one = PyLong_FromLong(1L)) == NULL) + goto Fail; + + if ((tmp1 = PyNumber_Subtract(hi, lo)) == NULL) + goto Fail; + + if ((diff = PyNumber_Subtract(tmp1, one)) == NULL) + goto Fail; + + if ((tmp2 = PyNumber_FloorDivide(diff, step)) == NULL) + goto Fail; + + if ((result = PyNumber_Add(tmp2, one)) == NULL) + goto Fail; + + Py_DECREF(tmp2); + Py_DECREF(diff); + Py_DECREF(step); + Py_DECREF(tmp1); + Py_DECREF(one); + return result; + + Fail: + Py_XDECREF(tmp2); + Py_XDECREF(diff); + Py_XDECREF(step); + Py_XDECREF(tmp1); + Py_XDECREF(one); + return NULL; } static Py_ssize_t range_length(rangeobject *r) { - return (Py_ssize_t)(r->len); + PyObject *len = range_length_obj(r); + Py_ssize_t result = -1; + if (len) { + result = PyLong_AsSsize_t(len); + Py_DECREF(len); + } + return result; +} + +/* range(...)[x] is necessary for: seq[:] = range(...) */ + +static PyObject * +range_item(rangeobject *r, Py_ssize_t i) +{ + Py_ssize_t len = range_length(r); + PyObject *rem, *incr, *result; + + /* XXX(nnorwitz): should negative indices be supported? */ + /* XXX(nnorwitz): should support range[x] where x > PY_SSIZE_T_MAX? */ + if (i < 0 || i >= len) { + if (!PyErr_Occurred()) + PyErr_SetString(PyExc_IndexError, + "xrange object index out of range"); + return NULL; + } + + /* XXX(nnorwitz): optimize for short ints. */ + rem = PyLong_FromSsize_t(i % len); + if (!rem) + return NULL; + incr = PyNumber_Multiply(rem, r->step); + Py_DECREF(rem); + if (!incr) + return NULL; + result = PyNumber_Add(r->start, incr); + Py_DECREF(incr); + return result; } static PyObject * range_repr(rangeobject *r) { - PyObject *rtn; - - if (r->start == 0 && r->step == 1) - rtn = PyString_FromFormat("xrange(%ld)", - r->start + r->len * r->step); - - else if (r->step == 1) - rtn = PyString_FromFormat("xrange(%ld, %ld)", - r->start, - r->start + r->len * r->step); - - else - rtn = PyString_FromFormat("xrange(%ld, %ld, %ld)", - r->start, - r->start + r->len * r->step, - r->step); - return rtn; + PyObject *start_str = NULL, *stop_str = NULL, *step_str = NULL; + PyObject *result = NULL; + Py_ssize_t istart, istep; + + /* We always need the stop value. */ + stop_str = PyObject_Str(r->stop); + if (!stop_str) + return NULL; + + /* XXX(nnorwitz): should we use PyObject_Repr instead of str? */ + + /* Check for special case values for printing. We don't always + need the start or step values. We don't care about errors + (it means overflow), so clear the errors. */ + istart = PyNumber_AsSsize_t(r->start, NULL); + if (istart != 0 || (istart == -1 && PyErr_Occurred())) { + PyErr_Clear(); + start_str = PyObject_Str(r->start); + } + + istep = PyNumber_AsSsize_t(r->step, NULL); + if (istep != 1 || (istep == -1 && PyErr_Occurred())) { + PyErr_Clear(); + step_str = PyObject_Str(r->step); + } + + if (istart == 0 && istep == 1) + result = PyString_FromFormat("range(%s)", + PyString_AS_STRING(stop_str)); + else if (istep == 1) { + if (start_str) + result = PyString_FromFormat("range(%s, %s)", + PyString_AS_STRING(start_str), + PyString_AS_STRING(stop_str)); + } + else if (start_str && step_str) + result = PyString_FromFormat("range(%s, %s, %s)", + PyString_AS_STRING(start_str), + PyString_AS_STRING(stop_str), + PyString_AS_STRING(step_str)); + /* else result is NULL and an error should already be set. */ + + Py_XDECREF(start_str); + Py_XDECREF(stop_str); + Py_XDECREF(step_str); + return result; } static PySequenceMethods range_as_sequence = { - (lenfunc)range_length, /* sq_length */ - 0, /* sq_concat */ - 0, /* sq_repeat */ - (ssizeargfunc)range_item, /* sq_item */ - 0, /* sq_slice */ + (lenfunc)range_length, /* sq_length */ + 0, /* sq_concat */ + 0, /* sq_repeat */ + (ssizeargfunc)range_item, /* sq_item */ + 0, /* sq_slice */ }; static PyObject * range_iter(PyObject *seq); @@ -144,17 +297,18 @@ PyDoc_STRVAR(reverse_doc, "Returns a reverse iterator."); static PyMethodDef range_methods[] = { - {"__reversed__", (PyCFunction)range_reverse, METH_NOARGS, reverse_doc}, - {NULL, NULL} /* sentinel */ + {"__reversed__", (PyCFunction)range_reverse, METH_NOARGS, + reverse_doc}, + {NULL, NULL} /* sentinel */ }; PyTypeObject PyRange_Type = { PyObject_HEAD_INIT(&PyType_Type) 0, /* Number of items for varobject */ - "xrange", /* Name of this type */ + "range", /* Name of this type */ sizeof(rangeobject), /* Basic object size */ 0, /* Item size for varobject */ - (destructor)PyObject_Del, /* tp_dealloc */ + (destructor)range_dealloc, /* tp_dealloc */ 0, /* tp_print */ 0, /* tp_getattr */ 0, /* tp_setattr */ @@ -192,6 +346,11 @@ PyTypeObject PyRange_Type = { /*********************** Xrange Iterator **************************/ +/* There are 2 types of iterators, one for C longs, the other for + Python longs (ie, PyObjects). This should make iteration fast + in the normal case, but possible for any numeric value. +*/ + typedef struct { PyObject_HEAD long index; @@ -203,25 +362,42 @@ typedef struct { static PyObject * rangeiter_next(rangeiterobject *r) { - if (r->index < r->len) - return PyInt_FromLong(r->start + (r->index++) * r->step); - return NULL; + if (r->index < r->len) + return PyInt_FromLong(r->start + (r->index++) * r->step); + return NULL; } static PyObject * rangeiter_len(rangeiterobject *r) { - return PyInt_FromLong(r->len - r->index); + return PyInt_FromLong(r->len - r->index); } -PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it))."); +typedef struct { + PyObject_HEAD + PyObject *index; + PyObject *start; + PyObject *step; + PyObject *len; +} longrangeiterobject; + +static PyObject * +longrangeiter_len(longrangeiterobject *r, PyObject *no_args) +{ + return PyNumber_Subtract(r->len, r->index); +} +static PyObject *rangeiter_new(PyTypeObject *, PyObject *args, PyObject *kw); + +PyDoc_STRVAR(length_hint_doc, + "Private method returning an estimate of len(list(it))."); static PyMethodDef rangeiter_methods[] = { - {"__length_hint__", (PyCFunction)rangeiter_len, METH_NOARGS, length_hint_doc}, - {NULL, NULL} /* sentinel */ + {"__length_hint__", (PyCFunction)rangeiter_len, METH_NOARGS, + length_hint_doc}, + {NULL, NULL} /* sentinel */ }; -static PyTypeObject Pyrangeiter_Type = { +PyTypeObject Pyrangeiter_Type = { PyObject_HEAD_INIT(&PyType_Type) 0, /* ob_size */ "rangeiterator", /* tp_name */ @@ -252,50 +428,279 @@ static PyTypeObject Pyrangeiter_Type = { PyObject_SelfIter, /* tp_iter */ (iternextfunc)rangeiter_next, /* tp_iternext */ rangeiter_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 */ + rangeiter_new, /* tp_new */ +}; + +/* Return number of items in range/xrange (lo, hi, step). step > 0 + * required. Return a value < 0 if & only if the true value is too + * large to fit in a signed long. + */ +static long +get_len_of_range(long lo, long hi, long step) +{ + /* ------------------------------------------------------------- + If lo >= hi, the range is empty. + Else if n values are in the range, the last one is + lo + (n-1)*step, which must be <= hi-1. Rearranging, + n <= (hi - lo - 1)/step + 1, so taking the floor of the RHS gives + the proper value. Since lo < hi in this case, hi-lo-1 >= 0, so + the RHS is non-negative and so truncation is the same as the + floor. Letting M be the largest positive long, the worst case + for the RHS numerator is hi=M, lo=-M-1, and then + hi-lo-1 = M-(-M-1)-1 = 2*M. Therefore unsigned long has enough + precision to compute the RHS exactly. + ---------------------------------------------------------------*/ + long n = 0; + if (lo < hi) { + unsigned long uhi = (unsigned long)hi; + unsigned long ulo = (unsigned long)lo; + unsigned long diff = uhi - ulo - 1; + n = (long)(diff / (unsigned long)step + 1); + } + return n; +} + +static PyObject * +int_range_iter(long start, long stop, long step) +{ + rangeiterobject *it = PyObject_New(rangeiterobject, &Pyrangeiter_Type); + if (it == NULL) + return NULL; + it->start = start; + it->step = step; + if (step > 0) + it->len = get_len_of_range(start, stop, step); + else + it->len = get_len_of_range(stop, start, -step); + it->index = 0; + return (PyObject *)it; +} + +static PyObject * +rangeiter_new(PyTypeObject *type, PyObject *args, PyObject *kw) +{ + long start, stop, step; + + if (!_PyArg_NoKeywords("rangeiter()", kw)) + return NULL; + + if (!PyArg_ParseTuple(args, "lll;rangeiter() requires 3 int arguments", + &start, &stop, &step)) + return NULL; + + return int_range_iter(start, stop, step); +} + +static PyMethodDef longrangeiter_methods[] = { + {"__length_hint__", (PyCFunction)longrangeiter_len, METH_NOARGS, + length_hint_doc}, + {NULL, NULL} /* sentinel */ +}; + +static void +longrangeiter_dealloc(longrangeiterobject *r) +{ + Py_XDECREF(r->index); + Py_DECREF(r->start); + Py_DECREF(r->step); + Py_DECREF(r->len); +} + +static PyObject * +longrangeiter_next(longrangeiterobject *r) +{ + PyObject *one, *product, *new_index, *result; + if (PyObject_RichCompareBool(r->index, r->len, Py_LT) != 1) + return NULL; + + one = PyLong_FromLong(1); + if (!one) + return NULL; + + product = PyNumber_Multiply(r->index, r->step); + if (!product) { + Py_DECREF(one); + return NULL; + } + + new_index = PyNumber_Add(r->index, one); + Py_DECREF(one); + if (!new_index) { + Py_DECREF(product); + return NULL; + } + + result = PyNumber_Add(r->start, product); + Py_DECREF(product); + if (result) { + Py_DECREF(r->index); + r->index = new_index; + } + + return result; +} + +static PyTypeObject Pylongrangeiter_Type = { + PyObject_HEAD_INIT(&PyType_Type) + 0, /* ob_size */ + "rangeiterator", /* tp_name */ + sizeof(longrangeiterobject), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)longrangeiter_dealloc, /* tp_dealloc */ + 0, /* tp_print */ + 0, /* tp_getattr */ + 0, /* tp_setattr */ + 0, /* tp_compare */ + 0, /* tp_repr */ + 0, /* tp_as_number */ + 0, /* 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 */ + 0, /* tp_doc */ + 0, /* tp_traverse */ + 0, /* tp_clear */ + 0, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + PyObject_SelfIter, /* tp_iter */ + (iternextfunc)longrangeiter_next, /* tp_iternext */ + longrangeiter_methods, /* tp_methods */ 0, }; static PyObject * range_iter(PyObject *seq) { - rangeiterobject *it; - - if (!PyRange_Check(seq)) { - PyErr_BadInternalCall(); - return NULL; - } - it = PyObject_New(rangeiterobject, &Pyrangeiter_Type); - if (it == NULL) - return NULL; - it->index = 0; - it->start = ((rangeobject *)seq)->start; - it->step = ((rangeobject *)seq)->step; - it->len = ((rangeobject *)seq)->len; - return (PyObject *)it; + rangeobject *r = (rangeobject *)seq; + longrangeiterobject *it; + PyObject *tmp, *len; + + assert(PyRange_Check(seq)); + if (_PyLong_FitsInLong(r->start) && + _PyLong_FitsInLong(r->stop) && + _PyLong_FitsInLong(r->step)) + return int_range_iter(PyLong_AsLong(r->start), + PyLong_AsLong(r->stop), + PyLong_AsLong(r->step)); + + it = PyObject_New(longrangeiterobject, &Pylongrangeiter_Type); + if (it == NULL) + return NULL; + it->start = r->start; + /* Calculate length: (r->stop - r->start) / r->step */ + tmp = PyNumber_Subtract(r->stop, r->start); + if (!tmp) + goto create_failure; + len = PyNumber_FloorDivide(tmp, r->step); + Py_DECREF(tmp); + if (!len) + goto create_failure; + it->len = len; + it->step = r->step; + it->index = PyLong_FromLong(0); + if (!it->index) + goto create_failure; + + Py_INCREF(it->start); + Py_INCREF(it->step); + Py_INCREF(it->len); + return (PyObject *)it; + +create_failure: + PyObject_Del(it); + return NULL; } static PyObject * range_reverse(PyObject *seq) { - rangeiterobject *it; - long start, step, len; - - if (!PyRange_Check(seq)) { - PyErr_BadInternalCall(); - return NULL; - } - it = PyObject_New(rangeiterobject, &Pyrangeiter_Type); - if (it == NULL) - return NULL; - - start = ((rangeobject *)seq)->start; - step = ((rangeobject *)seq)->step; - len = ((rangeobject *)seq)->len; - - it->index = 0; - it->start = start + (len-1) * step; - it->step = -step; - it->len = len; - - return (PyObject *)it; + rangeobject *range = (rangeobject*) seq; + longrangeiterobject *it; + PyObject *one, *sum, *diff, *len = NULL, *product; + + /* XXX(nnorwitz): do the calc for the new start/stop first, + then if they fit, call the proper iter()? + */ + assert(PyRange_Check(seq)); + if (_PyLong_FitsInLong(range->start) && + _PyLong_FitsInLong(range->stop) && + _PyLong_FitsInLong(range->step)) { + long start = PyLong_AsLong(range->start); + long step = PyLong_AsLong(range->step); + long stop = PyLong_AsLong(range->stop); + /* XXX(nnorwitz): need to check for overflow and simplify. */ + long len = get_len_of_range(start, stop, step); + long new_start = start + (len - 1) * step; + long new_stop = start; + if (step > 0) + new_stop -= 1; + else + new_stop += 1; + return int_range_iter(new_start, new_stop, -step); + } + + it = PyObject_New(longrangeiterobject, &Pylongrangeiter_Type); + if (it == NULL) + return NULL; + + /* start + (len - 1) * step */ + len = range_length_obj(range); + if (!len) + goto create_failure; + + one = PyLong_FromLong(1); + if (!one) + goto create_failure; + + diff = PyNumber_Subtract(len, one); + Py_DECREF(one); + if (!diff) + goto create_failure; + + product = PyNumber_Multiply(len, range->step); + if (!product) + goto create_failure; + + sum = PyNumber_Add(range->start, product); + Py_DECREF(product); + it->start = sum; + if (!it->start) + goto create_failure; + it->step = PyNumber_Negative(range->step); + if (!it->step) { + Py_DECREF(it->start); + PyObject_Del(it); + return NULL; + } + + /* Steal reference to len. */ + it->len = len; + + it->index = PyLong_FromLong(0); + if (!it->index) { + Py_DECREF(it); + return NULL; + } + + return (PyObject *)it; + +create_failure: + Py_XDECREF(len); + PyObject_Del(it); + return NULL; } |