summaryrefslogtreecommitdiffstats
path: root/Objects/rangeobject.c
diff options
context:
space:
mode:
authorGuido van Rossum <guido@python.org>2007-05-07 22:24:25 (GMT)
committerGuido van Rossum <guido@python.org>2007-05-07 22:24:25 (GMT)
commit805365ee39298f93e433e19ae0dd87c6f782145b (patch)
treeae8f8a3c315b49cfb2e7926d4b7e56f64c68b21c /Objects/rangeobject.c
parent598d98a7e8981e650e803e41e884ffc905b2311e (diff)
downloadcpython-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.c709
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;
}