summaryrefslogtreecommitdiffstats
path: root/Python/pyarena.c
blob: d67753215e12042e49b33bfb1356e2f5cbdd1c11 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#include "Python.h"
#include "pyarena.h"

/* An arena list is a linked list that can store either pointers or
   PyObjects.  The type is clear from context.
 */

typedef struct _arena_list {
  struct _arena_list *al_next;
  void *al_pointer;
} PyArenaList;

/* There are two linked lists in an arena, one for malloc pointers and
   one for PyObject.  For each list, there is a pointer to the head
   and to the tail.  The head is used to free the list.  The tail is
   used to add a new element to the list.

   The list always keeps one un-used node at the end of the list.
*/

struct _arena {
  PyArenaList *a_malloc_head;
  PyArenaList *a_malloc_tail;
  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) 
{
  if (!alist)
    return;

  while (alist) {
    PyArenaList *prev;
    Py_XDECREF((PyObject *)alist->al_pointer);
    alist->al_pointer = NULL;
    prev = alist;
    alist = alist->al_next;
    free(prev);
  }
}

static void
PyArenaList_FreeMalloc(PyArenaList *alist)
{
  if (!alist)
    return;

  while (alist) {
    PyArenaList *prev;
    if (alist->al_pointer) {
      free(alist->al_pointer);
    }
    alist->al_pointer = NULL;
    prev = alist;
    alist = alist->al_next;
    free(prev);
  }
}


PyArena *
PyArena_New()
{
  PyArena* arena = (PyArena *)malloc(sizeof(PyArena));
  if (!arena)
    return NULL;

  arena->a_object_head = PyArenaList_New();
  arena->a_object_tail = arena->a_object_head;
  arena->a_malloc_head = PyArenaList_New();
  arena->a_malloc_tail = arena->a_malloc_head;
  return arena;
}

void
PyArena_Free(PyArena *arena)
{
  assert(arena);
  PyArenaList_FreeObject(arena->a_object_head);
  PyArenaList_FreeMalloc(arena->a_malloc_head);
  free(arena);
}

void *
PyArena_Malloc(PyArena *arena, size_t size) 
{
  /* A better implementation might actually use an arena.  The current
     approach is just a trivial implementation of the API that allows
     it to be tested.
  */
  void *p;
  assert(size != 0);
  p = malloc(size);
  PyArena_AddMallocPointer(arena, p);
  return p;
}

int
PyArena_AddMallocPointer(PyArena *arena, void *pointer) 
{
  assert(pointer);
  PyArenaList *tail = arena->a_malloc_tail;
  assert(tail->al_pointer != pointer);
  tail->al_next = PyArenaList_New();
  tail->al_pointer = pointer;
  arena->a_malloc_tail = tail->al_next;
  return 1;
}

int
PyArena_AddPyObject(PyArena *arena, PyObject *pointer) 
{
  assert(pointer);
  PyArenaList *tail = arena->a_object_tail;
  tail->al_next = PyArenaList_New();
  tail->al_pointer = pointer;
  arena->a_object_tail = tail->al_next;
  return 1;
}