Home | History | Annotate | Line # | Download | only in libiberty
objalloc.c revision 1.5
      1  1.1     skrll /* objalloc.c -- routines to allocate memory for objects
      2  1.5  christos    Copyright (C) 1997-2022 Free Software Foundation, Inc.
      3  1.1     skrll    Written by Ian Lance Taylor, Cygnus Solutions.
      4  1.1     skrll 
      5  1.1     skrll This program is free software; you can redistribute it and/or modify it
      6  1.1     skrll under the terms of the GNU General Public License as published by the
      7  1.1     skrll Free Software Foundation; either version 2, or (at your option) any
      8  1.1     skrll later version.
      9  1.1     skrll 
     10  1.1     skrll This program is distributed in the hope that it will be useful,
     11  1.1     skrll but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  1.1     skrll MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13  1.1     skrll GNU General Public License for more details.
     14  1.1     skrll 
     15  1.1     skrll You should have received a copy of the GNU General Public License
     16  1.1     skrll along with this program; if not, write to the Free Software
     17  1.1     skrll Foundation, 51 Franklin Street - Fifth Floor,
     18  1.1     skrll Boston, MA 02110-1301, USA.  */
     19  1.1     skrll 
     20  1.1     skrll #include "config.h"
     21  1.1     skrll #include "ansidecl.h"
     22  1.1     skrll 
     23  1.1     skrll #include "objalloc.h"
     24  1.1     skrll 
     25  1.1     skrll /* Get a definition for NULL.  */
     26  1.1     skrll #include <stdio.h>
     27  1.1     skrll 
     28  1.1     skrll #if VMS
     29  1.1     skrll #include <stdlib.h>
     30  1.1     skrll #include <unixlib.h>
     31  1.1     skrll #else
     32  1.1     skrll 
     33  1.1     skrll /* Get a definition for size_t.  */
     34  1.1     skrll #include <stddef.h>
     35  1.1     skrll 
     36  1.1     skrll #ifdef HAVE_STDLIB_H
     37  1.1     skrll #include <stdlib.h>
     38  1.1     skrll #else
     39  1.1     skrll /* For systems with larger pointers than ints, this must be declared.  */
     40  1.5  christos extern void *malloc (size_t);
     41  1.5  christos extern void free (void *);
     42  1.1     skrll #endif
     43  1.1     skrll 
     44  1.1     skrll #endif
     45  1.1     skrll 
     46  1.1     skrll /* These routines allocate space for an object.  Freeing allocated
     47  1.1     skrll    space may or may not free all more recently allocated space.
     48  1.1     skrll 
     49  1.1     skrll    We handle large and small allocation requests differently.  If we
     50  1.1     skrll    don't have enough space in the current block, and the allocation
     51  1.1     skrll    request is for more than 512 bytes, we simply pass it through to
     52  1.1     skrll    malloc.  */
     53  1.1     skrll 
     54  1.1     skrll /* The objalloc structure is defined in objalloc.h.  */
     55  1.1     skrll 
     56  1.1     skrll /* This structure appears at the start of each chunk.  */
     57  1.1     skrll 
     58  1.1     skrll struct objalloc_chunk
     59  1.1     skrll {
     60  1.1     skrll   /* Next chunk.  */
     61  1.1     skrll   struct objalloc_chunk *next;
     62  1.1     skrll   /* If this chunk contains large objects, this is the value of
     63  1.1     skrll      current_ptr when this chunk was allocated.  If this chunk
     64  1.1     skrll      contains small objects, this is NULL.  */
     65  1.1     skrll   char *current_ptr;
     66  1.1     skrll };
     67  1.1     skrll 
     68  1.1     skrll /* The aligned size of objalloc_chunk.  */
     69  1.1     skrll 
     70  1.1     skrll #define CHUNK_HEADER_SIZE					\
     71  1.1     skrll   ((sizeof (struct objalloc_chunk) + OBJALLOC_ALIGN - 1)	\
     72  1.1     skrll    &~ (OBJALLOC_ALIGN - 1))
     73  1.1     skrll 
     74  1.1     skrll /* We ask for this much memory each time we create a chunk which is to
     75  1.1     skrll    hold small objects.  */
     76  1.1     skrll 
     77  1.1     skrll #define CHUNK_SIZE (4096 - 32)
     78  1.1     skrll 
     79  1.1     skrll /* A request for this amount or more is just passed through to malloc.  */
     80  1.1     skrll 
     81  1.1     skrll #define BIG_REQUEST (512)
     82  1.1     skrll 
     83  1.1     skrll /* Create an objalloc structure.  */
     84  1.1     skrll 
     85  1.1     skrll struct objalloc *
     86  1.1     skrll objalloc_create (void)
     87  1.1     skrll {
     88  1.1     skrll   struct objalloc *ret;
     89  1.1     skrll   struct objalloc_chunk *chunk;
     90  1.1     skrll 
     91  1.1     skrll   ret = (struct objalloc *) malloc (sizeof *ret);
     92  1.1     skrll   if (ret == NULL)
     93  1.1     skrll     return NULL;
     94  1.1     skrll 
     95  1.5  christos   ret->chunks = (void *) malloc (CHUNK_SIZE);
     96  1.1     skrll   if (ret->chunks == NULL)
     97  1.1     skrll     {
     98  1.1     skrll       free (ret);
     99  1.1     skrll       return NULL;
    100  1.1     skrll     }
    101  1.1     skrll 
    102  1.1     skrll   chunk = (struct objalloc_chunk *) ret->chunks;
    103  1.1     skrll   chunk->next = NULL;
    104  1.1     skrll   chunk->current_ptr = NULL;
    105  1.1     skrll 
    106  1.1     skrll   ret->current_ptr = (char *) chunk + CHUNK_HEADER_SIZE;
    107  1.1     skrll   ret->current_space = CHUNK_SIZE - CHUNK_HEADER_SIZE;
    108  1.1     skrll 
    109  1.1     skrll   return ret;
    110  1.1     skrll }
    111  1.1     skrll 
    112  1.1     skrll /* Allocate space from an objalloc structure.  */
    113  1.1     skrll 
    114  1.5  christos void *
    115  1.2  drochner _objalloc_alloc (struct objalloc *o, unsigned long original_len)
    116  1.1     skrll {
    117  1.2  drochner   unsigned long len = original_len;
    118  1.2  drochner 
    119  1.1     skrll   /* We avoid confusion from zero sized objects by always allocating
    120  1.1     skrll      at least 1 byte.  */
    121  1.1     skrll   if (len == 0)
    122  1.1     skrll     len = 1;
    123  1.1     skrll 
    124  1.1     skrll   len = (len + OBJALLOC_ALIGN - 1) &~ (OBJALLOC_ALIGN - 1);
    125  1.1     skrll 
    126  1.2  drochner   /* Check for overflow in the alignment operation above and the
    127  1.2  drochner      malloc argument below. */
    128  1.2  drochner   if (len + CHUNK_HEADER_SIZE < original_len)
    129  1.2  drochner     return NULL;
    130  1.2  drochner 
    131  1.1     skrll   if (len <= o->current_space)
    132  1.1     skrll     {
    133  1.1     skrll       o->current_ptr += len;
    134  1.1     skrll       o->current_space -= len;
    135  1.5  christos       return (void *) (o->current_ptr - len);
    136  1.1     skrll     }
    137  1.1     skrll 
    138  1.1     skrll   if (len >= BIG_REQUEST)
    139  1.1     skrll     {
    140  1.1     skrll       char *ret;
    141  1.1     skrll       struct objalloc_chunk *chunk;
    142  1.1     skrll 
    143  1.1     skrll       ret = (char *) malloc (CHUNK_HEADER_SIZE + len);
    144  1.1     skrll       if (ret == NULL)
    145  1.1     skrll 	return NULL;
    146  1.1     skrll 
    147  1.1     skrll       chunk = (struct objalloc_chunk *) ret;
    148  1.1     skrll       chunk->next = (struct objalloc_chunk *) o->chunks;
    149  1.1     skrll       chunk->current_ptr = o->current_ptr;
    150  1.1     skrll 
    151  1.5  christos       o->chunks = (void *) chunk;
    152  1.1     skrll 
    153  1.5  christos       return (void *) (ret + CHUNK_HEADER_SIZE);
    154  1.1     skrll     }
    155  1.1     skrll   else
    156  1.1     skrll     {
    157  1.1     skrll       struct objalloc_chunk *chunk;
    158  1.1     skrll 
    159  1.1     skrll       chunk = (struct objalloc_chunk *) malloc (CHUNK_SIZE);
    160  1.1     skrll       if (chunk == NULL)
    161  1.1     skrll 	return NULL;
    162  1.1     skrll       chunk->next = (struct objalloc_chunk *) o->chunks;
    163  1.1     skrll       chunk->current_ptr = NULL;
    164  1.1     skrll 
    165  1.1     skrll       o->current_ptr = (char *) chunk + CHUNK_HEADER_SIZE;
    166  1.1     skrll       o->current_space = CHUNK_SIZE - CHUNK_HEADER_SIZE;
    167  1.1     skrll 
    168  1.5  christos       o->chunks = (void *) chunk;
    169  1.1     skrll 
    170  1.1     skrll       return objalloc_alloc (o, len);
    171  1.1     skrll     }
    172  1.1     skrll }
    173  1.1     skrll 
    174  1.1     skrll /* Free an entire objalloc structure.  */
    175  1.1     skrll 
    176  1.1     skrll void
    177  1.1     skrll objalloc_free (struct objalloc *o)
    178  1.1     skrll {
    179  1.1     skrll   struct objalloc_chunk *l;
    180  1.1     skrll 
    181  1.1     skrll   l = (struct objalloc_chunk *) o->chunks;
    182  1.1     skrll   while (l != NULL)
    183  1.1     skrll     {
    184  1.1     skrll       struct objalloc_chunk *next;
    185  1.1     skrll 
    186  1.1     skrll       next = l->next;
    187  1.1     skrll       free (l);
    188  1.1     skrll       l = next;
    189  1.1     skrll     }
    190  1.1     skrll 
    191  1.1     skrll   free (o);
    192  1.1     skrll }
    193  1.1     skrll 
    194  1.1     skrll /* Free a block from an objalloc structure.  This also frees all more
    195  1.1     skrll    recently allocated blocks.  */
    196  1.1     skrll 
    197  1.1     skrll void
    198  1.5  christos objalloc_free_block (struct objalloc *o, void *block)
    199  1.1     skrll {
    200  1.1     skrll   struct objalloc_chunk *p, *small;
    201  1.1     skrll   char *b = (char *) block;
    202  1.1     skrll 
    203  1.1     skrll   /* First set P to the chunk which contains the block we are freeing,
    204  1.1     skrll      and set Q to the last small object chunk we see before P.  */
    205  1.1     skrll   small = NULL;
    206  1.1     skrll   for (p = (struct objalloc_chunk *) o->chunks; p != NULL; p = p->next)
    207  1.1     skrll     {
    208  1.1     skrll       if (p->current_ptr == NULL)
    209  1.1     skrll 	{
    210  1.1     skrll 	  if (b > (char *) p && b < (char *) p + CHUNK_SIZE)
    211  1.1     skrll 	    break;
    212  1.1     skrll 	  small = p;
    213  1.1     skrll 	}
    214  1.1     skrll       else
    215  1.1     skrll 	{
    216  1.1     skrll 	  if (b == (char *) p + CHUNK_HEADER_SIZE)
    217  1.1     skrll 	    break;
    218  1.1     skrll 	}
    219  1.1     skrll     }
    220  1.1     skrll 
    221  1.1     skrll   /* If we can't find the chunk, the caller has made a mistake.  */
    222  1.1     skrll   if (p == NULL)
    223  1.1     skrll     abort ();
    224  1.1     skrll 
    225  1.1     skrll   if (p->current_ptr == NULL)
    226  1.1     skrll     {
    227  1.1     skrll       struct objalloc_chunk *q;
    228  1.1     skrll       struct objalloc_chunk *first;
    229  1.1     skrll 
    230  1.1     skrll       /* The block is in a chunk containing small objects.  We can
    231  1.1     skrll 	 free every chunk through SMALL, because they have certainly
    232  1.1     skrll 	 been allocated more recently.  After SMALL, we will not see
    233  1.1     skrll 	 any chunks containing small objects; we can free any big
    234  1.1     skrll 	 chunk if the current_ptr is greater than or equal to B.  We
    235  1.1     skrll 	 can then reset the new current_ptr to B.  */
    236  1.1     skrll 
    237  1.1     skrll       first = NULL;
    238  1.1     skrll       q = (struct objalloc_chunk *) o->chunks;
    239  1.1     skrll       while (q != p)
    240  1.1     skrll 	{
    241  1.1     skrll 	  struct objalloc_chunk *next;
    242  1.1     skrll 
    243  1.1     skrll 	  next = q->next;
    244  1.1     skrll 	  if (small != NULL)
    245  1.1     skrll 	    {
    246  1.1     skrll 	      if (small == q)
    247  1.1     skrll 		small = NULL;
    248  1.1     skrll 	      free (q);
    249  1.1     skrll 	    }
    250  1.1     skrll 	  else if (q->current_ptr > b)
    251  1.1     skrll 	    free (q);
    252  1.1     skrll 	  else if (first == NULL)
    253  1.1     skrll 	    first = q;
    254  1.1     skrll 
    255  1.1     skrll 	  q = next;
    256  1.1     skrll 	}
    257  1.1     skrll 
    258  1.1     skrll       if (first == NULL)
    259  1.1     skrll 	first = p;
    260  1.5  christos       o->chunks = (void *) first;
    261  1.1     skrll 
    262  1.1     skrll       /* Now start allocating from this small block again.  */
    263  1.1     skrll       o->current_ptr = b;
    264  1.1     skrll       o->current_space = ((char *) p + CHUNK_SIZE) - b;
    265  1.1     skrll     }
    266  1.1     skrll   else
    267  1.1     skrll     {
    268  1.1     skrll       struct objalloc_chunk *q;
    269  1.1     skrll       char *current_ptr;
    270  1.1     skrll 
    271  1.1     skrll       /* This block is in a large chunk by itself.  We can free
    272  1.1     skrll          everything on the list up to and including this block.  We
    273  1.1     skrll          then start allocating from the next chunk containing small
    274  1.1     skrll          objects, setting current_ptr from the value stored with the
    275  1.1     skrll          large chunk we are freeing.  */
    276  1.1     skrll 
    277  1.1     skrll       current_ptr = p->current_ptr;
    278  1.1     skrll       p = p->next;
    279  1.1     skrll 
    280  1.1     skrll       q = (struct objalloc_chunk *) o->chunks;
    281  1.1     skrll       while (q != p)
    282  1.1     skrll 	{
    283  1.1     skrll 	  struct objalloc_chunk *next;
    284  1.1     skrll 
    285  1.1     skrll 	  next = q->next;
    286  1.1     skrll 	  free (q);
    287  1.1     skrll 	  q = next;
    288  1.1     skrll 	}
    289  1.1     skrll 
    290  1.5  christos       o->chunks = (void *) p;
    291  1.1     skrll 
    292  1.1     skrll       while (p->current_ptr != NULL)
    293  1.1     skrll 	p = p->next;
    294  1.1     skrll 
    295  1.1     skrll       o->current_ptr = current_ptr;
    296  1.1     skrll       o->current_space = ((char *) p + CHUNK_SIZE) - current_ptr;
    297  1.1     skrll     }
    298  1.1     skrll }
    299