Skip to content
Merged
Changes from 4 commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
181 changes: 109 additions & 72 deletions Objects/longobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,22 @@ class int "PyObject *" "&PyLong_Type"
_Py_IDENTIFIER(little);
_Py_IDENTIFIER(big);

/* convert a PyLong of size 1, 0 or -1 to an sdigit */
#define MEDIUM_VALUE(x) (assert(-1 <= Py_SIZE(x) && Py_SIZE(x) <= 1), \
Py_SIZE(x) < 0 ? -(sdigit)(x)->ob_digit[0] : \
(Py_SIZE(x) == 0 ? (sdigit)0 : \
(sdigit)(x)->ob_digit[0]))
/* Is this PyLong of size 1, 0 or -1? */
#define IS_MEDIUM_VALUE(x) (((size_t)Py_SIZE(x)) + 1 < 3)

/* convert a PyLong of size 1, 0 or -1 to a C integer */
static inline sdigit
medium_value(PyLongObject *x)
{
assert(IS_MEDIUM_VALUE(x));
return Py_SIZE(x) * x->ob_digit[0];
}

#define IS_SMALL_INT(ival) (-NSMALLNEGINTS <= (ival) && (ival) < NSMALLPOSINTS)
#define IS_SMALL_UINT(ival) ((ival) < NSMALLPOSINTS)

#define IS_MEDIUM_INT(x) (((unsigned long)x)+PyLong_MASK <= 2*PyLong_MASK)

