#include "Python.h"#include "pyarena.h"/* A simple arena block structure. Measurements with standard library modules suggest the average allocation is about 20 bytes and that most compiles use a single block. TODO(jhylton): Think about a realloc API, maybe just for the last allocation?*/#define DEFAULT_BLOCK_SIZE 8192#define ALIGNMENT 8#define ALIGNMENT_MASK (ALIGNMENT - 1)#define ROUNDUP(x) (((x) + ALIGNMENT_MASK) & ~ALIGNMENT_MASK)typedefstruct_block{/* Total number of bytes owned by this block available to pass out. * Read-only after initialization. The first such byte starts at * ab_mem. */size_tab_size;/* Total number of bytes already passed out. The next byte available * to pass out starts at ab_mem + ab_offset. */size_tab_offset;/* An arena maintains a singly-linked, NULL-terminated list of * all blocks owned by the arena. These are linked via the * ab_next member. */struct_block*ab_next;/* Pointer to the first allocatable byte owned by this block. Read- * only after initialization. */void*ab_mem;}block;/* The arena manages two kinds of memory, blocks of raw memory and a list of PyObject* pointers. PyObjects are decrefed when the arena is freed.*/struct_arena{/* Pointer to the first block allocated for the arena, never NULL. It is used only to find the first block when the arena is being freed. */block*a_head;/* Pointer to the block currently used for allocation. It's ab_next field should be NULL. If it is not-null after a call to block_alloc(), it means a new block has been allocated and a_cur should be reset to point it. */block*a_cur;/* A Python list object containing references to all the PyObject pointers associated with this area. They will be DECREFed when the arena is freed. */PyObject*a_objects;#if defined(Py_DEBUG)/* Debug output */size_ttotal_allocs;size_ttotal_size;size_ttotal_blocks;size_ttotal_block_size;size_ttotal_big_blocks;#endif};staticblock*block_new(size_tsize){/* Allocate header and block as one unit. ab_mem points just past header. */block*b=(block*)malloc(sizeof(block)+size);if(!b)returnNULL;b->ab_size=size;b->ab_mem=(void*)(b+1);b->ab_next=NULL;b->ab_offset=ROUNDUP((Py_uintptr_t)(b->ab_mem))-(Py_uintptr_t)(b->ab_mem);returnb;}staticvoidblock_free(block*b){while(b){block*next=b->ab_next;free(b);b=next;}}staticvoid*block_alloc(block*b,size_tsize){void*p;assert(b);size=ROUNDUP(size);if(b->ab_offset+size>b->ab_size){/* If we need to allocate more memory than will fit in the default block, allocate a one-off block that is exactly the right size. *//* TODO(jhylton): Think about space waste at end of block */block*newbl=block_new(size<DEFAULT_BLOCK_SIZE?DEFAULT_BLOCK_SIZE:size);if(!newbl)returnNULL;assert(!b->ab_next);b->ab_next=newbl;b=newbl;}assert(b->ab_offset+size<=b->ab_size);p=(void*)(((char*)b->ab_mem)+b->ab_offset);b->ab_offset+=size;returnp;}PyArena*PyArena_New(){PyArena*arena=(PyArena*)malloc(sizeof(PyArena));if(!arena)return(PyArena*)PyErr_NoMemory();arena->a_head=block_new(DEFAULT_BLOCK_SIZE);arena->a_cur=arena->a_head;if(!arena->a_head){free((void*)arena);return(PyArena*)PyErr_NoMemory();}arena->a_objects=PyList_New(0);if(!arena->a_objects){block_free(arena->a_head);free((void*)arena);return(PyArena*)PyErr_NoMemory();}#if defined(Py_DEBUG)arena->total_allocs=0;arena->total_size=0;arena->total_blocks=1;arena->total_block_size=DEFAULT_BLOCK_SIZE;arena->total_big_blocks=0;#endifreturnarena;}voidPyArena_Free(PyArena*arena){intr;assert(arena);#if defined(Py_DEBUG)/* fprintf(stderr, "alloc=%d size=%d blocks=%d block_size=%d big=%d objects=%d\n", arena->total_allocs, arena->total_size, arena->total_blocks, arena->total_block_size, arena->total_big_blocks, PyList_Size(arena->a_objects)); */#endifblock_free(arena->a_head);/* This property normally holds, except when the code being compiled is sys.getobjects(0), in which case there will be two references. assert(arena->a_objects->ob_refcnt == 1); *//* Clear all the elements from the list. This is necessary to guarantee that they will be DECREFed. */r=PyList_SetSlice(arena->a_objects,0,PyList_GET_SIZE(arena->a_objects),NULL);assert(r==0);assert(PyList_GET_SIZE(arena->a_objects)==0);Py_DECREF(arena->a_objects);free(arena);}void*PyArena_Malloc(PyArena*arena,size_tsize){void*p=block_alloc(arena->a_cur,size);if(!p)returnPyErr_NoMemory();#if defined(Py_DEBUG)arena->total_allocs++;arena->total_size+=size;#endif/* Reset cur if we allocated a new block. */if(arena->a_cur->ab_next){arena->a_cur=arena->a_cur->ab_next;#if defined(Py_DEBUG)arena->total_blocks++;arena->total_block_size+=arena->a_cur->ab_size;if(arena->a_cur->ab_size>DEFAULT_BLOCK_SIZE)++arena->total_big_blocks;#endif}returnp;}intPyArena_AddPyObject(PyArena*arena,PyObject*obj){intr=PyList_Append(arena->a_objects,obj);if(r>=0){Py_DECREF(obj);}returnr;}