fixed memory offsett when ENABLE_MEM_CHECK is defined and
[platform/upstream/glib.git] / glib / gmem.c
1 /* GLIB - Library of useful routines for C programming
2  * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 #include <stdlib.h>
20 #include <string.h>
21 #include "glib.h"
22
23 /* #define ENABLE_MEM_PROFILE */
24 /* #define ENABLE_MEM_PROFILE_EXCLUDES_MEM_CHUNKS */
25 /* #define ENABLE_MEM_CHECK */
26
27 /*
28  * This library can check for some attempts to do illegal things to
29  * memory (ENABLE_MEM_CHECK), and can do profiling
30  * (ENABLE_MEM_PROFILE).  Both features are implemented by storing
31  * words before the start of the memory chunk.
32  *
33  * The first, at offset -2*SIZEOF_LONG, is used only if
34  * ENABLE_MEM_CHECK is set, and stores 0 after the memory has been
35  * allocated and 1 when it has been freed.  The second, at offset
36  * -SIZEOF_LONG, is used if either flag is set and stores the size of
37  * the block.
38  *
39  * The MEM_CHECK flag is checked when memory is realloc'd and free'd,
40  * and it can be explicitly checked before using a block by calling
41  * g_mem_check().
42  */
43
44 #if defined(ENABLE_MEM_PROFILE) && defined(ENABLE_MEM_PROFILE_EXCLUDES_MEM_CHUNKS)
45 #define ENTER_MEM_CHUNK_ROUTINE() allocating_for_mem_chunk++
46 #define LEAVE_MEM_CHUNK_ROUTINE() allocating_for_mem_chunk--
47 #else
48 #define ENTER_MEM_CHUNK_ROUTINE()
49 #define LEAVE_MEM_CHUNK_ROUTINE()
50 #endif
51
52
53 #define MAX_MEM_AREA  65536L
54 #define MEM_AREA_SIZE 4L
55
56 #if SIZEOF_VOID_P > SIZEOF_LONG
57 #define MEM_ALIGN     SIZEOF_VOID_P
58 #else
59 #define MEM_ALIGN     SIZEOF_LONG
60 #endif
61
62
63 typedef struct _GFreeAtom      GFreeAtom;
64 typedef struct _GMemArea       GMemArea;
65 typedef struct _GRealMemChunk  GRealMemChunk;
66
67 struct _GFreeAtom
68 {
69   GFreeAtom *next;
70 };
71
72 struct _GMemArea
73 {
74   GMemArea *next;            /* the next mem area */
75   GMemArea *prev;            /* the previous mem area */
76   gulong index;              /* the current index into the "mem" array */
77   gulong free;               /* the number of free bytes in this mem area */
78   gulong allocated;          /* the number of atoms allocated from this area */
79   gulong mark;               /* is this mem area marked for deletion */
80   gchar mem[MEM_AREA_SIZE];  /* the mem array from which atoms get allocated
81                               * the actual size of this array is determined by
82                               *  the mem chunk "area_size". ANSI says that it
83                               *  must be declared to be the maximum size it
84                               *  can possibly be (even though the actual size
85                               *  may be less).
86                               */
87 };
88
89 struct _GRealMemChunk
90 {
91   gchar *name;               /* name of this MemChunk...used for debugging output */
92   gint type;                 /* the type of MemChunk: ALLOC_ONLY or ALLOC_AND_FREE */
93   gint num_mem_areas;        /* the number of memory areas */
94   gint num_marked_areas;     /* the number of areas marked for deletion */
95   guint atom_size;           /* the size of an atom */
96   gulong area_size;          /* the size of a memory area */
97   GMemArea *mem_area;        /* the current memory area */
98   GMemArea *mem_areas;       /* a list of all the mem areas owned by this chunk */
99   GMemArea *free_mem_area;   /* the free area...which is about to be destroyed */
100   GFreeAtom *free_atoms;     /* the free atoms list */
101   GTree *mem_tree;           /* tree of mem areas sorted by memory address */
102   GRealMemChunk *next;       /* pointer to the next chunk */
103   GRealMemChunk *prev;       /* pointer to the previous chunk */
104 };
105
106
107 static gulong g_mem_chunk_compute_size (gulong    size);
108 static gint   g_mem_chunk_area_compare (GMemArea *a,
109                                         GMemArea *b);
110 static gint   g_mem_chunk_area_search  (GMemArea *a,
111                                         gchar    *addr);
112
113
114 static GRealMemChunk *mem_chunks = NULL;
115
116 #ifdef ENABLE_MEM_PROFILE
117 static gulong allocations[4096] = { 0 };
118 static gulong allocated_mem = 0;
119 static gulong freed_mem = 0;
120 static gint allocating_for_mem_chunk = 0;
121 #endif /* ENABLE_MEM_PROFILE */
122
123
124 #ifndef USE_DMALLOC
125
126 gpointer
127 g_malloc (gulong size)
128 {
129   gpointer p;
130   
131   
132 #if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK)
133   gulong *t;
134 #endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */
135   
136   
137   if (size == 0)
138     return NULL;
139   
140   
141 #if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK)
142   size += SIZEOF_LONG;
143 #endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */
144   
145 #ifdef ENABLE_MEM_CHECK
146   size += SIZEOF_LONG;
147 #endif /* ENABLE_MEM_CHECK */
148   
149   
150   p = (gpointer) malloc (size);
151   if (!p)
152     g_error ("could not allocate %ld bytes", size);
153   
154   
155 #ifdef ENABLE_MEM_CHECK
156   size -= SIZEOF_LONG;
157   
158   t = p;
159   p = ((guchar*) p + SIZEOF_LONG);
160   *t = 0;
161 #endif /* ENABLE_MEM_CHECK */
162   
163 #if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK)
164   size -= SIZEOF_LONG;
165   
166   t = p;
167   p = ((guchar*) p + SIZEOF_LONG);
168   *t = size;
169   
170 #ifdef ENABLE_MEM_PROFILE
171 #  ifdef ENABLE_MEM_PROFILE_EXCLUDES_MEM_CHUNKS
172   if(!allocating_for_mem_chunk) {
173 #  endif
174     if (size <= 4095)
175       allocations[size-1] += 1;
176     else
177       allocations[4095] += 1;
178     allocated_mem += size;
179 #  ifdef ENABLE_MEM_PROFILE_EXCLUDES_MEM_CHUNKS
180   }
181 #  endif
182 #endif /* ENABLE_MEM_PROFILE */
183 #endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */
184   
185   
186   return p;
187 }
188
189 gpointer
190 g_malloc0 (gulong size)
191 {
192   gpointer p;
193   
194   
195 #if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK)
196   gulong *t;
197 #endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */
198   
199   
200   if (size == 0)
201     return NULL;
202   
203   
204 #if defined (ENABLE_MEM_PROFILE) || defined (ENABLE_MEM_CHECK)
205   size += SIZEOF_LONG;
206 #endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */
207   
208 #ifdef ENABLE_MEM_CHECK
209   size += SIZEOF_LONG;
210 #endif /* ENABLE_MEM_CHECK */
211   
212   
213   p = (gpointer) calloc (size, 1);
214   if (!p)
215     g_error ("could not allocate %ld bytes", size);
216   
217   
218 #ifdef ENABLE_MEM_CHECK
219   size -= SIZEOF_LONG;
220   
221   t = p;
222   p = ((guchar*) p + SIZEOF_LONG);
223   *t = 0;
224 #endif /* ENABLE_MEM_CHECK */
225   
226 #if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK)
227   size -= SIZEOF_LONG;
228   
229   t = p;
230   p = ((guchar*) p + SIZEOF_LONG);
231   *t = size;
232   
233 #  ifdef ENABLE_MEM_PROFILE
234 #    ifdef ENABLE_MEM_PROFILE_EXCLUDES_MEM_CHUNKS
235   if(!allocating_for_mem_chunk) {
236 #    endif
237     if (size <= 4095)
238       allocations[size-1] += 1;
239     else
240       allocations[4095] += 1;
241     allocated_mem += size;
242 #    ifdef ENABLE_MEM_PROFILE_EXCLUDES_MEM_CHUNKS
243   }
244 #    endif
245 #  endif /* ENABLE_MEM_PROFILE */
246 #endif /* ENABLE_MEM_PROFILE */
247   
248   
249   return p;
250 }
251
252 gpointer
253 g_realloc (gpointer mem,
254            gulong   size)
255 {
256   gpointer p;
257   
258 #if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK)
259   gulong *t;
260 #endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */
261   
262   
263   if (size == 0)
264     return NULL;
265   
266   
267 #if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK)
268   size += SIZEOF_LONG;
269 #endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */
270   
271 #ifdef ENABLE_MEM_CHECK
272   size += SIZEOF_LONG;
273 #endif /* ENABLE_MEM_CHECK */
274   
275   
276   if (!mem)
277     p = (gpointer) malloc (size);
278   else
279     {
280 #if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK)
281       t = (gulong*) ((guchar*) mem - SIZEOF_LONG);
282 #ifdef ENABLE_MEM_PROFILE
283       freed_mem += *t;
284 #endif /* ENABLE_MEM_PROFILE */
285       mem = t;
286 #endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */
287       
288 #ifdef ENABLE_MEM_CHECK
289       t = (gulong*) ((guchar*) mem - SIZEOF_LONG);
290       if (*t >= 1)
291         g_warning ("trying to realloc freed memory\n");
292       mem = t;
293 #endif /* ENABLE_MEM_CHECK */
294       
295       p = (gpointer) realloc (mem, size);
296     }
297   
298   if (!p)
299     g_error ("could not reallocate %lu bytes", (gulong) size);
300   
301   
302 #ifdef ENABLE_MEM_CHECK
303   size -= SIZEOF_LONG;
304   
305   t = p;
306   p = ((guchar*) p + SIZEOF_LONG);
307   *t = 0;
308 #endif /* ENABLE_MEM_CHECK */
309   
310 #if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK)
311   size -= SIZEOF_LONG;
312   
313   t = p;
314   p = ((guchar*) p + SIZEOF_LONG);
315   *t = size;
316   
317 #ifdef ENABLE_MEM_PROFILE
318 #ifdef ENABLE_MEM_PROFILE_EXCLUDES_MEM_CHUNKS
319   if(!allocating_for_mem_chunk) {
320 #endif
321     if (size <= 4095)
322       allocations[size-1] += 1;
323     else
324       allocations[4095] += 1;
325     allocated_mem += size;
326 #ifdef ENABLE_MEM_PROFILE_EXCLUDES_MEM_CHUNKS
327   }
328 #endif
329 #endif /* ENABLE_MEM_PROFILE */
330 #endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */
331   
332   
333   return p;
334 }
335
336 void
337 g_free (gpointer mem)
338 {
339   if (mem)
340     {
341 #if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK)
342       gulong *t;
343       gulong size;
344 #endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */
345       
346 #if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK)
347       t = (gulong*) ((guchar*) mem - SIZEOF_LONG);
348       size = *t;
349 #ifdef ENABLE_MEM_PROFILE
350       freed_mem += size;
351 #endif /* ENABLE_MEM_PROFILE */
352       mem = t;
353 #endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */
354       
355 #ifdef ENABLE_MEM_CHECK
356       t = (gulong*) ((guchar*) mem - SIZEOF_LONG);
357       if (*t >= 1)
358         g_warning ("freeing previously freed memory\n");
359       *t += 1;
360       mem = t;
361       
362       memset ((guchar*) mem + 8, 0, size);
363 #else /* ENABLE_MEM_CHECK */
364       free (mem);
365 #endif /* ENABLE_MEM_CHECK */
366     }
367 }
368
369 #endif /* ! USE_DMALLOC */
370
371
372 void
373 g_mem_profile (void)
374 {
375 #ifdef ENABLE_MEM_PROFILE
376   gint i;
377   
378   for (i = 0; i < 4095; i++)
379     if (allocations[i] > 0)
380       g_log (g_log_domain_glib, G_LOG_LEVEL_INFO,
381              "%lu allocations of %d bytes\n", allocations[i], i + 1);
382   
383   if (allocations[4095] > 0)
384     g_log (g_log_domain_glib, G_LOG_LEVEL_INFO,
385            "%lu allocations of greater than 4095 bytes\n", allocations[4095]);
386   g_log (g_log_domain_glib, G_LOG_LEVEL_INFO, "%lu bytes allocated\n", allocated_mem);
387   g_log (g_log_domain_glib, G_LOG_LEVEL_INFO, "%lu bytes freed\n", freed_mem);
388   g_log (g_log_domain_glib, G_LOG_LEVEL_INFO, "%lu bytes in use\n", allocated_mem - freed_mem);
389 #endif /* ENABLE_MEM_PROFILE */
390 }
391
392 void
393 g_mem_check (gpointer mem)
394 {
395 #ifdef ENABLE_MEM_CHECK
396   gulong *t;
397   
398   t = (gulong*) ((guchar*) mem - SIZEOF_LONG - SIZEOF_LONG);
399   
400   if (*t >= 1)
401     g_warning ("mem: 0x%08x has been freed %lu times\n", (gulong) mem, *t);
402 #endif /* ENABLE_MEM_CHECK */
403 }
404
405 GMemChunk*
406 g_mem_chunk_new (gchar  *name,
407                  gint    atom_size,
408                  gulong  area_size,
409                  gint    type)
410 {
411   GRealMemChunk *mem_chunk;
412   gulong rarea_size;
413
414   ENTER_MEM_CHUNK_ROUTINE();
415
416   mem_chunk = g_new (struct _GRealMemChunk, 1);
417   mem_chunk->name = name;
418   mem_chunk->type = type;
419   mem_chunk->num_mem_areas = 0;
420   mem_chunk->num_marked_areas = 0;
421   mem_chunk->mem_area = NULL;
422   mem_chunk->free_mem_area = NULL;
423   mem_chunk->free_atoms = NULL;
424   mem_chunk->mem_tree = NULL;
425   mem_chunk->mem_areas = NULL;
426   mem_chunk->atom_size = atom_size;
427   
428   if (mem_chunk->type == G_ALLOC_AND_FREE)
429     mem_chunk->mem_tree = g_tree_new ((GCompareFunc) g_mem_chunk_area_compare);
430   
431   if (mem_chunk->atom_size % MEM_ALIGN)
432     mem_chunk->atom_size += MEM_ALIGN - (mem_chunk->atom_size % MEM_ALIGN);
433   
434   mem_chunk->area_size = area_size;
435   if (mem_chunk->area_size > MAX_MEM_AREA)
436     mem_chunk->area_size = MAX_MEM_AREA;
437   while (mem_chunk->area_size < mem_chunk->atom_size)
438     mem_chunk->area_size *= 2;
439   
440   rarea_size = mem_chunk->area_size + sizeof (GMemArea) - MEM_AREA_SIZE;
441   rarea_size = g_mem_chunk_compute_size (rarea_size);
442   mem_chunk->area_size = rarea_size - (sizeof (GMemArea) - MEM_AREA_SIZE);
443   
444   /*
445     mem_chunk->area_size -= (sizeof (GMemArea) - MEM_AREA_SIZE);
446     if (mem_chunk->area_size < mem_chunk->atom_size)
447     {
448     mem_chunk->area_size = (mem_chunk->area_size + sizeof (GMemArea) - MEM_AREA_SIZE) * 2;
449     mem_chunk->area_size -= (sizeof (GMemArea) - MEM_AREA_SIZE);
450     }
451     
452     if (mem_chunk->area_size % mem_chunk->atom_size)
453     mem_chunk->area_size += mem_chunk->atom_size - (mem_chunk->area_size % mem_chunk->atom_size);
454   */
455   
456   mem_chunk->next = mem_chunks;
457   mem_chunk->prev = NULL;
458   if (mem_chunks)
459     mem_chunks->prev = mem_chunk;
460   mem_chunks = mem_chunk;
461
462   LEAVE_MEM_CHUNK_ROUTINE();
463
464   return ((GMemChunk*) mem_chunk);
465 }
466
467 void
468 g_mem_chunk_destroy (GMemChunk *mem_chunk)
469 {
470   GRealMemChunk *rmem_chunk;
471   GMemArea *mem_areas;
472   GMemArea *temp_area;
473   
474   g_assert (mem_chunk != NULL);
475
476   ENTER_MEM_CHUNK_ROUTINE();
477
478   rmem_chunk = (GRealMemChunk*) mem_chunk;
479   
480   mem_areas = rmem_chunk->mem_areas;
481   while (mem_areas)
482     {
483       temp_area = mem_areas;
484       mem_areas = mem_areas->next;
485       g_free (temp_area);
486     }
487   
488   if (rmem_chunk->next)
489     rmem_chunk->next->prev = rmem_chunk->prev;
490   if (rmem_chunk->prev)
491     rmem_chunk->prev->next = rmem_chunk->next;
492   
493   if (rmem_chunk == mem_chunks)
494     mem_chunks = mem_chunks->next;
495   
496   if (rmem_chunk->type == G_ALLOC_AND_FREE)
497     g_tree_destroy (rmem_chunk->mem_tree);
498   
499   g_free (rmem_chunk);
500
501   LEAVE_MEM_CHUNK_ROUTINE();
502 }
503
504 gpointer
505 g_mem_chunk_alloc (GMemChunk *mem_chunk)
506 {
507   GRealMemChunk *rmem_chunk;
508   GMemArea *temp_area;
509   gpointer mem;
510
511   ENTER_MEM_CHUNK_ROUTINE();
512
513   g_assert (mem_chunk != NULL);
514   
515   rmem_chunk = (GRealMemChunk*) mem_chunk;
516   
517   while (rmem_chunk->free_atoms)
518     {
519       /* Get the first piece of memory on the "free_atoms" list.
520        * We can go ahead and destroy the list node we used to keep
521        *  track of it with and to update the "free_atoms" list to
522        *  point to its next element.
523        */
524       mem = rmem_chunk->free_atoms;
525       rmem_chunk->free_atoms = rmem_chunk->free_atoms->next;
526       
527       /* Determine which area this piece of memory is allocated from */
528       temp_area = g_tree_search (rmem_chunk->mem_tree,
529                                  (GSearchFunc) g_mem_chunk_area_search,
530                                  mem);
531       
532       /* If the area has been marked, then it is being destroyed.
533        *  (ie marked to be destroyed).
534        * We check to see if all of the segments on the free list that
535        *  reference this area have been removed. This occurs when
536        *  the ammount of free memory is less than the allocatable size.
537        * If the chunk should be freed, then we place it in the "free_mem_area".
538        * This is so we make sure not to free the mem area here and then
539        *  allocate it again a few lines down.
540        * If we don't allocate a chunk a few lines down then the "free_mem_area"
541        *  will be freed.
542        * If there is already a "free_mem_area" then we'll just free this mem area.
543        */
544       if (temp_area->mark)
545         {
546           /* Update the "free" memory available in that area */
547           temp_area->free += rmem_chunk->atom_size;
548           
549           if (temp_area->free == rmem_chunk->area_size)
550             {
551               if (temp_area == rmem_chunk->mem_area)
552                 rmem_chunk->mem_area = NULL;
553               
554               if (rmem_chunk->free_mem_area)
555                 {
556                   rmem_chunk->num_mem_areas -= 1;
557                   
558                   if (temp_area->next)
559                     temp_area->next->prev = temp_area->prev;
560                   if (temp_area->prev)
561                     temp_area->prev->next = temp_area->next;
562                   if (temp_area == rmem_chunk->mem_areas)
563                     rmem_chunk->mem_areas = rmem_chunk->mem_areas->next;
564                   
565                   if (rmem_chunk->type == G_ALLOC_AND_FREE)
566                     g_tree_remove (rmem_chunk->mem_tree, temp_area);
567                   g_free (temp_area);
568                 }
569               else
570                 rmem_chunk->free_mem_area = temp_area;
571               
572               rmem_chunk->num_marked_areas -= 1;
573             }
574         }
575       else
576         {
577           /* Update the number of allocated atoms count.
578            */
579           temp_area->allocated += 1;
580           
581           /* The area wasn't marked...return the memory
582            */
583           goto outa_here;
584         }
585     }
586   
587   /* If there isn't a current mem area or the current mem area is out of space
588    *  then allocate a new mem area. We'll first check and see if we can use
589    *  the "free_mem_area". Otherwise we'll just malloc the mem area.
590    */
591   if ((!rmem_chunk->mem_area) ||
592       ((rmem_chunk->mem_area->index + rmem_chunk->atom_size) > rmem_chunk->area_size))
593     {
594       if (rmem_chunk->free_mem_area)
595         {
596           rmem_chunk->mem_area = rmem_chunk->free_mem_area;
597           rmem_chunk->free_mem_area = NULL;
598         }
599       else
600         {
601           rmem_chunk->mem_area = (GMemArea*) g_malloc (sizeof (GMemArea) -
602                                                        MEM_AREA_SIZE +
603                                                        rmem_chunk->area_size);
604           
605           rmem_chunk->num_mem_areas += 1;
606           rmem_chunk->mem_area->next = rmem_chunk->mem_areas;
607           rmem_chunk->mem_area->prev = NULL;
608           
609           if (rmem_chunk->mem_areas)
610             rmem_chunk->mem_areas->prev = rmem_chunk->mem_area;
611           rmem_chunk->mem_areas = rmem_chunk->mem_area;
612           
613           if (rmem_chunk->type == G_ALLOC_AND_FREE)
614             g_tree_insert (rmem_chunk->mem_tree, rmem_chunk->mem_area, rmem_chunk->mem_area);
615         }
616       
617       rmem_chunk->mem_area->index = 0;
618       rmem_chunk->mem_area->free = rmem_chunk->area_size;
619       rmem_chunk->mem_area->allocated = 0;
620       rmem_chunk->mem_area->mark = 0;
621     }
622   
623   /* Get the memory and modify the state variables appropriately.
624    */
625   mem = (gpointer) &rmem_chunk->mem_area->mem[rmem_chunk->mem_area->index];
626   rmem_chunk->mem_area->index += rmem_chunk->atom_size;
627   rmem_chunk->mem_area->free -= rmem_chunk->atom_size;
628   rmem_chunk->mem_area->allocated += 1;
629
630 outa_here:
631
632   LEAVE_MEM_CHUNK_ROUTINE();
633
634   return mem;
635 }
636
637 gpointer
638 g_mem_chunk_alloc0 (GMemChunk *mem_chunk)
639 {
640   gpointer mem;
641
642   mem = g_mem_chunk_alloc (mem_chunk);
643   if (mem)
644     {
645       GRealMemChunk *rmem_chunk = (GRealMemChunk*) mem_chunk;
646
647       memset (mem, 0, rmem_chunk->atom_size);
648     }
649
650   return mem;
651 }
652
653 void
654 g_mem_chunk_free (GMemChunk *mem_chunk,
655                   gpointer   mem)
656 {
657   GRealMemChunk *rmem_chunk;
658   GMemArea *temp_area;
659   GFreeAtom *free_atom;
660   
661   g_assert (mem_chunk != NULL);
662   g_assert (mem != NULL);
663
664   ENTER_MEM_CHUNK_ROUTINE();
665
666   rmem_chunk = (GRealMemChunk*) mem_chunk;
667   
668   /* Don't do anything if this is an ALLOC_ONLY chunk
669    */
670   if (rmem_chunk->type == G_ALLOC_AND_FREE)
671     {
672       /* Place the memory on the "free_atoms" list
673        */
674       free_atom = (GFreeAtom*) mem;
675       free_atom->next = rmem_chunk->free_atoms;
676       rmem_chunk->free_atoms = free_atom;
677       
678       temp_area = g_tree_search (rmem_chunk->mem_tree,
679                                  (GSearchFunc) g_mem_chunk_area_search,
680                                  mem);
681       
682       temp_area->allocated -= 1;
683       
684       if (temp_area->allocated == 0)
685         {
686           temp_area->mark = 1;
687           rmem_chunk->num_marked_areas += 1;
688         }
689     }
690
691   LEAVE_MEM_CHUNK_ROUTINE();
692 }
693
694 /* This doesn't free the free_area if there is one */
695 void
696 g_mem_chunk_clean (GMemChunk *mem_chunk)
697 {
698   GRealMemChunk *rmem_chunk;
699   GMemArea *mem_area;
700   GFreeAtom *prev_free_atom;
701   GFreeAtom *temp_free_atom;
702   gpointer mem;
703   
704   g_assert (mem_chunk != NULL);
705   
706   rmem_chunk = (GRealMemChunk*) mem_chunk;
707   
708   if (rmem_chunk->type == G_ALLOC_AND_FREE)
709     {
710       prev_free_atom = NULL;
711       temp_free_atom = rmem_chunk->free_atoms;
712       
713       while (temp_free_atom)
714         {
715           mem = (gpointer) temp_free_atom;
716           
717           mem_area = g_tree_search (rmem_chunk->mem_tree,
718                                     (GSearchFunc) g_mem_chunk_area_search,
719                                     mem);
720           
721           /* If this mem area is marked for destruction then delete the
722            *  area and list node and decrement the free mem.
723            */
724           if (mem_area->mark)
725             {
726               if (prev_free_atom)
727                 prev_free_atom->next = temp_free_atom->next;
728               else
729                 rmem_chunk->free_atoms = temp_free_atom->next;
730               temp_free_atom = temp_free_atom->next;
731               
732               mem_area->free += rmem_chunk->atom_size;
733               if (mem_area->free == rmem_chunk->area_size)
734                 {
735                   rmem_chunk->num_mem_areas -= 1;
736                   rmem_chunk->num_marked_areas -= 1;
737                   
738                   if (mem_area->next)
739                     mem_area->next->prev = mem_area->prev;
740                   if (mem_area->prev)
741                     mem_area->prev->next = mem_area->next;
742                   if (mem_area == rmem_chunk->mem_areas)
743                     rmem_chunk->mem_areas = rmem_chunk->mem_areas->next;
744                   if (mem_area == rmem_chunk->mem_area)
745                     rmem_chunk->mem_area = NULL;
746                   
747                   if (rmem_chunk->type == G_ALLOC_AND_FREE)
748                     g_tree_remove (rmem_chunk->mem_tree, mem_area);
749                   g_free (mem_area);
750                 }
751             }
752           else
753             {
754               prev_free_atom = temp_free_atom;
755               temp_free_atom = temp_free_atom->next;
756             }
757         }
758     }
759 }
760
761 void
762 g_mem_chunk_reset (GMemChunk *mem_chunk)
763 {
764   GRealMemChunk *rmem_chunk;
765   GMemArea *mem_areas;
766   GMemArea *temp_area;
767   
768   g_assert (mem_chunk != NULL);
769   
770   rmem_chunk = (GRealMemChunk*) mem_chunk;
771   
772   mem_areas = rmem_chunk->mem_areas;
773   rmem_chunk->num_mem_areas = 0;
774   rmem_chunk->mem_areas = NULL;
775   rmem_chunk->mem_area = NULL;
776   
777   while (mem_areas)
778     {
779       temp_area = mem_areas;
780       mem_areas = mem_areas->next;
781       g_free (temp_area);
782     }
783   
784   rmem_chunk->free_atoms = NULL;
785   
786   if (rmem_chunk->mem_tree)
787     g_tree_destroy (rmem_chunk->mem_tree);
788   rmem_chunk->mem_tree = g_tree_new ((GCompareFunc) g_mem_chunk_area_compare);
789 }
790
791 void
792 g_mem_chunk_print (GMemChunk *mem_chunk)
793 {
794   GRealMemChunk *rmem_chunk;
795   GMemArea *mem_areas;
796   gulong mem;
797   
798   g_assert (mem_chunk != NULL);
799   
800   rmem_chunk = (GRealMemChunk*) mem_chunk;
801   mem_areas = rmem_chunk->mem_areas;
802   mem = 0;
803   
804   while (mem_areas)
805     {
806       mem += rmem_chunk->area_size - mem_areas->free;
807       mem_areas = mem_areas->next;
808     }
809   
810   g_log (g_log_domain_glib, G_LOG_LEVEL_INFO,
811          "%s: %ld bytes using %d mem areas\n",
812          rmem_chunk->name, mem, rmem_chunk->num_mem_areas);
813 }
814
815 void
816 g_mem_chunk_info (void)
817 {
818   GRealMemChunk *mem_chunk;
819   gint count;
820   
821   count = 0;
822   mem_chunk = mem_chunks;
823   while (mem_chunk)
824     {
825       count += 1;
826       mem_chunk = mem_chunk->next;
827     }
828   
829   g_log (g_log_domain_glib, G_LOG_LEVEL_INFO, "%d mem chunks\n", count);
830   
831   mem_chunk = mem_chunks;
832   while (mem_chunk)
833     {
834       g_mem_chunk_print ((GMemChunk*) mem_chunk);
835       mem_chunk = mem_chunk->next;
836     }
837 }
838
839 void
840 g_blow_chunks (void)
841 {
842   GRealMemChunk *mem_chunk;
843   
844   mem_chunk = mem_chunks;
845   while (mem_chunk)
846     {
847       g_mem_chunk_clean ((GMemChunk*) mem_chunk);
848       mem_chunk = mem_chunk->next;
849     }
850 }
851
852
853 static gulong
854 g_mem_chunk_compute_size (gulong size)
855 {
856   gulong power_of_2;
857   gulong lower, upper;
858   
859   power_of_2 = 16;
860   while (power_of_2 < size)
861     power_of_2 <<= 1;
862   
863   lower = power_of_2 >> 1;
864   upper = power_of_2;
865   
866   if ((size - lower) < (upper - size))
867     return lower;
868   return upper;
869 }
870
871 static gint
872 g_mem_chunk_area_compare (GMemArea *a,
873                           GMemArea *b)
874 {
875   return (a->mem - b->mem);
876 }
877
878 static gint
879 g_mem_chunk_area_search (GMemArea *a,
880                          gchar    *addr)
881 {
882   if (a->mem <= addr)
883     {
884       if (addr < &a->mem[a->index])
885         return 0;
886       return 1;
887     }
888   return -1;
889 }