Bug 1003935: xrange overflows

Added XXX comment about why the undocumented PyRange_New() API function
is too broken to be worth the considerable pain of repairing.

Changed range_new() to stop using PyRange_New().  This fixes a variety
of bogus errors.  Nothing in the core uses PyRange_New() now.

Documented that xrange() is intended to be simple and fast, and that
CPython restricts its arguments, and length of its result sequence, to
native C longs.

Added some tests that failed before the patch, and repaired a test that
relied on a bogus OverflowError getting raised.
This commit is contained in:
Tim Peters 2004-08-08 07:17:39 +00:00
parent d976ab7caf
commit feec4533e2
3 changed files with 37 additions and 11 deletions

View File

@ -1099,6 +1099,12 @@ It's a function
them) except when a very large range is used on a memory-starved them) except when a very large range is used on a memory-starved
machine or when all of the range's elements are never used (such as machine or when all of the range's elements are never used (such as
when the loop is usually terminated with \keyword{break}). when the loop is usually terminated with \keyword{break}).
\note{\function{xrange()} is intended to be simple and fast.
Implementations may impose restrictions to achieve this.
The C implementation of Python restricts all arguments to
native C longs ("short" Python integers), and also requires
that that number of elements fit in a native C long.}
\end{funcdesc} \end{funcdesc}
\begin{funcdesc}{zip}{\optional{seq1, \moreargs}} \begin{funcdesc}{zip}{\optional{seq1, \moreargs}}

View File

@ -48,10 +48,15 @@ class XrangeTest(unittest.TestCase):
self.assertRaises(TypeError, xrange, 0, "spam") self.assertRaises(TypeError, xrange, 0, "spam")
self.assertRaises(TypeError, xrange, 0, 42, "spam") self.assertRaises(TypeError, xrange, 0, 42, "spam")
self.assertRaises(OverflowError, xrange, 0, sys.maxint, sys.maxint-1) self.assertEqual(len(xrange(0, sys.maxint, sys.maxint-1)), 2)
self.assertRaises(OverflowError, xrange, -sys.maxint, sys.maxint) self.assertRaises(OverflowError, xrange, -sys.maxint, sys.maxint)
self.assertRaises(OverflowError, xrange, 0, 2*sys.maxint) self.assertRaises(OverflowError, xrange, 0, 2*sys.maxint)
self.assertEqual(len(xrange(-sys.maxint, sys.maxint, 2)),
sys.maxint)
self.assertRaises(OverflowError, xrange, -sys.maxint-1, sys.maxint, 2)
def test_main(): def test_main():
test.test_support.run_unittest(XrangeTest) test.test_support.run_unittest(XrangeTest)

View File

@ -9,6 +9,13 @@ typedef struct {
long len; long len;
} rangeobject; } rangeobject;
/* XXX PyRange_New should be deprecated. It's not documented. It's not
* used in the core. Its error-checking is akin to Swiss cheese: accepts
* step == 0; accepts len < 0; ignores that (len - 1) * step may overflow;
* raises a baffling "integer addition" exception if it thinks the last
* item is "too big"; and doesn't compute whether "last item is too big"
* correctly even if the multiplication doesn't overflow.
*/
PyObject * PyObject *
PyRange_New(long start, long len, long step, int reps) PyRange_New(long start, long len, long step, int reps)
{ {
@ -79,6 +86,7 @@ get_len_of_range(long lo, long hi, long step)
static PyObject * static PyObject *
range_new(PyTypeObject *type, PyObject *args, PyObject *kw) range_new(PyTypeObject *type, PyObject *args, PyObject *kw)
{ {
rangeobject *obj;
long ilow = 0, ihigh = 0, istep = 1; long ilow = 0, ihigh = 0, istep = 1;
long n; long n;
@ -107,7 +115,14 @@ range_new(PyTypeObject *type, PyObject *args, PyObject *kw)
"xrange() result has too many items"); "xrange() result has too many items");
return NULL; return NULL;
} }
return PyRange_New(ilow, n, istep, 1);
obj = PyObject_New(rangeobject, &PyRange_Type);
if (obj == NULL)
return NULL;
obj->start = ilow;
obj->len = n;
obj->step = istep;
return (PyObject *) obj;
} }
PyDoc_STRVAR(range_doc, PyDoc_STRVAR(range_doc,