diff -r 4b97092aa4bd Doc/c-api/memory.rst --- a/Doc/c-api/memory.rst Sun Apr 27 18:02:35 2014 +0100 +++ b/Doc/c-api/memory.rst Mon Apr 28 00:21:56 2014 +0200 @@ -92,8 +92,8 @@ functions are thread-safe, the :term:`GI need to be held. The default raw memory block allocator uses the following functions: -:c:func:`malloc`, :c:func:`realloc` and :c:func:`free`; call ``malloc(1)`` when -requesting zero bytes. +:c:func:`malloc`, :c:func:`calloc`, :c:func:`realloc` and :c:func:`free`; call +``malloc(1)`` (or ``calloc(1, 1)``) when requesting zero bytes. .. versionadded:: 3.4 @@ -106,6 +106,16 @@ requesting zero bytes. been initialized in any way. +.. c:function:: void* PyMem_RawCalloc(size_t nelem, size_t elsize) + + Allocates *nelem* elements each whose size in bytes is *elsize* and returns + a pointer of type :c:type:`void\*` to the allocated memory, or *NULL* if the + request fails. Requesting zero elements or elements of size zero bytes + returns a distinct non-*NULL* pointer if possible, as if + ``PyMem_RawCalloc(1, 1)`` had been called instead. The memory is initialized + to zeros. + + .. c:function:: void* PyMem_RawRealloc(void *p, size_t n) Resizes the memory block pointed to by *p* to *n* bytes. The contents will @@ -136,8 +146,8 @@ behavior when requesting zero bytes, are memory from the Python heap. The default memory block allocator uses the following functions: -:c:func:`malloc`, :c:func:`realloc` and :c:func:`free`; call ``malloc(1)`` when -requesting zero bytes. +:c:func:`malloc`, :c:func:`calloc`, :c:func:`realloc` and :c:func:`free`; call +``malloc(1)`` (or ``calloc(1, 1)``) when requesting zero bytes. .. warning:: @@ -152,6 +162,15 @@ requesting zero bytes. been called instead. The memory will not have been initialized in any way. +.. c:function:: void* PyMem_Calloc(size_t nelem, size_t elsize) + + Allocates *nelem* elements each whose size in bytes is *elsize* and returns + a pointer of type :c:type:`void\*` to the allocated memory, or *NULL* if the + request fails. Requesting zero elements or elements of size zero bytes + returns a distinct non-*NULL* pointer if possible, as if ``PyMem_Calloc(1, + 1)`` had been called instead. The memory is initialized to zeros. + + .. c:function:: void* PyMem_Realloc(void *p, size_t n) Resizes the memory block pointed to by *p* to *n* bytes. The contents will be @@ -222,6 +241,9 @@ Customize Memory Allocators +----------------------------------------------------------+---------------------------------------+ | ``void* malloc(void *ctx, size_t size)`` | allocate a memory block | +----------------------------------------------------------+---------------------------------------+ + | ``void* calloc(void *ctx, size_t nelem, size_t elsize)`` | allocate a memory block initialized | + | | with zeros | + +----------------------------------------------------------+---------------------------------------+ | ``void* realloc(void *ctx, void *ptr, size_t new_size)`` | allocate or resize a memory block | +----------------------------------------------------------+---------------------------------------+ | ``void free(void *ctx, void *ptr)`` | free a memory block | diff -r 4b97092aa4bd Include/objimpl.h --- a/Include/objimpl.h Sun Apr 27 18:02:35 2014 +0100 +++ b/Include/objimpl.h Mon Apr 28 00:21:56 2014 +0200 @@ -95,6 +95,7 @@ PyObject_{New, NewVar, Del}. the raw memory. */ PyAPI_FUNC(void *) PyObject_Malloc(size_t size); +PyAPI_FUNC(void *) PyObject_Calloc(size_t nelem, size_t elsize); PyAPI_FUNC(void *) PyObject_Realloc(void *ptr, size_t new_size); PyAPI_FUNC(void) PyObject_Free(void *ptr); @@ -321,7 +322,8 @@ extern PyGC_Head *_PyGC_generation0; (!PyTuple_CheckExact(obj) || _PyObject_GC_IS_TRACKED(obj))) #endif /* Py_LIMITED_API */ -PyAPI_FUNC(PyObject *) _PyObject_GC_Malloc(size_t); +PyAPI_FUNC(PyObject *) _PyObject_GC_Malloc(size_t size); +PyAPI_FUNC(PyObject *) _PyObject_GC_Calloc(size_t nelem, size_t elsize); PyAPI_FUNC(PyObject *) _PyObject_GC_New(PyTypeObject *); PyAPI_FUNC(PyVarObject *) _PyObject_GC_NewVar(PyTypeObject *, Py_ssize_t); PyAPI_FUNC(void) PyObject_GC_Track(void *); diff -r 4b97092aa4bd Include/pymem.h --- a/Include/pymem.h Sun Apr 27 18:02:35 2014 +0100 +++ b/Include/pymem.h Mon Apr 28 00:21:56 2014 +0200 @@ -13,6 +13,7 @@ extern "C" { #ifndef Py_LIMITED_API PyAPI_FUNC(void *) PyMem_RawMalloc(size_t size); +PyAPI_FUNC(void *) PyMem_RawCalloc(size_t nelem, size_t elsize); PyAPI_FUNC(void *) PyMem_RawRealloc(void *ptr, size_t new_size); PyAPI_FUNC(void) PyMem_RawFree(void *ptr); #endif @@ -57,6 +58,7 @@ PyAPI_FUNC(void) PyMem_RawFree(void *ptr */ PyAPI_FUNC(void *) PyMem_Malloc(size_t size); +PyAPI_FUNC(void *) PyMem_Calloc(size_t nelem, size_t elsize); PyAPI_FUNC(void *) PyMem_Realloc(void *ptr, size_t new_size); PyAPI_FUNC(void) PyMem_Free(void *ptr); @@ -132,6 +134,9 @@ typedef struct { /* allocate a memory block */ void* (*malloc) (void *ctx, size_t size); + /* allocate a memory block initialized by zeros */ + void* (*calloc) (void *ctx, size_t nelem, size_t elsize); + /* allocate or resize a memory block */ void* (*realloc) (void *ctx, void *ptr, size_t new_size); diff -r 4b97092aa4bd Modules/gcmodule.c --- a/Modules/gcmodule.c Sun Apr 27 18:02:35 2014 +0100 +++ b/Modules/gcmodule.c Mon Apr 28 00:21:56 2014 +0200 @@ -1703,15 +1703,19 @@ PyObject_GC_UnTrack(void *op) _PyObject_GC_UNTRACK(op); } -PyObject * -_PyObject_GC_Malloc(size_t basicsize) +static PyObject * +_PyObject_GC_Alloc(size_t basicsize, int zero) { PyObject *op; PyGC_Head *g; + size_t size; if (basicsize > PY_SSIZE_T_MAX - sizeof(PyGC_Head)) return PyErr_NoMemory(); - g = (PyGC_Head *)PyObject_MALLOC( - sizeof(PyGC_Head) + basicsize); + size = sizeof(PyGC_Head) + basicsize; + if (zero) + g = (PyGC_Head *)PyObject_Calloc(1, size); + else + g = (PyGC_Head *)PyObject_Malloc(size); if (g == NULL) return PyErr_NoMemory(); g->gc.gc_refs = 0; @@ -1731,6 +1735,20 @@ PyObject * } PyObject * +_PyObject_GC_Malloc(size_t basicsize) +{ + return _PyObject_GC_Alloc(basicsize, 0); +} + +PyObject * +_PyObject_GC_Calloc(size_t nelem, size_t elsize) +{ + if (nelem > PY_SSIZE_T_MAX / elsize) + return PyErr_NoMemory(); + return _PyObject_GC_Alloc(nelem * elsize, 1); +} + +PyObject * _PyObject_GC_New(PyTypeObject *tp) { PyObject *op = _PyObject_GC_Malloc(_PyObject_SIZE(tp)); diff -r 4b97092aa4bd Objects/obmalloc.c --- a/Objects/obmalloc.c Sun Apr 27 18:02:35 2014 +0100 +++ b/Objects/obmalloc.c Mon Apr 28 00:21:56 2014 +0200 @@ -5,6 +5,7 @@ #ifdef PYMALLOC_DEBUG /* WITH_PYMALLOC && PYMALLOC_DEBUG */ /* Forward declaration */ static void* _PyMem_DebugMalloc(void *ctx, size_t size); +static void* _PyMem_DebugCalloc(void *ctx, size_t nelem, size_t elsize); static void _PyMem_DebugFree(void *ctx, void *p); static void* _PyMem_DebugRealloc(void *ctx, void *ptr, size_t size); @@ -43,6 +44,7 @@ static void _PyMem_DebugCheckAddress(cha /* Forward declaration */ static void* _PyObject_Malloc(void *ctx, size_t size); +static void* _PyObject_Calloc(void *ctx, size_t nelem, size_t elsize); static void _PyObject_Free(void *ctx, void *p); static void* _PyObject_Realloc(void *ctx, void *ptr, size_t size); #endif @@ -51,7 +53,7 @@ static void* _PyObject_Realloc(void *ctx static void * _PyMem_RawMalloc(void *ctx, size_t size) { - /* PyMem_Malloc(0) means malloc(1). Some systems would return NULL + /* PyMem_RawMalloc(0) means malloc(1). Some systems would return NULL for malloc(0), which would be treated as an error. Some platforms would return a pointer with no memory behind it, which would break pymalloc. To solve these problems, allocate an extra byte. */ @@ -61,6 +63,20 @@ static void * } static void * +_PyMem_RawCalloc(void *ctx, size_t nelem, size_t elsize) +{ + /* PyMem_RawCalloc(0, 0) means calloc(1, 1). Some systems would return NULL + for calloc(0, 0), which would be treated as an error. Some platforms + would return a pointer with no memory behind it, which would break + pymalloc. To solve these problems, allocate an extra byte. */ + if (nelem == 0 || elsize == 0) { + nelem = 1; + elsize = 1; + } + return calloc(nelem, elsize); +} + +static void * _PyMem_RawRealloc(void *ctx, void *ptr, size_t size) { if (size == 0) @@ -123,9 +139,9 @@ static void #endif -#define PYRAW_FUNCS _PyMem_RawMalloc, _PyMem_RawRealloc, _PyMem_RawFree +#define PYRAW_FUNCS _PyMem_RawMalloc, _PyMem_RawCalloc, _PyMem_RawRealloc, _PyMem_RawFree #ifdef WITH_PYMALLOC -# define PYOBJ_FUNCS _PyObject_Malloc, _PyObject_Realloc, _PyObject_Free +# define PYOBJ_FUNCS _PyObject_Malloc, _PyObject_Calloc, _PyObject_Realloc, _PyObject_Free #else # define PYOBJ_FUNCS PYRAW_FUNCS #endif @@ -147,7 +163,7 @@ static struct { {'o', {NULL, PYOBJ_FUNCS}} }; -#define PYDBG_FUNCS _PyMem_DebugMalloc, _PyMem_DebugRealloc, _PyMem_DebugFree +#define PYDBG_FUNCS _PyMem_DebugMalloc, _PyMem_DebugCalloc, _PyMem_DebugRealloc, _PyMem_DebugFree #endif static PyMemAllocator _PyMem_Raw = { @@ -196,6 +212,7 @@ PyMem_SetupDebugHooks(void) PyMemAllocator alloc; alloc.malloc = _PyMem_DebugMalloc; + alloc.calloc = _PyMem_DebugCalloc; alloc.realloc = _PyMem_DebugRealloc; alloc.free = _PyMem_DebugFree; @@ -228,11 +245,8 @@ PyMem_GetAllocator(PyMemAllocatorDomain case PYMEM_DOMAIN_MEM: *allocator = _PyMem; break; case PYMEM_DOMAIN_OBJ: *allocator = _PyObject; break; default: - /* unknown domain */ - allocator->ctx = NULL; - allocator->malloc = NULL; - allocator->realloc = NULL; - allocator->free = NULL; + /* unknown domain: set all attributes to NULL */ + memset(allocator, 0, sizeof(*allocator)); } } @@ -272,8 +286,16 @@ PyMem_RawMalloc(size_t size) */ if (size > (size_t)PY_SSIZE_T_MAX) return NULL; + return _PyMem_Raw.malloc(_PyMem_Raw.ctx, size); +} - return _PyMem_Raw.malloc(_PyMem_Raw.ctx, size); +void * +PyMem_RawCalloc(size_t nelem, size_t elsize) +{ + /* see PyMem_RawMalloc() */ + if (elsize != 0 && nelem > (size_t)PY_SSIZE_T_MAX / elsize) + return NULL; + return _PyMem_Raw.calloc(_PyMem_Raw.ctx, nelem, elsize); } void* @@ -300,6 +322,15 @@ PyMem_Malloc(size_t size) } void * +PyMem_Calloc(size_t nelem, size_t elsize) +{ + /* see PyMem_RawMalloc() */ + if (elsize != 0 && nelem > (size_t)PY_SSIZE_T_MAX / elsize) + return NULL; + return _PyMem.calloc(_PyMem.ctx, nelem, elsize); +} + +void * PyMem_Realloc(void *ptr, size_t new_size) { /* see PyMem_RawMalloc() */ @@ -352,6 +383,15 @@ PyObject_Malloc(size_t size) } void * +PyObject_Calloc(size_t nelem, size_t elsize) +{ + /* see PyMem_RawMalloc() */ + if (elsize != 0 && nelem > (size_t)PY_SSIZE_T_MAX / elsize) + return NULL; + return _PyObject.calloc(_PyObject.ctx, nelem, elsize); +} + +void * PyObject_Realloc(void *ptr, size_t new_size) { /* see PyMem_RawMalloc() */ @@ -1122,8 +1162,9 @@ int Py_ADDRESS_IN_RANGE(void *P, poolp p */ static void * -_PyObject_Malloc(void *ctx, size_t nbytes) +_PyObject_Alloc(void *ctx, size_t nelem, size_t elsize, int zero) { + size_t nbytes; block *bp; poolp pool; poolp next; @@ -1138,9 +1179,12 @@ static void * goto redirect; #endif - /* - * This implicitly redirects malloc(0). - */ + if (nelem == 0 || elsize == 0) + goto redirect; + + assert(nelem <= PY_SSIZE_T_MAX / elsize); + nbytes = nelem * elsize; + if ((nbytes - 1) < SMALL_REQUEST_THRESHOLD) { LOCK(); /* @@ -1158,6 +1202,8 @@ static void * assert(bp != NULL); if ((pool->freeblock = *(block **)bp) != NULL) { UNLOCK(); + if (zero) + memset(bp, 0, nbytes); return (void *)bp; } /* @@ -1170,6 +1216,8 @@ static void * pool->nextoffset += INDEX2SIZE(size); *(block **)(pool->freeblock) = NULL; UNLOCK(); + if (zero) + memset(bp, 0, nbytes); return (void *)bp; } /* Pool is full, unlink from used pools. */ @@ -1178,6 +1226,8 @@ static void * next->prevpool = pool; pool->nextpool = next; UNLOCK(); + if (zero) + memset(bp, 0, nbytes); return (void *)bp; } @@ -1257,6 +1307,8 @@ static void * assert(bp != NULL); pool->freeblock = *(block **)bp; UNLOCK(); + if (zero) + memset(bp, 0, nbytes); return (void *)bp; } /* @@ -1272,6 +1324,8 @@ static void * pool->freeblock = bp + size; *(block **)(pool->freeblock) = NULL; UNLOCK(); + if (zero) + memset(bp, 0, nbytes); return (void *)bp; } @@ -1311,13 +1365,31 @@ redirect: * has been reached. */ { - void *result = PyMem_RawMalloc(nbytes); + void *result; + if (zero) + result = PyMem_RawCalloc(nelem, elsize); + else { + assert(nelem == 1); + result = PyMem_RawMalloc(elsize); + } if (!result) _Py_AllocatedBlocks--; return result; } } +static void * +_PyObject_Malloc(void *ctx, size_t nbytes) +{ + return _PyObject_Alloc(ctx, 1, nbytes, 0); +} + +static void * +_PyObject_Calloc(void *ctx, size_t nelem, size_t elsize) +{ + return _PyObject_Alloc(ctx, nelem, elsize, 1); +} + /* free */ ATTRIBUTE_NO_ADDRESS_SAFETY_ANALYSIS @@ -1561,7 +1633,7 @@ static void * #endif if (p == NULL) - return _PyObject_Malloc(ctx, nbytes); + return _PyObject_Alloc(ctx, 1, nbytes, 0); #ifdef WITH_VALGRIND /* Treat running_on_valgrind == -1 the same as 0 */ @@ -1589,7 +1661,7 @@ static void * } size = nbytes; } - bp = _PyObject_Malloc(ctx, nbytes); + bp = _PyObject_Alloc(ctx, 1, nbytes, 0); if (bp != NULL) { memcpy(bp, p, size); _PyObject_Free(ctx, p); @@ -1745,7 +1817,7 @@ p[2*S+n+S: 2*S+n+2*S] */ static void * -_PyMem_DebugMalloc(void *ctx, size_t nbytes) +_PyMem_DebugAlloc(void *ctx, size_t nbytes, int zero) { debug_alloc_api_t *api = (debug_alloc_api_t *)ctx; uchar *p; /* base address of malloc'ed block */ @@ -1758,7 +1830,10 @@ static void * /* overflow: can't represent total as a size_t */ return NULL; - p = (uchar *)api->alloc.malloc(api->alloc.ctx, total); + if (zero) + p = (uchar *)api->alloc.calloc(api->alloc.ctx, 1, total); + else + p = (uchar *)api->alloc.malloc(api->alloc.ctx, total); if (p == NULL) return NULL; @@ -1767,7 +1842,7 @@ static void * p[SST] = (uchar)api->api_id; memset(p + SST + 1, FORBIDDENBYTE, SST-1); - if (nbytes > 0) + if (nbytes > 0 && !zero) memset(p + 2*SST, CLEANBYTE, nbytes); /* at tail, write pad (SST bytes) and serialno (SST bytes) */ @@ -1778,6 +1853,21 @@ static void * return p + 2*SST; } +static void * +_PyMem_DebugMalloc(void *ctx, size_t nbytes) +{ + return _PyMem_DebugAlloc(ctx, nbytes, 0); +} + +static void * +_PyMem_DebugCalloc(void *ctx, size_t nelem, size_t elsize) +{ + size_t nbytes; + assert(elsize == 0 || nelem <= PY_SSIZE_T_MAX / elsize); + nbytes = nelem * elsize; + return _PyMem_DebugAlloc(ctx, nbytes, 1); +} + /* The debug free first checks the 2*SST bytes on each end for sanity (in particular, that the FORBIDDENBYTEs with the api ID are still intact). Then fills the original bytes with DEADBYTE. @@ -1811,7 +1901,7 @@ static void * int i; if (p == NULL) - return _PyMem_DebugMalloc(ctx, nbytes); + return _PyMem_DebugAlloc(ctx, nbytes, 0); _PyMem_DebugCheckAddress(api->api_id, p); bumpserialno(); diff -r 4b97092aa4bd Modules/_tracemalloc.c --- a/Modules/_tracemalloc.c Sun Apr 27 18:02:35 2014 +0100 +++ b/Modules/_tracemalloc.c Mon Apr 28 00:21:11 2014 +0200 @@ -476,17 +476,24 @@ tracemalloc_remove_trace(void *ptr) } static void* -tracemalloc_malloc(void *ctx, size_t size) +tracemalloc_alloc(void *ctx, size_t nelem, size_t elsize, int zero) { PyMemAllocator *alloc = (PyMemAllocator *)ctx; void *ptr; - ptr = alloc->malloc(alloc->ctx, size); + assert(nelem <= PY_SIZE_MAX / elsize); + + if (zero) + ptr = alloc->calloc(alloc->ctx, nelem, elsize); + else { + assert(nelem == 1); + ptr = alloc->malloc(alloc->ctx, elsize); + } if (ptr == NULL) return NULL; TABLES_LOCK(); - if (tracemalloc_add_trace(ptr, size) < 0) { + if (tracemalloc_add_trace(ptr, nelem * elsize) < 0) { /* Failed to allocate a trace for the new memory block */ TABLES_UNLOCK(); alloc->free(alloc->ctx, ptr); @@ -560,13 +567,18 @@ tracemalloc_free(void *ctx, void *ptr) } static void* -tracemalloc_malloc_gil(void *ctx, size_t size) +tracemalloc_alloc_gil(void *ctx, size_t nelem, size_t elsize, int zero) { void *ptr; if (get_reentrant()) { PyMemAllocator *alloc = (PyMemAllocator *)ctx; - return alloc->malloc(alloc->ctx, size); + if (zero) + return alloc->calloc(alloc->ctx, nelem, elsize); + else { + assert(nelem == 1); + return alloc->malloc(alloc->ctx, elsize); + } } /* Ignore reentrant call. PyObjet_Malloc() calls PyMem_Malloc() for @@ -574,13 +586,25 @@ tracemalloc_malloc_gil(void *ctx, size_t allocation twice. */ set_reentrant(1); - ptr = tracemalloc_malloc(ctx, size); + ptr = tracemalloc_alloc(ctx, nelem, elsize, zero); set_reentrant(0); return ptr; } static void* +tracemalloc_malloc_gil(void *ctx, size_t size) +{ + return tracemalloc_alloc_gil(ctx, 1, size, 0); +} + +static void* +tracemalloc_calloc_gil(void *ctx, size_t nelem, size_t elsize) +{ + return tracemalloc_alloc_gil(ctx, nelem, elsize, 1); +} + +static void* tracemalloc_realloc_gil(void *ctx, void *ptr, size_t new_size) { void *ptr2; @@ -614,7 +638,7 @@ tracemalloc_realloc_gil(void *ctx, void #ifdef TRACE_RAW_MALLOC static void* -tracemalloc_raw_malloc(void *ctx, size_t size) +tracemalloc_raw_alloc(void *ctx, size_t nelem, size_t elsize, int zero) { #ifdef WITH_THREAD PyGILState_STATE gil_state; @@ -623,7 +647,12 @@ tracemalloc_raw_malloc(void *ctx, size_t if (get_reentrant()) { PyMemAllocator *alloc = (PyMemAllocator *)ctx; - return alloc->malloc(alloc->ctx, size); + if (zero) + return alloc->calloc(alloc->ctx, nelem, elsize); + else { + assert(nelem == 1); + return alloc->malloc(alloc->ctx, elsize); + } } /* Ignore reentrant call. PyGILState_Ensure() may call PyMem_RawMalloc() @@ -633,10 +662,10 @@ tracemalloc_raw_malloc(void *ctx, size_t #ifdef WITH_THREAD gil_state = PyGILState_Ensure(); - ptr = tracemalloc_malloc(ctx, size); + ptr = tracemalloc_alloc(ctx, nelem, elsize, zero); PyGILState_Release(gil_state); #else - ptr = tracemalloc_malloc(ctx, size); + ptr = tracemalloc_alloc(ctx, nelem, elsize, zero); #endif set_reentrant(0); @@ -644,6 +673,18 @@ tracemalloc_raw_malloc(void *ctx, size_t } static void* +tracemalloc_raw_malloc(void *ctx, size_t size) +{ + return tracemalloc_raw_alloc(ctx, 1, size, 0); +} + +static void* +tracemalloc_raw_calloc(void *ctx, size_t nelem, size_t elsize) +{ + return tracemalloc_raw_alloc(ctx, nelem, elsize, 1); +} + +static void* tracemalloc_raw_realloc(void *ctx, void *ptr, size_t new_size) { #ifdef WITH_THREAD @@ -856,6 +897,7 @@ tracemalloc_start(int max_nframe) #ifdef TRACE_RAW_MALLOC alloc.malloc = tracemalloc_raw_malloc; + alloc.calloc = tracemalloc_raw_calloc; alloc.realloc = tracemalloc_raw_realloc; alloc.free = tracemalloc_free; @@ -865,6 +907,7 @@ tracemalloc_start(int max_nframe) #endif alloc.malloc = tracemalloc_malloc_gil; + alloc.calloc = tracemalloc_calloc_gil; alloc.realloc = tracemalloc_realloc_gil; alloc.free = tracemalloc_free;