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