#include "Python.h" #include "pyarena.h" /* An arena list is a linked list that can store PyObjects. */ typedef struct _arena_list { struct _arena_list *al_next; void *al_pointer; } PyArenaList; /* A simple arena block structure */ /* TODO(jhylton): Measurement to justify block size. */ #define DEFAULT_BLOCK_SIZE 8192 typedef struct _block { size_t ab_size; size_t ab_offset; struct _block *ab_next; void *ab_mem; } block; struct _arena { block *a_head; block *a_cur; PyArenaList *a_object_head; PyArenaList *a_object_tail; }; static PyArenaList* PyArenaList_New(void) { PyArenaList *alist = (PyArenaList *)malloc(sizeof(PyArenaList)); if (!alist) return NULL; alist->al_next = NULL; alist->al_pointer = NULL; return alist; } static void PyArenaList_FreeObject(PyArenaList *alist) { while (alist) { PyArenaList *prev; Py_XDECREF((PyObject *)alist->al_pointer); alist->al_pointer = NULL; prev = alist; alist = alist->al_next; free(prev); } } 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 = 0; 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); 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 more about space waste at end of block */ block *new = block_new( size < DEFAULT_BLOCK_SIZE ? DEFAULT_BLOCK_SIZE : size); if (!new) return NULL; assert(!b->ab_next); b->ab_next = new; b = new; } 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 NULL; arena->a_head = block_new(DEFAULT_BLOCK_SIZE); arena->a_cur = arena->a_head; arena->a_object_head = PyArenaList_New(); arena->a_object_tail = arena->a_object_head; return arena; } void PyArena_Free(PyArena *arena) { assert(arena); block_free(arena->a_head); PyArenaList_FreeObject(arena->a_object_head); free(arena); } void * PyArena_Malloc(PyArena *arena, size_t size) { void *p = block_alloc(arena->a_cur, size); if (!p) return NULL; /* Reset cur if we allocated a new block. */ if (arena->a_cur->ab_next) { arena->a_cur = arena->a_cur->ab_next; } return p; } int PyArena_AddPyObject(PyArena *arena, PyObject *pointer) { PyArenaList *tail = arena->a_object_tail; assert(pointer); tail->al_next = PyArenaList_New(); tail->al_pointer = pointer; arena->a_object_tail = tail->al_next; return 1; }