[Python-checkins] python/dist/src/Python bltinmodule.c,2.284,2.285

gvanrossum@users.sourceforge.net gvanrossum@users.sourceforge.net
Fri, 11 Apr 2003 11:43:09 -0700


Update of /cvsroot/python/python/dist/src/Python
In directory sc8-pr-cvs1:/tmp/cvs-serv28123/Python

Modified Files:
	bltinmodule.c 
Log Message:
Patch by Chad Netzer (with significant change):
- range() now works even if the arguments are longs with magnitude
  larger than sys.maxint, as long as the total length of the sequence
  fits.  E.g., range(2**100, 2**101, 2**100) is the following list:
  [1267650600228229401496703205376L].  (SF patch #707427.)


Index: bltinmodule.c
===================================================================
RCS file: /cvsroot/python/python/dist/src/Python/bltinmodule.c,v
retrieving revision 2.284
retrieving revision 2.285
diff -C2 -d -r2.284 -r2.285
*** bltinmodule.c	6 Apr 2003 09:01:11 -0000	2.284
--- bltinmodule.c	11 Apr 2003 18:43:06 -0000	2.285
***************
*** 1253,1256 ****
--- 1253,1436 ----
  
  
+ 
+ /* 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 long
+ get_len_of_range_longs(PyObject *lo, PyObject *hi, PyObject *step)
+ {
+ 	/* -------------------------------------------------------------
+ 	Algorithm is equal to that of get_len_of_range(), but it operates
+ 	on PyObjects (which are assumed to be PyLong or PyInt objects).
+ 	---------------------------------------------------------------*/
+ 	long n;
+ 	PyObject *diff = NULL;
+ 	PyObject *one = NULL;
+ 	PyObject *tmp1 = NULL, *tmp2 = NULL, *tmp3 = NULL;
+ 		/* holds sub-expression evaluations */
+ 
+ 	/* if (lo >= hi), return length of 0. */
+ 	if (PyObject_Compare(lo, hi) >= 0)
+ 		return 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 ((tmp3 = PyNumber_Add(tmp2, one)) == NULL)
+ 		goto Fail;
+ 
+ 	n = PyLong_AsLong(tmp3);
+ 	if (PyErr_Occurred()) {  /* Check for Overflow */
+ 		PyErr_Clear();
+ 		goto Fail;
+ 	}
+ 
+ 	Py_DECREF(tmp3);
+ 	Py_DECREF(tmp2);
+ 	Py_DECREF(diff);
+ 	Py_DECREF(tmp1);
+ 	Py_DECREF(one);
+ 	return n;
+ 
+   Fail:
+ 	Py_XDECREF(tmp3);
+ 	Py_XDECREF(tmp2);
+ 	Py_XDECREF(diff);
+ 	Py_XDECREF(tmp1);
+ 	Py_XDECREF(one);
+ 	return -1;
+ }
+ 
+ /* An extension of builtin_range() that handles the case when PyLong
+  * arguments are given. */
+ static PyObject *
+ handle_range_longs(PyObject *self, PyObject *args)
+ {
+ 	PyObject *ilow;
+ 	PyObject *ihigh;
+ 	PyObject *zero = NULL;
+ 	PyObject *istep = NULL;
+ 	PyObject *curnum = NULL;
+ 	PyObject *v = NULL;
+ 	long bign;
+ 	int i, n;
+ 	int cmp_result;
+ 
+ 	zero = PyLong_FromLong(0L);
+ 	if (zero == NULL)
+ 		return NULL;
+ 
+ 	ilow = zero; /* Default lower bound */
+ 	if (!PyArg_ParseTuple(args, "O", &ihigh, &istep)) {
+ 		PyErr_Clear();
+ 		if (!PyArg_ParseTuple(args,
+ 			      "OO|O;range() requires 1-3 int arguments",
+ 			      &ilow, &ihigh, &istep))
+ 		goto Fail;
+ 	}
+ 
+ 	if (!PyInt_Check(ilow) && !PyLong_Check(ilow)) {
+ 		PyErr_SetString(PyExc_ValueError,
+ 				"integer start argument expected, got float.");
+ 		goto Fail;
+ 		return NULL;
+ 	}
+ 
+ 	if (!PyInt_Check(ihigh) && !PyLong_Check(ihigh)) {
+ 		PyErr_SetString(PyExc_ValueError,
+ 				"integer end argument expected, got float.");
+ 		goto Fail;
+ 		return NULL;
+ 	}
+ 
+ 	/* If no istep was supplied, default to 1. */
+ 	if (istep == NULL) {
+ 		istep = PyLong_FromLong(1L);
+ 		if (istep == NULL)
+ 			goto Fail;
+ 	}
+ 	else {
+ 		if (!PyInt_Check(istep) && !PyLong_Check(istep)) {
+ 			PyErr_SetString(PyExc_ValueError,
+ 				"integer step argument expected, got float.");
+ 			goto Fail;
+ 		}
+ 		Py_INCREF(istep);
+ 	}
+ 
+ 	if (PyObject_Cmp(istep, zero, &cmp_result) == -1) {
+ 		goto Fail;
+ 	}
+ 
+ 	if (cmp_result == 0) {
+ 		PyErr_SetString(PyExc_ValueError,
+ 				"range() arg 3 must not be zero");
+ 		goto Fail;
+ 	}
+ 
+ 	if (cmp_result > 0)
+ 		bign = get_len_of_range_longs(ilow, ihigh, istep);
+ 	else {
+ 		PyObject *neg_istep = PyNumber_Negative(istep);
+ 		if (neg_istep == NULL)
+ 			goto Fail;
+ 		bign = get_len_of_range_longs(ihigh, ilow, neg_istep);
+ 		Py_DECREF(neg_istep);
+ 	}
+ 
+ 	n = (int)bign;
+ 	if (bign < 0 || (long)n != bign) {
+ 		PyErr_SetString(PyExc_OverflowError,
+ 				"range() result has too many items");
+ 		goto Fail;
+ 	}
+ 
+ 	v = PyList_New(n);
+ 	if (v == NULL)
+ 		goto Fail;
+ 
+ 	curnum = ilow;
+ 	Py_INCREF(curnum);
+ 
+ 	for (i = 0; i < n; i++) {
+ 		PyObject *w = PyNumber_Long(curnum);
+ 		PyObject *tmp_num;
+ 		if (w == NULL)
+ 			goto Fail;
+ 
+ 		PyList_SET_ITEM(v, i, w);
+ 
+ 		tmp_num = PyNumber_Add(curnum, istep);
+ 		if (tmp_num == NULL)
+ 			goto Fail;
+ 
+ 		Py_DECREF(curnum);
+ 		curnum = tmp_num;
+ 	}
+ 	Py_DECREF(curnum);
+ 	Py_DECREF(istep);
+ 	Py_DECREF(zero);
+ 	return v;
+ 
+   Fail:
+ 	Py_XDECREF(curnum);
+ 	Py_XDECREF(istep);
+ 	Py_XDECREF(zero);
+ 	Py_XDECREF(v);
+ 	return NULL;
+ }
+ 
  /* 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
***************
*** 1294,1308 ****
  		if (!PyArg_ParseTuple(args,
  				"l;range() requires 1-3 int arguments",
! 				&ihigh))
! 			return NULL;
  	}
  	else {
  		if (!PyArg_ParseTuple(args,
  				"ll|l;range() requires 1-3 int arguments",
! 				&ilow, &ihigh, &istep))
! 			return NULL;
  	}
  	if (istep == 0) {
! 		PyErr_SetString(PyExc_ValueError, "range() arg 3 must not be zero");
  		return NULL;
  	}
--- 1474,1493 ----
  		if (!PyArg_ParseTuple(args,
  				"l;range() requires 1-3 int arguments",
! 				&ihigh)) {
! 			PyErr_Clear();
! 			return handle_range_longs(self, args);
! 		}
  	}
  	else {
  		if (!PyArg_ParseTuple(args,
  				"ll|l;range() requires 1-3 int arguments",
! 				&ilow, &ihigh, &istep)) {
! 			PyErr_Clear();
! 			return handle_range_longs(self, args);
! 		}
  	}
  	if (istep == 0) {
! 		PyErr_SetString(PyExc_ValueError,
! 				"range() arg 3 must not be zero");
  		return NULL;
  	}