11#include "Python.h"
22#include "pyarena.h"
33
4- /* An arena list is a linked list that can store either pointers or
5- PyObjects. The type is clear from context.
6- */
4+ /* An arena list is a linked list that can store PyObjects. */
75
86typedef struct _arena_list {
9- struct _arena_list * al_next ;
10- void * al_pointer ;
7+ struct _arena_list * al_next ;
8+ void * al_pointer ;
119} PyArenaList ;
1210
13- /* There are two linked lists in an arena, one for malloc pointers and
14- one for PyObject. For each list, there is a pointer to the head
15- and to the tail. The head is used to free the list. The tail is
16- used to add a new element to the list.
11+ /* A simple arena block structure */
12+ /* TODO(jhylton): Measurement to justify block size. */
1713
18- The list always keeps one un-used node at the end of the list.
19- */
14+ #define DEFAULT_BLOCK_SIZE 8192
15+ typedef struct _block {
16+ size_t ab_size ;
17+ size_t ab_offset ;
18+ struct _block * ab_next ;
19+ void * ab_mem ;
20+ } block ;
2021
2122struct _arena {
22- PyArenaList * a_malloc_head ;
23- PyArenaList * a_malloc_tail ;
24- PyArenaList * a_object_head ;
25- PyArenaList * a_object_tail ;
23+ block * a_head ;
24+ block * a_cur ;
25+ PyArenaList * a_object_head ;
26+ PyArenaList * a_object_tail ;
2627};
2728
2829static PyArenaList *
@@ -40,31 +41,62 @@ PyArenaList_New(void)
4041static void
4142PyArenaList_FreeObject (PyArenaList * alist )
4243{
43- while (alist ) {
44- PyArenaList * prev ;
45- Py_XDECREF ((PyObject * )alist -> al_pointer );
46- alist -> al_pointer = NULL ;
47- prev = alist ;
48- alist = alist -> al_next ;
49- free (prev );
50- }
44+ while (alist ) {
45+ PyArenaList * prev ;
46+ Py_XDECREF ((PyObject * )alist -> al_pointer );
47+ alist -> al_pointer = NULL ;
48+ prev = alist ;
49+ alist = alist -> al_next ;
50+ free (prev );
51+ }
5152}
5253
53- static void
54- PyArenaList_FreeMalloc ( PyArenaList * alist )
54+ static block *
55+ block_new ( size_t size )
5556{
56- while (alist ) {
57- PyArenaList * prev ;
58- if (alist -> al_pointer ) {
59- free (alist -> al_pointer );
57+ /* Allocate header and block as one unit. ab_mem points just past header. */
58+ block * b = (block * )malloc (sizeof (block ) + size );
59+ if (!b )
60+ return NULL ;
61+ b -> ab_size = size ;
62+ b -> ab_mem = (void * )(b + 1 );
63+ b -> ab_next = NULL ;
64+ b -> ab_offset = 0 ;
65+ return b ;
66+ }
67+
68+ static void
69+ block_free (block * b ) {
70+ while (b ) {
71+ block * next = b -> ab_next ;
72+ free (b );
73+ b = next ;
6074 }
61- alist -> al_pointer = NULL ;
62- prev = alist ;
63- alist = alist -> al_next ;
64- free (prev );
65- }
6675}
6776
77+ static void *
78+ block_alloc (block * b , size_t size )
79+ {
80+ void * p ;
81+ assert (b );
82+ if (b -> ab_offset + size > b -> ab_size ) {
83+ /* If we need to allocate more memory than will fit in the default
84+ block, allocate a one-off block that is exactly the right size. */
85+ /* TODO(jhylton): Think more about space waste at end of block */
86+ block * new = block_new (
87+ size < DEFAULT_BLOCK_SIZE ? DEFAULT_BLOCK_SIZE : size );
88+ if (!new )
89+ return NULL ;
90+ assert (!b -> ab_next );
91+ b -> ab_next = new ;
92+ b = new ;
93+ }
94+
95+ assert (b -> ab_offset + size <= b -> ab_size );
96+ p = (void * )(((char * )b -> ab_mem ) + b -> ab_offset );
97+ b -> ab_offset += size ;
98+ return p ;
99+ }
68100
69101PyArena *
70102PyArena_New ()
@@ -73,47 +105,33 @@ PyArena_New()
73105 if (!arena )
74106 return NULL ;
75107
108+ arena -> a_head = block_new (DEFAULT_BLOCK_SIZE );
109+ arena -> a_cur = arena -> a_head ;
76110 arena -> a_object_head = PyArenaList_New ();
77111 arena -> a_object_tail = arena -> a_object_head ;
78- arena -> a_malloc_head = PyArenaList_New ();
79- arena -> a_malloc_tail = arena -> a_malloc_head ;
80112 return arena ;
81113}
82114
83115void
84116PyArena_Free (PyArena * arena )
85117{
86- assert (arena );
87- PyArenaList_FreeObject (arena -> a_object_head );
88- PyArenaList_FreeMalloc (arena -> a_malloc_head );
89- free (arena );
118+ assert (arena );
119+ block_free (arena -> a_head );
120+ PyArenaList_FreeObject (arena -> a_object_head );
121+ free (arena );
90122}
91123
92124void *
93125PyArena_Malloc (PyArena * arena , size_t size )
94126{
95- /* A better implementation might actually use an arena. The current
96- approach is just a trivial implementation of the API that allows
97- it to be tested.
98- */
99- void * p ;
100- assert (size != 0 );
101- p = malloc (size );
102- if (p )
103- PyArena_AddMallocPointer (arena , p );
104- return p ;
105- }
106-
107- int
108- PyArena_AddMallocPointer (PyArena * arena , void * pointer )
109- {
110- PyArenaList * tail = arena -> a_malloc_tail ;
111- assert (pointer );
112- assert (tail -> al_pointer != pointer );
113- tail -> al_next = PyArenaList_New ();
114- tail -> al_pointer = pointer ;
115- arena -> a_malloc_tail = tail -> al_next ;
116- return 1 ;
127+ void * p = block_alloc (arena -> a_cur , size );
128+ if (!p )
129+ return NULL ;
130+ /* Reset cur if we allocated a new block. */
131+ if (arena -> a_cur -> ab_next ) {
132+ arena -> a_cur = arena -> a_cur -> ab_next ;
133+ }
134+ return p ;
117135}
118136
119137int
0 commit comments