diff options
Diffstat (limited to 'Python/marshal.c')
-rw-r--r-- | Python/marshal.c | 205 |
1 files changed, 132 insertions, 73 deletions
diff --git a/Python/marshal.c b/Python/marshal.c index 6f0ee5e83e..d26a997534 100644 --- a/Python/marshal.c +++ b/Python/marshal.c @@ -12,8 +12,7 @@ #include "longintrepr.h" #include "code.h" #include "marshal.h" - -#define ABS(x) ((x) < 0 ? -(x) : (x)) +#include "../Modules/hashtable.h" /* High water mark to determine when the marshalled object is dangerously deep * and risks coring the interpreter. When the object stack gets this deep, @@ -21,7 +20,7 @@ * On Windows debug builds, reduce this value. */ #if defined(MS_WINDOWS) && defined(_DEBUG) -#define MAX_MARSHAL_STACK_DEPTH 1500 +#define MAX_MARSHAL_STACK_DEPTH 1000 #else #define MAX_MARSHAL_STACK_DEPTH 2000 #endif @@ -66,54 +65,83 @@ typedef struct { FILE *fp; int error; /* see WFERR_* values */ int depth; - /* If fp == NULL, the following are valid: */ - PyObject *readable; /* Stream-like object being read from */ PyObject *str; - PyObject *current_filename; char *ptr; char *end; char *buf; - Py_ssize_t buf_size; - PyObject *refs; /* dict on marshal, list on unmarshal */ + _Py_hashtable_t *hashtable; int version; } WFILE; -#define w_byte(c, p) if (((p)->fp)) putc((c), (p)->fp); \ - else if ((p)->ptr != (p)->end) *(p)->ptr++ = (c); \ - else w_more((c), p) +#define w_byte(c, p) do { \ + if ((p)->ptr != (p)->end || w_reserve((p), 1)) \ + *(p)->ptr++ = (c); \ + } while(0) static void -w_more(char c, WFILE *p) +w_flush(WFILE *p) +{ + assert(p->fp != NULL); + fwrite(p->buf, 1, p->ptr - p->buf, p->fp); + p->ptr = p->buf; +} + +static int +w_reserve(WFILE *p, Py_ssize_t needed) { - Py_ssize_t size, newsize; - if (p->str == NULL) - return; /* An error already occurred */ + Py_ssize_t pos, size, delta; + if (p->ptr == NULL) + return 0; /* An error already occurred */ + if (p->fp != NULL) { + w_flush(p); + return needed <= p->end - p->ptr; + } + assert(p->str != NULL); + pos = p->ptr - p->buf; size = PyBytes_Size(p->str); - newsize = size + size + 1024; - if (newsize > 32*1024*1024) { - newsize = size + (size >> 3); /* 12.5% overallocation */ + if (size > 16*1024*1024) + delta = (size >> 3); /* 12.5% overallocation */ + else + delta = size + 1024; + delta = Py_MAX(delta, needed); + if (delta > PY_SSIZE_T_MAX - size) { + p->error = WFERR_NOMEMORY; + return 0; } - if (_PyBytes_Resize(&p->str, newsize) != 0) { - p->ptr = p->end = NULL; + size += delta; + if (_PyBytes_Resize(&p->str, size) != 0) { + p->ptr = p->buf = p->end = NULL; + return 0; } else { - p->ptr = PyBytes_AS_STRING((PyBytesObject *)p->str) + size; - p->end = - PyBytes_AS_STRING((PyBytesObject *)p->str) + newsize; - *p->ptr++ = c; + p->buf = PyBytes_AS_STRING(p->str); + p->ptr = p->buf + pos; + p->end = p->buf + size; + return 1; } } static void w_string(const char *s, Py_ssize_t n, WFILE *p) { + Py_ssize_t m; + if (!n || p->ptr == NULL) + return; + m = p->end - p->ptr; if (p->fp != NULL) { - fwrite(s, 1, n, p->fp); + if (n <= m) { + Py_MEMCPY(p->ptr, s, n); + p->ptr += n; + } + else { + w_flush(p); + fwrite(s, 1, n, p->fp); + } } else { - while (--n >= 0) { - w_byte(*s, p); - s++; + if (n <= m || w_reserve(p, n - m)) { + Py_MEMCPY(p->ptr, s, n); + p->ptr += n; } } } @@ -192,7 +220,7 @@ w_PyLong(const PyLongObject *ob, char flag, WFILE *p) } /* set l to number of base PyLong_MARSHAL_BASE digits */ - n = ABS(Py_SIZE(ob)); + n = Py_ABS(Py_SIZE(ob)); l = (n-1) * PyLong_MARSHAL_RATIO; d = ob->ob_digit[n-1]; assert(d != 0); /* a PyLong is always normalized */ @@ -225,46 +253,38 @@ w_PyLong(const PyLongObject *ob, char flag, WFILE *p) static int w_ref(PyObject *v, char *flag, WFILE *p) { - PyObject *id; - PyObject *idx; + _Py_hashtable_entry_t *entry; + int w; - if (p->version < 3 || p->refs == NULL) + if (p->version < 3 || p->hashtable == NULL) return 0; /* not writing object references */ /* if it has only one reference, it definitely isn't shared */ if (Py_REFCNT(v) == 1) return 0; - id = PyLong_FromVoidPtr((void*)v); - if (id == NULL) - goto err; - idx = PyDict_GetItem(p->refs, id); - if (idx != NULL) { + entry = _Py_hashtable_get_entry(p->hashtable, v); + if (entry != NULL) { /* write the reference index to the stream */ - long w = PyLong_AsLong(idx); - Py_DECREF(id); - if (w == -1 && PyErr_Occurred()) { - goto err; - } + _Py_HASHTABLE_ENTRY_READ_DATA(p->hashtable, &w, sizeof(w), entry); /* we don't store "long" indices in the dict */ assert(0 <= w && w <= 0x7fffffff); w_byte(TYPE_REF, p); w_long(w, p); return 1; } else { - int ok; - Py_ssize_t s = PyDict_Size(p->refs); + size_t s = p->hashtable->entries; /* we don't support long indices */ if (s >= 0x7fffffff) { PyErr_SetString(PyExc_ValueError, "too many objects"); goto err; } - idx = PyLong_FromSsize_t(s); - ok = idx && PyDict_SetItem(p->refs, id, idx) == 0; - Py_DECREF(id); - Py_XDECREF(idx); - if (!ok) + w = (int)s; + Py_INCREF(v); + if (_Py_HASHTABLE_SET(p->hashtable, v, w) < 0) { + Py_DECREF(v); goto err; + } *flag |= FLAG_REF; return 0; } @@ -547,37 +567,81 @@ w_complex_object(PyObject *v, char flag, WFILE *p) } } +static int +w_init_refs(WFILE *wf, int version) +{ + if (version >= 3) { + wf->hashtable = _Py_hashtable_new(sizeof(int), _Py_hashtable_hash_ptr, + _Py_hashtable_compare_direct); + if (wf->hashtable == NULL) { + PyErr_NoMemory(); + return -1; + } + } + return 0; +} + +static int +w_decref_entry(_Py_hashtable_entry_t *entry, void *Py_UNUSED(data)) +{ + Py_XDECREF(entry->key); + return 0; +} + +static void +w_clear_refs(WFILE *wf) +{ + if (wf->hashtable != NULL) { + _Py_hashtable_foreach(wf->hashtable, w_decref_entry, NULL); + _Py_hashtable_destroy(wf->hashtable); + } +} + /* version currently has no effect for writing ints. */ void PyMarshal_WriteLongToFile(long x, FILE *fp, int version) { + char buf[4]; WFILE wf; + memset(&wf, 0, sizeof(wf)); wf.fp = fp; + wf.ptr = wf.buf = buf; + wf.end = wf.ptr + sizeof(buf); wf.error = WFERR_OK; - wf.depth = 0; - wf.refs = NULL; wf.version = version; w_long(x, &wf); + w_flush(&wf); } void PyMarshal_WriteObjectToFile(PyObject *x, FILE *fp, int version) { + char buf[BUFSIZ]; WFILE wf; + memset(&wf, 0, sizeof(wf)); wf.fp = fp; + wf.ptr = wf.buf = buf; + wf.end = wf.ptr + sizeof(buf); wf.error = WFERR_OK; - wf.depth = 0; - if (version >= 3) { - if ((wf.refs = PyDict_New()) == NULL) - return; /* caller mush check PyErr_Occurred() */ - } else - wf.refs = NULL; wf.version = version; + if (w_init_refs(&wf, version)) + return; /* caller mush check PyErr_Occurred() */ w_object(x, &wf); - Py_XDECREF(wf.refs); + w_clear_refs(&wf); + w_flush(&wf); } -typedef WFILE RFILE; /* Same struct with different invariants */ +typedef struct { + FILE *fp; + int depth; + PyObject *readable; /* Stream-like object being read from */ + PyObject *current_filename; + char *ptr; + char *end; + char *buf; + Py_ssize_t buf_size; + PyObject *refs; /* a list */ +} RFILE; static char * r_string(Py_ssize_t n, RFILE *p) @@ -727,8 +791,8 @@ r_PyLong(RFILE *p) return NULL; } - size = 1 + (ABS(n) - 1) / PyLong_MARSHAL_RATIO; - shorts_in_top_digit = 1 + (ABS(n) - 1) % PyLong_MARSHAL_RATIO; + size = 1 + (Py_ABS(n) - 1) / PyLong_MARSHAL_RATIO; + shorts_in_top_digit = 1 + (Py_ABS(n) - 1) % PyLong_MARSHAL_RATIO; ob = _PyLong_New(size); if (ob == NULL) return NULL; @@ -1511,25 +1575,20 @@ PyMarshal_WriteObjectToString(PyObject *x, int version) { WFILE wf; - wf.fp = NULL; - wf.readable = NULL; + memset(&wf, 0, sizeof(wf)); wf.str = PyBytes_FromStringAndSize((char *)NULL, 50); if (wf.str == NULL) return NULL; - wf.ptr = PyBytes_AS_STRING((PyBytesObject *)wf.str); + wf.ptr = wf.buf = PyBytes_AS_STRING((PyBytesObject *)wf.str); wf.end = wf.ptr + PyBytes_Size(wf.str); wf.error = WFERR_OK; - wf.depth = 0; wf.version = version; - if (version >= 3) { - if ((wf.refs = PyDict_New()) == NULL) { - Py_DECREF(wf.str); - return NULL; - } - } else - wf.refs = NULL; + if (w_init_refs(&wf, version)) { + Py_DECREF(wf.str); + return NULL; + } w_object(x, &wf); - Py_XDECREF(wf.refs); + w_clear_refs(&wf); if (wf.str != NULL) { char *base = PyBytes_AS_STRING((PyBytesObject *)wf.str); if (wf.ptr - base > PY_SSIZE_T_MAX) { |