Index: Python/marshal.c =================================================================== --- Python/marshal.c (revision 69721) +++ Python/marshal.c (working copy) @@ -11,6 +11,8 @@ #include "code.h" #include "marshal.h" +#define ABS(x) ((x) < 0 ? -(x) : (x)) + /* High water mark to determine when the marshalled object is dangerously deep * and risks coring the interpreter. When the object stack gets this deep, * raise an exception instead of continuing. @@ -122,7 +124,57 @@ } #endif +/* We assume that Python longs are stored internally in base some power of + 2**15; for the sake of portability we'll always read and write them in base + exactly 2**15. */ + +#define PyLong_MARSHAL_SHIFT 15 +#define PyLong_MARSHAL_BASE ((short)1 << PyLong_MARSHAL_SHIFT) +#define PyLong_MARSHAL_MASK (PyLong_MARSHAL_BASE - 1) +#if PyLong_SHIFT % PyLong_MARSHAL_SHIFT != 0 +#error "PyLong_SHIFT must be a multiple of PyLong_MARSHAL_SHIFT" +#endif +#define PyLong_MARSHAL_RATIO (PyLong_SHIFT / PyLong_MARSHAL_SHIFT) + static void +w_PyLong(const PyLongObject *ob, WFILE *p) +{ + Py_ssize_t i, j, n, l; + digit d; + + w_byte(TYPE_LONG, p); + if (Py_SIZE(ob) == 0) { + w_long((long)0, p); + return; + } + + /* set l to number of base PyLong_MARSHAL_BASE digits */ + n = ABS(Py_SIZE(ob)); + l = (n-1) * PyLong_MARSHAL_RATIO; + d = ob->ob_digit[n-1]; + assert(d != 0); /* a PyLong is always normalized */ + do { + d >>= PyLong_MARSHAL_SHIFT; + l++; + } while (d != 0); + w_long((long)(Py_SIZE(ob) > 0 ? l : -l), p); + + for (i=0; i < n-1; i++) { + d = ob->ob_digit[i]; + for (j=0; j < PyLong_MARSHAL_RATIO; j++) { + w_short(d & PyLong_MARSHAL_MASK, p); + d >>= PyLong_MARSHAL_SHIFT; + } + assert (d == 0); + } + d = ob->ob_digit[n-1]; + do { + w_short(d & PyLong_MARSHAL_MASK, p); + d >>= PyLong_MARSHAL_SHIFT; + } while (d != 0); +} + +static void w_object(PyObject *v, WFILE *p) { Py_ssize_t i, n; @@ -155,14 +207,8 @@ if ((x == -1) && PyErr_Occurred()) { PyLongObject *ob = (PyLongObject *)v; PyErr_Clear(); - w_byte(TYPE_LONG, p); - n = Py_SIZE(ob); - w_long((long)n, p); - if (n < 0) - n = -n; - for (i = 0; i < n; i++) - w_short(ob->ob_digit[i], p); - } + w_PyLong(ob, p); + } else { #if SIZEOF_LONG > 4 long y = Py_ARITHMETIC_RIGHT_SHIFT(long, x, 31); @@ -481,6 +527,55 @@ } static PyObject * +r_PyLong(RFILE *p) +{ + PyLongObject *ob; + int size, i, j, md; + long n; + digit d; + + n = r_long(p); + if (n == 0) + return (PyObject *)_PyLong_New(0); + if (n < -INT_MAX || n > INT_MAX) + goto bad_data; + + size = 1 + (ABS(n)-1) / PyLong_MARSHAL_RATIO; + ob = _PyLong_New(size); + if (ob == NULL) + return NULL; + Py_SIZE(ob) = n > 0 ? size : -size; + + for (i = 0; i < size-1; i++) { + d = 0; + for (j=0; j < PyLong_MARSHAL_RATIO; j++) { + md = r_short(p); + if (md < 0 || md > PyLong_MARSHAL_BASE) { + Py_DECREF(ob); + goto bad_data; + } + d += (digit)md << j*PyLong_MARSHAL_SHIFT; + } + ob->ob_digit[i] = d; + } + d = 0; + for (j=0; j < (ABS(n)-1)%PyLong_MARSHAL_RATIO + 1; j++) { + md = r_short(p); + if (md < 0 || md > PyLong_MARSHAL_BASE) { + Py_DECREF(ob); + goto bad_data; + } + d += (digit)md << (j*PyLong_MARSHAL_SHIFT); + } + ob->ob_digit[size-1] = d; + return (PyObject *)ob; + bad_data: + PyErr_SetString(PyExc_ValueError, "bad marshal data"); + return NULL; +} + + +static PyObject * r_object(RFILE *p) { /* NULL is a valid return value, it does not necessarily means that @@ -544,38 +639,8 @@ break; case TYPE_LONG: - { - int size; - PyLongObject *ob; - n = r_long(p); - if (n < -INT_MAX || n > INT_MAX) { - PyErr_SetString(PyExc_ValueError, - "bad marshal data"); - retval = NULL; - break; - } - size = n<0 ? -n : n; - ob = _PyLong_New(size); - if (ob == NULL) { - retval = NULL; - break; - } - Py_SIZE(ob) = n; - for (i = 0; i < size; i++) { - int digit = r_short(p); - if (digit < 0) { - Py_DECREF(ob); - PyErr_SetString(PyExc_ValueError, - "bad marshal data"); - ob = NULL; - break; - } - if (ob != NULL) - ob->ob_digit[i] = digit; - } - retval = (PyObject *)ob; - break; - } + retval = r_PyLong(p); + break; case TYPE_FLOAT: { Index: Python/sysmodule.c =================================================================== --- Python/sysmodule.c (revision 69721) +++ Python/sysmodule.c (working copy) @@ -1012,6 +1012,7 @@ Static objects:\n\ \n\ float_info -- a dict with information about the float implementation.\n\ +int_info -- a struct sequence with information about the int implementation.\n\ maxsize -- the largest supported length of containers.\n\ maxunicode -- the largest supported character\n\ builtin_module_names -- tuple of module names built into this interpreter\n\ @@ -1375,6 +1376,8 @@ PyLong_FromSsize_t(PY_SSIZE_T_MAX)); SET_SYS_FROM_STRING("float_info", PyFloat_GetInfo()); + SET_SYS_FROM_STRING("int_info", + PyLong_GetInfo()); SET_SYS_FROM_STRING("maxunicode", PyLong_FromLong(PyUnicode_GetMax())); SET_SYS_FROM_STRING("builtin_module_names", Index: Include/pyport.h =================================================================== --- Include/pyport.h (revision 69721) +++ Include/pyport.h (working copy) @@ -69,6 +69,58 @@ #endif #endif /* HAVE_LONG_LONG */ +/* a build with 30-bit digits for Python long integers needs an exact-width + * 32-bit unsigned integer type to store thoses digits. (We could just use + * type 'long', but that would be wasteful on a system where longs are + * 64-bits.) On Unix systems, the autoconf macro AC_TYPE_UINT32_T defines + * uint32_t to be such a type unless stdint.h or inttypes.h defines uint32_t. + * However, it doesn't set HAVE_UINT32_T, so we do that here. + */ +#if (defined UINT32_MAX || defined uint32_t) +#ifndef PY_UINT32_T +#define HAVE_UINT32_T +#define PY_UINT32_T uint32_t +#endif +#endif + +/* Macros for a 64-bit unsigned integer type; used for type 'twodigits' in the + * long integer implementation, when 30-bit digits are enabled. + */ +#if (defined UINT64_MAX || defined uint64_t) +#ifndef PY_UINT64_T +#define HAVE_UINT64_T +#define PY_UINT64_T uint64_t +#endif +#endif + +/* Signed variants of the above */ +#if (defined INT32_MAX || defined int32_t) +#ifndef PY_INT32_T +#define HAVE_INT32_T +#define PY_INT32_T int32_t +#endif +#endif +#if (defined INT64_MAX || defined int64_t) +#ifndef PY_INT64_T +#define HAVE_INT64_T +#define PY_INT64_T int64_t +#endif +#endif + +/* If PYLONG_DIGIT_SIZE is not defined then we try to guess what +size long digits would be optimal, erring on the conservative +side (use 15-bit digits if there's any doubt). */ +#ifndef PYLONG_DIGIT_SIZE +#if (defined HAVE_UINT64_T && defined HAVE_INT64_T && \ + defined HAVE_UINT32_T && defined HAVE_INT32_T && \ + SIZEOF_VOID_P >= 8 && \ + (SIZEOF_LONG >= 8 || HAVE_LONG_LONG)) +#define PYLONG_DIGIT_SIZE 30 +#else +#define PYLONG_DIGIT_SIZE 15 +#endif +#endif + /* uintptr_t is the C9X name for an unsigned integral type such that a * legitimate void* can be cast to uintptr_t and then back to void* again * without loss of information. Similarly for intptr_t, wrt a signed Index: Include/longintrepr.h =================================================================== --- Include/longintrepr.h (revision 69721) +++ Include/longintrepr.h (working copy) @@ -18,13 +18,35 @@ And, at some places it is assumed that MASK fits in an int, as well. long_pow() requires that SHIFT be divisible by 5. */ +/* Additional restrictions: PyLong_SHIFT should be greater than or equal to 8, + and strictly less than the number of bits in an unsigned long. It should + also be strictly less than the number of bits in a Py_ssize_t. + Furthermore, NSMALLNEGINTS and NSMALLPOSINTS should fit in a digit. */ + + +#if HAVE_STDINT_H +#include +#endif + +#if PYLONG_DIGIT_SIZE == 30 +#if !(defined HAVE_UINT64_T && defined HAVE_UINT32_T && \ + defined HAVE_INT64_T && defined HAVE_INT32_T) +#error "30-bit long digits requested, but the necessary types are not available on this platform" +#endif +typedef PY_UINT32_T digit; +typedef PY_INT32_T sdigit; /* signed variant of digit */ +typedef PY_UINT64_T twodigits; +typedef PY_INT64_T stwodigits; /* signed variant of twodigits */ +#define PyLong_SHIFT 30 +#elif PYLONG_DIGIT_SIZE == 15 typedef unsigned short digit; -typedef short sdigit; /* signed variant of digit */ -#define BASE_TWODIGITS_TYPE long -typedef unsigned BASE_TWODIGITS_TYPE twodigits; -typedef BASE_TWODIGITS_TYPE stwodigits; /* signed variant of twodigits */ - +typedef short sdigit; /* signed variant of digit */ +typedef unsigned long twodigits; +typedef long stwodigits; /* signed variant of twodigits */ #define PyLong_SHIFT 15 +#else +#error "PYLONG_DIGIT_SIZE should be 15 or 30" +#endif #define PyLong_BASE ((digit)1 << PyLong_SHIFT) #define PyLong_MASK ((digit)(PyLong_BASE - 1)) Index: Include/longobject.h =================================================================== --- Include/longobject.h (revision 69721) +++ Include/longobject.h (working copy) @@ -26,6 +26,7 @@ PyAPI_FUNC(size_t) PyLong_AsSize_t(PyObject *); PyAPI_FUNC(unsigned long) PyLong_AsUnsignedLong(PyObject *); PyAPI_FUNC(unsigned long) PyLong_AsUnsignedLongMask(PyObject *); +PyAPI_FUNC(PyObject *) PyLong_GetInfo(void); /* It may be useful in the future. I've added it in the PyInt -> PyLong cleanup to keep the extra information. [CH] */ Index: configure.in =================================================================== --- configure.in (revision 69721) +++ configure.in (working copy) @@ -1288,6 +1288,10 @@ AC_TYPE_SIGNAL AC_TYPE_SIZE_T AC_TYPE_UID_T +AC_TYPE_UINT32_T +AC_TYPE_UINT64_T +AC_TYPE_INT32_T +AC_TYPE_INT64_T AC_CHECK_TYPE(ssize_t, AC_DEFINE(HAVE_SSIZE_T, 1, Define if your compiler provides ssize_t),,) @@ -3125,6 +3129,25 @@ LIBS=$LIBS_SAVE +# determine what size digit to use for Python's longs +AC_MSG_CHECKING([digit size for Python's longs]) +AC_ARG_ENABLE(big-digits, +AC_HELP_STRING([--enable-big-digits@<:@=BITS@:>@],[use big digits for Python longs [[BITS=30]]]), +[case $enable_big_digits in +yes) + enable_big_digits=30 ;; +no) + enable_big_digits=15 ;; +[15|30]) + ;; +*) + AC_MSG_ERROR([bad value $enable_big_digits for --enable-big-digits; value should be 15 or 30]) ;; +esac +AC_MSG_RESULT($enable_big_digits) +AC_DEFINE_UNQUOTED(PYLONG_DIGIT_SIZE, $enable_big_digits, [Define as the preferred size in bits of long digits]) +], +[AC_MSG_RESULT(no value specified)]) + # check for wchar.h AC_CHECK_HEADER(wchar.h, [ AC_DEFINE(HAVE_WCHAR_H, 1, Index: Objects/longobject.c =================================================================== --- Objects/longobject.c (revision 69721) +++ Objects/longobject.c (working copy) @@ -4,6 +4,7 @@ #include "Python.h" #include "longintrepr.h" +#include "structseq.h" #include #include @@ -204,6 +205,7 @@ return (PyObject*)v; } +#if PyLONG_SHIFT==15 /* 2 digits */ if (!(abs_ival >> 2*PyLong_SHIFT)) { v = _PyLong_New(2); @@ -216,6 +218,7 @@ } return (PyObject*)v; } +#endif /* Larger numbers: loop to determine number of digits */ t = abs_ival; @@ -2864,10 +2867,33 @@ CHECK_BINOP(a, b); + /* fast path for single-digit multiplication */ if (ABS(Py_SIZE(a)) <= 1 && ABS(Py_SIZE(b)) <= 1) { - PyObject *r; - r = PyLong_FromLong(MEDIUM_VALUE(a)*MEDIUM_VALUE(b)); - return r; + /* XXX benchmark this! Is is worth keeping? */ + twodigits absz; + int sign; + sign = Py_SIZE(a) * Py_SIZE(b); + if (sign == 0) + return PyLong_FromLong(0L); + absz = (twodigits)a->ob_digit[0] * b->ob_digit[0]; + if (absz < PyLong_BASE) { + CHECK_SMALL_INT((sdigit)(sign*absz)); + z = _PyLong_New(1); + if (z != NULL) { + Py_SIZE(z) = sign; + z->ob_digit[0] = (digit)absz; + } + } + else { + z = _PyLong_New(2); + if (z != NULL) { + Py_SIZE(z) = 2*sign; + z->ob_digit[0] = (digit)(absz & PyLong_MASK); + assert(absz >>= 2*PyLong_SHIFT == 0); + z->ob_digit[1] = (digit)(absz >> PyLong_SHIFT); + } + } + return (PyObject *)z; } z = k_mul(a, b); @@ -3991,6 +4017,45 @@ PyObject_Del, /* tp_free */ }; +static PyTypeObject Int_InfoType; + +PyDoc_STRVAR(int_info__doc__, +"sys.int_info\n\ +\n\ +A struct sequence that holds information about Python's\n\ +internal representation of integers. The attributes are read only."); + +static PyStructSequence_Field int_info_fields[] = { + {"bits_per_digit", "size of a digit in bits"}, + {"sizeof_digit", "size in bytes of the C type used to " + "represent a digit"}, + {NULL, NULL} +}; + +static PyStructSequence_Desc int_info_desc = { + "sys.int_info", /* name */ + int_info__doc__, /* doc */ + int_info_fields, /* fields */ + 2 /* number of fields */ +}; + +PyObject * +PyLong_GetInfo(void) +{ + PyObject* int_info; + int field = 0; + int_info = PyStructSequence_New(&Int_InfoType); + if (int_info == NULL) + return NULL; + PyStructSequence_SET_ITEM(int_info, field++, PyLong_FromLong(PyLong_SHIFT)); + PyStructSequence_SET_ITEM(int_info, field++, PyLong_FromLong(sizeof(digit))); + if (PyErr_Occurred()) { + Py_CLEAR(int_info); + return NULL; + } + return int_info; +} + int _PyLong_Init(void) { @@ -4023,6 +4088,10 @@ v->ob_digit[0] = abs(ival); } #endif + /* initialize int_info */ + if (Int_InfoType.tp_name == 0) + PyStructSequence_InitType(&Int_InfoType, &int_info_desc); + return 1; } Index: PC/pyconfig.h =================================================================== --- PC/pyconfig.h (revision 69721) +++ PC/pyconfig.h (working copy) @@ -307,6 +307,25 @@ # define PY_ULLONG_MAX _UI64_MAX #endif +/* define signed and unsigned exact-width 32-bit and 64-bit types, which may + be used in the implementation of Python long integers */ +#define HAVE_UINT32_T 1 +#ifndef PY_UINT32_T +#define PY_UINT32_T unsigned __int32 +#endif +#define HAVE_UINT64_T 1 +#ifndef PY_UINT64_T +#define PY_UINT64_T unsigned __int64 +#endif +#define HAVE_INT32_T 1 +#ifndef PY_INT32_T +#define PY_INT32_T __int32 +#endif +#define HAVE_INT64_T 1 +#ifndef PY_INT64_T +#define PY_INT64_T __int64 +#endif + /* For Windows the Python core is in a DLL by default. Test Py_NO_ENABLE_SHARED to find out. Also support MS_NO_COREDLL for b/w compat */ #if !defined(MS_NO_COREDLL) && !defined(Py_NO_ENABLE_SHARED) Index: Doc/library/sys.rst =================================================================== --- Doc/library/sys.rst (revision 69721) +++ Doc/library/sys.rst (working copy) @@ -413,6 +413,23 @@ same information. +.. data:: int_info + + A struct sequence that holds information about Python's + internal representation of integers. The attributes are read only. + + +-------------------------+----------------------------------------------+ + | attribute | explanation | + +=========================+==============================================+ + | :const:`bits_per_digit` | number of bits held in each digit. Python | + | | integers are stored internally in base | + | | ``2**int_info.bits_per_digit`` | + +-------------------------+----------------------------------------------+ + | :const:`sizeof_digit` | size in bytes of the C type used to | + | | represent a digit | + +-------------------------+----------------------------------------------+ + + .. function:: intern(string) Enter *string* in the table of "interned" strings and return the interned string Index: Lib/test/test_long.py =================================================================== --- Lib/test/test_long.py (revision 69721) +++ Lib/test/test_long.py (working copy) @@ -15,7 +15,7 @@ return self.format % self.args # SHIFT should match the value in longintrepr.h for best testing. -SHIFT = 15 +SHIFT = sys.int_info.bits_per_digit BASE = 2 ** SHIFT MASK = BASE - 1 KARATSUBA_CUTOFF = 70 # from longobject.c Index: Lib/test/test_sys.py =================================================================== --- Lib/test/test_sys.py (revision 69721) +++ Lib/test/test_sys.py (working copy) @@ -291,6 +291,9 @@ self.assert_(isinstance(sys.executable, str)) self.assertEqual(len(sys.float_info), 11) self.assertEqual(sys.float_info.radix, 2) + self.assertEqual(len(sys.int_info), 2) + self.assert_(sys.int_info.bits_per_digit % 5 == 0) + self.assert_(sys.int_info.sizeof_digit >= 1) self.assert_(isinstance(sys.hexversion, int)) self.assert_(isinstance(sys.maxsize, int)) self.assert_(isinstance(sys.maxunicode, int)) @@ -395,6 +398,7 @@ if hasattr(sys, "gettotalrefcount"): self.header += '2P' self.vheader += '2P' + self.longdigit = sys.int_info.sizeof_digit import _testcapi self.gc_headsize = _testcapi.SIZEOF_PYGC_HEAD self.file = open(test.support.TESTFN, 'wb') @@ -429,7 +433,7 @@ size = self.calcsize gc_header_size = self.gc_headsize # bool objects are not gc tracked - self.assertEqual(sys.getsizeof(True), size(vh) + self.H) + self.assertEqual(sys.getsizeof(True), size(vh) + self.longdigit) # but lists are self.assertEqual(sys.getsizeof([]), size(vh + 'PP') + gc_header_size) @@ -437,8 +441,8 @@ h = self.header vh = self.vheader size = self.calcsize - self.assertEqual(sys.getsizeof(True), size(vh) + self.H) - self.assertEqual(sys.getsizeof(True, -1), size(vh) + self.H) + self.assertEqual(sys.getsizeof(True), size(vh) + self.longdigit) + self.assertEqual(sys.getsizeof(True, -1), size(vh) + self.longdigit) def test_objecttypes(self): # check all types defined in Objects/ @@ -447,7 +451,7 @@ size = self.calcsize check = self.check_sizeof # bool - check(True, size(vh) + self.H) + check(True, size(vh) + self.longdigit) # buffer # XXX # builtin_function_or_method @@ -565,11 +569,12 @@ check(reversed([]), size(h + 'lP')) # long check(0, size(vh)) - check(1, size(vh) + self.H) - check(-1, size(vh) + self.H) - check(32768, size(vh) + 2*self.H) - check(32768*32768-1, size(vh) + 2*self.H) - check(32768*32768, size(vh) + 3*self.H) + check(1, size(vh) + self.longdigit) + check(-1, size(vh) + self.longdigit) + PyLong_BASE = 2**sys.int_info.bits_per_digit + check(PyLong_BASE, size(vh) + 2*self.longdigit) + check(PyLong_BASE**2-1, size(vh) + 2*self.longdigit) + check(PyLong_BASE**2, size(vh) + 3*self.longdigit) # memory check(memoryview(b''), size(h + 'P PP2P2i7P')) # module