diff options
author | Mark Dickinson <dickinsm@gmail.com> | 2009-02-15 10:13:41 (GMT) |
---|---|---|
committer | Mark Dickinson <dickinsm@gmail.com> | 2009-02-15 10:13:41 (GMT) |
commit | 2ffb26fb83b4c559d1f4cd3a26cda6d7234e809d (patch) | |
tree | fc58678a4ae4a4fab0f600fa8a23dc9b03c3d010 | |
parent | 5b8c701eef1b34fb5cdf3b1ecb4fefd927df2fae (diff) | |
download | cpython-2ffb26fb83b4c559d1f4cd3a26cda6d7234e809d.zip cpython-2ffb26fb83b4c559d1f4cd3a26cda6d7234e809d.tar.gz cpython-2ffb26fb83b4c559d1f4cd3a26cda6d7234e809d.tar.bz2 |
Issue #5260: Various portability and standards compliance fixes, optimizations
and cleanups in Objects/longobject.c. The most significant change is that
longs now use less memory: average savings are 2 bytes per long on 32-bit
systems and 6 bytes per long on 64-bit systems. (This memory saving already
exists in py3k.)
-rw-r--r-- | Include/longintrepr.h | 3 | ||||
-rw-r--r-- | Lib/test/test_sys.py | 12 | ||||
-rw-r--r-- | Misc/NEWS | 4 | ||||
-rw-r--r-- | Objects/longobject.c | 81 |
4 files changed, 46 insertions, 54 deletions
diff --git a/Include/longintrepr.h b/Include/longintrepr.h index df157a8..c32d72c 100644 --- a/Include/longintrepr.h +++ b/Include/longintrepr.h @@ -19,14 +19,13 @@ extern "C" { long_pow() requires that SHIFT be divisible by 5. */ typedef unsigned short digit; -typedef unsigned int wdigit; /* digit widened to parameter size */ #define BASE_TWODIGITS_TYPE long typedef unsigned BASE_TWODIGITS_TYPE twodigits; typedef BASE_TWODIGITS_TYPE stwodigits; /* signed variant of twodigits */ #define PyLong_SHIFT 15 #define PyLong_BASE ((digit)1 << PyLong_SHIFT) -#define PyLong_MASK ((int)(PyLong_BASE - 1)) +#define PyLong_MASK ((digit)(PyLong_BASE - 1)) /* b/w compatibility with Python 2.5 */ #define SHIFT PyLong_SHIFT diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index bd819c6..128880d 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -593,12 +593,12 @@ class SizeofTest(unittest.TestCase): # listreverseiterator (list) check(reversed([]), size(h + 'lP')) # long - check(0L, size(vh + 'H') - self.H) - check(1L, size(vh + 'H')) - check(-1L, size(vh + 'H')) - check(32768L, size(vh + 'H') + self.H) - check(32768L*32768L-1, size(vh + 'H') + self.H) - check(32768L*32768L, size(vh + 'H') + 2*self.H) + check(0L, size(vh)) + check(1L, size(vh) + self.H) + check(-1L, size(vh) + self.H) + check(32768L, size(vh) + 2*self.H) + check(32768L*32768L-1, size(vh) + 2*self.H) + check(32768L*32768L, size(vh) + 3*self.H) # module check(unittest, size(h + 'P')) # None @@ -12,6 +12,10 @@ What's New in Python 2.7 alpha 1 Core and Builtins ----------------- +- Issue #5260: Long integers now consume less memory: average + saving is 2 bytes per long on a 32-bit system and 6 bytes per long + on a 64-bit system. + - Issue #5186: Reduce hash collisions for objects with no __hash__ method by rotating the object pointer by 4 bits to the right. diff --git a/Objects/longobject.c b/Objects/longobject.c index ddfa72b..af222de 100644 --- a/Objects/longobject.c +++ b/Objects/longobject.c @@ -8,6 +8,7 @@ #include "longintrepr.h" #include <ctype.h> +#include <stddef.h> /* For long multiplication, use the O(N**2) school algorithm unless * both operands contain more than KARATSUBA_CUTOFF digits (this @@ -30,12 +31,6 @@ #define MAX(x, y) ((x) < (y) ? (y) : (x)) #define MIN(x, y) ((x) > (y) ? (y) : (x)) -/* Forward */ -static PyLongObject *long_normalize(PyLongObject *); -static PyLongObject *mul1(PyLongObject *, wdigit); -static PyLongObject *muladd1(PyLongObject *, wdigit, wdigit); -static PyLongObject *divrem1(PyLongObject *, digit, digit *); - #define SIGCHECK(PyTryBlock) \ if (--_Py_Ticker < 0) { \ _Py_Ticker = _Py_CheckInterval; \ @@ -62,16 +57,20 @@ long_normalize(register PyLongObject *v) /* Allocate a new long int object with size digits. Return NULL and set exception if we run out of memory. */ +#define MAX_LONG_DIGITS \ + ((PY_SSIZE_T_MAX - offsetof(PyLongObject, ob_digit))/sizeof(digit)) + PyLongObject * _PyLong_New(Py_ssize_t size) { - if (size > PY_SSIZE_T_MAX) { - PyErr_NoMemory(); + if (size > MAX_LONG_DIGITS) { + PyErr_SetString(PyExc_OverflowError, + "too many digits in integer"); return NULL; } /* coverity[ampersand_in_size] */ - /* XXX(nnorwitz): This can overflow -- - PyObject_NEW_VAR / _PyObject_VAR_SIZE need to detect overflow */ + /* XXX(nnorwitz): PyObject_NEW_VAR / _PyObject_VAR_SIZE need to detect + overflow */ return PyObject_NEW_VAR(PyLongObject, &PyLong_Type, size); } @@ -197,8 +196,8 @@ PyLong_FromDouble(double dval) return NULL; frac = ldexp(frac, (expo-1) % PyLong_SHIFT + 1); for (i = ndig; --i >= 0; ) { - long bits = (long)frac; - v->ob_digit[i] = (digit) bits; + digit bits = (digit)frac; + v->ob_digit[i] = bits; frac = frac - (double)bits; frac = ldexp(frac, PyLong_SHIFT); } @@ -438,9 +437,9 @@ _PyLong_FromByteArray(const unsigned char* bytes, size_t n, int incr; /* direction to move pstartbyte */ const unsigned char* pendbyte; /* MSB of bytes */ size_t numsignificantbytes; /* number of bytes that matter */ - size_t ndigits; /* number of Python long digits */ + Py_ssize_t ndigits; /* number of Python long digits */ PyLongObject* v; /* result */ - int idigit = 0; /* next free index in v->ob_digit */ + Py_ssize_t idigit = 0; /* next free index in v->ob_digit */ if (n == 0) return PyLong_FromLong(0L); @@ -483,12 +482,16 @@ _PyLong_FromByteArray(const unsigned char* bytes, size_t n, } /* How many Python long digits do we need? We have - 8*numsignificantbytes bits, and each Python long digit has PyLong_SHIFT - bits, so it's the ceiling of the quotient. */ + 8*numsignificantbytes bits, and each Python long digit has + PyLong_SHIFT bits, so it's the ceiling of the quotient. */ + /* catch overflow before it happens */ + if (numsignificantbytes > (PY_SSIZE_T_MAX - PyLong_SHIFT) / 8) { + PyErr_SetString(PyExc_OverflowError, + "byte array too long to convert to int"); + return NULL; + } ndigits = (numsignificantbytes * 8 + PyLong_SHIFT - 1) / PyLong_SHIFT; - if (ndigits > (size_t)INT_MAX) - return PyErr_NoMemory(); - v = _PyLong_New((int)ndigits); + v = _PyLong_New(ndigits); if (v == NULL) return NULL; @@ -517,8 +520,9 @@ _PyLong_FromByteArray(const unsigned char* bytes, size_t n, accumbits += 8; if (accumbits >= PyLong_SHIFT) { /* There's enough to fill a Python digit. */ - assert(idigit < (int)ndigits); - v->ob_digit[idigit] = (digit)(accum & PyLong_MASK); + assert(idigit < ndigits); + v->ob_digit[idigit] = (digit)(accum & + PyLong_MASK); ++idigit; accum >>= PyLong_SHIFT; accumbits -= PyLong_SHIFT; @@ -527,7 +531,7 @@ _PyLong_FromByteArray(const unsigned char* bytes, size_t n, } assert(accumbits < PyLong_SHIFT); if (accumbits) { - assert(idigit < (int)ndigits); + assert(idigit < ndigits); v->ob_digit[idigit] = (digit)accum; ++idigit; } @@ -1124,19 +1128,11 @@ v_isub(digit *x, Py_ssize_t m, digit *y, Py_ssize_t n) /* Multiply by a single digit, ignoring the sign. */ static PyLongObject * -mul1(PyLongObject *a, wdigit n) -{ - return muladd1(a, n, (digit)0); -} - -/* Multiply by a single digit and add a single digit, ignoring the sign. */ - -static PyLongObject * -muladd1(PyLongObject *a, wdigit n, wdigit extra) +mul1(PyLongObject *a, digit n) { Py_ssize_t size_a = ABS(Py_SIZE(a)); PyLongObject *z = _PyLong_New(size_a+1); - twodigits carry = extra; + twodigits carry = 0; Py_ssize_t i; if (z == NULL) @@ -1757,8 +1753,6 @@ PyLong_FromUnicode(Py_UNICODE *u, Py_ssize_t length, int base) static PyLongObject *x_divrem (PyLongObject *, PyLongObject *, PyLongObject **); static PyObject *long_long(PyObject *v); -static int long_divrem(PyLongObject *, PyLongObject *, - PyLongObject **, PyLongObject **); /* Long division with remainder, top-level routine */ @@ -1976,14 +1970,12 @@ long_hash(PyLongObject *v) sign = -1; i = -(i); } -#define LONG_BIT_PyLong_SHIFT (8*sizeof(long) - PyLong_SHIFT) /* The following loop produces a C unsigned long x such that x is congruent to the absolute value of v modulo ULONG_MAX. The resulting x is nonzero if and only if v is. */ while (--i >= 0) { /* Force a native long #-bits (32 or 64) circular shift */ - x = ((x << PyLong_SHIFT) & ~PyLong_MASK) | - ((x >> LONG_BIT_PyLong_SHIFT) & PyLong_MASK); + x = (x >> (8*SIZEOF_LONG-PyLong_SHIFT)) | (x << PyLong_SHIFT); x += v->ob_digit[i]; /* If the addition above overflowed we compensate by incrementing. This preserves the value modulo @@ -1991,11 +1983,10 @@ long_hash(PyLongObject *v) if (x < v->ob_digit[i]) x++; } -#undef LONG_BIT_PyLong_SHIFT x = x * sign; - if (x == -1) - x = -2; - return x; + if (x == (unsigned long)-1) + x = (unsigned long)-2; + return (long)x; } @@ -3337,7 +3328,7 @@ long_new(PyTypeObject *type, PyObject *args, PyObject *kwds) /* Since PyLong_FromString doesn't have a length parameter, * check here for possible NULs in the string. */ char *string = PyString_AS_STRING(x); - if (strlen(string) != PyString_Size(x)) { + if (strlen(string) != (size_t)PyString_Size(x)) { /* create a repr() of the input string, * just like PyLong_FromString does. */ PyObject *srepr; @@ -3443,9 +3434,7 @@ long_sizeof(PyLongObject *v) { Py_ssize_t res; - res = v->ob_type->tp_basicsize; - if (v->ob_size != 0) - res += abs(v->ob_size) * sizeof(digit); + res = v->ob_type->tp_basicsize + ABS(Py_SIZE(v))*sizeof(digit); return PyInt_FromSsize_t(res); } @@ -3619,7 +3608,7 @@ PyTypeObject PyLong_Type = { PyObject_HEAD_INIT(&PyType_Type) 0, /* ob_size */ "long", /* tp_name */ - sizeof(PyLongObject) - sizeof(digit), /* tp_basicsize */ + offsetof(PyLongObject, ob_digit), /* tp_basicsize */ sizeof(digit), /* tp_itemsize */ long_dealloc, /* tp_dealloc */ 0, /* tp_print */ |