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