Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Include/internal/pycore_long.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@ _PyLong_IsPositiveSingleDigit(PyObject* sub) {
return ((size_t)signed_size) <= 1;
}

long _PyLong_AsLongAndOverflow(const PyLongObject *v, int *overflow);


#ifdef __cplusplus
}
#endif
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Optimize construction of ``range`` object for medium size integers.
88 changes: 52 additions & 36 deletions Objects/longobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -474,45 +474,21 @@ PyLong_FromDouble(double dval)
#define PY_ABS_LONG_MIN (0-(unsigned long)LONG_MIN)
#define PY_ABS_SSIZE_T_MIN (0-(size_t)PY_SSIZE_T_MIN)

/* Get a C long int from an int object or any object that has an __index__
method.
/* Get a C long int from a PyLong object

On overflow, return -1 and set *overflow to 1 or -1 depending on the sign of
the result. Otherwise *overflow is 0.
the result. Otherwise *overflow is unchanged.

For other errors (e.g., TypeError), return -1 and set an error condition.
In this case *overflow will be 0.
Internal version, does not perform error checking
*/

long
PyLong_AsLongAndOverflow(PyObject *vv, int *overflow)
inline long
_PyLong_AsLongAndOverflow(const PyLongObject *v, int *overflow)
{
/* This version by Tim Peters */
PyLongObject *v;
unsigned long x, prev;
long res;
Py_ssize_t i;
int sign;
int do_decref = 0; /* if PyNumber_Index was called */

*overflow = 0;
if (vv == NULL) {
PyErr_BadInternalCall();
return -1;
}

if (PyLong_Check(vv)) {
v = (PyLongObject *)vv;
}
else {
v = (PyLongObject *)_PyNumber_Index(vv);
if (v == NULL)
return -1;
do_decref = 1;
}
assert(v != NULL);
assert( PyLong_Check(v) );

res = -1;
i = Py_SIZE(v);
long res = -1;
Py_ssize_t i = Py_SIZE(v);

switch (i) {
case -1:
Expand All @@ -525,14 +501,15 @@ PyLong_AsLongAndOverflow(PyObject *vv, int *overflow)
res = v->ob_digit[0];
break;
default:
sign = 1;
x = 0;
{
int sign = 1;
unsigned long x = 0;
if (i < 0) {
sign = -1;
i = -(i);
}
while (--i >= 0) {
prev = x;
unsigned long prev = x;
x = (x << PyLong_SHIFT) | v->ob_digit[i];
if ((x >> PyLong_SHIFT) != prev) {
*overflow = sign;
Expand All @@ -553,7 +530,46 @@ PyLong_AsLongAndOverflow(PyObject *vv, int *overflow)
/* res is already set to -1 */
}
}
}
exit:
return res;
}


/* Get a C long int from an int object or any object that has an __index__
method.

On overflow, return -1 and set *overflow to 1 or -1 depending on the sign of
the result. Otherwise *overflow is 0.

For other errors (e.g., TypeError), return -1 and set an error condition.
In this case *overflow will be 0.
*/
long
PyLong_AsLongAndOverflow(PyObject *vv, int *overflow)
{
/* This version by Tim Peters */
PyLongObject *v;
int do_decref = 0; /* if PyNumber_Index was called */
*overflow = 0;

if (vv == NULL) {
PyErr_BadInternalCall();
return -1;
}

if (PyLong_Check(vv)) {
v = (PyLongObject *)vv;
}
else {
v = (PyLongObject *)_PyNumber_Index(vv);
if (v == NULL)
return -1;
do_decref = 1;
}

long res = _PyLong_AsLongAndOverflow(v, overflow);

if (do_decref) {
Py_DECREF(v);
}
Expand Down
28 changes: 28 additions & 0 deletions Objects/rangeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,28 @@ range_dealloc(rangeobject *r)
PyObject_Free(r);
}

static unsigned long
get_len_of_range(long lo, long hi, long step);

/// Return the length as a long, or -1 on error
static inline long compute_range_length_long(PyLongObject *start, PyLongObject *stop, PyLongObject *step) {
int overflow = 0;

long long_start = _PyLong_AsLongAndOverflow(start, &overflow);
if (overflow) {
return -1;
}
long long_stop = _PyLong_AsLongAndOverflow(stop, &overflow);
if (overflow) {
return -1;
}
long long_step = _PyLong_AsLongAndOverflow(step, &overflow);
if (overflow) {
return -1;
}
return get_len_of_range(long_start, long_stop, long_step);
}

/* Return number of items in range (lo, hi, step) as a PyLong object,
* when arguments are PyLong objects. Arguments MUST return 1 with
* PyLong_Check(). Return NULL when there is an error.
Expand All @@ -182,6 +204,12 @@ compute_range_length(PyObject *start, PyObject *stop, PyObject *step)
Algorithm is equal to that of get_len_of_range(), but it operates
on PyObjects (which are assumed to be PyLong objects).
---------------------------------------------------------------*/

// fast path when all arguments fit into a long integer
long len = compute_range_length_long((PyLongObject*)start, (PyLongObject*)stop, (PyLongObject*)step);
if (len>=0) {
return PyLong_FromLong(len);
}
int cmp_result;
PyObject *lo, *hi;
PyObject *diff = NULL;
Expand Down