cpython/Objects/rangeobject.c
Amaury Forgeot d'Arc b7f17e4bb4 Found another memory leak in longrangeiter. And redo the previous correction
without calling PyType_Ready().

Question 1: Should the interpreter register all types with PyType_Ready()?
Many types seem to avoid it.

Question 2: To reproduce the problem, run the following code:
    def f():
        while True:
           for a in iter(range(0,1,10**20)):
              pass
    f()
And watch the memory used by the process.
How do we test this in a unittest?
2007-11-15 20:52:21 +00:00

680 lines
19 KiB
C

/* Range object implementation */
#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
PyObject *start;
PyObject *stop;
PyObject *step;
} rangeobject;
/* Helper function for validating step. Always returns a new reference or
NULL on error.
*/
static PyObject *
validate_step(PyObject *step)
{
/* 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 = 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,
"range([start,] stop[, step]) -> range object\n\
\n\
Returns an iterator that generates the numbers in the range on demand.");
static void
range_dealloc(rangeobject *r)
{
Py_DECREF(r->start);
Py_DECREF(r->stop);
Py_DECREF(r->step);
PyObject_Del(r);
}
/* 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)
{
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,
"range 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)
{
Py_ssize_t istep;
/* Check for special case values for printing. We don't always
need the step value. We don't care about errors
(it means overflow), so clear the errors. */
istep = PyNumber_AsSsize_t(r->step, NULL);
if (istep != 1 || (istep == -1 && PyErr_Occurred())) {
PyErr_Clear();
}
if (istep == 1)
return PyUnicode_FromFormat("range(%R, %R)", r->start, r->stop);
else
return PyUnicode_FromFormat("range(%R, %R, %R)",
r->start, r->stop, r->step);
}
static PySequenceMethods range_as_sequence = {
(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);
static PyObject * range_reverse(PyObject *seq);
PyDoc_STRVAR(reverse_doc,
"Returns a reverse iterator.");
static PyMethodDef range_methods[] = {
{"__reversed__", (PyCFunction)range_reverse, METH_NOARGS,
reverse_doc},
{NULL, NULL} /* sentinel */
};
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 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;
long start;
long step;
long len;
} rangeiterobject;
static PyObject *
rangeiter_next(rangeiterobject *r)
{
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);
}
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 */
};
PyTypeObject Pyrangeiter_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"rangeiterator", /* tp_name */
sizeof(rangeiterobject), /* tp_basicsize */
0, /* tp_itemsize */
/* methods */
(destructor)PyObject_Del, /* 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)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_XDECREF(r->start);
Py_XDECREF(r->step);
Py_XDECREF(r->len);
PyObject_Del(r);
}
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 = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"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)
{
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;
/* Do all initialization here, so we can DECREF on failure. */
it->start = r->start;
it->step = r->step;
Py_INCREF(it->start);
Py_INCREF(it->step);
it->len = it->index = NULL;
/* 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->index = PyLong_FromLong(0);
if (!it->index)
goto create_failure;
return (PyObject *)it;
create_failure:
Py_DECREF(it);
return NULL;
}
static PyObject *
range_reverse(PyObject *seq)
{
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;
}