Mercurial > dovecot > original-hg > dovecot-1.2
comparison src/lib/mempool-allocfree.c @ 0:3b1985cbc908 HEAD
Initial revision
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Fri, 09 Aug 2002 12:15:38 +0300 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:3b1985cbc908 |
---|---|
1 /* | |
2 mempool-allocfree.c : Memory pool manager for custom alloc+free | |
3 | |
4 Copyright (c) 2001-2002 Timo Sirainen | |
5 | |
6 Permission is hereby granted, free of charge, to any person obtaining | |
7 a copy of this software and associated documentation files (the | |
8 "Software"), to deal in the Software without restriction, including | |
9 without limitation the rights to use, copy, modify, merge, publish, | |
10 distribute, sublicense, and/or sell copies of the Software, and to | |
11 permit persons to whom the Software is furnished to do so, subject to | |
12 the following conditions: | |
13 | |
14 The above copyright notice and this permission notice shall be | |
15 included in all copies or substantial portions of the Software. | |
16 | |
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
18 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
19 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
20 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | |
21 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | |
22 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | |
23 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
24 */ | |
25 | |
26 | |
27 /* TODO: | |
28 | |
29 - p_free() could check better that it's freeing a properly allocated | |
30 memory from pool it was told to. | |
31 | |
32 - try to keep optimal pool sizes ie. keep them in the size they're | |
33 using 90% of the time | |
34 - add statistics functions that print/return the memory usage | |
35 - don't free() anything, save them to unused-list and reuse later. | |
36 */ | |
37 | |
38 /* extensive debugging */ | |
39 /* #define POOL_DEBUG */ | |
40 | |
41 /* always clear the memory area that has been free'd. for debugging mostly */ | |
42 /* #define POOL_FREE_CLEAR 0xd0 */ | |
43 | |
44 /* Save the used pool block for each memory allocation, so you can be sure | |
45 that the memory is being free'd from correct pool. Also, with this option | |
46 we don't need to compare pointers which makes this code fully ANSI-C | |
47 compatible :) */ | |
48 #define POOL_SAVE_BLOCK | |
49 | |
50 #include "lib.h" | |
51 #include "mempool.h" | |
52 | |
53 #include <stdlib.h> | |
54 | |
55 #define is_pool(pool) ((pool) != NULL && (pool)->magic == 0xbeef) | |
56 | |
57 #define check_pool(pool) \ | |
58 if (!is_pool(pool)) \ | |
59 i_panic("Trying to use invalid memory pool, aborting") | |
60 | |
61 #define MEM_FREE_BIT 0x80000000 | |
62 | |
63 #define is_mem_free(size) (((size) & MEM_FREE_BIT) != 0) | |
64 #define mem_size(size) ((size) & ~MEM_FREE_BIT) | |
65 | |
66 #define mem2int(_mem, _int) \ | |
67 memcpy(&(_int), ((unsigned char *) (_mem)), sizeof(int)); | |
68 | |
69 #define int2mem(_mem, _int) \ | |
70 memcpy(((unsigned char *) (_mem)), &(_int), sizeof(int)); | |
71 | |
72 #ifdef POOL_SAVE_BLOCK | |
73 # define ALLOC_EXTRA_SIZE sizeof(PoolBlock *) | |
74 #else | |
75 # define ALLOC_EXTRA_SIZE 0 | |
76 #endif | |
77 | |
78 /* max. number of bytes to even try to allocate. This is done just to avoid | |
79 allocating less memory than was actually requested because of integer | |
80 overflows. -128 is much more than is actually needed. */ | |
81 #define MAX_ALLOC_SIZE (UINT_MAX - 128) | |
82 | |
83 typedef struct { | |
84 /* total number of bytes in data[] */ | |
85 unsigned int size; | |
86 /* free data position at data+free_index, none if >= size */ | |
87 unsigned int free_index; | |
88 /* largest data[] size (not including int size) */ | |
89 unsigned int largest_free_space; | |
90 | |
91 /* unsigned int size - if the last bit is set the data block is used | |
92 if 0, the rest of the block is free | |
93 unsigned char data[] | |
94 unsigned int size | |
95 ... */ | |
96 unsigned char data[1]; | |
97 } PoolBlock; | |
98 | |
99 #define BLOCK_INCREMENT_COUNT 5 | |
100 typedef struct { | |
101 struct Pool pool; | |
102 | |
103 unsigned short magic; /* 0xbeef */ | |
104 int refcount; | |
105 | |
106 int num_blocks, used_blocks; | |
107 int first_block_with_space; /* -1 = all used */ | |
108 PoolBlock **blocks; /* num_blocks size */ | |
109 | |
110 char name[1]; /* human readable name for blocks - used in statistics. | |
111 variable size */ | |
112 } AllocfreePool; | |
113 | |
114 static struct Pool static_allocfree_pool; | |
115 | |
116 static void pool_allocfree_free(Pool pool, void *mem); | |
117 | |
118 static void pool_block_create(AllocfreePool *apool, unsigned int size) | |
119 { | |
120 PoolBlock *block; | |
121 int alloc_size; | |
122 | |
123 i_assert(size > sizeof(int)); | |
124 | |
125 if (apool->used_blocks >= apool->num_blocks) { | |
126 apool->num_blocks += BLOCK_INCREMENT_COUNT; | |
127 | |
128 alloc_size = sizeof(PoolBlock *) * apool->num_blocks; | |
129 apool->blocks = apool->blocks == NULL ? malloc(alloc_size) : | |
130 realloc(apool->blocks, alloc_size); | |
131 if (apool->blocks == NULL) { | |
132 i_panic("pool_block_create(): " | |
133 "Out of memory when reallocating %d bytes", | |
134 alloc_size); | |
135 } | |
136 } | |
137 | |
138 alloc_size = sizeof(PoolBlock)-1 + size; | |
139 block = malloc(alloc_size); | |
140 if (block == NULL) { | |
141 i_panic("pool_block_create(): " | |
142 "Out of memory when allocating %d bytes", alloc_size); | |
143 } | |
144 | |
145 block->size = size; | |
146 block->free_index = 0; | |
147 block->largest_free_space = size-sizeof(int)-ALLOC_EXTRA_SIZE; | |
148 memset(block->data, 0, sizeof(int)); | |
149 | |
150 if (apool->first_block_with_space == -1) | |
151 apool->first_block_with_space = apool->used_blocks; | |
152 apool->blocks[apool->used_blocks] = block; | |
153 apool->used_blocks++; | |
154 } | |
155 | |
156 static int pool_block_get_pos(AllocfreePool *apool, PoolBlock *block) | |
157 { | |
158 int pos; | |
159 | |
160 for (pos = 0; pos < apool->used_blocks; pos++) { | |
161 if (apool->blocks[pos] == block) | |
162 return pos; | |
163 } | |
164 | |
165 return -1; | |
166 } | |
167 | |
168 static void pool_reset_first_free_block(AllocfreePool *apool) | |
169 { | |
170 int pos; | |
171 | |
172 pos = apool->first_block_with_space; | |
173 i_assert(pos >= 0); | |
174 | |
175 apool->first_block_with_space = -1; | |
176 for (; pos < apool->used_blocks; pos++) { | |
177 if (apool->blocks[pos]->largest_free_space > 0) { | |
178 apool->first_block_with_space = pos; | |
179 break; | |
180 } | |
181 } | |
182 } | |
183 | |
184 static void pool_block_destroy(AllocfreePool *apool, PoolBlock *block) | |
185 { | |
186 int pos; | |
187 | |
188 pos = pool_block_get_pos(apool, block); | |
189 if (pos == -1) | |
190 return; | |
191 | |
192 memmove(apool->blocks+pos, apool->blocks+pos+1, | |
193 sizeof(PoolBlock *) * (apool->num_blocks-pos-1)); | |
194 apool->used_blocks--; | |
195 | |
196 if (apool->first_block_with_space > pos) | |
197 apool->first_block_with_space--; | |
198 else if (apool->first_block_with_space == pos) | |
199 pool_reset_first_free_block(apool); | |
200 free(block); | |
201 } | |
202 | |
203 static PoolBlock *pool_block_find(AllocfreePool *apool, void *mem) | |
204 { | |
205 PoolBlock *block; | |
206 int pos; | |
207 | |
208 #ifdef POOL_SAVE_BLOCK | |
209 memcpy(&block, (unsigned char *) mem - sizeof(PoolBlock *), | |
210 sizeof(PoolBlock *)); | |
211 | |
212 for (pos = 0; pos < apool->used_blocks; pos++) { | |
213 if (apool->blocks[pos] == block) | |
214 return block; | |
215 } | |
216 #else | |
217 | |
218 /* NOTE: this may be a portability problem, since it compares | |
219 unrelated pointers. */ | |
220 for (pos = 0; pos < apool->used_blocks; pos++) { | |
221 block = apool->blocks[pos]; | |
222 | |
223 if ((unsigned char *) mem >= block->data && | |
224 (unsigned char *) mem < block->data + block->size) | |
225 return block; | |
226 } | |
227 #endif | |
228 | |
229 return NULL; | |
230 } | |
231 | |
232 #ifdef POOL_DEBUG | |
233 static void pool_block_dump(PoolBlock *block) | |
234 { | |
235 unsigned char *mem; | |
236 unsigned int holesize, holesize_real; | |
237 | |
238 printf("Block: size %d\n", block->size); | |
239 | |
240 mem = block->data; | |
241 mem2int(mem, holesize); | |
242 while (holesize > 0) { | |
243 holesize_real = mem_size(holesize); | |
244 printf(" - %u %s\n", holesize_real, | |
245 is_mem_free(holesize) ? " (free)" : ""); | |
246 | |
247 mem += holesize_real + sizeof(int); | |
248 if (mem-block->data == block->size) | |
249 break; | |
250 | |
251 if (mem-block->data > block->size) | |
252 i_panic("pool_block_alloc() : corrupted pool"); | |
253 | |
254 mem2int(mem, holesize); | |
255 } | |
256 } | |
257 | |
258 static void pool_dump(AllocfreePool *apool) | |
259 { | |
260 int i; | |
261 | |
262 printf("Dumping pool: %s\n", apool->name); | |
263 | |
264 for (i = 0; i < apool->used_blocks; i++) | |
265 pool_block_dump(apool->blocks[i]); | |
266 } | |
267 #endif | |
268 | |
269 static void *pool_block_alloc(AllocfreePool *apool, PoolBlock *block, | |
270 unsigned int size) | |
271 { | |
272 unsigned char *mem, *alloc; | |
273 unsigned int largest, holesize, holesize_real, avail_size; | |
274 unsigned int alloc_index, next_free_index; | |
275 | |
276 #ifdef POOL_DEBUG | |
277 printf("pool_block_alloc(%s, %d)\n", apool->name, size); | |
278 #endif | |
279 | |
280 size += ALLOC_EXTRA_SIZE; | |
281 | |
282 /* search for first large enough free space in the block. | |
283 remember the largest free block so far so if we use the | |
284 current largest, we don't have to scan the largest one | |
285 from the beginning. */ | |
286 largest = 0; | |
287 mem = block->data + block->free_index; | |
288 mem2int(mem, holesize); | |
289 while (holesize > 0) { | |
290 holesize_real = mem_size(holesize); | |
291 if (holesize_real + sizeof(int)*2 > block->size) | |
292 i_panic("pool_block_alloc() : corrupted pool"); | |
293 | |
294 if (is_mem_free(holesize)) { | |
295 if (size <= holesize_real) | |
296 break; | |
297 | |
298 if (holesize_real-ALLOC_EXTRA_SIZE > largest) | |
299 largest = holesize_real-ALLOC_EXTRA_SIZE; | |
300 } | |
301 | |
302 mem += holesize_real + sizeof(int); | |
303 mem2int(mem, holesize); | |
304 } | |
305 | |
306 avail_size = mem_size(holesize); | |
307 if (avail_size == 0) { | |
308 /* rest of the block is free */ | |
309 if ((int) (mem-block->data)+size >= block->size) { | |
310 i_panic("pool_block_alloc(): not enough space " | |
311 "in block (should have been)"); | |
312 } | |
313 | |
314 avail_size = block->size - (int) (mem-block->data) - | |
315 sizeof(int); | |
316 } else if (avail_size-ALLOC_EXTRA_SIZE != block->largest_free_space) { | |
317 /* we didn't use the largest free space in block, | |
318 so we don't need to scan the rest of the data to | |
319 search it */ | |
320 largest = block->largest_free_space; | |
321 } | |
322 | |
323 if (avail_size < size+sizeof(int)*3) { | |
324 /* don't bother leaving small holes in the pool */ | |
325 size = avail_size; | |
326 } | |
327 | |
328 #ifdef POOL_DEBUG | |
329 if (size == avail_size) | |
330 printf(" - pool is now full\n"); | |
331 #endif | |
332 | |
333 /* [i.....] -> [iXXi..] (i = space size, X = data, . = empty space) */ | |
334 int2mem(mem, size); | |
335 alloc = mem + sizeof(int); | |
336 alloc_index = (int) (mem-block->data); | |
337 | |
338 #ifdef POOL_SAVE_BLOCK | |
339 memcpy(alloc, &block, sizeof(PoolBlock *)); | |
340 alloc += sizeof(PoolBlock *); | |
341 #endif | |
342 | |
343 /* set mem point to beginning of next hole */ | |
344 mem += size + sizeof(int); | |
345 | |
346 if (size < avail_size) { | |
347 /* we didn't use the whole space, mark it unused */ | |
348 avail_size = holesize == 0 ? 0 : MEM_FREE_BIT | | |
349 (avail_size - size - sizeof(int)); | |
350 int2mem(mem, avail_size); | |
351 next_free_index = (int) (mem-block->data); | |
352 } else if (block->free_index == alloc_index) { | |
353 /* we used the first free hole, get the next one */ | |
354 next_free_index = block->size; | |
355 while (mem < block->data+block->size) { | |
356 mem2int(mem, holesize); | |
357 if (holesize == 0) { | |
358 next_free_index = (int) (mem-block->data); | |
359 break; | |
360 } | |
361 | |
362 holesize_real = mem_size(holesize); | |
363 if (is_mem_free(holesize)) { | |
364 next_free_index = | |
365 (int) (mem-block->data); | |
366 break; | |
367 } | |
368 | |
369 mem += holesize_real + sizeof(int); | |
370 } | |
371 } else { | |
372 /* just to suppress compiler warnings */ | |
373 next_free_index = 0; | |
374 } | |
375 | |
376 /* update the first free space index */ | |
377 if (block->free_index == alloc_index) | |
378 block->free_index = next_free_index; | |
379 | |
380 if (holesize > 0 && largest < block->largest_free_space) { | |
381 /* we just used the largest space in the block, | |
382 we'll need to find the next largest one and it | |
383 could be after the space we just used */ | |
384 while (mem < block->data+block->size) { | |
385 mem2int(mem, holesize); | |
386 if (holesize == 0) | |
387 break; | |
388 | |
389 holesize_real = mem_size(holesize); | |
390 if (holesize_real + | |
391 sizeof(int)*2 > block->size) | |
392 i_panic("pool_block_alloc() : corrupted pool"); | |
393 | |
394 if (is_mem_free(holesize) && | |
395 holesize_real-ALLOC_EXTRA_SIZE > largest) | |
396 largest = holesize_real-ALLOC_EXTRA_SIZE; | |
397 | |
398 mem += holesize_real + sizeof(int); | |
399 } | |
400 } | |
401 | |
402 if (holesize == 0 && mem < block->data+block->size) { | |
403 /* rest of the block is free */ | |
404 holesize = block->size - (int) (mem-block->data + sizeof(int)); | |
405 if (holesize-ALLOC_EXTRA_SIZE > largest) | |
406 largest = holesize-ALLOC_EXTRA_SIZE; | |
407 } | |
408 | |
409 block->largest_free_space = largest; | |
410 if (largest == 0) | |
411 pool_reset_first_free_block(apool); | |
412 | |
413 return alloc; | |
414 } | |
415 | |
416 /* Returns the previous block for "mem", or NULL if either there's no | |
417 previous block or if previous block is before any of the free blocks. */ | |
418 static unsigned char * | |
419 pool_block_prev(PoolBlock *block, unsigned char *find_mem) | |
420 { | |
421 unsigned char *mem, *next_mem; | |
422 unsigned int holesize; | |
423 | |
424 if (block->free_index >= (unsigned int) (find_mem-block->data)) | |
425 return NULL; | |
426 | |
427 mem = block->data + block->free_index; | |
428 mem2int(mem, holesize); | |
429 while (holesize > 0) { | |
430 next_mem = mem + mem_size(holesize) + sizeof(int); | |
431 if (next_mem == find_mem) | |
432 return mem; | |
433 | |
434 mem = next_mem; | |
435 mem2int(mem, holesize); | |
436 } | |
437 | |
438 return NULL; | |
439 } | |
440 | |
441 static void pool_block_free(AllocfreePool *apool, PoolBlock *block, | |
442 unsigned char *mem) | |
443 { | |
444 unsigned char *next_mem, *prev_mem; | |
445 unsigned int holesize, next_holesize, avail_space, prevsize; | |
446 unsigned int next_free_index; | |
447 int pos; | |
448 | |
449 #ifdef POOL_SAVE_BLOCK | |
450 mem -= sizeof(PoolBlock *); | |
451 #endif | |
452 mem -= sizeof(int); | |
453 mem2int(mem, holesize); | |
454 | |
455 #ifdef POOL_DEBUG | |
456 printf("pool_block_free(%s, %u)\n", apool->name, mem_size(holesize)); | |
457 #endif | |
458 | |
459 if ((holesize & MEM_FREE_BIT) != 0 || | |
460 mem_size(holesize) + sizeof(int) > block->size) | |
461 i_panic("pool_block_free() : corrupted pool"); | |
462 | |
463 if ((int) (mem-block->data) + holesize + sizeof(int) >= block->size) { | |
464 /* this is the last space in block */ | |
465 holesize = 0; | |
466 } else { | |
467 next_mem = ((unsigned char *) mem) + holesize + sizeof(int); | |
468 mem2int(next_mem, next_holesize); | |
469 | |
470 if (next_holesize == 0) { | |
471 /* last used space - combine to free space at end */ | |
472 holesize = 0; | |
473 } else if (!is_mem_free(next_holesize)) { | |
474 /* mark the space free */ | |
475 holesize |= MEM_FREE_BIT; | |
476 } else { | |
477 /* combine the two free spaces */ | |
478 holesize = (mem_size(holesize) + sizeof(int) + | |
479 mem_size(next_holesize)) | MEM_FREE_BIT; | |
480 } | |
481 } | |
482 int2mem(mem, holesize); | |
483 | |
484 /* if previous block is free, we can combine the free space */ | |
485 prev_mem = pool_block_prev(block, mem); | |
486 if (prev_mem != NULL) { | |
487 mem2int(prev_mem, prevsize); | |
488 if (is_mem_free(prevsize)) { | |
489 if (holesize != 0) { | |
490 holesize = (mem_size(holesize) + sizeof(int) + | |
491 mem_size(prevsize)) | MEM_FREE_BIT; | |
492 } | |
493 int2mem(prev_mem, holesize); | |
494 mem = prev_mem; | |
495 } | |
496 } | |
497 | |
498 /* update largest free space size */ | |
499 avail_space = holesize != 0 ? mem_size(holesize) : | |
500 block->size - (int) (mem-block->data + sizeof(int)); | |
501 | |
502 #ifdef POOL_FREE_CLEAR | |
503 memset(mem + sizeof(int), POOL_FREE_CLEAR, avail_space); | |
504 #elif defined (POOL_SAVE_BLOCK) | |
505 memset(mem + sizeof(int), 0, sizeof(PoolBlock *)); | |
506 #endif | |
507 | |
508 if (block->largest_free_space < avail_space-ALLOC_EXTRA_SIZE) | |
509 block->largest_free_space = avail_space-ALLOC_EXTRA_SIZE; | |
510 | |
511 /* update the first free space index */ | |
512 next_free_index = (int) (mem-block->data); | |
513 if (block->free_index > next_free_index) | |
514 block->free_index = next_free_index; | |
515 | |
516 /* update pool's first block with free space index */ | |
517 pos = pool_block_get_pos(apool, block); | |
518 if (apool->first_block_with_space < 0 || | |
519 pos < apool->first_block_with_space) | |
520 apool->first_block_with_space = pos; | |
521 | |
522 if (holesize == 0 && mem == block->data) { | |
523 /* FIXME: the block is completely unused, if there's more | |
524 than two empty blocks, free them */ | |
525 } | |
526 } | |
527 | |
528 Pool pool_allocfree_create(const char *name, unsigned int size) | |
529 { | |
530 AllocfreePool *apool; | |
531 | |
532 i_assert(size > sizeof(int)); | |
533 | |
534 apool = calloc(sizeof(AllocfreePool) + strlen(name), 1); | |
535 if (apool == NULL) | |
536 i_panic("pool_create(): Out of memory"); | |
537 | |
538 apool->pool = static_allocfree_pool; | |
539 apool->magic = 0xbeef; | |
540 apool->refcount = 1; | |
541 | |
542 apool->first_block_with_space = -1; | |
543 pool_block_create(apool, nearest_power(size)); | |
544 | |
545 strcpy(apool->name, name); | |
546 return &apool->pool; | |
547 } | |
548 | |
549 #ifdef POOL_CHECK_LEAKS | |
550 static const char *get_leak_string(const unsigned char *mem, int size) | |
551 { | |
552 int i; | |
553 | |
554 mem += sizeof(int); | |
555 | |
556 #ifdef POOL_SAVE_BLOCK | |
557 mem += sizeof(PoolBlock *); | |
558 size -= sizeof(PoolBlock *); | |
559 #endif | |
560 | |
561 for (i = 0; i < size; i++) { | |
562 if (mem[i] == '\0') | |
563 return (const char *) mem; | |
564 | |
565 if ((mem[i] & 0x7f) < 32) | |
566 break; | |
567 } | |
568 | |
569 return NULL; | |
570 } | |
571 | |
572 static const char *pool_block_count_leaks(PoolBlock *block, int *leak_count, | |
573 int *leak_size) | |
574 { | |
575 const char *leak_string; | |
576 unsigned char *mem; | |
577 unsigned int holesize, holesize_real; | |
578 | |
579 leak_string = NULL; | |
580 | |
581 mem = block->data; | |
582 mem2int(mem, holesize); | |
583 while (holesize > 0) { | |
584 holesize_real = mem_size(holesize); | |
585 if (!is_mem_free(holesize)) { | |
586 (*leak_count)++; | |
587 *leak_size += holesize_real; | |
588 | |
589 if (leak_string == NULL) { | |
590 leak_string = get_leak_string(mem, | |
591 holesize_real); | |
592 } | |
593 } | |
594 | |
595 if (holesize_real + sizeof(int)*2 > block->size) | |
596 i_panic("pool_block_count_leaks() : corrupted pool"); | |
597 | |
598 mem += holesize_real + sizeof(int); | |
599 mem2int(mem, holesize); | |
600 } | |
601 | |
602 return leak_string; | |
603 } | |
604 | |
605 static void pool_check_leaks(AllocfreePool *apool) | |
606 { | |
607 const char *leak_string; | |
608 int i, leak_count, leak_size; | |
609 | |
610 leak_string = NULL; | |
611 leak_count = leak_size = 0; | |
612 for (i = 0; i < apool->used_blocks; i++) { | |
613 PoolBlock *block = apool->blocks[i]; | |
614 | |
615 if (block->free_index < block->size) { | |
616 leak_string = pool_block_count_leaks(block, &leak_count, | |
617 &leak_size); | |
618 } | |
619 } | |
620 | |
621 if (leak_count > 0) { | |
622 i_warning("Pool '%s' leaked %d allocs with " | |
623 "total size of %d (%s)", | |
624 apool->name, leak_count, leak_size, | |
625 leak_string == NULL ? "" : leak_string); | |
626 } | |
627 } | |
628 #endif | |
629 | |
630 static void pool_destroy(AllocfreePool *apool) | |
631 { | |
632 check_pool(apool); | |
633 | |
634 #ifdef POOL_CHECK_LEAKS | |
635 pool_check_leaks(apool); | |
636 #endif | |
637 | |
638 while (apool->used_blocks > 0) | |
639 pool_block_destroy(apool, apool->blocks[0]); | |
640 free(apool->blocks); | |
641 free(apool->name); | |
642 free(apool); | |
643 } | |
644 | |
645 static void pool_allocfree_ref(Pool pool) | |
646 { | |
647 AllocfreePool *apool = (AllocfreePool *) pool; | |
648 | |
649 apool->refcount++; | |
650 } | |
651 | |
652 static void pool_allocfree_unref(Pool pool) | |
653 { | |
654 AllocfreePool *apool = (AllocfreePool *) pool; | |
655 | |
656 if (--apool->refcount == 0) | |
657 pool_destroy(apool); | |
658 } | |
659 | |
660 static void *pool_allocfree_malloc(Pool pool, unsigned int size) | |
661 { | |
662 AllocfreePool *apool = (AllocfreePool *) pool; | |
663 PoolBlock *block; | |
664 void *mem; | |
665 int i, allocsize; | |
666 | |
667 if (size == 0) | |
668 return NULL; | |
669 | |
670 if (size > MAX_ALLOC_SIZE) | |
671 i_panic("Trying to allocate too much memory"); | |
672 | |
673 /* allocate only aligned amount of memory so alignment comes | |
674 always properly */ | |
675 size = (size + MEM_ALIGN-1) & ~(MEM_ALIGN-1); | |
676 | |
677 check_pool(apool); | |
678 | |
679 /* check if there's enough space in one of the existing blocks */ | |
680 block = NULL; | |
681 for (i = 0; i < apool->used_blocks; i++) { | |
682 if (apool->blocks[i]->largest_free_space >= size) { | |
683 block = apool->blocks[i]; | |
684 break; | |
685 } | |
686 } | |
687 | |
688 if (block == NULL) { | |
689 /* create new block to pool */ | |
690 allocsize = 2*apool->blocks[apool->used_blocks-1]->size; | |
691 if (allocsize-sizeof(int)-ALLOC_EXTRA_SIZE < size) | |
692 allocsize = size*2 + sizeof(int) + ALLOC_EXTRA_SIZE; | |
693 | |
694 pool_block_create(apool, nearest_power(allocsize)); | |
695 block = apool->blocks[apool->used_blocks-1]; | |
696 } | |
697 | |
698 mem = pool_block_alloc(apool, block, size); | |
699 memset(mem, 0, size); | |
700 | |
701 #ifdef POOL_DEBUG | |
702 pool_dump(apool); | |
703 #endif | |
704 return mem; | |
705 } | |
706 | |
707 static void *pool_allocfree_realloc(Pool pool, void *mem, unsigned int size) | |
708 { | |
709 AllocfreePool *apool = (AllocfreePool *) pool; | |
710 unsigned char *mem_size_pos, *oldmem; | |
711 unsigned int memsize; | |
712 | |
713 if (size == 0) { | |
714 pool_allocfree_free(pool, mem); | |
715 return NULL; | |
716 } | |
717 | |
718 if (mem == NULL) | |
719 return pool_allocfree_malloc(pool, size); | |
720 | |
721 check_pool(apool); | |
722 | |
723 mem_size_pos = (unsigned char *) mem - sizeof(int); | |
724 #ifdef POOL_SAVE_BLOCK | |
725 mem_size_pos -= sizeof(PoolBlock *); | |
726 #endif | |
727 | |
728 mem2int(mem_size_pos, memsize); | |
729 if (memsize == size) | |
730 return mem; | |
731 | |
732 /* FIXME: shrinking could be done more efficiently, also growing | |
733 might be able to check if it can extend it's current allocation */ | |
734 oldmem = mem; | |
735 mem = pool_allocfree_malloc(pool, size); | |
736 memcpy(mem, oldmem, memsize < size ? memsize : size); | |
737 pool_allocfree_free(pool, oldmem); | |
738 | |
739 #ifdef POOL_DEBUG | |
740 pool_dump(apool); | |
741 #endif | |
742 | |
743 if (size > memsize) | |
744 memset((char *) mem + memsize, 0, size-memsize); | |
745 return mem; | |
746 } | |
747 | |
748 static void *pool_allocfree_realloc_min(Pool pool, void *mem, | |
749 unsigned int size) | |
750 { | |
751 unsigned char *mem_size_pos; | |
752 unsigned int memsize; | |
753 | |
754 if (mem == NULL) | |
755 return pool_allocfree_malloc(pool, size); | |
756 | |
757 mem_size_pos = (unsigned char *) mem - sizeof(int); | |
758 #ifdef POOL_SAVE_BLOCK | |
759 mem_size_pos -= sizeof(PoolBlock *); | |
760 #endif | |
761 | |
762 mem2int(mem_size_pos, memsize); | |
763 if (size <= memsize) | |
764 return mem; | |
765 | |
766 return pool_allocfree_realloc(pool, mem, size); | |
767 } | |
768 | |
769 static void pool_allocfree_free(Pool pool, void *mem) | |
770 { | |
771 AllocfreePool *apool = (AllocfreePool *) pool; | |
772 PoolBlock *block; | |
773 | |
774 if (mem == NULL) | |
775 return; | |
776 | |
777 check_pool(apool); | |
778 | |
779 block = pool_block_find(apool, mem); | |
780 if (block == NULL) | |
781 i_panic("pool_allocfree_free(): invalid memory address"); | |
782 | |
783 pool_block_free(apool, block, mem); | |
784 | |
785 #ifdef POOL_DEBUG | |
786 pool_dump(apool); | |
787 #endif | |
788 } | |
789 | |
790 static void pool_allocfree_clear(Pool pool) | |
791 { | |
792 AllocfreePool *apool = (AllocfreePool *) pool; | |
793 int i; | |
794 | |
795 apool->first_block_with_space = 0; | |
796 | |
797 for (i = 0; i < apool->used_blocks; i++) { | |
798 PoolBlock *block = apool->blocks[i]; | |
799 | |
800 block->free_index = 0; | |
801 block->largest_free_space = block->size - | |
802 sizeof(int) - ALLOC_EXTRA_SIZE; | |
803 memset(block->data, 0, sizeof(int)); | |
804 } | |
805 } | |
806 | |
807 static struct Pool static_allocfree_pool = { | |
808 pool_allocfree_ref, | |
809 pool_allocfree_unref, | |
810 | |
811 pool_allocfree_malloc, | |
812 pool_allocfree_free, | |
813 | |
814 pool_allocfree_realloc, | |
815 pool_allocfree_realloc_min, | |
816 | |
817 pool_allocfree_clear | |
818 }; | |
819 | |
820 #ifdef POOL_DEBUG | |
821 #include <stdlib.h> | |
822 | |
823 void mempool_test(void) | |
824 { | |
825 Pool p; | |
826 void *arr[100]; | |
827 int i, j; | |
828 | |
829 memset(arr, 0, sizeof(arr)); | |
830 | |
831 p = pool_create("temp", 32); | |
832 for (i = 0; i < 10000; i++) { | |
833 arr[rand()%100] = p_malloc(p, 4*(rand()%10+1)); | |
834 | |
835 if (rand()%3 == 1) { | |
836 for (j = 0; j < 100; j++) { | |
837 if (arr[j] != NULL) | |
838 p_free_and_null(p, arr[j]); | |
839 } | |
840 } | |
841 } | |
842 } | |
843 #endif |