diff -r e2e85ed7f8ba Include/accu.h --- a/Include/accu.h Tue Aug 14 18:42:54 2012 +0300 +++ b/Include/accu.h Tue Aug 14 23:23:34 2012 +0300 @@ -28,6 +28,7 @@ PyAPI_FUNC(PyObject *) _PyAccu_FinishAsList(_PyAccu *acc); PyAPI_FUNC(PyObject *) _PyAccu_Finish(_PyAccu *acc); PyAPI_FUNC(void) _PyAccu_Destroy(_PyAccu *acc); +PyAPI_FUNC(PyObject *) _PyAccu_SizeOf(_PyAccu *acc); #ifdef __cplusplus } diff -r e2e85ed7f8ba Include/sysmodule.h --- a/Include/sysmodule.h Tue Aug 14 18:42:54 2012 +0300 +++ b/Include/sysmodule.h Tue Aug 14 23:23:34 2012 +0300 @@ -22,6 +22,7 @@ #ifndef Py_LIMITED_API PyAPI_DATA(PyObject *) _PySys_TraceFunc, *_PySys_ProfileFunc; +PyAPI_DATA(PyObject *) _PySys_GetSizeOf(PyObject *); #endif PyAPI_FUNC(void) PySys_ResetWarnOptions(void); diff -r e2e85ed7f8ba Lib/test/test_memoryio.py --- a/Lib/test/test_memoryio.py Tue Aug 14 18:42:54 2012 +0300 +++ b/Lib/test/test_memoryio.py Tue Aug 14 23:23:34 2012 +0300 @@ -9,6 +9,8 @@ import io import _pyio as pyio import pickle +import sys +import os class MemorySeekTestMixin: @@ -712,6 +714,48 @@ memio.close() self.assertRaises(ValueError, memio.__setstate__, ("closed", "", 0, None)) + check_sizeof = support.check_sizeof + + @support.cpython_only + def test_sizeof(self): + basesize = support.calcobjsize('P2nNi 2P 4c5P') + stdsize = basesize + 2 * 4 + sys.getsizeof(os.linesep) + check = self.check_sizeof + self.assertEqual(object.__sizeof__(io.StringIO()), basesize) + check(io.StringIO(), stdsize + sys.getsizeof([])) + # newline + for newline in '', '\n', '\r', '\r\n': + check(io.StringIO(newline=newline), + basesize + 2 * 4 + sys.getsizeof(newline) + sys.getsizeof([])) + # UCS4 buffer + for c in 'a', '\x80', '\u0100', '\U00010000': + check(io.StringIO(c * 1000), stdsize + 1000 * 4) + # _PyAccu + sio = io.StringIO() + small = [] + check(sio, stdsize + sys.getsizeof([])) + sio.write('egg') + small.append('egg') + check(sio, stdsize + sys.getsizeof(small) + sys.getsizeof('egg')) + sio.write('spam') + small.append('spam') + check(sio, stdsize + sys.getsizeof(small) + + sys.getsizeof('egg') + sys.getsizeof('spam')) + ACCU_LIMIT = 100000 + sio = io.StringIO() + small = [] + for i in range(ACCU_LIMIT - 1): + sio.write('a') + small.append('a') + check(sio, stdsize + sys.getsizeof(small) + sys.getsizeof('a')) + sio.write('a') + small.append('a') + large = [] + large.append(''.join(small)) + small = [] + check(sio, stdsize + sys.getsizeof(small) + sys.getsizeof(large) + + sys.getsizeof(large[0])) + class CStringIOPickleTest(PyStringIOPickleTest): UnsupportedOperation = io.UnsupportedOperation diff -r e2e85ed7f8ba Modules/_io/stringio.c --- a/Modules/_io/stringio.c Tue Aug 14 18:42:54 2012 +0300 +++ b/Modules/_io/stringio.c Tue Aug 14 23:23:34 2012 +0300 @@ -214,7 +214,7 @@ } if (self->state == STATE_ACCUMULATING) { - if (self->string_size == self->pos) { + if (self->string_size == self->pos && PyUnicode_CheckExact(decoded)) { if (_PyAccu_Accumulate(&self->accu, decoded)) goto fail; goto success; @@ -599,6 +599,7 @@ static int stringio_clear(stringio *self) { + Py_CLEAR(self->decoder); Py_CLEAR(self->dict); return 0; } @@ -759,6 +760,37 @@ return 0; } +#define ADD_NUM(res,num) do { \ + PyObject *x = res; \ + PyObject *y = (num); \ + if (x == NULL) \ + return NULL; \ + res = PyNumber_InPlaceAdd(x, y); \ + Py_DECREF(x); \ + if (res == NULL) { \ + Py_DECREF(y); \ + return NULL; \ + } \ + } while (0) + +static PyObject * +stringio_sizeof(stringio *self, void *unused) +{ + PyObject *res; + + res = PyLong_FromSsize_t(sizeof(stringio)); + if (res == NULL) + return NULL; + + if (self->buf) + ADD_NUM(res, PyLong_FromSsize_t(self->buf_size * sizeof(Py_UCS4))); + if (self->readnl) + ADD_NUM(res, _PySys_GetSizeOf(self->readnl)); + if (self->state == STATE_ACCUMULATING) + ADD_NUM(res, _PyAccu_SizeOf(&self->accu)); + return res; +} + /* Properties and pseudo-properties */ static PyObject * stringio_seekable(stringio *self, PyObject *args) @@ -962,6 +994,7 @@ {"__getstate__", (PyCFunction)stringio_getstate, METH_NOARGS}, {"__setstate__", (PyCFunction)stringio_setstate, METH_O}, + {"__sizeof__", (PyCFunction)stringio_sizeof, METH_NOARGS}, {NULL, NULL} /* sentinel */ }; diff -r e2e85ed7f8ba Objects/accu.c --- a/Objects/accu.c Tue Aug 14 18:42:54 2012 +0300 +++ b/Objects/accu.c Tue Aug 14 23:23:34 2012 +0300 @@ -113,3 +113,85 @@ Py_CLEAR(acc->small); Py_CLEAR(acc->large); } + +#define ADD_NUM(res,num) do { \ + PyObject *x = res; \ + PyObject *y = (num); \ + if (x == NULL) \ + return NULL; \ + res = PyNumber_InPlaceAdd(x, y); \ + Py_DECREF(x); \ + if (res == NULL) { \ + Py_DECREF(y); \ + return NULL; \ + } \ + } while (0) + +static int +collect_ids(PyObject *ids, PyObject *list) +{ + Py_ssize_t size, i; + + size = PyList_GET_SIZE(list); + for (i = 0; i < size; i++) { + PyObject *id = PyLong_FromVoidPtr(PyList_GET_ITEM(list, i)); + if (id == NULL) + return -1; + if (PySet_Add(ids, id)) { + Py_DECREF(id); + return -1; + } + Py_DECREF(id); + } + return 0; +} + +static PyObject * +summ_sizes(PyObject *res, PyObject *iter) +{ + PyObject *id; + + while ((id = PyIter_Next(iter)) != NULL) + ADD_NUM(res, _PySys_GetSizeOf(PyLong_AsVoidPtr(id))); + return res; +} + +PyObject * _PyAccu_SizeOf(_PyAccu *acc) +{ + PyObject *res; + PyObject *ids; + PyObject *iter; + + if (acc->small == NULL) + return PyLong_FromLong(0); + + res = _PySys_GetSizeOf(acc->small); + if (res == NULL) + return NULL; + if (acc->large != NULL) + ADD_NUM(res, _PySys_GetSizeOf(acc->large)); + + ids = PySet_New(NULL); + if (ids == NULL) + goto error; + + if (collect_ids(ids, acc->small) < 0) + goto error; + if (acc->large != NULL) + if (collect_ids(ids, acc->large) < 0) + goto error; + + iter = PyObject_GetIter(ids); + if (iter == NULL) + goto error; + + res = summ_sizes(res, iter); + Py_DECREF(iter); + Py_DECREF(ids); + return res; + +error: + Py_XDECREF(ids); + Py_DECREF(res); + return NULL; +} diff -r e2e85ed7f8ba Python/sysmodule.c --- a/Python/sysmodule.c Tue Aug 14 18:42:54 2012 +0300 +++ b/Python/sysmodule.c Tue Aug 14 23:23:34 2012 +0300 @@ -810,20 +810,14 @@ } #endif /* USE_MALLOPT */ -static PyObject * -sys_getsizeof(PyObject *self, PyObject *args, PyObject *kwds) +PyObject * +_PySys_GetSizeOf(PyObject *o) { PyObject *res = NULL; static PyObject *gc_head_size = NULL; - static char *kwlist[] = {"object", "default", 0}; - PyObject *o, *dflt = NULL; PyObject *method; _Py_IDENTIFIER(__sizeof__); - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:getsizeof", - kwlist, &o, &dflt)) - return NULL; - /* Initialize static variable for GC head size */ if (gc_head_size == NULL) { gc_head_size = PyLong_FromSsize_t(sizeof(PyGC_Head)); @@ -847,6 +841,31 @@ Py_DECREF(method); } + if (res == NULL) + return res; + + /* add gc_head size */ + if (PyObject_IS_GC(o)) { + PyObject *tmp = res; + res = PyNumber_Add(tmp, gc_head_size); + Py_DECREF(tmp); + } + return res; +} + +static PyObject * +sys_getsizeof(PyObject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"object", "default", 0}; + PyObject *res; + PyObject *o, *dflt = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|O:getsizeof", + kwlist, &o, &dflt)) + return NULL; + + res = _PySys_GetSizeOf(o); + /* Has a default value been given */ if ((res == NULL) && (dflt != NULL) && PyErr_ExceptionMatches(PyExc_TypeError)) @@ -855,15 +874,7 @@ Py_INCREF(dflt); return dflt; } - else if (res == NULL) - return res; - /* add gc_head size */ - if (PyObject_IS_GC(o)) { - PyObject *tmp = res; - res = PyNumber_Add(tmp, gc_head_size); - Py_DECREF(tmp); - } return res; }