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