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