|  | #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; | 
|  | } |