Scippy

SCIP

Solving Constraint Integer Programs

memory.c
Go to the documentation of this file.
1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /* */
3 /* This file is part of the library */
4 /* BMS --- Block Memory Shell */
5 /* */
6 /* Copyright (C) 2002-2014 Konrad-Zuse-Zentrum */
7 /* fuer Informationstechnik Berlin */
8 /* */
9 /* BMS is distributed under the terms of the ZIB Academic License. */
10 /* */
11 /* You should have received a copy of the ZIB Academic License */
12 /* along with BMS; see the file COPYING. If not email to achterberg@zib.de. */
13 /* */
14 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
15 
16 /**@file memory.c
17  * @brief memory allocation routines
18  * @author Tobias Achterberg
19  */
20 
21 /*---+----1----+----2----+----3----+----4----+----5----+----6----+----7----+----8----+----9----+----0----+----1----+----2*/
22 
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <assert.h>
26 #include <string.h>
27 
28 #ifdef WITH_SCIPDEF
29 #include "scip/def.h"
30 #include "scip/pub_message.h"
31 #endif
32 
33 #include "blockmemshell/memory.h"
34 
35 /*#define CHECKMEM*/
36 
37 
38 /* if we are included in SCIP, use SCIP's message output methods */
39 #ifdef SCIPdebugMessage
40 #define debugMessage SCIPdebugMessage
41 #define errorMessage SCIPerrorMessage
42 #else
43 #define debugMessage while( FALSE ) printf
44 #define errorMessage printf
45 #define printErrorHeader(f,l) printf("[%s:%d] ERROR: ", f, l)
46 #define printError printf
47 #endif
48 
49 #define warningMessage printf
50 #define printInfo printf
51 
52 /* define some macros (if not already defined) */
53 #ifndef FALSE
54 #define FALSE 0
55 #define TRUE 1
56 #endif
57 #ifndef MAX
58 #define MAX(x,y) ((x) >= (y) ? (x) : (y))
59 #define MIN(x,y) ((x) <= (y) ? (x) : (y))
60 #endif
61 
62 
63 
64 
65 /*************************************************************************************
66  * Standard Memory Management
67  *
68  * In debug mode, these methods extend malloc() and free() by logging all currently
69  * allocated memory elements in an allocation list. This can be used as a simple leak
70  * detection.
71  *************************************************************************************/
72 #if !defined(NDEBUG) && defined(NPARASCIP)
73 
74 typedef struct Memlist MEMLIST; /**< memory list for debugging purposes */
75 
76 /** memory list for debugging purposes */
77 struct Memlist
78 {
79  const void* ptr; /**< pointer to allocated memory */
80  size_t size; /**< size of memory element */
81  char* filename; /**< source file where the allocation was performed */
82  int line; /**< line number in source file where the allocation was performed */
83  MEMLIST* next; /**< next entry in the memory list */
84 };
85 
86 static MEMLIST* memlist = NULL; /**< global memory list for debugging purposes */
87 static long long memused = 0; /**< number of allocated bytes */
88 
89 #ifdef CHECKMEM
90 /** checks, whether the number of allocated bytes match the entries in the memory list */
91 static
92 void checkMemlist(
93  void
94  )
95 {
96  MEMLIST* list = memlist;
97  long long used = 0;
98 
99  while( list != NULL )
100  {
101  used += list->size;
102  list = list->next;
103  }
104  assert(used == memused);
105 }
106 #else
107 #define checkMemlist() /**/
108 #endif
109 
110 /** adds entry to list of allocated memory */
111 static
112 void addMemlistEntry(
113  const void* ptr, /**< pointer to allocated memory */
114  size_t size, /**< size of memory element */
115  const char* filename, /**< source file where the allocation was performed */
116  int line /**< line number in source file where the allocation was performed */
117  )
118 {
119  MEMLIST* list;
120 
121  assert(ptr != NULL && size > 0);
122 
123  list = (MEMLIST*)malloc(sizeof(MEMLIST));
124  assert(list != NULL);
125 
126  list->ptr = ptr;
127  list->size = size;
128  list->filename = strdup(filename);
129  assert(list->filename != NULL);
130  list->line = line;
131  list->next = memlist;
132  memlist = list;
133  memused += (long long)size;
134  checkMemlist();
135 }
136 
137 /** removes entry from the list of allocated memory */
138 static
139 void removeMemlistEntry(
140  const void* ptr, /**< pointer to allocated memory */
141  const char* filename, /**< source file where the deallocation was performed */
142  int line /**< line number in source file where the deallocation was performed */
143  )
144 {
145  MEMLIST* list;
146  MEMLIST** listptr;
147 
148  assert(ptr != NULL);
149 
150  list = memlist;
151  listptr = &memlist;
152  while( list != NULL && ptr != list->ptr )
153  {
154  listptr = &(list->next);
155  list = list->next;
156  }
157  if( list != NULL )
158  {
159  assert(ptr == list->ptr);
160 
161  *listptr = list->next;
162  memused -= (long long)list->size;
163  free(list->filename);
164  free(list);
165  }
166  else
167  {
168  printErrorHeader(filename, line);
169  printError("Tried to free unknown pointer <%p>\n", ptr);
170  }
171  checkMemlist();
172 }
173 
174 /** returns the size of an allocated memory element */
176  const void* ptr /**< pointer to allocated memory */
177  )
178 {
179  MEMLIST* list;
180 
181  list = memlist;
182  while( list != NULL && ptr != list->ptr )
183  list = list->next;
184  if( list != NULL )
185  return list->size;
186  else
187  return 0;
188 }
189 
190 /** outputs information about currently allocated memory to the screen */
192  void
193  )
194 {
195  MEMLIST* list;
196  long long used;
197 
198  printInfo("Allocated memory:\n");
199  list = memlist;
200  used = 0;
201  while( list != NULL )
202  {
203  printInfo("%12p %8lld %s:%d\n", list->ptr, (long long)(list->size), list->filename, list->line);
204  used += (long long)list->size;
205  list = list->next;
206  }
207  printInfo("Total: %8lld\n", memused);
208  if( used != memused )
209  {
210  errorMessage("Used memory in list sums up to %lld instead of %lld\n", used, memused);
211  }
212  checkMemlist();
213 }
214 
215 /** displays a warning message on the screen, if allocated memory exists */
217  void
218  )
219 {
220  if( memlist != NULL || memused > 0 )
221  {
222  warningMessage("Memory list not empty.\n");
224  }
225 }
226 
227 /** returns total number of allocated bytes */
228 long long BMSgetMemoryUsed_call(
229  void
230  )
231 {
232  return memused;
233 }
234 
235 #else
236 
237 /* these methods are implemented even in optimized mode, such that a program, that includes memory.h in debug mode
238  * but links the optimized version compiles
239  */
240 
241 /** returns the size of an allocated memory element */
243  const void* ptr /**< pointer to allocated memory */
244  )
245 {
246  return 0;
247 }
248 
249 /** outputs information about currently allocated memory to the screen */
251  void
252  )
253 {
254 #ifdef NPARASCIP
255  printInfo("optimized version of memory shell linked - no memory diagnostics available\n");
256 #endif
257 }
258 
259 /** displays a warning message on the screen, if allocated memory exists */
261  void
262  )
263 {
264 #ifdef NPARASCIP
265  printInfo("optimized version of memory shell linked - no memory leakage check available\n");
266 #endif
267 }
268 
269 /** returns total number of allocated bytes */
271  void
272  )
273 {
274  return 0;
275 }
276 
277 #endif
278 
279 /** allocates memory and initializes it with 0; returns NULL if memory allocation failed */
281  size_t num, /**< number of memory element to allocate */
282  size_t size, /**< size of one memory element to allocate */
283  const char* filename, /**< source file where the allocation is performed */
284  int line /**< line number in source file where the allocation is performed */
285  )
286 {
287  void* ptr;
288 
289  debugMessage("calloc %lld elements of %lld bytes [%s:%d]\n", (long long) num, (long long)size, filename, line);
290 
291  num = MAX(num, 1);
292  size = MAX(size, 1);
293  ptr = calloc(num, size);
294 
295  if( ptr == NULL )
296  {
297  printErrorHeader(filename, line);
298  printError("Insufficient memory for allocation of %lld bytes\n", ((long long) num) * ((long long) size));
299  }
300 #if !defined(NDEBUG) && defined(NPARASCIP)
301  else
302  addMemlistEntry(ptr, num*size, filename, line);
303 #endif
304 
305  return ptr;
306 }
307 
308 /** allocates memory; returns NULL if memory allocation failed */
310  size_t size, /**< size of memory element to allocate */
311  const char* filename, /**< source file where the allocation is performed */
312  int line /**< line number in source file where the allocation is performed */
313  )
314 {
315  void* ptr;
316 
317  debugMessage("malloc %lld bytes [%s:%d]\n", (long long)size, filename, line);
318 
319  size = MAX(size, 1);
320  ptr = malloc(size);
321 
322  if( ptr == NULL )
323  {
324  printErrorHeader(filename, line);
325  printError("Insufficient memory for allocation of %lld bytes\n", (long long) size);
326  }
327 #if !defined(NDEBUG) && defined(NPARASCIP)
328  else
329  addMemlistEntry(ptr, size, filename, line);
330 #endif
331 
332  return ptr;
333 }
334 
335 /** allocates memory; returns NULL if memory allocation failed */
337  void* ptr, /**< pointer to memory to reallocate */
338  size_t size, /**< new size of memory element */
339  const char* filename, /**< source file where the reallocation is performed */
340  int line /**< line number in source file where the reallocation is performed */
341  )
342 {
343  void* newptr;
344 
345 #if !defined(NDEBUG) && defined(NPARASCIP)
346  if( ptr != NULL )
347  removeMemlistEntry(ptr, filename, line);
348 #endif
349 
350  size = MAX(size, 1);
351  newptr = realloc(ptr, size);
352 
353  if( newptr == NULL )
354  {
355  printErrorHeader(filename, line);
356  printError("Insufficient memory for reallocation of %lld bytes\n", (long long) size);
357  }
358 #if !defined(NDEBUG) && defined(NPARASCIP)
359  else
360  addMemlistEntry(newptr, size, filename, line);
361 #endif
362 
363  /* fprintf(stderr, "mem: %p %d\n", newptr, (int)size); */
364 
365  return newptr;
366 }
367 
368 /** clears a memory element (i.e. fills it with zeros) */
370  void* ptr, /**< pointer to memory element */
371  size_t size /**< size of memory element */
372  )
373 {
374  if( size > 0 )
375  {
376  assert(ptr != NULL);
377  memset(ptr, 0, size);
378  }
379 }
380 
381 /** copies the contents of one memory element into another memory element */
383  void* ptr, /**< pointer to target memory element */
384  const void* source, /**< pointer to source memory element */
385  size_t size /**< size of memory element to copy */
386  )
387 {
388  if( size > 0 )
389  {
390  assert(ptr != NULL);
391  assert(source != NULL);
392  memcpy(ptr, source, size);
393  }
394 }
395 
396 /** moves the contents of one memory element into another memory element, should be used if both elements overlap,
397  * otherwise BMScopyMemory is faster
398  */
400  void* ptr, /**< pointer to target memory element */
401  const void* source, /**< pointer to source memory element */
402  size_t size /**< size of memory element to copy */
403  )
404 {
405  if( size > 0 )
406  {
407  assert(ptr != NULL);
408  assert(source != NULL);
409  memmove(ptr, source, size);
410  }
411 }
412 
413 /** allocates memory and copies the contents of the given memory element into the new memory element */
415  const void* source, /**< pointer to source memory element */
416  size_t size, /**< size of memory element to copy */
417  const char* filename, /**< source file where the duplication is performed */
418  int line /**< line number in source file where the duplication is performed */
419  )
420 {
421  void* ptr;
422 
423  assert(source != NULL || size == 0);
424 
425  ptr = BMSallocMemory_call(size, filename, line);
426  if( ptr != NULL )
427  BMScopyMemory_call(ptr, source, size);
428 
429  return ptr;
430 }
431 
432 /** frees an allocated memory element */
434  void* ptr, /**< pointer to memory element */
435  const char* filename, /**< source file where the deallocation is performed */
436  int line /**< line number in source file where the deallocation is performed */
437  )
438 {
439  if( ptr != NULL )
440  {
441 #if !defined(NDEBUG) && defined(NPARASCIP)
442  removeMemlistEntry(ptr, filename, line);
443 #endif
444  free(ptr);
445  }
446  else
447  {
448  printErrorHeader(filename, line);
449  printError("Tried to free null pointer\n");
450  }
451 }
452 
453 
454 
455 
456 /********************************************************************
457  * Chunk Memory Management
458  *
459  * Efficient memory management for multiple objects of the same size
460  ********************************************************************/
461 
462 /*
463  * block memory methods for faster memory access
464  */
465 
466 #define CHUNKLENGTH_MIN 1024 /**< minimal size of a chunk (in bytes) */
467 #define CHUNKLENGTH_MAX 1048576 /**< maximal size of a chunk (in bytes) */
468 #define STORESIZE_MAX 8192 /**< maximal number of elements in one chunk */
469 #define GARBAGE_SIZE 256 /**< size of lazy free list to start garbage collection */
470 #define ALIGNMENT (sizeof(FREELIST)) /**< minimal alignment of chunks */
471 
472 typedef struct Freelist FREELIST; /**< linked list of free memory elements */
473 typedef struct Chunk CHUNK; /**< chunk of memory elements */
474 
475 /** linked list of free memory elements */
476 struct Freelist
477 {
478  FREELIST* next; /**< pointer to the next free element */
479 };
480 
481 /** chunk of memory elements */
482 struct Chunk
483 {
484  void* store; /**< data storage */
485  void* storeend; /**< points to the first byte in memory not belonging to the chunk */
486  FREELIST* eagerfree; /**< eager free list */
487  CHUNK* nexteager; /**< next chunk, that has a non-empty eager free list */
488  CHUNK* preveager; /**< previous chunk, that has a non-empty eager free list */
489  BMS_CHKMEM* chkmem; /**< chunk memory collection, this chunk belongs to */
490  int elemsize; /**< size of each element in the chunk */
491  int storesize; /**< number of elements in this chunk */
492  int eagerfreesize; /**< number of elements in the eager free list */
493  int arraypos; /**< position of chunk in the chunk header's chunkarray */
494 }; /* the chunk data structure must be aligned, because the storage is allocated directly behind the chunk header! */
495 
496 /** collection of memory chunks of the same element size */
497 struct BMS_ChkMem
498 {
499  FREELIST* lazyfree; /**< lazy free list of unused memory elements of all chunks of this chunk block */
500  CHUNK** chunks; /**< array with the chunks of the chunk header */
501  CHUNK* firsteager; /**< first chunk with a non-empty eager free list */
502  BMS_CHKMEM* nextchkmem; /**< next chunk block in the block memory's hash list */
503  int elemsize; /**< size of each memory element in the chunk memory */
504  int chunkssize; /**< size of the chunks array */
505  int nchunks; /**< number of chunks in this chunk block (used slots of the chunk array) */
506  int lastchunksize; /**< number of elements in the last allocated chunk */
507  int storesize; /**< total number of elements in this chunk block */
508  int lazyfreesize; /**< number of elements in the lazy free list of the chunk block */
509  int eagerfreesize; /**< total number of elements of all eager free lists of the block's chunks */
510  int initchunksize; /**< number of elements in the first chunk */
511  int garbagefactor; /**< garbage collector is called, if at least garbagefactor * avg. chunksize
512  * elements are free (-1: disable garbage collection) */
513 #ifndef NDEBUG
514  char* filename; /**< source file, where this chunk block was created */
515  int line; /**< source line, where this chunk block was created */
516  int ngarbagecalls; /**< number of times, the garbage collector was called */
517  int ngarbagefrees; /**< number of chunks, the garbage collector freed */
518 #endif
519 };
520 
521 
522 /** aligns the given byte size corresponding to the minimal alignment */
523 static
525  size_t* size /**< pointer to the size to align */
526  )
527 {
528  if( *size < ALIGNMENT )
529  *size = ALIGNMENT;
530  else
531  *size = ((*size + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT;
532 }
533 
534 /** aligns the given byte size corresponding to the minimal alignment for chunk and block memory */
536  size_t* size /**< pointer to the size to align */
537  )
538 {
539  assert(ALIGNMENT == sizeof(void*));
540  alignSize(size);
541 }
542 
543 /** checks whether the given size meets the alignment conditions for chunk and block memory */
545  size_t size /**< size to check for alignment */
546  )
547 {
548  assert(ALIGNMENT == sizeof(void*));
549  return( size >= ALIGNMENT && size % ALIGNMENT == 0 );
550 }
551 
552 #ifndef NDEBUG
553 /** checks, if the given pointer belongs to the given chunk */
554 static
556  const CHUNK* chunk, /**< memory chunk */
557  const void* ptr /**< pointer */
558  )
559 {
560  assert(chunk != NULL);
561  assert(chunk->store <= chunk->storeend);
562 
563  return (ptr >= (void*)(chunk->store) && ptr < (void*)(chunk->storeend));
564 }
565 #endif
566 
567 /** given a pointer, finds the chunk this pointer points to in the chunk array of the given chunk block;
568  * binary search is used;
569  * returns NULL if the pointer does not belong to the chunk block
570  */
571 static
573  const BMS_CHKMEM* chkmem, /**< chunk block */
574  const void* ptr /**< pointer */
575  )
576 {
577  CHUNK* chunk;
578  int left;
579  int right;
580  int middle;
581 
582  assert(chkmem != NULL);
583  assert(ptr != NULL);
584 
585  /* binary search for the chunk containing the ptr */
586  left = 0;
587  right = chkmem->nchunks-1;
588  while( left <= right )
589  {
590  middle = (left+right)/2;
591  assert(0 <= middle && middle < chkmem->nchunks);
592  chunk = chkmem->chunks[middle];
593  assert(chunk != NULL);
594  if( ptr < chunk->store )
595  right = middle-1;
596  else if( ptr >= chunk->storeend )
597  left = middle+1;
598  else
599  return chunk;
600  }
601 
602  /* ptr was not found in chunk */
603  return NULL;
604 }
605 
606 /** checks, if a pointer belongs to a chunk of the given chunk block */
607 static
609  const BMS_CHKMEM* chkmem, /**< chunk block */
610  const void* ptr /**< pointer */
611  )
612 {
613  assert(chkmem != NULL);
614 
615  return (findChunk(chkmem, ptr) != NULL);
616 }
617 
618 
619 
620 /*
621  * debugging methods
622  */
623 
624 #ifdef CHECKMEM
625 /** sanity check for a memory chunk */
626 static
627 void checkChunk(
628  const CHUNK* chunk /**< memory chunk */
629  )
630 {
631  FREELIST* eager;
632  int eagerfreesize;
633 
634  assert(chunk != NULL);
635  assert(chunk->store != NULL);
636  assert(chunk->storeend == (void*)((char*)(chunk->store) + chunk->elemsize * chunk->storesize));
637  assert(chunk->chkmem != NULL);
638  assert(chunk->chkmem->elemsize == chunk->elemsize);
639 
640  if( chunk->eagerfree == NULL )
641  assert(chunk->nexteager == NULL && chunk->preveager == NULL);
642  else if( chunk->preveager == NULL )
643  assert(chunk->chkmem->firsteager == chunk);
644 
645  if( chunk->nexteager != NULL )
646  assert(chunk->nexteager->preveager == chunk);
647  if( chunk->preveager != NULL )
648  assert(chunk->preveager->nexteager == chunk);
649 
650  eagerfreesize = 0;
651  eager = chunk->eagerfree;
652  while( eager != NULL )
653  {
654  assert(isPtrInChunk(chunk, eager));
655  eagerfreesize++;
656  eager = eager->next;
657  }
658  assert(chunk->eagerfreesize == eagerfreesize);
659 }
660 
661 /** sanity check for a chunk block */
662 static
663 void checkChkmem(
664  const BMS_CHKMEM* chkmem /**< chunk block */
665  )
666 {
667  CHUNK* chunk;
668  FREELIST* lazy;
669  int nchunks;
670  int storesize;
671  int lazyfreesize;
672  int eagerfreesize;
673  int i;
674 
675  assert(chkmem != NULL);
676  assert(chkmem->chunks != NULL || chkmem->chunkssize == 0);
677  assert(chkmem->nchunks <= chkmem->chunkssize);
678 
679  nchunks = 0;
680  storesize = 0;
681  lazyfreesize = 0;
682  eagerfreesize = 0;
683 
684  for( i = 0; i < chkmem->nchunks; ++i )
685  {
686  chunk = chkmem->chunks[i];
687  assert(chunk != NULL);
688 
689  checkChunk(chunk);
690  nchunks++;
691  storesize += chunk->storesize;
692  eagerfreesize += chunk->eagerfreesize;
693  }
694  assert(chkmem->nchunks == nchunks);
695  assert(chkmem->storesize == storesize);
696  assert(chkmem->eagerfreesize == eagerfreesize);
697 
698  assert((chkmem->eagerfreesize == 0) ^ (chkmem->firsteager != NULL));
699 
700  if( chkmem->firsteager != NULL )
701  assert(chkmem->firsteager->preveager == NULL);
702 
703  lazy = chkmem->lazyfree;
704  while( lazy != NULL )
705  {
706  chunk = findChunk(chkmem, lazy);
707  assert(chunk != NULL);
708  assert(chunk->chkmem == chkmem);
709  lazyfreesize++;
710  lazy = lazy->next;
711  }
712  assert(chkmem->lazyfreesize == lazyfreesize);
713 }
714 #else
715 #define checkChunk(chunk) /**/
716 #define checkChkmem(chkmem) /**/
717 #endif
718 
719 
720 /** links chunk to the block's chunk array, sort it by store pointer;
721  * returns TRUE if successful, FALSE otherwise
722  */
723 static
725  BMS_CHKMEM* chkmem, /**< chunk block */
726  CHUNK* chunk /**< memory chunk */
727  )
728 {
729  CHUNK* curchunk;
730  int left;
731  int right;
732  int middle;
733  int i;
734 
735  assert(chkmem != NULL);
736  assert(chkmem->nchunks <= chkmem->chunkssize);
737  assert(chunk != NULL);
738  assert(chunk->store != NULL);
739 
740  debugMessage("linking chunk %p to chunk block %p [elemsize:%d, %d chunks]\n",
741  (void*)chunk, (void*)chkmem, chkmem->elemsize, chkmem->nchunks);
742 
743  /* binary search for the position to insert the chunk */
744  left = -1;
745  right = chkmem->nchunks;
746  while( left < right-1 )
747  {
748  middle = (left+right)/2;
749  assert(0 <= middle && middle < chkmem->nchunks);
750  assert(left < middle && middle < right);
751  curchunk = chkmem->chunks[middle];
752  assert(curchunk != NULL);
753  if( chunk->store < curchunk->store )
754  right = middle;
755  else
756  {
757  assert(chunk->store >= curchunk->storeend);
758  left = middle;
759  }
760  }
761  assert(-1 <= left && left < chkmem->nchunks);
762  assert(0 <= right && right <= chkmem->nchunks);
763  assert(left+1 == right);
764  assert(left == -1 || chkmem->chunks[left]->storeend <= chunk->store);
765  assert(right == chkmem->nchunks || chunk->storeend <= chkmem->chunks[right]->store);
766 
767  /* ensure, that chunk array can store the additional chunk */
768  if( chkmem->nchunks == chkmem->chunkssize )
769  {
770  chkmem->chunkssize = 2*(chkmem->nchunks+1);
771  BMSreallocMemoryArray(&chkmem->chunks, chkmem->chunkssize);
772  if( chkmem->chunks == NULL )
773  return FALSE;
774  }
775  assert(chkmem->nchunks < chkmem->chunkssize);
776  assert(chkmem->chunks != NULL);
777 
778  /* move all chunks from 'right' to end one position to the right */
779  for( i = chkmem->nchunks; i > right; --i )
780  {
781  chkmem->chunks[i] = chkmem->chunks[i-1];
782  chkmem->chunks[i]->arraypos = i;
783  }
784 
785  /* insert chunk at position 'right' */
786  chunk->arraypos = right;
787  chkmem->chunks[right] = chunk;
788  chkmem->nchunks++;
789  chkmem->storesize += chunk->storesize;
790 
791  return TRUE;
792 }
793 
794 /** unlinks chunk from the chunk block's chunk list */
795 static
797  CHUNK* chunk /**< memory chunk */
798  )
799 {
800  BMS_CHKMEM* chkmem;
801  int i;
802 
803  assert(chunk != NULL);
804  assert(chunk->eagerfree == NULL);
805  assert(chunk->nexteager == NULL);
806  assert(chunk->preveager == NULL);
807 
808  chkmem = chunk->chkmem;
809  assert(chkmem != NULL);
810  assert(chkmem->elemsize == chunk->elemsize);
811  assert(0 <= chunk->arraypos && chunk->arraypos < chkmem->nchunks);
812  assert(chkmem->chunks[chunk->arraypos] == chunk);
813 
814  debugMessage("unlinking chunk %p from chunk block %p [elemsize:%d, %d chunks]\n",
815  (void*)chunk, (void*)chkmem, chkmem->elemsize, chkmem->nchunks);
816 
817  /* remove the chunk from the chunks of the chunk block */
818  for( i = chunk->arraypos; i < chkmem->nchunks-1; ++i )
819  {
820  chkmem->chunks[i] = chkmem->chunks[i+1];
821  chkmem->chunks[i]->arraypos = i;
822  }
823  chkmem->nchunks--;
824  chkmem->storesize -= chunk->storesize;
825 }
826 
827 /** links chunk to the chunk block's eager chunk list */
828 static
830  BMS_CHKMEM* chkmem, /**< chunk block */
831  CHUNK* chunk /**< memory chunk */
832  )
833 {
834  assert(chunk->chkmem == chkmem);
835  assert(chunk->nexteager == NULL);
836  assert(chunk->preveager == NULL);
837 
838  chunk->nexteager = chkmem->firsteager;
839  chunk->preveager = NULL;
840  if( chkmem->firsteager != NULL )
841  {
842  assert(chkmem->firsteager->preveager == NULL);
843  chkmem->firsteager->preveager = chunk;
844  }
845  chkmem->firsteager = chunk;
846 }
847 
848 /** unlinks chunk from the chunk block's eager chunk list */
849 static
851  CHUNK* chunk /**< memory chunk */
852  )
853 {
854  assert(chunk != NULL);
855  assert(chunk->eagerfreesize == 0 || chunk->eagerfreesize == chunk->storesize);
856 
857  if( chunk->nexteager != NULL )
858  chunk->nexteager->preveager = chunk->preveager;
859  if( chunk->preveager != NULL )
860  chunk->preveager->nexteager = chunk->nexteager;
861  else
862  {
863  assert(chunk->chkmem->firsteager == chunk);
864  chunk->chkmem->firsteager = chunk->nexteager;
865  }
866  chunk->nexteager = NULL;
867  chunk->preveager = NULL;
868  chunk->eagerfree = NULL;
869 }
870 
871 /** creates a new memory chunk in the given chunk block and adds memory elements to the lazy free list;
872  * returns TRUE if successful, FALSE otherwise
873  */
874 static
876  BMS_CHKMEM* chkmem /**< chunk block */
877  )
878 {
879  CHUNK *newchunk;
880  FREELIST *freelist;
881  int i;
882  int storesize;
883  int retval;
884 
885  assert(chkmem != NULL);
886 
887  debugMessage("creating new chunk in chunk block %p [elemsize: %d]\n", (void*)chkmem, chkmem->elemsize);
888 
889  /* calculate store size */
890  if( chkmem->nchunks == 0 )
891  storesize = chkmem->initchunksize;
892  else
893  storesize = 2 * chkmem->lastchunksize;
894  assert(storesize > 0);
895  storesize = MAX(storesize, CHUNKLENGTH_MIN / chkmem->elemsize);
896  storesize = MIN(storesize, CHUNKLENGTH_MAX / chkmem->elemsize);
897  storesize = MIN(storesize, STORESIZE_MAX);
898  storesize = MAX(storesize, 1);
899  chkmem->lastchunksize = storesize;
900 
901  /* create new chunk */
902  assert(BMSisAligned(sizeof(CHUNK)));
903  assert( chkmem->elemsize < INT_MAX / storesize );
904  assert( sizeof(CHUNK) < UINT_MAX - (size_t)(storesize * chkmem->elemsize) );
905  BMSallocMemorySize(&newchunk, sizeof(CHUNK) + storesize * chkmem->elemsize);
906  if( newchunk == NULL )
907  return FALSE;
908 
909  /* the store is allocated directly behind the chunk header */
910  newchunk->store = (void*) ((char*) newchunk + sizeof(CHUNK));
911  newchunk->storeend = (void*) ((char*) newchunk->store + storesize * chkmem->elemsize);
912  newchunk->eagerfree = NULL;
913  newchunk->nexteager = NULL;
914  newchunk->preveager = NULL;
915  newchunk->chkmem = chkmem;
916  newchunk->elemsize = chkmem->elemsize;
917  newchunk->storesize = storesize;
918  newchunk->eagerfreesize = 0;
919  newchunk->arraypos = -1;
920 
921  debugMessage("allocated new chunk %p: %d elements with size %lld\n",
922  (void*)newchunk, newchunk->storesize, (long long)(newchunk->elemsize));
923 
924  /* add new memory to the lazy free list */
925  for( i = 0; i < newchunk->storesize - 1; ++i )
926  {
927  freelist = (FREELIST*) ((char*) (newchunk->store) + i * chkmem->elemsize); /*lint !e826*/
928  freelist->next = (FREELIST*) ((char*) (newchunk->store) + (i + 1) * chkmem->elemsize); /*lint !e826*/
929  }
930 
931  freelist = (FREELIST*) ((char*) (newchunk->store) + (newchunk->storesize - 1) * chkmem->elemsize); /*lint !e826*/
932  freelist->next = chkmem->lazyfree;
933  chkmem->lazyfree = (FREELIST*) (newchunk->store);
934  chkmem->lazyfreesize += newchunk->storesize;
935 
936  /* link chunk into chunk block */
937  retval = linkChunk(chkmem, newchunk);
938 
939  checkChkmem(chkmem);
940 
941  return retval;
942 }
943 
944 /** destroys a chunk without updating the chunk lists */
945 static
947  CHUNK* chunk /**< memory chunk */
948  )
949 {
950  assert(chunk != NULL);
951 
952  debugMessage("destroying chunk %p\n", (void*)chunk);
953 
954  /* free chunk header and store (allocated in one call) */
955  BMSfreeMemory(&chunk);
956 }
957 
958 /** removes a completely unused chunk, i.e. a chunk with all elements in the eager free list */
959 static
961  CHUNK* chunk /**< memory chunk */
962  )
963 {
964  assert(chunk != NULL);
965  assert(chunk->store != NULL);
966  assert(chunk->eagerfree != NULL);
967  assert(chunk->chkmem != NULL);
968  assert(chunk->chkmem->chunks != NULL);
969  assert(chunk->chkmem->firsteager != NULL);
970  assert(chunk->eagerfreesize == chunk->storesize);
971 
972  debugMessage("freeing chunk %p of chunk block %p [elemsize: %d]\n", (void*)chunk, (void*)chunk->chkmem, chunk->chkmem->elemsize);
973 
974  /* count the deleted eager free slots */
975  chunk->chkmem->eagerfreesize -= chunk->eagerfreesize;
976  assert(chunk->chkmem->eagerfreesize >= 0);
977 
978  /* remove chunk from eager chunk list */
979  unlinkEagerChunk(chunk);
980 
981  /* remove chunk from chunk list */
982  unlinkChunk(chunk);
983 
984  /* destroy the chunk */
985  destroyChunk(chunk);
986 }
987 
988 /** returns an element of the eager free list and removes it from the list */
989 static
991  CHUNK* chunk /**< memory chunk */
992  )
993 {
994  FREELIST* ptr;
995 
996  assert(chunk != NULL);
997  assert(chunk->eagerfree != NULL);
998  assert(chunk->eagerfreesize > 0);
999  assert(chunk->chkmem != NULL);
1000 
1001  debugMessage("allocating chunk element in chunk %p [elemsize: %d]\n", (void*)chunk, chunk->chkmem->elemsize);
1002 
1003  /* unlink first element in the eager free list */
1004  ptr = chunk->eagerfree;
1005  chunk->eagerfree = ptr->next;
1006  chunk->eagerfreesize--;
1007  chunk->chkmem->eagerfreesize--;
1008 
1009  assert((chunk->eagerfreesize == 0 && chunk->eagerfree == NULL)
1010  || (chunk->eagerfreesize != 0 && chunk->eagerfree != NULL));
1011  assert(chunk->chkmem->eagerfreesize >= 0);
1012 
1013  /* unlink chunk from eager chunk list if necessary */
1014  if( chunk->eagerfree == NULL )
1015  {
1016  assert(chunk->eagerfreesize == 0);
1017  unlinkEagerChunk(chunk);
1018  }
1019 
1020  checkChunk(chunk);
1021 
1022  return (void*) ptr;
1023 }
1024 
1025 /** puts given pointer into the eager free list and adds the chunk to the eager list of its chunk block, if necessary */
1026 static
1028  CHUNK* chunk, /**< memory chunk */
1029  void* ptr /**< pointer */
1030  )
1031 {
1032  assert(chunk != NULL);
1033  assert(chunk->chkmem != NULL);
1034  assert(isPtrInChunk(chunk, ptr));
1035 
1036  debugMessage("freeing chunk element %p of chunk %p [elemsize: %d]\n", (void*)ptr, (void*)chunk, chunk->chkmem->elemsize);
1037 
1038  /* link chunk to the eager chunk list if necessary */
1039  if( chunk->eagerfree == NULL )
1040  {
1041  assert(chunk->eagerfreesize == 0);
1042  linkEagerChunk(chunk->chkmem, chunk);
1043  }
1044 
1045  /* add ptr to the chunks eager free list */
1046  ((FREELIST*)ptr)->next = chunk->eagerfree;
1047  chunk->eagerfree = (FREELIST*)ptr;
1048  chunk->eagerfreesize++;
1049  chunk->chkmem->eagerfreesize++;
1050 
1051  checkChunk(chunk);
1052 }
1053 
1054 /** creates a new chunk block data structure */
1055 static
1057  int size, /**< element size of the chunk block */
1058  int initchunksize, /**< number of elements in the first chunk of the chunk block */
1059  int garbagefactor /**< garbage collector is called, if at least garbagefactor * avg. chunksize
1060  * elements are free (-1: disable garbage collection) */
1061  )
1062 {
1063  BMS_CHKMEM* chkmem;
1064 
1065  assert(BMSisAligned((size_t)size));
1066 
1067  BMSallocMemory(&chkmem);
1068  if( chkmem == NULL )
1069  return NULL;
1070 
1071  chkmem->lazyfree = NULL;
1072  chkmem->chunks = NULL;
1073  chkmem->firsteager = NULL;
1074  chkmem->nextchkmem = NULL;
1075  chkmem->elemsize = size;
1076  chkmem->chunkssize = 0;
1077  chkmem->nchunks = 0;
1078  chkmem->lastchunksize = 0;
1079  chkmem->storesize = 0;
1080  chkmem->lazyfreesize = 0;
1081  chkmem->eagerfreesize = 0;
1082  chkmem->initchunksize = initchunksize;
1083  chkmem->garbagefactor = garbagefactor;
1084 #ifndef NDEBUG
1085  chkmem->filename = NULL;
1086  chkmem->line = 0;
1087  chkmem->ngarbagecalls = 0;
1088  chkmem->ngarbagefrees = 0;
1089 #endif
1090 
1091  return chkmem;
1092 }
1093 
1094 /** destroys all chunks of the chunk block, but keeps the chunk block header structure */
1095 static
1097  BMS_CHKMEM* chkmem /**< chunk block */
1098  )
1099 {
1100  int i;
1101 
1102  assert(chkmem != NULL);
1103 
1104  /* destroy all chunks of the chunk block */
1105  for( i = 0; i < chkmem->nchunks; ++i )
1106  destroyChunk(chkmem->chunks[i]);
1107 
1108  chkmem->lazyfree = NULL;
1109  chkmem->firsteager = NULL;
1110  chkmem->nchunks = 0;
1111  chkmem->lastchunksize = 0;
1112  chkmem->storesize = 0;
1113  chkmem->lazyfreesize = 0;
1114  chkmem->eagerfreesize = 0;
1115 }
1116 
1117 /** deletes chunk block and frees all associated memory chunks */
1118 static
1120  BMS_CHKMEM** chkmem /**< pointer to chunk block */
1121  )
1122 {
1123  assert(chkmem != NULL);
1124  assert(*chkmem != NULL);
1125 
1126  clearChkmem(*chkmem);
1127  BMSfreeMemoryArrayNull(&(*chkmem)->chunks);
1128 
1129 #ifndef NDEBUG
1130  BMSfreeMemoryArrayNull(&(*chkmem)->filename);
1131 #endif
1132 
1133  BMSfreeMemory(chkmem);
1134 }
1135 
1136 /** allocates a new memory element from the chunk block */
1137 static
1139  BMS_CHKMEM* chkmem /**< chunk block */
1140  )
1141 {
1142  FREELIST* ptr;
1143 
1144  assert(chkmem != NULL);
1145 
1146  /* if the lazy freelist is empty, we have to find the memory element somewhere else */
1147  if( chkmem->lazyfree == NULL )
1148  {
1149  assert(chkmem->lazyfreesize == 0);
1150 
1151  /* check for a free element in the eager freelists */
1152  if( chkmem->firsteager != NULL )
1153  return allocChunkElement(chkmem->firsteager);
1154 
1155  /* allocate a new chunk */
1156  if( !createChunk(chkmem) )
1157  return NULL;
1158  }
1159 
1160  /* now the lazy freelist should contain an element */
1161  assert(chkmem->lazyfree != NULL);
1162  assert(chkmem->lazyfreesize > 0);
1163 
1164  ptr = chkmem->lazyfree;
1165  chkmem->lazyfree = ptr->next;
1166  chkmem->lazyfreesize--;
1167 
1168  checkChkmem(chkmem);
1169 
1170  return (void*) ptr;
1171 }
1172 
1173 /** sorts the lazy free list of the chunk block into the eager free lists of the chunks, and removes completely
1174  * unused chunks
1175  */
1176 static
1178  BMS_CHKMEM* chkmem /**< chunk block */
1179  )
1180 {
1181  CHUNK* chunk;
1182  CHUNK* nexteager;
1183  FREELIST* lazyfree;
1184 
1185  assert(chkmem != NULL);
1186 
1187  debugMessage("garbage collection for chunk block %p [elemsize: %d]\n", (void*)chkmem, chkmem->elemsize);
1188 
1189  /* check, if the chunk block is completely unused */
1190  if( chkmem->lazyfreesize + chkmem->eagerfreesize == chkmem->storesize )
1191  {
1192  clearChkmem(chkmem);
1193  return;
1194  }
1195 
1196 #ifndef NDEBUG
1197  chkmem->ngarbagecalls++;
1198 #endif
1199 
1200  /* put the lazy free elements into the eager free lists */
1201  while( chkmem->lazyfree != NULL )
1202  {
1203  /* unlink first element from the lazy free list */
1204  lazyfree = chkmem->lazyfree;
1205  chkmem->lazyfree = chkmem->lazyfree->next;
1206  chkmem->lazyfreesize--;
1207 
1208  /* identify the chunk of the element */
1209  chunk = findChunk(chkmem, (void*)lazyfree);
1210 #ifndef NDEBUG
1211  if( chunk == NULL )
1212  {
1213  errorMessage("chunk for lazy free chunk %p not found in chunk block %p\n", (void*)lazyfree, (void*)chkmem);
1214  }
1215 #endif
1216  assert(chunk != NULL);
1217 
1218  /* add the element to the chunk's eager free list */
1219  freeChunkElement(chunk, (void*)lazyfree);
1220  assert(chunk->eagerfreesize > 0);
1221  }
1222  assert(chkmem->lazyfreesize == 0);
1223 
1224  /* delete completely unused chunks, but keep at least one */
1225  chunk = chkmem->firsteager;
1226  while( chunk != NULL && chkmem->nchunks > 1 )
1227  {
1228  nexteager = chunk->nexteager;
1229  if( chunk->eagerfreesize == chunk->storesize )
1230  {
1231 #ifndef NDEBUG
1232  chkmem->ngarbagefrees++;
1233 #endif
1234  freeChunk(chunk);
1235  }
1236  chunk = nexteager;
1237  }
1238 
1239  checkChkmem(chkmem);
1240 }
1241 
1242 /** frees a memory element and returns it to the lazy freelist of the chunk block */
1243 static
1245  BMS_CHKMEM* chkmem, /**< chunk block */
1246  void* ptr, /**< memory element */
1247  const char* filename, /**< source file of the function call */
1248  int line /**< line number in source file of the function call */
1249  )
1250 { /*lint --e{715}*/
1251  assert(chkmem != NULL);
1252  assert(ptr != NULL);
1253 
1254 #ifdef BMS_CHKMEM
1255  /* check, if ptr belongs to the chunk block */
1256  if( !isPtrInChkmem(chkmem, ptr) )
1257  {
1258  BMS_CHKMEM* correctchkmem;
1259 
1260  printErrorHeader(filename, line);
1261  printError("pointer %p does not belong to chunk block %p (size: %lld)\n",
1262  ptr, chkmem, (long long)(chkmem->elemsize));
1263  }
1264 #endif
1265 
1266  /* put ptr in lazy free list */
1267  ((FREELIST*)ptr)->next = chkmem->lazyfree;
1268  chkmem->lazyfree = (FREELIST*)ptr;
1269  chkmem->lazyfreesize++;
1270 
1271  /* check if we want to apply garbage collection */
1272  if( chkmem->garbagefactor >= 0 && chkmem->nchunks > 0 && chkmem->lazyfreesize >= GARBAGE_SIZE
1273  && chkmem->lazyfreesize + chkmem->eagerfreesize
1274  > chkmem->garbagefactor * (double)(chkmem->storesize) / (double)(chkmem->nchunks) )
1275  {
1276  garbagecollectChkmem(chkmem);
1277  }
1278 
1279  checkChkmem(chkmem);
1280 }
1281 
1282 /** creates a new chunk block data structure */
1284  size_t size, /**< element size of the chunk block */
1285  int initchunksize, /**< number of elements in the first chunk of the chunk block */
1286  int garbagefactor, /**< garbage collector is called, if at least garbagefactor * avg. chunksize
1287  * elements are free (-1: disable garbage collection) */
1288  const char* filename, /**< source file of the function call */
1289  int line /**< line number in source file of the function call */
1290  )
1291 {
1292  BMS_CHKMEM* chkmem;
1293 
1294  alignSize(&size);
1295  chkmem = createChkmem((int) size, initchunksize, garbagefactor);
1296  if( chkmem == NULL )
1297  {
1298  printErrorHeader(filename, line);
1299  printError("Insufficient memory for chunk block\n");
1300  }
1301  debugMessage("created chunk memory %p [elemsize: %d]\n", (void*)chkmem, (int)size);
1302 
1303  return chkmem;
1304 }
1305 
1306 /** clears a chunk block data structure */
1308  BMS_CHKMEM* chkmem, /**< chunk block */
1309  const char* filename, /**< source file of the function call */
1310  int line /**< line number in source file of the function call */
1311  )
1312 {
1313  debugMessage("clearing chunk memory %p [elemsize: %d]\n", (void*)chkmem, chkmem->elemsize);
1314 
1315  if( chkmem != NULL )
1316  clearChkmem(chkmem);
1317  else
1318  {
1319  printErrorHeader(filename, line);
1320  printError("Tried to clear null chunk block\n");
1321  }
1322 }
1323 
1324 /** destroys and frees a chunk block data structure */
1326  BMS_CHKMEM** chkmem, /**< pointer to chunk block */
1327  const char* filename, /**< source file of the function call */
1328  int line /**< line number in source file of the function call */
1329  )
1330 {
1331  assert(chkmem != NULL);
1332 
1333  debugMessage("destroying chunk memory %p [elemsize: %d]\n", (void*)*chkmem, (*chkmem)->elemsize);
1334 
1335  if( *chkmem != NULL )
1336  destroyChkmem(chkmem);
1337  else
1338  {
1339  printErrorHeader(filename, line);
1340  printError("Tried to destroy null chunk block\n");
1341  }
1342 }
1343 
1344 /** allocates a memory element of the given chunk block */
1346  BMS_CHKMEM* chkmem, /**< chunk block */
1347  size_t size, /**< size of memory element to allocate (only needed for sanity check) */
1348  const char* filename, /**< source file of the function call */
1349  int line /**< line number in source file of the function call */
1350  )
1351 {
1352  void* ptr;
1353 
1354  assert(chkmem != NULL);
1355  assert((int)size == chkmem->elemsize);
1356 
1357  /* get memory inside the chunk block */
1358  ptr = allocChkmemElement(chkmem);
1359  if( ptr == NULL )
1360  {
1361  printErrorHeader(filename, line);
1362  printError("Insufficient memory for new chunk\n");
1363  }
1364  debugMessage("alloced %8lld bytes in %p [%s:%d]\n", (long long)size, (void*)ptr, filename, line);
1365 
1366  checkChkmem(chkmem);
1367 
1368  return ptr;
1369 }
1370 
1371 /** duplicates a given memory element by allocating a new element of the same chunk block and copying the data */
1373  BMS_CHKMEM* chkmem, /**< chunk block */
1374  const void* source, /**< source memory element */
1375  size_t size, /**< size of memory element to allocate (only needed for sanity check) */
1376  const char* filename, /**< source file of the function call */
1377  int line /**< line number in source file of the function call */
1378  )
1379 {
1380  void* ptr;
1381 
1382  assert(chkmem != NULL);
1383  assert(source != NULL);
1384  assert((int)size == chkmem->elemsize);
1385 
1386  ptr = BMSallocChunkMemory_call(chkmem, size, filename, line);
1387  if( ptr != NULL )
1388  BMScopyMemorySize(ptr, source, chkmem->elemsize);
1389 
1390  return ptr;
1391 }
1392 
1393 /** frees a memory element of the given chunk block */
1395  BMS_CHKMEM* chkmem, /**< chunk block */
1396  void* ptr, /**< memory element to free */
1397  size_t size, /**< size of memory element to allocate (only needed for sanity check) */
1398  const char* filename, /**< source file of the function call */
1399  int line /**< line number in source file of the function call */
1400  )
1401 {
1402  assert(chkmem != NULL);
1403  assert((int)size == chkmem->elemsize);
1404 
1405  debugMessage("free %8lld bytes in %p [%s:%d]\n", (long long)chkmem->elemsize, ptr, filename, line);
1406 
1407  if( ptr == NULL )
1408  {
1409  printErrorHeader(filename, line);
1410  printError("Tried to free null block pointer\n");
1411  return;
1412  }
1413 
1414  /* free memory in chunk block */
1415  freeChkmemElement(chkmem, ptr, filename, line);
1416 
1417  checkChkmem(chkmem);
1418 }
1419 
1420 /** calls garbage collection of chunk block and frees chunks without allocated memory elements */
1422  BMS_CHKMEM* chkmem /**< chunk block */
1423  )
1424 {
1425  debugMessage("garbage collection on chunk memory %p [elemsize: %d]\n", (void*)chkmem, chkmem->elemsize);
1426 
1427  garbagecollectChkmem(chkmem);
1428 }
1429 
1430 /** returns the number of allocated bytes in the chunk block */
1432  const BMS_CHKMEM* chkmem /**< chunk block */
1433  )
1434 {
1435  long long chkmemused;
1436  int i;
1437 
1438  assert(chkmem != NULL);
1439 
1440  chkmemused = 0;
1441  for( i = 0; i < chkmem->nchunks; ++i )
1442  chkmemused += (long long)(chkmem->chunks[i]->elemsize) * (long long)(chkmem->chunks[i]->storesize);
1443 
1444  return chkmemused;
1445 }
1446 
1447 
1448 
1449 
1450 /***********************************************************
1451  * Block Memory Management
1452  *
1453  * Efficient memory management for objects of varying sizes
1454  ***********************************************************/
1455 
1456 #define CHKHASH_SIZE 1013 /**< size of chunk block hash table; should be prime */
1457 
1458 /** collection of chunk blocks */
1459 struct BMS_BlkMem
1460 {
1461  BMS_CHKMEM* chkmemhash[CHKHASH_SIZE]; /**< hash table with chunk blocks */
1462  long long memused; /**< total number of used bytes in the memory header */
1463  int initchunksize; /**< number of elements in the first chunk of each chunk block */
1464  int garbagefactor; /**< garbage collector is called, if at least garbagefactor * avg. chunksize
1465  * elements are free (-1: disable garbage collection) */
1466 };
1467 
1468 
1469 
1470 /*
1471  * debugging methods
1472  */
1473 
1474 #ifdef CHECKMEM
1475 static
1476 void checkBlkmem(
1477  const BMS_BLKMEM* blkmem /**< block memory */
1478  )
1479 {
1480  const BMS_CHKMEM* chkmem;
1481  int i;
1482 
1483  assert(blkmem != NULL);
1484  assert(blkmem->chkmemhash != NULL);
1485 
1486  for( i = 0; i < CHKHASH_SIZE; ++i )
1487  {
1488  chkmem = blkmem->chkmemhash[i];
1489  while( chkmem != NULL )
1490  {
1491  checkChkmem(chkmem);
1492  chkmem = chkmem->nextchkmem;
1493  }
1494  }
1495 }
1496 #else
1497 #define checkBlkmem(blkmem) /**/
1498 #endif
1499 
1500 
1501 /** finds the chunk block, to whick the given pointer belongs to;
1502  * this could be done by selecting the chunk block of the corresponding element size, but in a case of an
1503  * error (free gives an incorrect element size), we want to identify and output the correct element size
1504  */
1505 static
1507  const BMS_BLKMEM* blkmem, /**< block memory */
1508  const void* ptr /**< memory element to search */
1509  )
1510 {
1511  BMS_CHKMEM* chkmem;
1512  int i;
1513 
1514  assert(blkmem != NULL);
1515 
1516  chkmem = NULL;
1517  for( i = 0; chkmem == NULL && i < CHKHASH_SIZE; ++i )
1518  {
1519  chkmem = blkmem->chkmemhash[i];
1520  while( chkmem != NULL && !isPtrInChkmem(chkmem, ptr) )
1521  chkmem = chkmem->nextchkmem;
1522  }
1523 
1524  return chkmem;
1525 }
1526 
1527 /** calculates hash number of memory size */
1528 static
1530  int size /**< element size */
1531  )
1532 {
1533  assert(BMSisAligned((size_t)size));
1534 
1535  return (size % CHKHASH_SIZE);
1536 }
1537 
1538 /** creates a block memory allocation data structure */
1540  int initchunksize, /**< number of elements in the first chunk of each chunk block */
1541  int garbagefactor, /**< garbage collector is called, if at least garbagefactor * avg. chunksize
1542  * elements are free (-1: disable garbage collection) */
1543  const char* filename, /**< source file of the function call */
1544  int line /**< line number in source file of the function call */
1545  )
1546 {
1547  BMS_BLKMEM* blkmem;
1548  int i;
1549 
1550  BMSallocMemory(&blkmem);
1551  if( blkmem != NULL )
1552  {
1553  for( i = 0; i < CHKHASH_SIZE; ++i )
1554  blkmem->chkmemhash[i] = NULL;
1555  blkmem->initchunksize = initchunksize;
1556  blkmem->garbagefactor = garbagefactor;
1557  blkmem->memused = 0;
1558  }
1559  else
1560  {
1561  printErrorHeader(filename, line);
1562  printError("Insufficient memory for block memory header\n");
1563  }
1564 
1565  return blkmem;
1566 }
1567 
1568 /** frees all chunk blocks in the block memory */
1570  BMS_BLKMEM* blkmem, /**< block memory */
1571  const char* filename, /**< source file of the function call */
1572  int line /**< line number in source file of the function call */
1573  )
1574 {
1575  BMS_CHKMEM* chkmem;
1576  BMS_CHKMEM* nextchkmem;
1577  int i;
1578 
1579  if( blkmem != NULL )
1580  {
1581  for( i = 0; i < CHKHASH_SIZE; ++i )
1582  {
1583  chkmem = blkmem->chkmemhash[i];
1584  while( chkmem != NULL )
1585  {
1586  nextchkmem = chkmem->nextchkmem;
1587  destroyChkmem(&chkmem);
1588  chkmem = nextchkmem;
1589  }
1590  blkmem->chkmemhash[i] = NULL;
1591  }
1592  blkmem->memused = 0;
1593  }
1594  else
1595  {
1596  printErrorHeader(filename, line);
1597  printError("Tried to clear null block memory\n");
1598  }
1599 }
1600 
1601 /** clears and deletes block memory */
1603  BMS_BLKMEM** blkmem, /**< pointer to block memory */
1604  const char* filename, /**< source file of the function call */
1605  int line /**< line number in source file of the function call */
1606  )
1607 {
1608  assert(blkmem != NULL);
1609 
1610  if( *blkmem != NULL )
1611  {
1612  BMSclearBlockMemory_call(*blkmem, filename, line);
1613  BMSfreeMemory(blkmem);
1614  assert(*blkmem == NULL);
1615  }
1616  else
1617  {
1618  printErrorHeader(filename, line);
1619  printError("Tried to destroy null block memory\n");
1620  }
1621 }
1622 
1623 /** allocates memory in the block memory pool */
1625  BMS_BLKMEM* blkmem, /**< block memory */
1626  size_t size, /**< size of memory element to allocate */
1627  const char* filename, /**< source file of the function call */
1628  int line /**< line number in source file of the function call */
1629  )
1630 {
1631  BMS_CHKMEM** chkmemptr;
1632  int hashnumber;
1633  void* ptr;
1634 
1635  assert( blkmem != NULL );
1636 
1637  /* calculate hash number of given size */
1638  alignSize(&size);
1639  hashnumber = getHashNumber((int)size);
1640 
1641  /* find correspoding chunk block */
1642  chkmemptr = &(blkmem->chkmemhash[hashnumber]);
1643  while( *chkmemptr != NULL && (*chkmemptr)->elemsize != (int)size )
1644  chkmemptr = &((*chkmemptr)->nextchkmem);
1645 
1646  /* create new chunk block if necessary */
1647  if( *chkmemptr == NULL )
1648  {
1649  *chkmemptr = createChkmem((int)size, blkmem->initchunksize, blkmem->garbagefactor);
1650  if( *chkmemptr == NULL )
1651  {
1652  printErrorHeader(filename, line);
1653  printError("Insufficient memory for chunk block\n");
1654  return NULL;
1655  }
1656 #ifndef NDEBUG
1657  BMSduplicateMemoryArray(&(*chkmemptr)->filename, filename, strlen(filename) + 1);
1658  (*chkmemptr)->line = line;
1659 #endif
1660  }
1661 
1662  /* get memory inside the chunk block */
1663  ptr = allocChkmemElement(*chkmemptr);
1664  if( ptr == NULL )
1665  {
1666  printErrorHeader(filename, line);
1667  printError("Insufficient memory for new chunk\n");
1668  }
1669  debugMessage("alloced %8lld bytes in %p [%s:%d]\n", (long long)size, ptr, filename, line);
1670 
1671  blkmem->memused += size;
1672 
1673  checkBlkmem(blkmem);
1674 
1675  return ptr;
1676 }
1677 
1678 /** resizes memory element in the block memory pool, and copies the data */
1680  BMS_BLKMEM* blkmem, /**< block memory */
1681  void* ptr, /**< memory element to reallocated */
1682  size_t oldsize, /**< old size of memory element */
1683  size_t newsize, /**< new size of memory element */
1684  const char* filename, /**< source file of the function call */
1685  int line /**< line number in source file of the function call */
1686  )
1687 {
1688  void* newptr;
1689 
1690  if( ptr == NULL )
1691  {
1692  assert(oldsize == 0);
1693  return BMSallocBlockMemory_call(blkmem, newsize, filename, line);
1694  }
1695 
1696  alignSize(&oldsize);
1697  alignSize(&newsize);
1698  if( oldsize == newsize )
1699  return ptr;
1700 
1701  newptr = BMSallocBlockMemory_call(blkmem, newsize, filename, line);
1702  if( newptr != NULL )
1703  BMScopyMemorySize(newptr, ptr, MIN(oldsize, newsize));
1704  BMSfreeBlockMemory_call(blkmem, ptr, oldsize, filename, line);
1705 
1706  return newptr;
1707 }
1708 
1709 /** duplicates memory element in the block memory pool, and copies the data */
1711  BMS_BLKMEM* blkmem, /**< block memory */
1712  const void* source, /**< memory element to duplicate */
1713  size_t size, /**< size of memory elements */
1714  const char* filename, /**< source file of the function call */
1715  int line /**< line number in source file of the function call */
1716  )
1717 {
1718  void* ptr;
1719 
1720  assert(source != NULL);
1721 
1722  ptr = BMSallocBlockMemory_call(blkmem, size, filename, line);
1723  if( ptr != NULL )
1724  BMScopyMemorySize(ptr, source, size);
1725 
1726  return ptr;
1727 }
1728 
1729 /** frees memory element in the block memory pool */
1731  BMS_BLKMEM* blkmem, /**< block memory */
1732  void* ptr, /**< memory element to free */
1733  size_t size, /**< size of memory element */
1734  const char* filename, /**< source file of the function call */
1735  int line /**< line number in source file of the function call */
1736  )
1737 {
1738  BMS_CHKMEM* chkmem;
1739  int hashnumber;
1740 
1741  assert( blkmem != NULL );
1742 
1743  if( ptr != NULL )
1744  {
1745  /* calculate hash number of given size */
1746  alignSize(&size);
1747  hashnumber = getHashNumber((int)size);
1748 
1749  debugMessage("free %8lld bytes in %p [%s:%d]\n", (long long)size, ptr, filename, line);
1750 
1751  /* find correspoding chunk block */
1752  assert( blkmem->chkmemhash != NULL );
1753  chkmem = blkmem->chkmemhash[hashnumber];
1754  while( chkmem != NULL && chkmem->elemsize != (int)size )
1755  chkmem = chkmem->nextchkmem;
1756  if( chkmem == NULL )
1757  {
1758  printErrorHeader(filename, line);
1759  printError("Tried to free pointer <%p> in block memory <%p> of unknown size %lld\n",
1760  ptr, (void*)blkmem, (long long) size);
1761  return;
1762  }
1763  assert(chkmem->elemsize == (int)size);
1764 
1765  /* free memory in chunk block */
1766  freeChkmemElement(chkmem, ptr, filename, line);
1767 
1768  blkmem->memused -= size;
1769  assert(blkmem->memused >= 0);
1770  }
1771  else if( size != 0 )
1772  {
1773  printErrorHeader(filename, line);
1774  printError("Tried to free null block pointer\n");
1775  }
1776 
1777  checkBlkmem(blkmem);
1778 }
1779 
1780 /** calls garbage collection of block memory, frees chunks without allocated memory elements, and frees
1781  * chunk blocks without any chunks
1782  */
1784  BMS_BLKMEM* blkmem /**< block memory */
1785  )
1786 {
1787  int i;
1788 
1789  assert(blkmem != NULL);
1790 
1791  for( i = 0; i < CHKHASH_SIZE; ++i )
1792  {
1793  BMS_CHKMEM** chkmemptr;
1794 
1795  chkmemptr = &blkmem->chkmemhash[i];
1796  while( *chkmemptr != NULL )
1797  {
1798  garbagecollectChkmem(*chkmemptr);
1799  if( (*chkmemptr)->nchunks == 0 )
1800  {
1801  BMS_CHKMEM* nextchkmem;
1802 
1803  nextchkmem = (*chkmemptr)->nextchkmem;
1804  destroyChkmem(chkmemptr);
1805  *chkmemptr = nextchkmem;
1806  }
1807  else
1808  chkmemptr = &(*chkmemptr)->nextchkmem;
1809  }
1810  }
1811 }
1812 
1813 /** returns the number of allocated bytes in the block memory */
1815  const BMS_BLKMEM* blkmem /**< block memory */
1816  )
1817 {
1818  assert(blkmem != NULL);
1819 
1820  return blkmem->memused;
1821 }
1822 
1823 /** returns the size of the given memory element; returns 0, if the element is not member of the block memory */
1825  const BMS_BLKMEM* blkmem, /**< block memory */
1826  const void* ptr /**< memory element */
1827  )
1828 {
1829  const BMS_CHKMEM* chkmem;
1830 
1831  assert(blkmem != NULL);
1832 
1833  if( ptr == NULL )
1834  return 0;
1835 
1836  chkmem = findChkmem(blkmem, ptr);
1837  if( chkmem == NULL )
1838  return 0;
1839 
1840  return (size_t)(chkmem->elemsize);
1841 }
1842 
1843 /** outputs allocation diagnostics of block memory */
1845  const BMS_BLKMEM* blkmem /**< block memory */
1846  )
1847 {
1848  const BMS_CHKMEM* chkmem;
1849  int nblocks = 0;
1850  int nunusedblocks = 0;
1851  int totalnchunks = 0;
1852  int totalneagerchunks = 0;
1853  int totalnelems = 0;
1854  int totalneagerelems = 0;
1855  int totalnlazyelems = 0;
1856 #ifndef NDEBUG
1857  int totalngarbagecalls = 0;
1858  int totalngarbagefrees = 0;
1859 #endif
1860  long long allocedmem = 0;
1861  long long freemem = 0;
1862  int i;
1863  int c;
1864 
1865 #ifndef NDEBUG
1866  printInfo(" ElSize #Chunk #Eag #Elems #EagFr #LazFr #GCl #GFr Free MBytes First Allocator\n");
1867 #else
1868  printInfo(" ElSize #Chunk #Eag #Elems #EagFr #LazFr Free MBytes\n");
1869 #endif
1870 
1871  assert(blkmem != NULL);
1872 
1873  for( i = 0; i < CHKHASH_SIZE; ++i )
1874  {
1875  chkmem = blkmem->chkmemhash[i];
1876  while( chkmem != NULL )
1877  {
1878  const CHUNK* chunk;
1879  int nchunks = 0;
1880  int nelems = 0;
1881  int neagerchunks = 0;
1882  int neagerelems = 0;
1883 
1884  for( c = 0; c < chkmem->nchunks; ++c )
1885  {
1886  chunk = chkmem->chunks[c];
1887  assert(chunk != NULL);
1888  assert(chunk->elemsize == chkmem->elemsize);
1889  assert(chunk->chkmem == chkmem);
1890  nchunks++;
1891  nelems += chunk->storesize;
1892  if( chunk->eagerfree != NULL )
1893  {
1894  neagerchunks++;
1895  neagerelems += chunk->eagerfreesize;
1896  }
1897  }
1898 
1899  assert(nchunks == chkmem->nchunks);
1900  assert(nelems == chkmem->storesize);
1901  assert(neagerelems == chkmem->eagerfreesize);
1902 
1903  if( nelems > 0 )
1904  {
1905  nblocks++;
1906  allocedmem += (long long)chkmem->elemsize * (long long)nelems;
1907  freemem += (long long)chkmem->elemsize * ((long long)neagerelems + (long long)chkmem->lazyfreesize);
1908 
1909 #ifndef NDEBUG
1910  printInfo("%7lld %6d %4d %7d %7d %7d %5d %4d %5.1f%% %6.1f %s:%d\n",
1911  (long long)(chkmem->elemsize), nchunks, neagerchunks, nelems,
1912  neagerelems, chkmem->lazyfreesize, chkmem->ngarbagecalls, chkmem->ngarbagefrees,
1913  100.0 * (double) (neagerelems + chkmem->lazyfreesize) / (double) (nelems),
1914  (double)chkmem->elemsize * nelems / (1024.0*1024.0),
1915  chkmem->filename, chkmem->line);
1916 #else
1917  printInfo("%7lld %6d %4d %7d %7d %7d %5.1f%% %6.1f\n",
1918  (long long)(chkmem->elemsize), nchunks, neagerchunks, nelems,
1919  neagerelems, chkmem->lazyfreesize,
1920  100.0 * (double) (neagerelems + chkmem->lazyfreesize) / (double) (nelems),
1921  (double)chkmem->elemsize * nelems / (1024.0*1024.0));
1922 #endif
1923  }
1924  else
1925  {
1926 #ifndef NDEBUG
1927  printInfo("%7lld <unused> %5d %4d %s:%d\n",
1928  (long long)(chkmem->elemsize), chkmem->ngarbagecalls, chkmem->ngarbagefrees,
1929  chkmem->filename, chkmem->line);
1930 #else
1931  printInfo("%7lld <unused>\n", (long long)(chkmem->elemsize));
1932 #endif
1933  nunusedblocks++;
1934  }
1935  totalnchunks += nchunks;
1936  totalneagerchunks += neagerchunks;
1937  totalnelems += nelems;
1938  totalneagerelems += neagerelems;
1939  totalnlazyelems += chkmem->lazyfreesize;
1940 #ifndef NDEBUG
1941  totalngarbagecalls += chkmem->ngarbagecalls;
1942  totalngarbagefrees += chkmem->ngarbagefrees;
1943 #endif
1944  chkmem = chkmem->nextchkmem;
1945  }
1946  }
1947 #ifndef NDEBUG
1948  printInfo(" Total %6d %4d %7d %7d %7d %5d %4d %5.1f%% %6.1f\n",
1949  totalnchunks, totalneagerchunks, totalnelems, totalneagerelems, totalnlazyelems,
1950  totalngarbagecalls, totalngarbagefrees,
1951  totalnelems > 0 ? 100.0 * (double) (totalneagerelems + totalnlazyelems) / (double) (totalnelems) : 0.0,
1952  (double)allocedmem/(1024.0*1024.0));
1953 #else
1954  printInfo(" Total %6d %4d %7d %7d %7d %5.1f%% %6.1f\n",
1955  totalnchunks, totalneagerchunks, totalnelems, totalneagerelems, totalnlazyelems,
1956  totalnelems > 0 ? 100.0 * (double) (totalneagerelems + totalnlazyelems) / (double) (totalnelems) : 0.0,
1957  (double)allocedmem/(1024.0*1024.0));
1958 #endif
1959  printInfo("%d blocks (%d unused), %lld bytes allocated, %lld bytes free",
1960  nblocks + nunusedblocks, nunusedblocks, allocedmem, freemem);
1961  if( allocedmem > 0 )
1962  printInfo(" (%.1f%%)", 100.0 * (double) freemem / (double) allocedmem);
1963  printInfo("\n");
1964 }
1965 
1966 /** outputs warning messages, if there are allocated elements in the block memory */
1968  const BMS_BLKMEM* blkmem /**< block memory */
1969  )
1970 {
1971  const BMS_CHKMEM* chkmem;
1972  long long allocedmem = 0;
1973  long long freemem = 0;
1974  int i;
1975  int c;
1976 
1977  assert(blkmem != NULL);
1978 
1979  for( i = 0; i < CHKHASH_SIZE; ++i )
1980  {
1981  chkmem = blkmem->chkmemhash[i];
1982  while( chkmem != NULL )
1983  {
1984  const CHUNK* chunk;
1985  int nchunks = 0;
1986  int nelems = 0;
1987  int neagerelems = 0;
1988 
1989  for( c = 0; c < chkmem->nchunks; ++c )
1990  {
1991  chunk = chkmem->chunks[c];
1992  assert(chunk != NULL);
1993  assert(chunk->elemsize == chkmem->elemsize);
1994  assert(chunk->chkmem == chkmem);
1995  nchunks++;
1996  nelems += chunk->storesize;
1997  if( chunk->eagerfree != NULL )
1998  neagerelems += chunk->eagerfreesize;
1999  }
2000 
2001  assert(nchunks == chkmem->nchunks);
2002  assert(nelems == chkmem->storesize);
2003  assert(neagerelems == chkmem->eagerfreesize);
2004 
2005  if( nelems > 0 )
2006  {
2007  allocedmem += (long long)chkmem->elemsize * (long long)nelems;
2008  freemem += (long long)chkmem->elemsize * ((long long)neagerelems + (long long)chkmem->lazyfreesize);
2009 
2010  if( nelems != neagerelems + chkmem->lazyfreesize )
2011  {
2012 #ifndef NDEBUG
2013  printInfo("%lld bytes (%d elements of size %lld) not freed. First Allocator: %s:%d\n",
2014  (((long long)nelems - (long long)neagerelems) - (long long)chkmem->lazyfreesize)
2015  * (long long)(chkmem->elemsize),
2016  (nelems - neagerelems) - chkmem->lazyfreesize, (long long)(chkmem->elemsize),
2017  chkmem->filename, chkmem->line);
2018 #else
2019  printInfo("%lld bytes (%d elements of size %lld) not freed.\n",
2020  ((nelems - neagerelems) - chkmem->lazyfreesize) * (long long)(chkmem->elemsize),
2021  (nelems - neagerelems) - chkmem->lazyfreesize, (long long)(chkmem->elemsize));
2022 #endif
2023  }
2024  }
2025  chkmem = chkmem->nextchkmem;
2026  }
2027  }
2028 
2029  if( allocedmem != freemem )
2030  printInfo("%lld bytes not freed in total.\n", allocedmem - freemem);
2031 }
2032