53ccb152e26eb8d49ad424a9d45bd72adb1da16e
[platform/upstream/glib.git] / 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 Lesser 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  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser 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-2000.  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_private_set (allocating_for_mem_chunk, \
64                 g_private_get (allocating_for_mem_chunk) + 1)
65 #define LEAVE_MEM_CHUNK_ROUTINE() \
66   g_private_set (allocating_for_mem_chunk, \
67                 g_private_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 MEM_AREA_SIZE 4L
75
76 #if SIZEOF_VOID_P > SIZEOF_LONG
77 #define MEM_ALIGN     SIZEOF_VOID_P
78 #else
79 #define MEM_ALIGN     SIZEOF_LONG
80 #endif
81
82
83 typedef struct _GFreeAtom      GFreeAtom;
84 typedef struct _GMemArea       GMemArea;
85 typedef struct _GRealMemChunk  GRealMemChunk;
86
87 struct _GFreeAtom
88 {
89   GFreeAtom *next;
90 };
91
92 struct _GMemArea
93 {
94   GMemArea *next;            /* the next mem area */
95   GMemArea *prev;            /* the previous mem area */
96   gulong index;              /* the current index into the "mem" array */
97   gulong free;               /* the number of free bytes in this mem area */
98   gulong allocated;          /* the number of atoms allocated from this area */
99   gulong mark;               /* is this mem area marked for deletion */
100   gchar mem[MEM_AREA_SIZE];  /* the mem array from which atoms get allocated
101                               * the actual size of this array is determined by
102                               *  the mem chunk "area_size". ANSI says that it
103                               *  must be declared to be the maximum size it
104                               *  can possibly be (even though the actual size
105                               *  may be less).
106                               */
107 };
108
109 struct _GRealMemChunk
110 {
111   gchar *name;               /* name of this MemChunk...used for debugging output */
112   gint type;                 /* the type of MemChunk: ALLOC_ONLY or ALLOC_AND_FREE */
113   gint num_mem_areas;        /* the number of memory areas */
114   gint num_marked_areas;     /* the number of areas marked for deletion */
115   guint atom_size;           /* the size of an atom */
116   gulong area_size;          /* the size of a memory area */
117   GMemArea *mem_area;        /* the current memory area */
118   GMemArea *mem_areas;       /* a list of all the mem areas owned by this chunk */
119   GMemArea *free_mem_area;   /* the free area...which is about to be destroyed */
120   GFreeAtom *free_atoms;     /* the free atoms list */
121   GTree *mem_tree;           /* tree of mem areas sorted by memory address */
122   GRealMemChunk *next;       /* pointer to the next chunk */
123   GRealMemChunk *prev;       /* pointer to the previous chunk */
124 };
125
126
127 static gulong g_mem_chunk_compute_size (gulong    size,
128                                         gulong    min_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_private_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     {
313 #ifdef REALLOC_0_WORKS
314       p = (gpointer) realloc (NULL, size);
315 #else /* !REALLOC_0_WORKS */
316       p = (gpointer) malloc (size);
317 #endif /* !REALLOC_0_WORKS */
318     }
319   else
320     {
321 #if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK)
322       t = (gulong*) ((guchar*) mem - SIZEOF_LONG);
323 #ifdef ENABLE_MEM_PROFILE
324       g_mutex_lock (mem_profile_lock);
325       freed_mem += *t;
326       g_mutex_unlock (mem_profile_lock);
327 #endif /* ENABLE_MEM_PROFILE */
328       mem = t;
329 #endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */
330       
331 #ifdef ENABLE_MEM_CHECK
332       t = (gulong*) ((guchar*) mem - SIZEOF_LONG);
333       if (*t >= 1)
334         g_warning ("trying to realloc freed memory\n");
335       mem = t;
336 #endif /* ENABLE_MEM_CHECK */
337       
338       p = (gpointer) realloc (mem, size);
339     }
340   
341   if (!p)
342     g_error ("could not reallocate %lu bytes", (gulong) size);
343   
344   
345 #ifdef ENABLE_MEM_CHECK
346   size -= SIZEOF_LONG;
347   
348   t = p;
349   p = ((guchar*) p + SIZEOF_LONG);
350   *t = 0;
351 #endif /* ENABLE_MEM_CHECK */
352   
353 #if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK)
354   size -= SIZEOF_LONG;
355   
356   t = p;
357   p = ((guchar*) p + SIZEOF_LONG);
358   *t = size;
359   
360 #ifdef ENABLE_MEM_PROFILE
361   g_mutex_lock (mem_profile_lock);
362 #ifdef ENABLE_MEM_PROFILE_EXCLUDES_MEM_CHUNKS
363   if(!IS_IN_MEM_CHUNK_ROUTINE()) {
364 #endif
365     if (size <= (MEM_PROFILE_TABLE_SIZE - 1))
366       allocations[size-1] += 1;
367     else
368       allocations[MEM_PROFILE_TABLE_SIZE - 1] += 1;
369     allocated_mem += size;
370 #ifdef ENABLE_MEM_PROFILE_EXCLUDES_MEM_CHUNKS
371   }
372 #endif
373   g_mutex_unlock (mem_profile_lock);
374 #endif /* ENABLE_MEM_PROFILE */
375 #endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */
376   
377   
378   return p;
379 }
380
381 void
382 g_free (gpointer mem)
383 {
384   if (mem)
385     {
386 #if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK)
387       gulong *t;
388       gulong size;
389 #endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */
390       
391 #if defined(ENABLE_MEM_PROFILE) || defined(ENABLE_MEM_CHECK)
392       t = (gulong*) ((guchar*) mem - SIZEOF_LONG);
393       size = *t;
394 #ifdef ENABLE_MEM_PROFILE     
395       g_mutex_lock (mem_profile_lock);
396       freed_mem += size;
397       g_mutex_unlock (mem_profile_lock);
398 #endif /* ENABLE_MEM_PROFILE */
399       mem = t;
400 #endif /* ENABLE_MEM_PROFILE || ENABLE_MEM_CHECK */
401       
402 #ifdef ENABLE_MEM_CHECK
403       t = (gulong*) ((guchar*) mem - SIZEOF_LONG);
404       if (*t >= 1)
405         g_warning ("freeing previously freed (%lu times) memory\n", *t);
406       *t += 1;
407       mem = t;
408       
409       memset ((guchar*) mem + 2 * SIZEOF_LONG, 0, size);
410 #else /* ENABLE_MEM_CHECK */
411       free (mem);
412 #endif /* ENABLE_MEM_CHECK */
413     }
414 }
415
416 #endif /* ! USE_DMALLOC */
417
418
419 void
420 g_mem_profile (void)
421 {
422 #ifdef ENABLE_MEM_PROFILE
423   gint i;
424   gulong local_allocations[MEM_PROFILE_TABLE_SIZE];
425   gulong local_allocated_mem;
426   gulong local_freed_mem;  
427
428   g_mutex_lock (mem_profile_lock);
429   for (i = 0; i < MEM_PROFILE_TABLE_SIZE; i++)
430     local_allocations[i] = allocations[i];
431   local_allocated_mem = allocated_mem;
432   local_freed_mem = freed_mem;
433   g_mutex_unlock (mem_profile_lock);
434
435   for (i = 0; i < (MEM_PROFILE_TABLE_SIZE - 1); i++)
436     if (local_allocations[i] > 0)
437       g_log (g_log_domain_glib, G_LOG_LEVEL_INFO,
438              "%lu allocations of %d bytes", local_allocations[i], i + 1);
439   
440   if (local_allocations[MEM_PROFILE_TABLE_SIZE - 1] > 0)
441     g_log (g_log_domain_glib, G_LOG_LEVEL_INFO,
442            "%lu allocations of greater than %d bytes",
443            local_allocations[MEM_PROFILE_TABLE_SIZE - 1], MEM_PROFILE_TABLE_SIZE - 1);
444   g_log (g_log_domain_glib, G_LOG_LEVEL_INFO, "%lu bytes allocated", local_allocated_mem);
445   g_log (g_log_domain_glib, G_LOG_LEVEL_INFO, "%lu bytes freed", local_freed_mem);
446   g_log (g_log_domain_glib, G_LOG_LEVEL_INFO, "%lu bytes in use", local_allocated_mem - local_freed_mem);
447 #endif /* ENABLE_MEM_PROFILE */
448 }
449
450 void
451 g_mem_check (gpointer mem)
452 {
453 #ifdef ENABLE_MEM_CHECK
454   gulong *t;
455   
456   t = (gulong*) ((guchar*) mem - SIZEOF_LONG - SIZEOF_LONG);
457   
458   if (*t >= 1)
459     g_warning ("mem: 0x%08lx has been freed %lu times\n", (gulong) mem, *t);
460 #endif /* ENABLE_MEM_CHECK */
461 }
462
463 GMemChunk*
464 g_mem_chunk_new (gchar  *name,
465                  gint    atom_size,
466                  gulong  area_size,
467                  gint    type)
468 {
469   GRealMemChunk *mem_chunk;
470   gulong rarea_size;
471
472   g_return_val_if_fail (atom_size > 0, NULL);
473   g_return_val_if_fail (area_size >= atom_size, NULL);
474
475   ENTER_MEM_CHUNK_ROUTINE();
476
477   area_size = (area_size + atom_size - 1) / atom_size;
478   area_size *= atom_size;
479
480   mem_chunk = g_new (struct _GRealMemChunk, 1);
481   mem_chunk->name = name;
482   mem_chunk->type = type;
483   mem_chunk->num_mem_areas = 0;
484   mem_chunk->num_marked_areas = 0;
485   mem_chunk->mem_area = NULL;
486   mem_chunk->free_mem_area = NULL;
487   mem_chunk->free_atoms = NULL;
488   mem_chunk->mem_tree = NULL;
489   mem_chunk->mem_areas = NULL;
490   mem_chunk->atom_size = atom_size;
491   
492   if (mem_chunk->type == G_ALLOC_AND_FREE)
493     mem_chunk->mem_tree = g_tree_new ((GCompareFunc) g_mem_chunk_area_compare);
494   
495   if (mem_chunk->atom_size % MEM_ALIGN)
496     mem_chunk->atom_size += MEM_ALIGN - (mem_chunk->atom_size % MEM_ALIGN);
497
498   rarea_size = area_size + sizeof (GMemArea) - MEM_AREA_SIZE;
499   rarea_size = g_mem_chunk_compute_size (rarea_size, atom_size + sizeof (GMemArea) - MEM_AREA_SIZE);
500   mem_chunk->area_size = rarea_size - (sizeof (GMemArea) - MEM_AREA_SIZE);
501
502   g_mutex_lock (mem_chunks_lock);
503   mem_chunk->next = mem_chunks;
504   mem_chunk->prev = NULL;
505   if (mem_chunks)
506     mem_chunks->prev = mem_chunk;
507   mem_chunks = mem_chunk;
508   g_mutex_unlock (mem_chunks_lock);
509
510   LEAVE_MEM_CHUNK_ROUTINE();
511
512   return ((GMemChunk*) mem_chunk);
513 }
514
515 void
516 g_mem_chunk_destroy (GMemChunk *mem_chunk)
517 {
518   GRealMemChunk *rmem_chunk;
519   GMemArea *mem_areas;
520   GMemArea *temp_area;
521   
522   g_return_if_fail (mem_chunk != NULL);
523
524   ENTER_MEM_CHUNK_ROUTINE();
525
526   rmem_chunk = (GRealMemChunk*) mem_chunk;
527   
528   mem_areas = rmem_chunk->mem_areas;
529   while (mem_areas)
530     {
531       temp_area = mem_areas;
532       mem_areas = mem_areas->next;
533       g_free (temp_area);
534     }
535   
536   if (rmem_chunk->next)
537     rmem_chunk->next->prev = rmem_chunk->prev;
538   if (rmem_chunk->prev)
539     rmem_chunk->prev->next = rmem_chunk->next;
540   
541   g_mutex_lock (mem_chunks_lock);
542   if (rmem_chunk == mem_chunks)
543     mem_chunks = mem_chunks->next;
544   g_mutex_unlock (mem_chunks_lock);
545   
546   if (rmem_chunk->type == G_ALLOC_AND_FREE)
547     g_tree_destroy (rmem_chunk->mem_tree);
548   
549   g_free (rmem_chunk);
550
551   LEAVE_MEM_CHUNK_ROUTINE();
552 }
553
554 gpointer
555 g_mem_chunk_alloc (GMemChunk *mem_chunk)
556 {
557   GRealMemChunk *rmem_chunk;
558   GMemArea *temp_area;
559   gpointer mem;
560
561   ENTER_MEM_CHUNK_ROUTINE();
562
563   g_return_val_if_fail (mem_chunk != NULL, NULL);
564   
565   rmem_chunk = (GRealMemChunk*) mem_chunk;
566   
567   while (rmem_chunk->free_atoms)
568     {
569       /* Get the first piece of memory on the "free_atoms" list.
570        * We can go ahead and destroy the list node we used to keep
571        *  track of it with and to update the "free_atoms" list to
572        *  point to its next element.
573        */
574       mem = rmem_chunk->free_atoms;
575       rmem_chunk->free_atoms = rmem_chunk->free_atoms->next;
576       
577       /* Determine which area this piece of memory is allocated from */
578       temp_area = g_tree_search (rmem_chunk->mem_tree,
579                                  (GCompareFunc) g_mem_chunk_area_search,
580                                  mem);
581       
582       /* If the area has been marked, then it is being destroyed.
583        *  (ie marked to be destroyed).
584        * We check to see if all of the segments on the free list that
585        *  reference this area have been removed. This occurs when
586        *  the ammount of free memory is less than the allocatable size.
587        * If the chunk should be freed, then we place it in the "free_mem_area".
588        * This is so we make sure not to free the mem area here and then
589        *  allocate it again a few lines down.
590        * If we don't allocate a chunk a few lines down then the "free_mem_area"
591        *  will be freed.
592        * If there is already a "free_mem_area" then we'll just free this mem area.
593        */
594       if (temp_area->mark)
595         {
596           /* Update the "free" memory available in that area */
597           temp_area->free += rmem_chunk->atom_size;
598           
599           if (temp_area->free == rmem_chunk->area_size)
600             {
601               if (temp_area == rmem_chunk->mem_area)
602                 rmem_chunk->mem_area = NULL;
603               
604               if (rmem_chunk->free_mem_area)
605                 {
606                   rmem_chunk->num_mem_areas -= 1;
607                   
608                   if (temp_area->next)
609                     temp_area->next->prev = temp_area->prev;
610                   if (temp_area->prev)
611                     temp_area->prev->next = temp_area->next;
612                   if (temp_area == rmem_chunk->mem_areas)
613                     rmem_chunk->mem_areas = rmem_chunk->mem_areas->next;
614                   
615                   if (rmem_chunk->type == G_ALLOC_AND_FREE)
616                     g_tree_remove (rmem_chunk->mem_tree, temp_area);
617                   g_free (temp_area);
618                 }
619               else
620                 rmem_chunk->free_mem_area = temp_area;
621               
622               rmem_chunk->num_marked_areas -= 1;
623             }
624         }
625       else
626         {
627           /* Update the number of allocated atoms count.
628            */
629           temp_area->allocated += 1;
630           
631           /* The area wasn't marked...return the memory
632            */
633           goto outa_here;
634         }
635     }
636   
637   /* If there isn't a current mem area or the current mem area is out of space
638    *  then allocate a new mem area. We'll first check and see if we can use
639    *  the "free_mem_area". Otherwise we'll just malloc the mem area.
640    */
641   if ((!rmem_chunk->mem_area) ||
642       ((rmem_chunk->mem_area->index + rmem_chunk->atom_size) > rmem_chunk->area_size))
643     {
644       if (rmem_chunk->free_mem_area)
645         {
646           rmem_chunk->mem_area = rmem_chunk->free_mem_area;
647           rmem_chunk->free_mem_area = NULL;
648         }
649       else
650         {
651 #ifdef ENABLE_GC_FRIENDLY
652           rmem_chunk->mem_area = (GMemArea*) g_malloc0 (sizeof (GMemArea) -
653                                                         MEM_AREA_SIZE +
654                                                         rmem_chunk->area_size); 
655 #else /* !ENABLE_GC_FRIENDLY */
656           rmem_chunk->mem_area = (GMemArea*) g_malloc (sizeof (GMemArea) -
657                                                        MEM_AREA_SIZE +
658                                                        rmem_chunk->area_size);
659 #endif /* ENABLE_GC_FRIENDLY */
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_return_if_fail (mem_chunk != NULL);
718   g_return_if_fail (mem != NULL);
719
720   ENTER_MEM_CHUNK_ROUTINE();
721
722   rmem_chunk = (GRealMemChunk*) mem_chunk;
723   
724 #ifdef ENABLE_GC_FRIENDLY
725   memset (mem, 0, rmem_chunk->atom_size);
726 #endif /* ENABLE_GC_FRIENDLY */
727
728   /* Don't do anything if this is an ALLOC_ONLY chunk
729    */
730   if (rmem_chunk->type == G_ALLOC_AND_FREE)
731     {
732       /* Place the memory on the "free_atoms" list
733        */
734       free_atom = (GFreeAtom*) mem;
735       free_atom->next = rmem_chunk->free_atoms;
736       rmem_chunk->free_atoms = free_atom;
737       
738       temp_area = g_tree_search (rmem_chunk->mem_tree,
739                                  (GCompareFunc) g_mem_chunk_area_search,
740                                  mem);
741       
742       temp_area->allocated -= 1;
743       
744       if (temp_area->allocated == 0)
745         {
746           temp_area->mark = 1;
747           rmem_chunk->num_marked_areas += 1;
748         }
749     }
750
751   LEAVE_MEM_CHUNK_ROUTINE();
752 }
753
754 /* This doesn't free the free_area if there is one */
755 void
756 g_mem_chunk_clean (GMemChunk *mem_chunk)
757 {
758   GRealMemChunk *rmem_chunk;
759   GMemArea *mem_area;
760   GFreeAtom *prev_free_atom;
761   GFreeAtom *temp_free_atom;
762   gpointer mem;
763   
764   g_return_if_fail (mem_chunk != NULL);
765   
766   rmem_chunk = (GRealMemChunk*) mem_chunk;
767   
768   if (rmem_chunk->type == G_ALLOC_AND_FREE)
769     {
770       prev_free_atom = NULL;
771       temp_free_atom = rmem_chunk->free_atoms;
772       
773       while (temp_free_atom)
774         {
775           mem = (gpointer) temp_free_atom;
776           
777           mem_area = g_tree_search (rmem_chunk->mem_tree,
778                                     (GCompareFunc) g_mem_chunk_area_search,
779                                     mem);
780           
781           /* If this mem area is marked for destruction then delete the
782            *  area and list node and decrement the free mem.
783            */
784           if (mem_area->mark)
785             {
786               if (prev_free_atom)
787                 prev_free_atom->next = temp_free_atom->next;
788               else
789                 rmem_chunk->free_atoms = temp_free_atom->next;
790               temp_free_atom = temp_free_atom->next;
791               
792               mem_area->free += rmem_chunk->atom_size;
793               if (mem_area->free == rmem_chunk->area_size)
794                 {
795                   rmem_chunk->num_mem_areas -= 1;
796                   rmem_chunk->num_marked_areas -= 1;
797                   
798                   if (mem_area->next)
799                     mem_area->next->prev = mem_area->prev;
800                   if (mem_area->prev)
801                     mem_area->prev->next = mem_area->next;
802                   if (mem_area == rmem_chunk->mem_areas)
803                     rmem_chunk->mem_areas = rmem_chunk->mem_areas->next;
804                   if (mem_area == rmem_chunk->mem_area)
805                     rmem_chunk->mem_area = NULL;
806                   
807                   if (rmem_chunk->type == G_ALLOC_AND_FREE)
808                     g_tree_remove (rmem_chunk->mem_tree, mem_area);
809                   g_free (mem_area);
810                 }
811             }
812           else
813             {
814               prev_free_atom = temp_free_atom;
815               temp_free_atom = temp_free_atom->next;
816             }
817         }
818     }
819 }
820
821 void
822 g_mem_chunk_reset (GMemChunk *mem_chunk)
823 {
824   GRealMemChunk *rmem_chunk;
825   GMemArea *mem_areas;
826   GMemArea *temp_area;
827   
828   g_return_if_fail (mem_chunk != NULL);
829   
830   rmem_chunk = (GRealMemChunk*) mem_chunk;
831   
832   mem_areas = rmem_chunk->mem_areas;
833   rmem_chunk->num_mem_areas = 0;
834   rmem_chunk->mem_areas = NULL;
835   rmem_chunk->mem_area = NULL;
836   
837   while (mem_areas)
838     {
839       temp_area = mem_areas;
840       mem_areas = mem_areas->next;
841       g_free (temp_area);
842     }
843   
844   rmem_chunk->free_atoms = NULL;
845   
846   if (rmem_chunk->mem_tree)
847     g_tree_destroy (rmem_chunk->mem_tree);
848   rmem_chunk->mem_tree = g_tree_new ((GCompareFunc) g_mem_chunk_area_compare);
849 }
850
851 void
852 g_mem_chunk_print (GMemChunk *mem_chunk)
853 {
854   GRealMemChunk *rmem_chunk;
855   GMemArea *mem_areas;
856   gulong mem;
857   
858   g_return_if_fail (mem_chunk != NULL);
859   
860   rmem_chunk = (GRealMemChunk*) mem_chunk;
861   mem_areas = rmem_chunk->mem_areas;
862   mem = 0;
863   
864   while (mem_areas)
865     {
866       mem += rmem_chunk->area_size - mem_areas->free;
867       mem_areas = mem_areas->next;
868     }
869   
870   g_log (g_log_domain_glib, G_LOG_LEVEL_INFO,
871          "%s: %ld bytes using %d mem areas",
872          rmem_chunk->name, mem, rmem_chunk->num_mem_areas);
873 }
874
875 void
876 g_mem_chunk_info (void)
877 {
878   GRealMemChunk *mem_chunk;
879   gint count;
880   
881   count = 0;
882   g_mutex_lock (mem_chunks_lock);
883   mem_chunk = mem_chunks;
884   while (mem_chunk)
885     {
886       count += 1;
887       mem_chunk = mem_chunk->next;
888     }
889   g_mutex_unlock (mem_chunks_lock);
890   
891   g_log (g_log_domain_glib, G_LOG_LEVEL_INFO, "%d mem chunks", count);
892   
893   g_mutex_lock (mem_chunks_lock);
894   mem_chunk = mem_chunks;
895   g_mutex_unlock (mem_chunks_lock);
896
897   while (mem_chunk)
898     {
899       g_mem_chunk_print ((GMemChunk*) mem_chunk);
900       mem_chunk = mem_chunk->next;
901     }  
902 }
903
904 void
905 g_blow_chunks (void)
906 {
907   GRealMemChunk *mem_chunk;
908   
909   g_mutex_lock (mem_chunks_lock);
910   mem_chunk = mem_chunks;
911   g_mutex_unlock (mem_chunks_lock);
912   while (mem_chunk)
913     {
914       g_mem_chunk_clean ((GMemChunk*) mem_chunk);
915       mem_chunk = mem_chunk->next;
916     }
917 }
918
919
920 static gulong
921 g_mem_chunk_compute_size (gulong size,
922                           gulong min_size)
923 {
924   gulong power_of_2;
925   gulong lower, upper;
926   
927   power_of_2 = 16;
928   while (power_of_2 < size)
929     power_of_2 <<= 1;
930   
931   lower = power_of_2 >> 1;
932   upper = power_of_2;
933   
934   if (size - lower < upper - size && lower >= min_size)
935     return lower;
936   else
937     return upper;
938 }
939
940 static gint
941 g_mem_chunk_area_compare (GMemArea *a,
942                           GMemArea *b)
943 {
944   if (a->mem > b->mem)
945     return 1;
946   else if (a->mem < b->mem)
947     return -1;
948   return 0;
949 }
950
951 static gint
952 g_mem_chunk_area_search (GMemArea *a,
953                          gchar    *addr)
954 {
955   if (a->mem <= addr)
956     {
957       if (addr < &a->mem[a->index])
958         return 0;
959       return 1;
960     }
961   return -1;
962 }
963
964 /* generic allocators
965  */
966 struct _GAllocator /* from gmem.c */
967 {
968   gchar         *name;
969   guint16        n_preallocs;
970   guint          is_unused : 1;
971   guint          type : 4;
972   GAllocator    *last;
973   GMemChunk     *mem_chunk;
974   gpointer       dummy; /* implementation specific */
975 };
976
977 GAllocator*
978 g_allocator_new (const gchar *name,
979                  guint        n_preallocs)
980 {
981   GAllocator *allocator;
982
983   g_return_val_if_fail (name != NULL, NULL);
984
985   allocator = g_new0 (GAllocator, 1);
986   allocator->name = g_strdup (name);
987   allocator->n_preallocs = CLAMP (n_preallocs, 1, 65535);
988   allocator->is_unused = TRUE;
989   allocator->type = 0;
990   allocator->last = NULL;
991   allocator->mem_chunk = NULL;
992   allocator->dummy = NULL;
993
994   return allocator;
995 }
996
997 void
998 g_allocator_free (GAllocator *allocator)
999 {
1000   g_return_if_fail (allocator != NULL);
1001   g_return_if_fail (allocator->is_unused == TRUE);
1002
1003   g_free (allocator->name);
1004   if (allocator->mem_chunk)
1005     g_mem_chunk_destroy (allocator->mem_chunk);
1006
1007   g_free (allocator);
1008 }
1009
1010 void
1011 g_mem_init (void)
1012 {
1013   mem_chunks_lock = g_mutex_new();
1014 #ifdef ENABLE_MEM_PROFILE
1015   mem_profile_lock = g_mutex_new();
1016   allocating_for_mem_chunk = g_private_new(NULL);
1017 #endif
1018 }