diff -r 17a8c5f8ca48 Python/marshal.c --- a/Python/marshal.c Tue Feb 03 22:27:21 2015 +0100 +++ b/Python/marshal.c Wed Feb 04 11:53:05 2015 +0200 @@ -64,15 +64,13 @@ 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 */ + unsigned int refs_count; + /* refs[0] is id->idx dict, refs[1] to refs[6] are value->idx dicts */ + PyObject *refs[7]; int version; } WFILE; @@ -223,24 +221,45 @@ w_PyLong(const PyLongObject *ob, char fl static int w_ref(PyObject *v, char *flag, WFILE *p) { - PyObject *id; + PyObject *id = v; PyObject *idx; + PyObject *refs = NULL; - if (p->version < 3 || p->refs == NULL) + if (p->version < 3 || p->refs[0] == 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 (PyUnicode_CheckExact(v)) { + if (PyUnicode_GET_LENGTH(v) < 1000) + refs = p->refs[PyUnicode_CHECK_INTERNED(v) ? 1 : 2]; + } + else if (PyBytes_CheckExact(v)) { + if (PyBytes_GET_SIZE(v) < 1000) + refs = p->refs[3]; + } + else if (PyLong_CheckExact(v)) + refs = p->refs[4]; + else if (PyFloat_CheckExact(v)) + refs = p->refs[5]; + else if (PyComplex_CheckExact(v)) + refs = p->refs[6]; + + if (refs == NULL) { + refs = p->refs[0]; + id = PyLong_FromVoidPtr((void*)v); + if (id == NULL) + goto err; + } + + idx = PyDict_GetItem(refs, id); if (idx != NULL) { /* write the reference index to the stream */ long w = PyLong_AsLong(idx); - Py_DECREF(id); + if (id != v) + Py_DECREF(id); if (w == -1 && PyErr_Occurred()) { goto err; } @@ -251,18 +270,20 @@ w_ref(PyObject *v, char *flag, WFILE *p) return 1; } else { int ok; - Py_ssize_t s = PyDict_Size(p->refs); + Py_ssize_t s = p->refs_count; /* 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); + ok = idx && PyDict_SetItem(refs, id, idx) == 0; + if (id != v) + Py_DECREF(id); Py_XDECREF(idx); if (!ok) goto err; + p->refs_count++; *flag |= FLAG_REF; return 0; } @@ -545,15 +566,37 @@ w_complex_object(PyObject *v, char flag, } } +static int +w_init_refs(WFILE *wf, int version) +{ + unsigned int i; + if (version >= 3) { + for (i = 0; i < Py_ARRAY_LENGTH(wf->refs); i++) + if ((wf->refs[i] = PyDict_New()) == NULL) { + for (i = 0; i < Py_ARRAY_LENGTH(wf->refs); i++) + Py_CLEAR(wf->refs[i]); + return -1; + } + } + return 0; +} + +static void +w_clear_refs(WFILE *wf) +{ + unsigned int i; + for (i = 0; i < Py_ARRAY_LENGTH(wf->refs); i++) + Py_XDECREF(wf->refs[i]); +} + /* version currently has no effect for writing ints. */ void PyMarshal_WriteLongToFile(long x, FILE *fp, int version) { WFILE wf; + memset(&wf, 0, sizeof(wf)); wf.fp = fp; wf.error = WFERR_OK; - wf.depth = 0; - wf.refs = NULL; wf.version = version; w_long(x, &wf); } @@ -562,20 +605,27 @@ void PyMarshal_WriteObjectToFile(PyObject *x, FILE *fp, int version) { WFILE wf; + memset(&wf, 0, sizeof(wf)); wf.fp = fp; 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; + if (w_init_refs(&wf, version)) + return; /* caller mush check PyErr_Occurred() */ wf.version = version; w_object(x, &wf); - Py_XDECREF(wf.refs); + w_clear_refs(&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) @@ -1509,25 +1559,20 @@ PyMarshal_WriteObjectToString(PyObject * { 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.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) {