doxygen: add newline after \file
[blender.git] / source / blender / blenlib / intern / BLI_memarena.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
17  * All rights reserved.
18  * Efficient memory allocation for lots of similar small chunks.
19  */
20
21 /** \file
22  * \ingroup bli
23  * \brief Memory arena ADT.
24  * \section aboutmemarena Memory Arena
25  *
26  * Memory arena's are commonly used when the program
27  * needs to quickly allocate lots of little bits of data,
28  * which are all freed at the same moment.
29  *
30  * \note Memory can't be freed during the arenas lifetime.
31  */
32
33 #include <stdlib.h>
34 #include <string.h>
35
36 #include "MEM_guardedalloc.h"
37
38 #include "BLI_utildefines.h"
39 #include "BLI_memarena.h"
40 #include "BLI_strict_flags.h"
41
42 #ifdef WITH_MEM_VALGRIND
43 #  include "valgrind/memcheck.h"
44 #endif
45
46 struct MemBuf {
47         struct MemBuf *next;
48         uchar data[0];
49 };
50
51 struct MemArena {
52         unsigned char *curbuf;
53         const char *name;
54         struct MemBuf *bufs;
55
56         size_t bufsize, cursize;
57         size_t align;
58
59         bool use_calloc;
60 };
61
62 static void memarena_buf_free_all(struct MemBuf *mb)
63 {
64         while (mb != NULL) {
65                 struct MemBuf *mb_next = mb->next;
66                 MEM_freeN(mb);
67                 mb = mb_next;
68         }
69 }
70
71 MemArena *BLI_memarena_new(const size_t bufsize, const char *name)
72 {
73         MemArena *ma = MEM_callocN(sizeof(*ma), "memarena");
74         ma->bufsize = bufsize;
75         ma->align = 8;
76         ma->name = name;
77
78 #ifdef WITH_MEM_VALGRIND
79         VALGRIND_CREATE_MEMPOOL(ma, 0, false);
80 #endif
81
82         return ma;
83 }
84
85 void BLI_memarena_use_calloc(MemArena *ma)
86 {
87         ma->use_calloc = 1;
88 }
89
90 void BLI_memarena_use_malloc(MemArena *ma)
91 {
92         ma->use_calloc = 0;
93 }
94
95 void BLI_memarena_use_align(struct MemArena *ma, const size_t align)
96 {
97         /* align should be a power of two */
98         ma->align = align;
99 }
100
101 void BLI_memarena_free(MemArena *ma)
102 {
103         memarena_buf_free_all(ma->bufs);
104 #ifdef WITH_MEM_VALGRIND
105         VALGRIND_DESTROY_MEMPOOL(ma);
106 #endif
107
108         MEM_freeN(ma);
109 }
110
111 /* amt must be power of two */
112 #define PADUP(num, amt) (((num) + ((amt) - 1)) & ~((amt) - 1))
113
114 /* align alloc'ed memory (needed if align > 8) */
115 static void memarena_curbuf_align(MemArena *ma)
116 {
117         unsigned char *tmp;
118
119         tmp = (unsigned char *)PADUP((intptr_t)ma->curbuf, (int)ma->align);
120         ma->cursize -= (size_t)(tmp - ma->curbuf);
121         ma->curbuf = tmp;
122 }
123
124 void *BLI_memarena_alloc(MemArena *ma, size_t size)
125 {
126         void *ptr;
127
128         /* ensure proper alignment by rounding
129          * size up to multiple of 8 */
130         size = PADUP(size, ma->align);
131
132         if (UNLIKELY(size > ma->cursize)) {
133                 if (size > ma->bufsize - (ma->align - 1)) {
134                         ma->cursize = PADUP(size + 1, ma->align);
135                 }
136                 else {
137                         ma->cursize = ma->bufsize;
138                 }
139
140                 struct MemBuf *mb = (ma->use_calloc ? MEM_callocN : MEM_mallocN)(sizeof(*mb) + ma->cursize, ma->name);
141                 ma->curbuf = mb->data;
142                 mb->next = ma->bufs;
143                 ma->bufs = mb;
144
145                 memarena_curbuf_align(ma);
146         }
147
148         ptr = ma->curbuf;
149         ma->curbuf += size;
150         ma->cursize -= size;
151
152 #ifdef WITH_MEM_VALGRIND
153         VALGRIND_MEMPOOL_ALLOC(ma, ptr, size);
154 #endif
155
156         return ptr;
157 }
158
159 void *BLI_memarena_calloc(MemArena *ma, size_t size)
160 {
161         void *ptr;
162
163         /* no need to use this function call if we're calloc'ing by default */
164         BLI_assert(ma->use_calloc == false);
165
166         ptr = BLI_memarena_alloc(ma, size);
167         memset(ptr, 0, size);
168
169         return ptr;
170 }
171
172 /**
173  * Clear for reuse, avoids re-allocation when an arena may
174  * otherwise be free'd and recreated.
175  */
176 void BLI_memarena_clear(MemArena *ma)
177 {
178         if (ma->bufs) {
179                 unsigned char *curbuf_prev;
180                 size_t curbuf_used;
181
182                 if (ma->bufs->next) {
183                         memarena_buf_free_all(ma->bufs->next);
184                         ma->bufs->next = NULL;
185                 }
186
187                 curbuf_prev = ma->curbuf;
188                 ma->curbuf = ma->bufs->data;
189                 memarena_curbuf_align(ma);
190
191                 /* restore to original size */
192                 curbuf_used = (size_t)(curbuf_prev - ma->curbuf);
193                 ma->cursize += curbuf_used;
194
195                 if (ma->use_calloc) {
196                         memset(ma->curbuf, 0, curbuf_used);
197                 }
198         }
199
200 #ifdef WITH_MEM_VALGRIND
201         VALGRIND_DESTROY_MEMPOOL(ma);
202         VALGRIND_CREATE_MEMPOOL(ma, 0, false);
203 #endif
204
205 }