29 #include "cx/mempool.h" |
29 #include "cx/mempool.h" |
30 |
30 |
31 #include <string.h> |
31 #include <string.h> |
32 #include <errno.h> |
32 #include <errno.h> |
33 |
33 |
34 struct cx_mempool_memory_s { |
|
35 /** The destructor. */ |
|
36 cx_destructor_func destructor; |
|
37 /** The actual memory. */ |
|
38 char c[]; |
|
39 }; |
|
40 |
|
41 static int cx_mempool_ensure_capacity( |
34 static int cx_mempool_ensure_capacity( |
42 struct cx_mempool_s *pool, |
35 struct cx_mempool_s *pool, |
43 size_t needed_capacity |
36 size_t needed_capacity |
44 ) { |
37 ) { |
45 if (needed_capacity <= pool->capacity) return 0; |
38 if (needed_capacity <= pool->capacity) return 0; |
46 size_t newcap = pool->capacity >= 1000 ? |
39 size_t newcap = pool->capacity >= 1000 ? |
47 pool->capacity + 1000 : pool->capacity * 2; |
40 pool->capacity + 1000 : pool->capacity * 2; |
48 size_t newmsize; |
41 size_t newmsize; |
49 if (pool->capacity > newcap || cx_szmul(newcap, |
42 if (pool->capacity > newcap |
50 sizeof(struct cx_mempool_memory_s*), &newmsize)) { |
43 || cx_szmul(newcap, sizeof(void*), &newmsize)) { |
51 errno = EOVERFLOW; |
44 errno = EOVERFLOW; |
52 return 1; |
45 return 1; |
53 } |
46 } |
54 struct cx_mempool_memory_s **newdata = cxRealloc( |
47 void **newdata = cxReallocDefault(pool->data, newmsize); |
55 cxDefaultAllocator, pool->data, newmsize); |
|
56 if (newdata == NULL) return 1; |
48 if (newdata == NULL) return 1; |
57 pool->data = newdata; |
49 pool->data = newdata; |
58 pool->capacity = newcap; |
50 pool->capacity = newcap; |
59 return 0; |
51 return 0; |
60 } |
52 } |
61 |
53 |
62 static void *cx_mempool_malloc( |
54 static int cx_mempool_ensure_registered_capacity( |
|
55 struct cx_mempool_s *pool, |
|
56 size_t needed_capacity |
|
57 ) { |
|
58 if (needed_capacity <= pool->registered_capacity) return 0; |
|
59 // we do not expect so many registrations |
|
60 size_t newcap = pool->registered_capacity + 8; |
|
61 size_t newmsize; |
|
62 if (pool->registered_capacity > newcap || cx_szmul(newcap, |
|
63 sizeof(struct cx_mempool_foreign_memory_s), &newmsize)) { |
|
64 errno = EOVERFLOW; |
|
65 return 1; |
|
66 } |
|
67 void *newdata = cxReallocDefault(pool->registered, newmsize); |
|
68 if (newdata == NULL) return 1; |
|
69 pool->registered = newdata; |
|
70 pool->registered_capacity = newcap; |
|
71 return 0; |
|
72 } |
|
73 |
|
74 static void *cx_mempool_malloc_simple( |
63 void *p, |
75 void *p, |
64 size_t n |
76 size_t n |
65 ) { |
77 ) { |
66 struct cx_mempool_s *pool = p; |
78 struct cx_mempool_s *pool = p; |
67 |
79 |
68 if (cx_mempool_ensure_capacity(pool, pool->size + 1)) { |
80 if (cx_mempool_ensure_capacity(pool, pool->size + 1)) { |
69 return NULL; |
81 return NULL; |
70 } |
82 } |
71 |
83 |
72 struct cx_mempool_memory_s *mem = cxMalloc( |
84 struct cx_mempool_memory_s *mem = |
73 cxDefaultAllocator, sizeof(cx_destructor_func) + n); |
85 cxMallocDefault(sizeof(struct cx_mempool_memory_s) + n); |
74 if (mem == NULL) return NULL; |
86 if (mem == NULL) return NULL; |
75 |
87 mem->destructor = NULL; |
76 mem->destructor = pool->auto_destr; |
|
77 pool->data[pool->size] = mem; |
88 pool->data[pool->size] = mem; |
78 pool->size++; |
89 pool->size++; |
79 |
90 |
80 return mem->c; |
91 return mem->c; |
81 } |
92 } |
82 |
93 |
83 static void *cx_mempool_calloc( |
94 static void *cx_mempool_calloc_simple( |
84 void *p, |
95 void *p, |
85 size_t nelem, |
96 size_t nelem, |
86 size_t elsize |
97 size_t elsize |
87 ) { |
98 ) { |
88 size_t msz; |
99 size_t msz; |
89 if (cx_szmul(nelem, elsize, &msz)) { |
100 if (cx_szmul(nelem, elsize, &msz)) { |
90 errno = EOVERFLOW; |
101 errno = EOVERFLOW; |
91 return NULL; |
102 return NULL; |
92 } |
103 } |
93 void *ptr = cx_mempool_malloc(p, msz); |
104 void *ptr = cx_mempool_malloc_simple(p, msz); |
94 if (ptr == NULL) return NULL; |
105 if (ptr == NULL) return NULL; |
95 memset(ptr, 0, nelem * elsize); |
106 memset(ptr, 0, nelem * elsize); |
96 return ptr; |
107 return ptr; |
97 } |
108 } |
98 |
109 |
99 static void *cx_mempool_realloc( |
110 static void *cx_mempool_realloc_simple( |
100 void *p, |
111 void *p, |
101 void *ptr, |
112 void *ptr, |
102 size_t n |
113 size_t n |
103 ) { |
114 ) { |
104 struct cx_mempool_s *pool = p; |
115 struct cx_mempool_s *pool = p; |
105 |
116 |
106 struct cx_mempool_memory_s *mem, *newm; |
117 const unsigned overhead = sizeof(struct cx_mempool_memory_s); |
107 mem = (struct cx_mempool_memory_s*)(((char *) ptr) - sizeof(cx_destructor_func)); |
118 struct cx_mempool_memory_s *mem = |
108 newm = cxReallocDefault(mem, n + sizeof(cx_destructor_func)); |
119 (void *) (((char *) ptr) - overhead); |
|
120 struct cx_mempool_memory_s *newm = |
|
121 cxReallocDefault(mem, n + overhead); |
109 |
122 |
110 if (newm == NULL) return NULL; |
123 if (newm == NULL) return NULL; |
111 if (mem != newm) { |
124 if (mem != newm) { |
112 for (size_t i = 0; i < pool->size; i++) { |
125 for (size_t i = 0; i < pool->size; i++) { |
113 if (pool->data[i] == mem) { |
126 if (pool->data[i] == mem) { |
114 pool->data[i] = newm; |
127 pool->data[i] = newm; |
115 return ((char*)newm) + sizeof(cx_destructor_func); |
128 return ((char*)newm) + overhead; |
116 } |
129 } |
117 } |
130 } |
118 abort(); // LCOV_EXCL_LINE |
131 abort(); // LCOV_EXCL_LINE |
119 } else { |
132 } else { |
120 return ptr; |
133 return ptr; |
121 } |
134 } |
122 } |
135 } |
123 |
136 |
124 static void cx_mempool_free( |
137 static void cx_mempool_free_simple( |
125 void *p, |
138 void *p, |
126 void *ptr |
139 void *ptr |
127 ) { |
140 ) { |
128 if (!ptr) return; |
141 if (!ptr) return; |
129 struct cx_mempool_s *pool = p; |
142 struct cx_mempool_s *pool = p; |
130 |
143 |
131 struct cx_mempool_memory_s *mem = (struct cx_mempool_memory_s *) |
144 struct cx_mempool_memory_s *mem = |
132 ((char *) ptr - sizeof(cx_destructor_func)); |
145 (void*) ((char *) ptr - sizeof(struct cx_mempool_memory_s)); |
133 |
146 |
134 for (size_t i = 0; i < pool->size; i++) { |
147 for (size_t i = 0; i < pool->size; i++) { |
135 if (mem == pool->data[i]) { |
148 if (mem == pool->data[i]) { |
136 if (mem->destructor) { |
149 if (mem->destructor) { |
137 mem->destructor(mem->c); |
150 mem->destructor(mem->c); |
|
151 } |
|
152 if (pool->destr) { |
|
153 pool->destr(mem->c); |
|
154 } |
|
155 if (pool->destr2) { |
|
156 pool->destr2(pool->destr2_data, mem->c); |
138 } |
157 } |
139 cxFreeDefault(mem); |
158 cxFreeDefault(mem); |
140 size_t last_index = pool->size - 1; |
159 size_t last_index = pool->size - 1; |
141 if (i != last_index) { |
160 if (i != last_index) { |
142 pool->data[i] = pool->data[last_index]; |
161 pool->data[i] = pool->data[last_index]; |
147 } |
166 } |
148 } |
167 } |
149 abort(); // LCOV_EXCL_LINE |
168 abort(); // LCOV_EXCL_LINE |
150 } |
169 } |
151 |
170 |
|
171 static void cx_mempool_free_all_simple(const struct cx_mempool_s *pool) { |
|
172 const bool has_destr = pool->destr; |
|
173 const bool has_destr2 = pool->destr2; |
|
174 for (size_t i = 0; i < pool->size; i++) { |
|
175 struct cx_mempool_memory_s *mem = pool->data[i]; |
|
176 if (mem->destructor) { |
|
177 mem->destructor(mem->c); |
|
178 } |
|
179 if (has_destr) { |
|
180 pool->destr(mem->c); |
|
181 } |
|
182 if (has_destr2) { |
|
183 pool->destr2(pool->destr2_data, mem->c); |
|
184 } |
|
185 cxFreeDefault(mem); |
|
186 } |
|
187 } |
|
188 |
|
189 static void cx_mempool_free_foreign(const struct cx_mempool_s *pool) { |
|
190 for (size_t i = 0; i < pool->registered_size; i++) { |
|
191 struct cx_mempool_foreign_memory_s info = pool->registered[i]; |
|
192 if (info.destr2_data == NULL) { |
|
193 if (info.destr) { |
|
194 info.destr(info.mem); |
|
195 } |
|
196 } else { |
|
197 info.destr2(info.destr2_data, info.mem); |
|
198 } |
|
199 } |
|
200 } |
|
201 |
|
202 static cx_allocator_class cx_mempool_simple_allocator_class = { |
|
203 cx_mempool_malloc_simple, |
|
204 cx_mempool_realloc_simple, |
|
205 cx_mempool_calloc_simple, |
|
206 cx_mempool_free_simple |
|
207 }; |
|
208 |
152 void cxMempoolFree(CxMempool *pool) { |
209 void cxMempoolFree(CxMempool *pool) { |
153 if (pool == NULL) return; |
210 if (pool == NULL) return; |
154 struct cx_mempool_memory_s *mem; |
211 if (pool->allocator->cl == &cx_mempool_simple_allocator_class) { |
155 for (size_t i = 0; i < pool->size; i++) { |
212 cx_mempool_free_all_simple(pool); |
156 mem = pool->data[i]; |
213 } else { |
157 if (mem->destructor) { |
214 // TODO: implement |
158 mem->destructor(mem->c); |
215 } |
159 } |
216 cx_mempool_free_foreign(pool); |
160 cxFreeDefault(mem); |
|
161 } |
|
162 cxFreeDefault(pool->data); |
217 cxFreeDefault(pool->data); |
|
218 cxFreeDefault(pool->registered); |
163 cxFreeDefault((void*) pool->allocator); |
219 cxFreeDefault((void*) pool->allocator); |
164 cxFreeDefault(pool); |
220 cxFreeDefault(pool); |
165 } |
221 } |
166 |
222 |
167 void cxMempoolSetDestructor( |
223 void cxMempoolSetDestructor( |
171 *(cx_destructor_func *) ((char *) ptr - sizeof(cx_destructor_func)) = func; |
227 *(cx_destructor_func *) ((char *) ptr - sizeof(cx_destructor_func)) = func; |
172 } |
228 } |
173 |
229 |
174 void cxMempoolRemoveDestructor(void *ptr) { |
230 void cxMempoolRemoveDestructor(void *ptr) { |
175 *(cx_destructor_func *) ((char *) ptr - sizeof(cx_destructor_func)) = NULL; |
231 *(cx_destructor_func *) ((char *) ptr - sizeof(cx_destructor_func)) = NULL; |
176 } |
|
177 |
|
178 struct cx_mempool_foreign_mem_s { |
|
179 cx_destructor_func destr; |
|
180 void* mem; |
|
181 }; |
|
182 |
|
183 static void cx_mempool_destr_foreign_mem(void* ptr) { |
|
184 struct cx_mempool_foreign_mem_s *fm = ptr; |
|
185 fm->destr(fm->mem); |
|
186 } |
232 } |
187 |
233 |
188 int cxMempoolRegister( |
234 int cxMempoolRegister( |
189 CxMempool *pool, |
235 CxMempool *pool, |
190 void *memory, |
236 void *memory, |
191 cx_destructor_func destr |
237 cx_destructor_func destr |
192 ) { |
238 ) { |
193 struct cx_mempool_foreign_mem_s *fm = cx_mempool_malloc( |
239 if (cx_mempool_ensure_registered_capacity(pool, pool->registered_size + 1)) { |
194 pool, |
240 return 1; // LCOV_EXCL_LINE |
195 sizeof(struct cx_mempool_foreign_mem_s) |
241 } |
196 ); |
242 |
197 if (fm == NULL) return 1; |
243 pool->registered[pool->registered_size++] = |
198 |
244 (struct cx_mempool_foreign_memory_s) { |
199 fm->mem = memory; |
245 .mem = memory, |
200 fm->destr = destr; |
246 .destr = destr, |
201 *(cx_destructor_func *) ((char *) fm - sizeof(cx_destructor_func)) = cx_mempool_destr_foreign_mem; |
247 .destr2_data = NULL |
|
248 }; |
202 |
249 |
203 return 0; |
250 return 0; |
204 } |
251 } |
205 |
|
206 static cx_allocator_class cx_mempool_allocator_class = { |
|
207 cx_mempool_malloc, |
|
208 cx_mempool_realloc, |
|
209 cx_mempool_calloc, |
|
210 cx_mempool_free |
|
211 }; |
|
212 |
252 |
213 CxMempool *cxMempoolCreate( |
253 CxMempool *cxMempoolCreate( |
214 size_t capacity, |
254 size_t capacity, |
215 cx_destructor_func destr |
255 enum cx_mempool_type type |
216 ) { |
256 ) { |
|
257 if (capacity == 0) capacity = 16; |
217 size_t poolsize; |
258 size_t poolsize; |
218 if (cx_szmul(capacity, sizeof(struct cx_mempool_memory_s*), &poolsize)) { |
259 if (cx_szmul(capacity, sizeof(void*), &poolsize)) { |
219 errno = EOVERFLOW; |
260 errno = EOVERFLOW; |
220 return NULL; |
261 return NULL; |
221 } |
262 } |
222 |
|
223 struct cx_mempool_s *pool = |
|
224 cxMallocDefault(sizeof(struct cx_mempool_s)); |
|
225 if (pool == NULL) return NULL; |
|
226 |
263 |
227 CxAllocator *provided_allocator = cxMallocDefault(sizeof(CxAllocator)); |
264 CxAllocator *provided_allocator = cxMallocDefault(sizeof(CxAllocator)); |
228 if (provided_allocator == NULL) { // LCOV_EXCL_START |
265 if (provided_allocator == NULL) { // LCOV_EXCL_START |
229 cxFreeDefault(pool); |
|
230 return NULL; |
266 return NULL; |
231 } // LCOV_EXCL_STOP |
267 } // LCOV_EXCL_STOP |
232 provided_allocator->cl = &cx_mempool_allocator_class; |
268 |
|
269 CxMempool *pool = cxCallocDefault(1, sizeof(CxMempool)); |
|
270 if (pool == NULL) { |
|
271 cxFreeDefault(provided_allocator); |
|
272 return NULL; |
|
273 } |
|
274 |
233 provided_allocator->data = pool; |
275 provided_allocator->data = pool; |
234 |
|
235 pool->allocator = provided_allocator; |
276 pool->allocator = provided_allocator; |
|
277 if (type == CX_MEMPOOL_TYPE_SIMPLE) { |
|
278 provided_allocator->cl = &cx_mempool_simple_allocator_class; |
|
279 } else if (type == CX_MEMPOOL_TYPE_ADVANCED) { |
|
280 // TODO: implement |
|
281 } else { |
|
282 // TODO: implement |
|
283 } |
236 |
284 |
237 pool->data = cxMallocDefault(poolsize); |
285 pool->data = cxMallocDefault(poolsize); |
238 if (pool->data == NULL) { // LCOV_EXCL_START |
286 if (pool->data == NULL) { // LCOV_EXCL_START |
239 cxFreeDefault(provided_allocator); |
287 cxFreeDefault(provided_allocator); |
240 cxFreeDefault(pool); |
288 cxFreeDefault(pool); |
241 return NULL; |
289 return NULL; |
242 } // LCOV_EXCL_STOP |
290 } // LCOV_EXCL_STOP |
243 |
291 |
244 pool->size = 0; |
292 pool->size = 0; |
245 pool->capacity = capacity; |
293 pool->capacity = capacity; |
246 pool->auto_destr = destr; |
|
247 |
294 |
248 return pool; |
295 return pool; |
249 } |
296 } |
250 |
297 |
251 static void cx_mempool_free_transferred_allocator(void *al) { |
298 static void cx_mempool_free_transferred_allocator(void *al) { |
254 |
301 |
255 int cxMempoolTransfer( |
302 int cxMempoolTransfer( |
256 CxMempool *source, |
303 CxMempool *source, |
257 CxMempool *dest |
304 CxMempool *dest |
258 ) { |
305 ) { |
259 // safety check |
306 // safety checks |
260 if (source == dest) return 1; |
307 if (source == dest) return 1; |
|
308 if (source->allocator->cl != dest->allocator->cl) return 1; |
261 |
309 |
262 // ensure enough capacity in the destination pool |
310 // ensure enough capacity in the destination pool |
263 if (cx_mempool_ensure_capacity(dest, dest->size + source->size + 1)) { |
311 if (cx_mempool_ensure_capacity(dest, dest->size + source->size)) { |
|
312 return 1; // LCOV_EXCL_LINE |
|
313 } |
|
314 if (cx_mempool_ensure_registered_capacity(dest, |
|
315 dest->registered_size + source->registered_size)) { |
264 return 1; // LCOV_EXCL_LINE |
316 return 1; // LCOV_EXCL_LINE |
265 } |
317 } |
266 |
318 |
267 // allocate a replacement allocator for the source pool |
319 // allocate a replacement allocator for the source pool |
268 CxAllocator *new_source_allocator = cxMallocDefault(sizeof(CxAllocator)); |
320 CxAllocator *new_source_allocator = cxMallocDefault(sizeof(CxAllocator)); |
269 if (new_source_allocator == NULL) { // LCOV_EXCL_START |
321 if (new_source_allocator == NULL) { // LCOV_EXCL_START |
270 return 1; |
322 return 1; |
271 } // LCOV_EXCL_STOP |
323 } // LCOV_EXCL_STOP |
272 new_source_allocator->cl = &cx_mempool_allocator_class; |
324 new_source_allocator->cl = source->allocator->cl; |
273 new_source_allocator->data = source; |
325 new_source_allocator->data = source; |
274 |
326 |
275 // transfer all the data |
327 // transfer all the data |
276 memcpy(&dest->data[dest->size], source->data, sizeof(source->data[0])*source->size); |
328 memcpy(&dest->data[dest->size], source->data, sizeof(void*)*source->size); |
277 dest->size += source->size; |
329 dest->size += source->size; |
|
330 |
|
331 // transfer all registered memory |
|
332 memcpy(&dest->registered[dest->registered_size], source->registered, |
|
333 sizeof(struct cx_mempool_foreign_memory_s) * source->size); |
|
334 dest->registered_size += source->registered_size; |
278 |
335 |
279 // register the old allocator with the new pool |
336 // register the old allocator with the new pool |
280 // we have to remove const-ness for this, but that's okay here |
337 // we have to remove const-ness for this, but that's okay here |
281 CxAllocator *transferred_allocator = (CxAllocator*) source->allocator; |
338 CxAllocator *transferred_allocator = (CxAllocator*) source->allocator; |
282 transferred_allocator->data = dest; |
339 transferred_allocator->data = dest; |
283 cxMempoolRegister(dest, transferred_allocator, cx_mempool_free_transferred_allocator); |
340 cxMempoolRegister(dest, transferred_allocator, |
|
341 cx_mempool_free_transferred_allocator); |
284 |
342 |
285 // prepare the source pool for re-use |
343 // prepare the source pool for re-use |
286 source->allocator = new_source_allocator; |
344 source->allocator = new_source_allocator; |
287 memset(source->data, 0, source->size * sizeof(source->data[0])); |
345 memset(source->data, 0, source->size * sizeof(void*)); |
|
346 memset(source->registered, 0, |
|
347 source->registered_size * sizeof(struct cx_mempool_foreign_memory_s)); |
288 source->size = 0; |
348 source->size = 0; |
|
349 source->registered_size = 0; |
289 |
350 |
290 return 0; |
351 return 0; |
291 } |
352 } |
292 |
353 |
293 int cxMempoolTransferObject( |
354 int cxMempoolTransferObject( |