Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 124eff0

Browse files
committed
Patch by Tim Peters to improve the range checks for range() and
xrange(), especially for platforms where int and long are different sizes (so sys.maxint isn't actually the theoretical limit for the length of a list, but the largest C int is -- sys.maxint is the largest Python int, which is actually a C long).
1 parent 717d1fd commit 124eff0

1 file changed

Lines changed: 48 additions & 30 deletions

File tree

Python/bltinmodule.c

Lines changed: 48 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1395,13 +1395,47 @@ With two arguments, equivalent to x**y. With three arguments,\n\
13951395
equivalent to (x**y) % z, but may be more efficient (e.g. for longs).";
13961396

13971397

1398+
/* Return number of items in range/xrange (lo, hi, step). step > 0
1399+
* required. Return a value < 0 if & only if the true value is too
1400+
* large to fit in a signed long.
1401+
*/
1402+
static long
1403+
get_len_of_range(lo, hi, step)
1404+
long lo;
1405+
long hi;
1406+
long step; /* must be > 0 */
1407+
{
1408+
/* -------------------------------------------------------------
1409+
If lo >= hi, the range is empty.
1410+
Else if n values are in the range, the last one is
1411+
lo + (n-1)*step, which must be <= hi-1. Rearranging,
1412+
n <= (hi - lo - 1)/step + 1, so taking the floor of the RHS gives
1413+
the proper value. Since lo < hi in this case, hi-lo-1 >= 0, so
1414+
the RHS is non-negative and so truncation is the same as the
1415+
floor. Letting M be the largest positive long, the worst case
1416+
for the RHS numerator is hi=M, lo=-M-1, and then
1417+
hi-lo-1 = M-(-M-1)-1 = 2*M. Therefore unsigned long has enough
1418+
precision to compute the RHS exactly.
1419+
---------------------------------------------------------------*/
1420+
long n = 0;
1421+
if (lo < hi) {
1422+
unsigned long uhi = (unsigned long)hi;
1423+
unsigned long ulo = (unsigned long)lo;
1424+
unsigned long diff = uhi - ulo - 1;
1425+
n = (long)(diff / (unsigned long)step + 1);
1426+
}
1427+
return n;
1428+
}
1429+
13981430
static PyObject *
13991431
builtin_range(self, args)
14001432
PyObject *self;
14011433
PyObject *args;
14021434
{
14031435
long ilow = 0, ihigh = 0, istep = 1;
1436+
long bign;
14041437
int i, n;
1438+
14051439
PyObject *v;
14061440

14071441
if (PyTuple_Size(args) <= 1) {
@@ -1420,32 +1454,14 @@ builtin_range(self, args)
14201454
PyErr_SetString(PyExc_ValueError, "zero step for range()");
14211455
return NULL;
14221456
}
1423-
/* A bit convoluted because this might overflow; due to Tim Peters */
1424-
if (istep > 0) {
1425-
if (ihigh <= ilow)
1426-
n = 0;
1427-
else {
1428-
unsigned long hi = (unsigned long)ihigh;
1429-
unsigned long lo = (unsigned long)ilow;
1430-
unsigned long diff = hi - lo - 1;
1431-
n = (long)(diff / istep + 1);
1432-
}
1433-
}
1434-
else {
1435-
/* But any errors in this branch are my own --Guido */
1436-
if (ihigh >= ilow)
1437-
n = 0;
1438-
else {
1439-
/* Swap lo and hi; use abs(istep) */
1440-
unsigned long hi = (unsigned long)ilow;
1441-
unsigned long lo = (unsigned long)ihigh;
1442-
unsigned long diff = hi - lo - 1;
1443-
n = (long)(diff / (-istep) + 1);
1444-
}
1445-
}
1446-
if (n < 0) {
1457+
if (istep > 0)
1458+
bign = get_len_of_range(ilow, ihigh, istep);
1459+
else
1460+
bign = get_len_of_range(ihigh, ilow, -istep);
1461+
n = (int)bign;
1462+
if (bign < 0 || (long)n != bign) {
14471463
PyErr_SetString(PyExc_OverflowError,
1448-
"range() has more than sys.maxint items");
1464+
"range() has too many items");
14491465
return NULL;
14501466
}
14511467
v = PyList_New(n);
@@ -1497,13 +1513,15 @@ builtin_xrange(self, args)
14971513
PyErr_SetString(PyExc_ValueError, "zero step for xrange()");
14981514
return NULL;
14991515
}
1500-
/* XXX ought to check overflow of subtraction */
15011516
if (istep > 0)
1502-
n = (ihigh - ilow + istep - 1) / istep;
1517+
n = get_len_of_range(ilow, ihigh, istep);
15031518
else
1504-
n = (ihigh - ilow + istep + 1) / istep;
1505-
if (n < 0)
1506-
n = 0;
1519+
n = get_len_of_range(ihigh, ilow, -istep);
1520+
if (n < 0) {
1521+
PyErr_SetString(PyExc_OverflowError,
1522+
"xrange() has more than sys.maxint items");
1523+
return NULL;
1524+
}
15071525
return PyRange_New(ilow, n, istep, 1);
15081526
}
15091527

0 commit comments

Comments
 (0)