14fee23f9Smrg /* objalloc.c -- routines to allocate memory for objects
2*e9e6e0f6Smrg Copyright (C) 1997-2022 Free Software Foundation, Inc.
34fee23f9Smrg Written by Ian Lance Taylor, Cygnus Solutions.
44fee23f9Smrg
54fee23f9Smrg This program is free software; you can redistribute it and/or modify it
64fee23f9Smrg under the terms of the GNU General Public License as published by the
74fee23f9Smrg Free Software Foundation; either version 2, or (at your option) any
84fee23f9Smrg later version.
94fee23f9Smrg
104fee23f9Smrg This program is distributed in the hope that it will be useful,
114fee23f9Smrg but WITHOUT ANY WARRANTY; without even the implied warranty of
124fee23f9Smrg MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
134fee23f9Smrg GNU General Public License for more details.
144fee23f9Smrg
154fee23f9Smrg You should have received a copy of the GNU General Public License
164fee23f9Smrg along with this program; if not, write to the Free Software
174fee23f9Smrg Foundation, 51 Franklin Street - Fifth Floor,
184fee23f9Smrg Boston, MA 02110-1301, USA. */
194fee23f9Smrg
204fee23f9Smrg #include "config.h"
214fee23f9Smrg #include "ansidecl.h"
224fee23f9Smrg
234fee23f9Smrg #include "objalloc.h"
244fee23f9Smrg
254fee23f9Smrg /* Get a definition for NULL. */
264fee23f9Smrg #include <stdio.h>
274fee23f9Smrg
284fee23f9Smrg #if VMS
294fee23f9Smrg #include <stdlib.h>
304fee23f9Smrg #include <unixlib.h>
314fee23f9Smrg #else
324fee23f9Smrg
334fee23f9Smrg /* Get a definition for size_t. */
344fee23f9Smrg #include <stddef.h>
354fee23f9Smrg
364fee23f9Smrg #ifdef HAVE_STDLIB_H
374fee23f9Smrg #include <stdlib.h>
384fee23f9Smrg #else
394fee23f9Smrg /* For systems with larger pointers than ints, this must be declared. */
404fee23f9Smrg extern PTR malloc (size_t);
414fee23f9Smrg extern void free (PTR);
424fee23f9Smrg #endif
434fee23f9Smrg
444fee23f9Smrg #endif
454fee23f9Smrg
464fee23f9Smrg /* These routines allocate space for an object. Freeing allocated
474fee23f9Smrg space may or may not free all more recently allocated space.
484fee23f9Smrg
494fee23f9Smrg We handle large and small allocation requests differently. If we
504fee23f9Smrg don't have enough space in the current block, and the allocation
514fee23f9Smrg request is for more than 512 bytes, we simply pass it through to
524fee23f9Smrg malloc. */
534fee23f9Smrg
544fee23f9Smrg /* The objalloc structure is defined in objalloc.h. */
554fee23f9Smrg
564fee23f9Smrg /* This structure appears at the start of each chunk. */
574fee23f9Smrg
584fee23f9Smrg struct objalloc_chunk
594fee23f9Smrg {
604fee23f9Smrg /* Next chunk. */
614fee23f9Smrg struct objalloc_chunk *next;
624fee23f9Smrg /* If this chunk contains large objects, this is the value of
634fee23f9Smrg current_ptr when this chunk was allocated. If this chunk
644fee23f9Smrg contains small objects, this is NULL. */
654fee23f9Smrg char *current_ptr;
664fee23f9Smrg };
674fee23f9Smrg
684fee23f9Smrg /* The aligned size of objalloc_chunk. */
694fee23f9Smrg
704fee23f9Smrg #define CHUNK_HEADER_SIZE \
714fee23f9Smrg ((sizeof (struct objalloc_chunk) + OBJALLOC_ALIGN - 1) \
724fee23f9Smrg &~ (OBJALLOC_ALIGN - 1))
734fee23f9Smrg
744fee23f9Smrg /* We ask for this much memory each time we create a chunk which is to
754fee23f9Smrg hold small objects. */
764fee23f9Smrg
774fee23f9Smrg #define CHUNK_SIZE (4096 - 32)
784fee23f9Smrg
794fee23f9Smrg /* A request for this amount or more is just passed through to malloc. */
804fee23f9Smrg
814fee23f9Smrg #define BIG_REQUEST (512)
824fee23f9Smrg
834fee23f9Smrg /* Create an objalloc structure. */
844fee23f9Smrg
854fee23f9Smrg struct objalloc *
objalloc_create(void)864fee23f9Smrg objalloc_create (void)
874fee23f9Smrg {
884fee23f9Smrg struct objalloc *ret;
894fee23f9Smrg struct objalloc_chunk *chunk;
904fee23f9Smrg
914fee23f9Smrg ret = (struct objalloc *) malloc (sizeof *ret);
924fee23f9Smrg if (ret == NULL)
934fee23f9Smrg return NULL;
944fee23f9Smrg
954fee23f9Smrg ret->chunks = (PTR) malloc (CHUNK_SIZE);
964fee23f9Smrg if (ret->chunks == NULL)
974fee23f9Smrg {
984fee23f9Smrg free (ret);
994fee23f9Smrg return NULL;
1004fee23f9Smrg }
1014fee23f9Smrg
1024fee23f9Smrg chunk = (struct objalloc_chunk *) ret->chunks;
1034fee23f9Smrg chunk->next = NULL;
1044fee23f9Smrg chunk->current_ptr = NULL;
1054fee23f9Smrg
1064fee23f9Smrg ret->current_ptr = (char *) chunk + CHUNK_HEADER_SIZE;
1074fee23f9Smrg ret->current_space = CHUNK_SIZE - CHUNK_HEADER_SIZE;
1084fee23f9Smrg
1094fee23f9Smrg return ret;
1104fee23f9Smrg }
1114fee23f9Smrg
1124fee23f9Smrg /* Allocate space from an objalloc structure. */
1134fee23f9Smrg
1144fee23f9Smrg PTR
_objalloc_alloc(struct objalloc * o,unsigned long original_len)115d18545deSdrochner _objalloc_alloc (struct objalloc *o, unsigned long original_len)
1164fee23f9Smrg {
117d18545deSdrochner unsigned long len = original_len;
118d18545deSdrochner
1194fee23f9Smrg /* We avoid confusion from zero sized objects by always allocating
1204fee23f9Smrg at least 1 byte. */
1214fee23f9Smrg if (len == 0)
1224fee23f9Smrg len = 1;
1234fee23f9Smrg
1244fee23f9Smrg len = (len + OBJALLOC_ALIGN - 1) &~ (OBJALLOC_ALIGN - 1);
1254fee23f9Smrg
126d18545deSdrochner /* Check for overflow in the alignment operation above and the
127d18545deSdrochner malloc argument below. */
128d18545deSdrochner if (len + CHUNK_HEADER_SIZE < original_len)
129d18545deSdrochner return NULL;
130d18545deSdrochner
1314fee23f9Smrg if (len <= o->current_space)
1324fee23f9Smrg {
1334fee23f9Smrg o->current_ptr += len;
1344fee23f9Smrg o->current_space -= len;
1354fee23f9Smrg return (PTR) (o->current_ptr - len);
1364fee23f9Smrg }
1374fee23f9Smrg
1384fee23f9Smrg if (len >= BIG_REQUEST)
1394fee23f9Smrg {
1404fee23f9Smrg char *ret;
1414fee23f9Smrg struct objalloc_chunk *chunk;
1424fee23f9Smrg
1434fee23f9Smrg ret = (char *) malloc (CHUNK_HEADER_SIZE + len);
1444fee23f9Smrg if (ret == NULL)
1454fee23f9Smrg return NULL;
1464fee23f9Smrg
1474fee23f9Smrg chunk = (struct objalloc_chunk *) ret;
1484fee23f9Smrg chunk->next = (struct objalloc_chunk *) o->chunks;
1494fee23f9Smrg chunk->current_ptr = o->current_ptr;
1504fee23f9Smrg
1514fee23f9Smrg o->chunks = (PTR) chunk;
1524fee23f9Smrg
1534fee23f9Smrg return (PTR) (ret + CHUNK_HEADER_SIZE);
1544fee23f9Smrg }
1554fee23f9Smrg else
1564fee23f9Smrg {
1574fee23f9Smrg struct objalloc_chunk *chunk;
1584fee23f9Smrg
1594fee23f9Smrg chunk = (struct objalloc_chunk *) malloc (CHUNK_SIZE);
1604fee23f9Smrg if (chunk == NULL)
1614fee23f9Smrg return NULL;
1624fee23f9Smrg chunk->next = (struct objalloc_chunk *) o->chunks;
1634fee23f9Smrg chunk->current_ptr = NULL;
1644fee23f9Smrg
1654fee23f9Smrg o->current_ptr = (char *) chunk + CHUNK_HEADER_SIZE;
1664fee23f9Smrg o->current_space = CHUNK_SIZE - CHUNK_HEADER_SIZE;
1674fee23f9Smrg
1684fee23f9Smrg o->chunks = (PTR) chunk;
1694fee23f9Smrg
1704fee23f9Smrg return objalloc_alloc (o, len);
1714fee23f9Smrg }
1724fee23f9Smrg }
1734fee23f9Smrg
1744fee23f9Smrg /* Free an entire objalloc structure. */
1754fee23f9Smrg
1764fee23f9Smrg void
objalloc_free(struct objalloc * o)1774fee23f9Smrg objalloc_free (struct objalloc *o)
1784fee23f9Smrg {
1794fee23f9Smrg struct objalloc_chunk *l;
1804fee23f9Smrg
1814fee23f9Smrg l = (struct objalloc_chunk *) o->chunks;
1824fee23f9Smrg while (l != NULL)
1834fee23f9Smrg {
1844fee23f9Smrg struct objalloc_chunk *next;
1854fee23f9Smrg
1864fee23f9Smrg next = l->next;
1874fee23f9Smrg free (l);
1884fee23f9Smrg l = next;
1894fee23f9Smrg }
1904fee23f9Smrg
1914fee23f9Smrg free (o);
1924fee23f9Smrg }
1934fee23f9Smrg
1944fee23f9Smrg /* Free a block from an objalloc structure. This also frees all more
1954fee23f9Smrg recently allocated blocks. */
1964fee23f9Smrg
1974fee23f9Smrg void
objalloc_free_block(struct objalloc * o,PTR block)1984fee23f9Smrg objalloc_free_block (struct objalloc *o, PTR block)
1994fee23f9Smrg {
2004fee23f9Smrg struct objalloc_chunk *p, *small;
2014fee23f9Smrg char *b = (char *) block;
2024fee23f9Smrg
2034fee23f9Smrg /* First set P to the chunk which contains the block we are freeing,
2044fee23f9Smrg and set Q to the last small object chunk we see before P. */
2054fee23f9Smrg small = NULL;
2064fee23f9Smrg for (p = (struct objalloc_chunk *) o->chunks; p != NULL; p = p->next)
2074fee23f9Smrg {
2084fee23f9Smrg if (p->current_ptr == NULL)
2094fee23f9Smrg {
2104fee23f9Smrg if (b > (char *) p && b < (char *) p + CHUNK_SIZE)
2114fee23f9Smrg break;
2124fee23f9Smrg small = p;
2134fee23f9Smrg }
2144fee23f9Smrg else
2154fee23f9Smrg {
2164fee23f9Smrg if (b == (char *) p + CHUNK_HEADER_SIZE)
2174fee23f9Smrg break;
2184fee23f9Smrg }
2194fee23f9Smrg }
2204fee23f9Smrg
2214fee23f9Smrg /* If we can't find the chunk, the caller has made a mistake. */
2224fee23f9Smrg if (p == NULL)
2234fee23f9Smrg abort ();
2244fee23f9Smrg
2254fee23f9Smrg if (p->current_ptr == NULL)
2264fee23f9Smrg {
2274fee23f9Smrg struct objalloc_chunk *q;
2284fee23f9Smrg struct objalloc_chunk *first;
2294fee23f9Smrg
2304fee23f9Smrg /* The block is in a chunk containing small objects. We can
2314fee23f9Smrg free every chunk through SMALL, because they have certainly
2324fee23f9Smrg been allocated more recently. After SMALL, we will not see
2334fee23f9Smrg any chunks containing small objects; we can free any big
2344fee23f9Smrg chunk if the current_ptr is greater than or equal to B. We
2354fee23f9Smrg can then reset the new current_ptr to B. */
2364fee23f9Smrg
2374fee23f9Smrg first = NULL;
2384fee23f9Smrg q = (struct objalloc_chunk *) o->chunks;
2394fee23f9Smrg while (q != p)
2404fee23f9Smrg {
2414fee23f9Smrg struct objalloc_chunk *next;
2424fee23f9Smrg
2434fee23f9Smrg next = q->next;
2444fee23f9Smrg if (small != NULL)
2454fee23f9Smrg {
2464fee23f9Smrg if (small == q)
2474fee23f9Smrg small = NULL;
2484fee23f9Smrg free (q);
2494fee23f9Smrg }
2504fee23f9Smrg else if (q->current_ptr > b)
2514fee23f9Smrg free (q);
2524fee23f9Smrg else if (first == NULL)
2534fee23f9Smrg first = q;
2544fee23f9Smrg
2554fee23f9Smrg q = next;
2564fee23f9Smrg }
2574fee23f9Smrg
2584fee23f9Smrg if (first == NULL)
2594fee23f9Smrg first = p;
2604fee23f9Smrg o->chunks = (PTR) first;
2614fee23f9Smrg
2624fee23f9Smrg /* Now start allocating from this small block again. */
2634fee23f9Smrg o->current_ptr = b;
2644fee23f9Smrg o->current_space = ((char *) p + CHUNK_SIZE) - b;
2654fee23f9Smrg }
2664fee23f9Smrg else
2674fee23f9Smrg {
2684fee23f9Smrg struct objalloc_chunk *q;
2694fee23f9Smrg char *current_ptr;
2704fee23f9Smrg
2714fee23f9Smrg /* This block is in a large chunk by itself. We can free
2724fee23f9Smrg everything on the list up to and including this block. We
2734fee23f9Smrg then start allocating from the next chunk containing small
2744fee23f9Smrg objects, setting current_ptr from the value stored with the
2754fee23f9Smrg large chunk we are freeing. */
2764fee23f9Smrg
2774fee23f9Smrg current_ptr = p->current_ptr;
2784fee23f9Smrg p = p->next;
2794fee23f9Smrg
2804fee23f9Smrg q = (struct objalloc_chunk *) o->chunks;
2814fee23f9Smrg while (q != p)
2824fee23f9Smrg {
2834fee23f9Smrg struct objalloc_chunk *next;
2844fee23f9Smrg
2854fee23f9Smrg next = q->next;
2864fee23f9Smrg free (q);
2874fee23f9Smrg q = next;
2884fee23f9Smrg }
2894fee23f9Smrg
2904fee23f9Smrg o->chunks = (PTR) p;
2914fee23f9Smrg
2924fee23f9Smrg while (p->current_ptr != NULL)
2934fee23f9Smrg p = p->next;
2944fee23f9Smrg
2954fee23f9Smrg o->current_ptr = current_ptr;
2964fee23f9Smrg o->current_space = ((char *) p + CHUNK_SIZE) - current_ptr;
2974fee23f9Smrg }
2984fee23f9Smrg }
299