static PyObject *
get_small_int(sdigit ival)
{
Expand All @@ -47,8 +54,8 @@ get_small_int(sdigit ival)
static PyLongObject *
maybe_small_long(PyLongObject *v)
{
if (v && Py_ABS(Py_SIZE(v)) <= 1) {
sdigit ival = MEDIUM_VALUE(v);
if (v && IS_MEDIUM_VALUE(v)) {
long ival = medium_value(v);
if (IS_SMALL_INT(ival)) {
Py_DECREF(v);
return (PyLongObject *)get_small_int(ival);
Expand All @@ -70,7 +77,7 @@ _PyLong_Negate(PyLongObject **x_p)
return;
}

*x_p = (PyLongObject *)PyLong_FromLong(-MEDIUM_VALUE(x));
*x_p = (PyLongObject *)PyLong_FromLong(-medium_value(x));
Py_DECREF(x);
}

Expand Down Expand Up @@ -121,18 +128,21 @@ PyLongObject *
_PyLong_New(Py_ssize_t size)
{
PyLongObject *result;
/* Number of bytes needed is: offsetof(PyLongObject, ob_digit) +
sizeof(digit)*size. Previous incarnations of this code used
sizeof(PyVarObject) instead of the offsetof, but this risks being
incorrect in the presence of padding between the PyVarObject header
and the digits. */
if (size > (Py_ssize_t)MAX_LONG_DIGITS) {
PyErr_SetString(PyExc_OverflowError,
"too many digits in integer");
return NULL;
}
/* Fast operations for single digit integers (including zero)
* assume that there is always at least one digit present. */
Py_ssize_t ndigits = size ? size : 1;
/* Number of bytes needed is: offsetof(PyLongObject, ob_digit) +
sizeof(digit)*size. Previous incarnations of this code used
sizeof(PyVarObject) instead of the offsetof, but this risks being
incorrect in the presence of padding between the PyVarObject header
and the digits. */
result = PyObject_Malloc(offsetof(PyLongObject, ob_digit) +
size*sizeof(digit));
ndigits*sizeof(digit));
if (!result) {
PyErr_NoMemory();
return NULL;
Expand All @@ -152,7 +162,7 @@ _PyLong_Copy(PyLongObject *src)
if (i < 0)
i = -(i);
if (i < 2) {
sdigit ival = MEDIUM_VALUE(src);
sdigit ival = medium_value(src);
if (IS_SMALL_INT(ival)) {
return get_small_int(ival);
}
Expand All @@ -167,20 +177,33 @@ _PyLong_Copy(PyLongObject *src)
return (PyObject *)result;
}

/* Create a new int object from a C long int */
static PyObject *
_PyLong_FromMedium(sdigit x)
{
assert(!IS_SMALL_INT(x));
assert(IS_MEDIUM_INT(x));
/* We could use a freelist here */
PyLongObject *v = PyObject_Malloc(sizeof(PyLongObject));
if (v == NULL) {
PyErr_NoMemory();
return NULL;
}
Py_ssize_t sign = x < 0 ? -1: 1;
digit abs_x = x < 0 ? -x : x;
_PyObject_InitVar((PyVarObject*)v, &PyLong_Type, sign);
v->ob_digit[0] = abs_x;
return (PyObject*)v;
}

PyObject *
PyLong_FromLong(long ival)
static PyObject *
_PyLong_FromLarge(long ival)
{
PyLongObject *v;
unsigned long abs_ival;
unsigned long t; /* unsigned so >> doesn't propagate sign bit */
int ndigits = 0;
int sign;

if (IS_SMALL_INT(ival)) {
return get_small_int((sdigit)ival);
}
assert(!IS_MEDIUM_INT(ival));

if (ival < 0) {
/* negate: can't write this as abs_ival = -ival since that
Expand All @@ -190,36 +213,9 @@ PyLong_FromLong(long ival)
}
else {
abs_ival = (unsigned long)ival;
sign = ival == 0 ? 0 : 1;
}

/* Fast path for single-digit ints */
if (!(abs_ival >> PyLong_SHIFT)) {
v = _PyLong_New(1);
if (v) {
Py_SET_SIZE(v, sign);
v->ob_digit[0] = Py_SAFE_DOWNCAST(
abs_ival, unsigned long, digit);
}
return (PyObject*)v;
}

#if PyLong_SHIFT==15
/* 2 digits */
if (!(abs_ival >> 2*PyLong_SHIFT)) {
v = _PyLong_New(2);
if (v) {
Py_SET_SIZE(v, 2 * sign);
v->ob_digit[0] = Py_SAFE_DOWNCAST(
abs_ival & PyLong_MASK, unsigned long, digit);
v->ob_digit[1] = Py_SAFE_DOWNCAST(
abs_ival >> PyLong_SHIFT, unsigned long, digit);
}
return (PyObject*)v;
sign = 1;
}
#endif

/* Larger numbers: loop to determine number of digits */
/* Loop to determine number of digits */
t = abs_ival;
while (t) {
++ndigits;
Expand All @@ -239,6 +235,38 @@ PyLong_FromLong(long ival)
return (PyObject *)v;
}

/* Convert result of add/subtract of medium values
* to a PyLong.
*/
static inline PyObject *
_PyLong_FromSDigit(sdigit x)
{
if (IS_SMALL_INT(x)) {
return get_small_int(x);
}
assert(x != 0);
if (IS_MEDIUM_INT(x)) {
return _PyLong_FromMedium(x);
}
return _PyLong_FromLarge(x);
}

/* Create a new int object from a C long int */

PyObject *
PyLong_FromLong(long ival)
{
if (IS_SMALL_INT(ival)) {
return get_small_int((sdigit)ival);
}

assert(ival != 0);
if (IS_MEDIUM_INT(ival)) {
return _PyLong_FromMedium(ival);
}
return _PyLong_FromLarge(ival);
}

#define PYLONG_FROM_UINT(INT_TYPE, ival) \
do { \
if (IS_SMALL_UINT(ival)) { \
Expand Down Expand Up @@ -2860,12 +2888,12 @@ PyLong_AsDouble(PyObject *v)
PyErr_SetString(PyExc_TypeError, "an integer is required");
return -1.0;
}
if (Py_ABS(Py_SIZE(v)) <= 1) {
if (IS_MEDIUM_VALUE(v)) {
/* Fast path; single digit long (31 bits) will cast safely
to double. This improves performance of FP/long operations
by 20%.
*/
return (double)MEDIUM_VALUE((PyLongObject *)v);
return (double)medium_value((PyLongObject *)v);
}
x = _PyLong_Frexp((PyLongObject *)v, &exponent);
if ((x == -1.0 && PyErr_Occurred()) || exponent > DBL_MAX_EXP) {
Expand Down Expand Up @@ -3067,8 +3095,8 @@ long_add(PyLongObject *a, PyLongObject *b)

CHECK_BINOP(a, b);

if (Py_ABS(Py_SIZE(a)) <= 1 && Py_ABS(Py_SIZE(b)) <= 1) {
return PyLong_FromLong(MEDIUM_VALUE(a) + MEDIUM_VALUE(b));
if (IS_MEDIUM_VALUE(a) && IS_MEDIUM_VALUE(b)) {
return PyLong_FromLong(medium_value(a) + medium_value(b));
}
if (Py_SIZE(a) < 0) {
if (Py_SIZE(b) < 0) {
Expand Down Expand Up @@ -3101,8 +3129,8 @@ long_sub(PyLongObject *a, PyLongObject *b)

CHECK_BINOP(a, b);

if (Py_ABS(Py_SIZE(a)) <= 1 && Py_ABS(Py_SIZE(b)) <= 1) {
return PyLong_FromLong(MEDIUM_VALUE(a) - MEDIUM_VALUE(b));
if (IS_MEDIUM_VALUE(a) && IS_MEDIUM_VALUE(b)) {
return _PyLong_FromSDigit(medium_value(a) - medium_value(b));
}
if (Py_SIZE(a) < 0) {
if (Py_SIZE(b) < 0) {
Expand Down Expand Up @@ -3536,8 +3564,8 @@ long_mul(PyLongObject *a, PyLongObject *b)
CHECK_BINOP(a, b);

/* fast path for single-digit multiplication */
if (Py_ABS(Py_SIZE(a)) <= 1 && Py_ABS(Py_SIZE(b)) <= 1) {
stwodigits v = (stwodigits)(MEDIUM_VALUE(a)) * MEDIUM_VALUE(b);
if (IS_MEDIUM_VALUE(a) && IS_MEDIUM_VALUE(b)) {
stwodigits v = (stwodigits)(medium_value(a)) * medium_value(b);
return PyLong_FromLongLong((long long)v);
}

Expand Down Expand Up @@ -4343,8 +4371,8 @@ long_invert(PyLongObject *v)
{
/* Implement ~x as -(x+1) */
PyLongObject *x;
if (Py_ABS(Py_SIZE(v)) <=1)
return PyLong_FromLong(-(MEDIUM_VALUE(v)+1));
if (IS_MEDIUM_VALUE(v))
return PyLong_FromLong(-(medium_value(v)+1));
x = (PyLongObject *) long_add(v, (PyLongObject *)_PyLong_GetOne());
if (x == NULL)
return NULL;
Expand All @@ -4358,8 +4386,8 @@ static PyObject *
long_neg(PyLongObject *v)
{
PyLongObject *z;
if (Py_ABS(Py_SIZE(v)) <= 1)
return PyLong_FromLong(-MEDIUM_VALUE(v));
if (IS_MEDIUM_VALUE(v))
return PyLong_FromLong(-medium_value(v));
z = (PyLongObject *)_PyLong_Copy(v);
if (z != NULL)
Py_SET_SIZE(z, -(Py_SIZE(v)));
Expand Down Expand Up @@ -4704,28 +4732,37 @@ long_bitwise(PyLongObject *a,
static PyObject *
long_and(PyObject *a, PyObject *b)
{
PyObject *c;
CHECK_BINOP(a, b);
c = long_bitwise((PyLongObject*)a, '&', (PyLongObject*)b);
return c;
PyLongObject *x = (PyLongObject*)a;
PyLongObject *y = (PyLongObject*)b;
if (IS_MEDIUM_VALUE(x) && IS_MEDIUM_VALUE(y)) {
return _PyLong_FromSDigit(medium_value(x) & medium_value(y));
}
return long_bitwise(x, '&', y);
}

static PyObject *
long_xor(PyObject *a, PyObject *b)
{
PyObject *c;
CHECK_BINOP(a, b);
c = long_bitwise((PyLongObject*)a, '^', (PyLongObject*)b);
return c;
PyLongObject *x = (PyLongObject*)a;
PyLongObject *y = (PyLongObject*)b;
if (IS_MEDIUM_VALUE(x) && IS_MEDIUM_VALUE(y)) {
return _PyLong_FromSDigit(medium_value(x) ^ medium_value(y));
}
return long_bitwise(x, '^', y);
}

static PyObject *
long_or(PyObject *a, PyObject *b)
{
PyObject *c;
CHECK_BINOP(a, b);
c = long_bitwise((PyLongObject*)a, '|', (PyLongObject*)b);
return c;
PyLongObject *x = (PyLongObject*)a;
PyLongObject *y = (PyLongObject*)b;
if (IS_MEDIUM_VALUE(x) && IS_MEDIUM_VALUE(y)) {
return _PyLong_FromSDigit(medium_value(x) | medium_value(y));
}
return long_bitwise(x, '|', y);
}

static PyObject *
Expand Down