| #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) | 
 |  | 
 | typedef struct _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_t ab_size; | 
 |  | 
 | 	/* Total number of bytes already passed out.  The next byte available | 
 | 	 * to pass out starts at ab_mem + ab_offset. | 
 | 	 */ | 
 | 	size_t ab_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_t total_allocs; | 
 |         size_t total_size; | 
 |         size_t total_blocks; | 
 |         size_t total_block_size; | 
 |         size_t total_big_blocks; | 
 | #endif | 
 | }; | 
 |  | 
 | static block * | 
 | block_new(size_t size) | 
 | { | 
 | 	/* Allocate header and block as one unit. | 
 | 	   ab_mem points just past header. */ | 
 | 	block *b = (block *)malloc(sizeof(block) + size); | 
 | 	if (!b) | 
 | 		return NULL; | 
 | 	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); | 
 | 	return b; | 
 | } | 
 |  | 
 | static void | 
 | block_free(block *b) { | 
 | 	while (b) { | 
 | 		block *next = b->ab_next; | 
 | 		free(b); | 
 | 		b = next; | 
 | 	} | 
 | } | 
 |  | 
 | static void * | 
 | block_alloc(block *b, size_t size) | 
 | { | 
 | 	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) | 
 | 			return NULL; | 
 | 		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; | 
 | 	return p; | 
 | } | 
 |  | 
 | 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; | 
 | #endif | 
 | 	return arena; | 
 | } | 
 |  | 
 | void | 
 | PyArena_Free(PyArena *arena) | 
 | { | 
 |         int r; | 
 | 	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)); | 
 |         */ | 
 | #endif | 
 | 	block_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_t size) | 
 | { | 
 | 	void *p = block_alloc(arena->a_cur, size); | 
 | 	if (!p) | 
 | 		return PyErr_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 | 
 | 	} | 
 | 	return p; | 
 | } | 
 |  | 
 | int | 
 | PyArena_AddPyObject(PyArena *arena, PyObject *obj) | 
 | { | 
 |         int r = PyList_Append(arena->a_objects, obj); | 
 |         if (r >= 0) { | 
 |                 Py_DECREF(obj); | 
 |         } | 
 |         return r; | 
 | } |