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