xref: /netbsd-src/external/gpl3/gdb/dist/libiberty/objalloc.c (revision 7e120ff03ede3fe64e2c8620c01465d528502ddb)
198b9484cSchristos /* objalloc.c -- routines to allocate memory for objects
2*7e120ff0Schristos    Copyright (C) 1997-2024 Free Software Foundation, Inc.
398b9484cSchristos    Written by Ian Lance Taylor, Cygnus Solutions.
498b9484cSchristos 
598b9484cSchristos This program is free software; you can redistribute it and/or modify it
698b9484cSchristos under the terms of the GNU General Public License as published by the
798b9484cSchristos Free Software Foundation; either version 2, or (at your option) any
898b9484cSchristos later version.
998b9484cSchristos 
1098b9484cSchristos This program is distributed in the hope that it will be useful,
1198b9484cSchristos but WITHOUT ANY WARRANTY; without even the implied warranty of
1298b9484cSchristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1398b9484cSchristos GNU General Public License for more details.
1498b9484cSchristos 
1598b9484cSchristos You should have received a copy of the GNU General Public License
1698b9484cSchristos along with this program; if not, write to the Free Software
1798b9484cSchristos Foundation, 51 Franklin Street - Fifth Floor,
1898b9484cSchristos Boston, MA 02110-1301, USA.  */
1998b9484cSchristos 
2098b9484cSchristos #include "config.h"
2198b9484cSchristos #include "ansidecl.h"
2298b9484cSchristos 
2398b9484cSchristos #include "objalloc.h"
2498b9484cSchristos 
2598b9484cSchristos /* Get a definition for NULL.  */
2698b9484cSchristos #include <stdio.h>
2798b9484cSchristos 
2898b9484cSchristos #if VMS
2998b9484cSchristos #include <stdlib.h>
3098b9484cSchristos #include <unixlib.h>
3198b9484cSchristos #else
3298b9484cSchristos 
3398b9484cSchristos /* Get a definition for size_t.  */
3498b9484cSchristos #include <stddef.h>
3598b9484cSchristos 
3698b9484cSchristos #ifdef HAVE_STDLIB_H
3798b9484cSchristos #include <stdlib.h>
3898b9484cSchristos #else
3998b9484cSchristos /* For systems with larger pointers than ints, this must be declared.  */
404b169a6bSchristos extern void *malloc (size_t);
414b169a6bSchristos extern void free (void *);
4298b9484cSchristos #endif
4398b9484cSchristos 
4498b9484cSchristos #endif
4598b9484cSchristos 
4698b9484cSchristos /* These routines allocate space for an object.  Freeing allocated
4798b9484cSchristos    space may or may not free all more recently allocated space.
4898b9484cSchristos 
4998b9484cSchristos    We handle large and small allocation requests differently.  If we
5098b9484cSchristos    don't have enough space in the current block, and the allocation
5198b9484cSchristos    request is for more than 512 bytes, we simply pass it through to
5298b9484cSchristos    malloc.  */
5398b9484cSchristos 
5498b9484cSchristos /* The objalloc structure is defined in objalloc.h.  */
5598b9484cSchristos 
5698b9484cSchristos /* This structure appears at the start of each chunk.  */
5798b9484cSchristos 
5898b9484cSchristos struct objalloc_chunk
5998b9484cSchristos {
6098b9484cSchristos   /* Next chunk.  */
6198b9484cSchristos   struct objalloc_chunk *next;
6298b9484cSchristos   /* If this chunk contains large objects, this is the value of
6398b9484cSchristos      current_ptr when this chunk was allocated.  If this chunk
6498b9484cSchristos      contains small objects, this is NULL.  */
6598b9484cSchristos   char *current_ptr;
6698b9484cSchristos };
6798b9484cSchristos 
6898b9484cSchristos /* The aligned size of objalloc_chunk.  */
6998b9484cSchristos 
7098b9484cSchristos #define CHUNK_HEADER_SIZE					\
7198b9484cSchristos   ((sizeof (struct objalloc_chunk) + OBJALLOC_ALIGN - 1)	\
7298b9484cSchristos    &~ (OBJALLOC_ALIGN - 1))
7398b9484cSchristos 
7498b9484cSchristos /* We ask for this much memory each time we create a chunk which is to
7598b9484cSchristos    hold small objects.  */
7698b9484cSchristos 
7798b9484cSchristos #define CHUNK_SIZE (4096 - 32)
7898b9484cSchristos 
7998b9484cSchristos /* A request for this amount or more is just passed through to malloc.  */
8098b9484cSchristos 
8198b9484cSchristos #define BIG_REQUEST (512)
8298b9484cSchristos 
8398b9484cSchristos /* Create an objalloc structure.  */
8498b9484cSchristos 
8598b9484cSchristos struct objalloc *
8698b9484cSchristos objalloc_create (void)
8798b9484cSchristos {
8898b9484cSchristos   struct objalloc *ret;
8998b9484cSchristos   struct objalloc_chunk *chunk;
9098b9484cSchristos 
9198b9484cSchristos   ret = (struct objalloc *) malloc (sizeof *ret);
9298b9484cSchristos   if (ret == NULL)
9398b9484cSchristos     return NULL;
9498b9484cSchristos 
954b169a6bSchristos   ret->chunks = (void *) malloc (CHUNK_SIZE);
9698b9484cSchristos   if (ret->chunks == NULL)
9798b9484cSchristos     {
9898b9484cSchristos       free (ret);
9998b9484cSchristos       return NULL;
10098b9484cSchristos     }
10198b9484cSchristos 
10298b9484cSchristos   chunk = (struct objalloc_chunk *) ret->chunks;
10398b9484cSchristos   chunk->next = NULL;
10498b9484cSchristos   chunk->current_ptr = NULL;
10598b9484cSchristos 
10698b9484cSchristos   ret->current_ptr = (char *) chunk + CHUNK_HEADER_SIZE;
10798b9484cSchristos   ret->current_space = CHUNK_SIZE - CHUNK_HEADER_SIZE;
10898b9484cSchristos 
10998b9484cSchristos   return ret;
11098b9484cSchristos }
11198b9484cSchristos 
11298b9484cSchristos /* Allocate space from an objalloc structure.  */
11398b9484cSchristos 
1144b169a6bSchristos void *
115a2e2270fSchristos _objalloc_alloc (struct objalloc *o, unsigned long original_len)
11698b9484cSchristos {
117a2e2270fSchristos   unsigned long len = original_len;
118a2e2270fSchristos 
11998b9484cSchristos   /* We avoid confusion from zero sized objects by always allocating
12098b9484cSchristos      at least 1 byte.  */
12198b9484cSchristos   if (len == 0)
12298b9484cSchristos     len = 1;
12398b9484cSchristos 
12498b9484cSchristos   len = (len + OBJALLOC_ALIGN - 1) &~ (OBJALLOC_ALIGN - 1);
12598b9484cSchristos 
126a2e2270fSchristos   /* Check for overflow in the alignment operation above and the
127a2e2270fSchristos      malloc argument below. */
128a2e2270fSchristos   if (len + CHUNK_HEADER_SIZE < original_len)
129a2e2270fSchristos     return NULL;
130a2e2270fSchristos 
13198b9484cSchristos   if (len <= o->current_space)
13298b9484cSchristos     {
13398b9484cSchristos       o->current_ptr += len;
13498b9484cSchristos       o->current_space -= len;
1354b169a6bSchristos       return (void *) (o->current_ptr - len);
13698b9484cSchristos     }
13798b9484cSchristos 
13898b9484cSchristos   if (len >= BIG_REQUEST)
13998b9484cSchristos     {
14098b9484cSchristos       char *ret;
14198b9484cSchristos       struct objalloc_chunk *chunk;
14298b9484cSchristos 
14398b9484cSchristos       ret = (char *) malloc (CHUNK_HEADER_SIZE + len);
14498b9484cSchristos       if (ret == NULL)
14598b9484cSchristos 	return NULL;
14698b9484cSchristos 
14798b9484cSchristos       chunk = (struct objalloc_chunk *) ret;
14898b9484cSchristos       chunk->next = (struct objalloc_chunk *) o->chunks;
14998b9484cSchristos       chunk->current_ptr = o->current_ptr;
15098b9484cSchristos 
1514b169a6bSchristos       o->chunks = (void *) chunk;
15298b9484cSchristos 
1534b169a6bSchristos       return (void *) (ret + CHUNK_HEADER_SIZE);
15498b9484cSchristos     }
15598b9484cSchristos   else
15698b9484cSchristos     {
15798b9484cSchristos       struct objalloc_chunk *chunk;
15898b9484cSchristos 
15998b9484cSchristos       chunk = (struct objalloc_chunk *) malloc (CHUNK_SIZE);
16098b9484cSchristos       if (chunk == NULL)
16198b9484cSchristos 	return NULL;
16298b9484cSchristos       chunk->next = (struct objalloc_chunk *) o->chunks;
16398b9484cSchristos       chunk->current_ptr = NULL;
16498b9484cSchristos 
16598b9484cSchristos       o->current_ptr = (char *) chunk + CHUNK_HEADER_SIZE;
16698b9484cSchristos       o->current_space = CHUNK_SIZE - CHUNK_HEADER_SIZE;
16798b9484cSchristos 
1684b169a6bSchristos       o->chunks = (void *) chunk;
16998b9484cSchristos 
17098b9484cSchristos       return objalloc_alloc (o, len);
17198b9484cSchristos     }
17298b9484cSchristos }
17398b9484cSchristos 
17498b9484cSchristos /* Free an entire objalloc structure.  */
17598b9484cSchristos 
17698b9484cSchristos void
17798b9484cSchristos objalloc_free (struct objalloc *o)
17898b9484cSchristos {
17998b9484cSchristos   struct objalloc_chunk *l;
18098b9484cSchristos 
18198b9484cSchristos   l = (struct objalloc_chunk *) o->chunks;
18298b9484cSchristos   while (l != NULL)
18398b9484cSchristos     {
18498b9484cSchristos       struct objalloc_chunk *next;
18598b9484cSchristos 
18698b9484cSchristos       next = l->next;
18798b9484cSchristos       free (l);
18898b9484cSchristos       l = next;
18998b9484cSchristos     }
19098b9484cSchristos 
19198b9484cSchristos   free (o);
19298b9484cSchristos }
19398b9484cSchristos 
19498b9484cSchristos /* Free a block from an objalloc structure.  This also frees all more
19598b9484cSchristos    recently allocated blocks.  */
19698b9484cSchristos 
19798b9484cSchristos void
1984b169a6bSchristos objalloc_free_block (struct objalloc *o, void *block)
19998b9484cSchristos {
20098b9484cSchristos   struct objalloc_chunk *p, *small;
20198b9484cSchristos   char *b = (char *) block;
20298b9484cSchristos 
20398b9484cSchristos   /* First set P to the chunk which contains the block we are freeing,
20498b9484cSchristos      and set Q to the last small object chunk we see before P.  */
20598b9484cSchristos   small = NULL;
20698b9484cSchristos   for (p = (struct objalloc_chunk *) o->chunks; p != NULL; p = p->next)
20798b9484cSchristos     {
20898b9484cSchristos       if (p->current_ptr == NULL)
20998b9484cSchristos 	{
21098b9484cSchristos 	  if (b > (char *) p && b < (char *) p + CHUNK_SIZE)
21198b9484cSchristos 	    break;
21298b9484cSchristos 	  small = p;
21398b9484cSchristos 	}
21498b9484cSchristos       else
21598b9484cSchristos 	{
21698b9484cSchristos 	  if (b == (char *) p + CHUNK_HEADER_SIZE)
21798b9484cSchristos 	    break;
21898b9484cSchristos 	}
21998b9484cSchristos     }
22098b9484cSchristos 
22198b9484cSchristos   /* If we can't find the chunk, the caller has made a mistake.  */
22298b9484cSchristos   if (p == NULL)
22398b9484cSchristos     abort ();
22498b9484cSchristos 
22598b9484cSchristos   if (p->current_ptr == NULL)
22698b9484cSchristos     {
22798b9484cSchristos       struct objalloc_chunk *q;
22898b9484cSchristos       struct objalloc_chunk *first;
22998b9484cSchristos 
23098b9484cSchristos       /* The block is in a chunk containing small objects.  We can
23198b9484cSchristos 	 free every chunk through SMALL, because they have certainly
23298b9484cSchristos 	 been allocated more recently.  After SMALL, we will not see
23398b9484cSchristos 	 any chunks containing small objects; we can free any big
23498b9484cSchristos 	 chunk if the current_ptr is greater than or equal to B.  We
23598b9484cSchristos 	 can then reset the new current_ptr to B.  */
23698b9484cSchristos 
23798b9484cSchristos       first = NULL;
23898b9484cSchristos       q = (struct objalloc_chunk *) o->chunks;
23998b9484cSchristos       while (q != p)
24098b9484cSchristos 	{
24198b9484cSchristos 	  struct objalloc_chunk *next;
24298b9484cSchristos 
24398b9484cSchristos 	  next = q->next;
24498b9484cSchristos 	  if (small != NULL)
24598b9484cSchristos 	    {
24698b9484cSchristos 	      if (small == q)
24798b9484cSchristos 		small = NULL;
24898b9484cSchristos 	      free (q);
24998b9484cSchristos 	    }
25098b9484cSchristos 	  else if (q->current_ptr > b)
25198b9484cSchristos 	    free (q);
25298b9484cSchristos 	  else if (first == NULL)
25398b9484cSchristos 	    first = q;
25498b9484cSchristos 
25598b9484cSchristos 	  q = next;
25698b9484cSchristos 	}
25798b9484cSchristos 
25898b9484cSchristos       if (first == NULL)
25998b9484cSchristos 	first = p;
2604b169a6bSchristos       o->chunks = (void *) first;
26198b9484cSchristos 
26298b9484cSchristos       /* Now start allocating from this small block again.  */
26398b9484cSchristos       o->current_ptr = b;
26498b9484cSchristos       o->current_space = ((char *) p + CHUNK_SIZE) - b;
26598b9484cSchristos     }
26698b9484cSchristos   else
26798b9484cSchristos     {
26898b9484cSchristos       struct objalloc_chunk *q;
26998b9484cSchristos       char *current_ptr;
27098b9484cSchristos 
27198b9484cSchristos       /* This block is in a large chunk by itself.  We can free
27298b9484cSchristos          everything on the list up to and including this block.  We
27398b9484cSchristos          then start allocating from the next chunk containing small
27498b9484cSchristos          objects, setting current_ptr from the value stored with the
27598b9484cSchristos          large chunk we are freeing.  */
27698b9484cSchristos 
27798b9484cSchristos       current_ptr = p->current_ptr;
27898b9484cSchristos       p = p->next;
27998b9484cSchristos 
28098b9484cSchristos       q = (struct objalloc_chunk *) o->chunks;
28198b9484cSchristos       while (q != p)
28298b9484cSchristos 	{
28398b9484cSchristos 	  struct objalloc_chunk *next;
28498b9484cSchristos 
28598b9484cSchristos 	  next = q->next;
28698b9484cSchristos 	  free (q);
28798b9484cSchristos 	  q = next;
28898b9484cSchristos 	}
28998b9484cSchristos 
2904b169a6bSchristos       o->chunks = (void *) p;
29198b9484cSchristos 
29298b9484cSchristos       while (p->current_ptr != NULL)
29398b9484cSchristos 	p = p->next;
29498b9484cSchristos 
29598b9484cSchristos       o->current_ptr = current_ptr;
29698b9484cSchristos       o->current_space = ((char *) p + CHUNK_SIZE) - current_ptr;
29798b9484cSchristos     }
29898b9484cSchristos }
299