summaryrefslogtreecommitdiff
path: root/Python/marshal.c
diff options
context:
space:
mode:
authorMark Dickinson <dickinsm@gmail.com>2009-03-20 15:51:55 +0000
committerMark Dickinson <dickinsm@gmail.com>2009-03-20 15:51:55 +0000
commitefc82f7e8eff19d8e844a3dc268a88de7fbcb173 (patch)
tree58198f2e7610ba6d33865884487de006de30af85 /Python/marshal.c
parentc8e81ef508f0f1dc4e5c31bd0bec2766867fead5 (diff)
downloadcpython-git-efc82f7e8eff19d8e844a3dc268a88de7fbcb173.tar.gz
Issue #4258: Use 30-bit digits for Python longs, on 64-bit platforms.
Backport of r70459.
Diffstat (limited to 'Python/marshal.c')
-rw-r--r--Python/marshal.c144
1 files changed, 105 insertions, 39 deletions
diff --git a/Python/marshal.c b/Python/marshal.c
index 3460bc3b6a..16cfc20251 100644
--- a/Python/marshal.c
+++ b/Python/marshal.c
@@ -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.
@@ -119,6 +121,56 @@ w_long64(long x, WFILE *p)
}
#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)
{
@@ -164,13 +216,7 @@ w_object(PyObject *v, WFILE *p)
}
else if (PyLong_CheckExact(v)) {
PyLongObject *ob = (PyLongObject *)v;
- w_byte(TYPE_LONG, p);
- n = ob->ob_size;
- 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 (PyFloat_CheckExact(v)) {
if (p->version > 1) {
@@ -507,6 +553,56 @@ r_long64(RFILE *p)
}
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) {
+ PyErr_SetString(PyExc_ValueError,
+ "bad marshal data (long size out of range)");
+ return NULL;
+ }
+
+ 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)
+ goto bad_digit;
+ 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)
+ goto bad_digit;
+ d += (digit)md << j*PyLong_MARSHAL_SHIFT;
+ }
+ ob->ob_digit[size-1] = d;
+ return (PyObject *)ob;
+ bad_digit:
+ Py_DECREF(ob);
+ PyErr_SetString(PyExc_ValueError,
+ "bad marshal data (digit out of range in long)");
+ return NULL;
+}
+
+
+static PyObject *
r_object(RFILE *p)
{
/* NULL is a valid return value, it does not necessarily means that
@@ -570,38 +666,8 @@ r_object(RFILE *p)
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 (long size out of range)");
- retval = NULL;
- break;
- }
- size = n<0 ? -n : n;
- ob = _PyLong_New(size);
- if (ob == NULL) {
- retval = NULL;
- break;
- }
- ob->ob_size = 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 (negative digit in long)");
- ob = NULL;
- break;
- }
- if (ob != NULL)
- ob->ob_digit[i] = digit;
- }
- retval = (PyObject *)ob;
- break;
- }
+ retval = r_PyLong(p);
+ break;
case TYPE_FLOAT:
{