diff options
author | Niklas Hallqvist <niklas@cvs.openbsd.org> | 1997-05-29 09:30:28 +0000 |
---|---|---|
committer | Niklas Hallqvist <niklas@cvs.openbsd.org> | 1997-05-29 09:30:28 +0000 |
commit | 5e8c4117fd51ae22bed414c7358df77884a216cf (patch) | |
tree | 11a6818390376768cc2c91b0c9a929723b71bebe /gnu/usr.bin/binutils/libiberty | |
parent | 0939272cc89838d2549c2e7bcd4c5cf92eba738c (diff) |
Import of binutils-2.8.1 from Cygnus/FSF
Diffstat (limited to 'gnu/usr.bin/binutils/libiberty')
-rw-r--r-- | gnu/usr.bin/binutils/libiberty/objalloc.c | 289 |
1 files changed, 289 insertions, 0 deletions
diff --git a/gnu/usr.bin/binutils/libiberty/objalloc.c b/gnu/usr.bin/binutils/libiberty/objalloc.c new file mode 100644 index 00000000000..34687d3891a --- /dev/null +++ b/gnu/usr.bin/binutils/libiberty/objalloc.c @@ -0,0 +1,289 @@ +/* objalloc.c -- routines to allocate memory for objects + Copyright 1997 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Cygnus Solutions. + +This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include "ansidecl.h" +#include "objalloc.h" + +/* Get a definition for NULL. */ +#include <stdio.h> + +#if VMS +#include <stdlib.h> +#include <unixlib.h> +#else + +#ifdef ANSI_PROTOTYPES +/* Get a definition for size_t. */ +#include <stddef.h> +#endif + +/* For systems with larger pointers than ints, this must be declared. */ +extern PTR malloc PARAMS ((size_t)); +#endif + +/* These routines allocate space for an object. Freeing allocated + space may or may not free all more recently allocated space. + + We handle large and small allocation requests differently. If we + don't have enough space in the current block, and the allocation + request is for more than 512 bytes, we simply pass it through to + malloc. */ + +/* The objalloc structure is defined in objalloc.h. */ + +/* This structure appears at the start of each chunk. */ + +struct objalloc_chunk +{ + /* Next chunk. */ + struct objalloc_chunk *next; + /* If this chunk contains large objects, this is the value of + current_ptr when this chunk was allocated. If this chunk + contains small objects, this is NULL. */ + char *current_ptr; +}; + +/* The aligned size of objalloc_chunk. */ + +#define CHUNK_HEADER_SIZE \ + ((sizeof (struct objalloc_chunk) + OBJALLOC_ALIGN - 1) \ + &~ (OBJALLOC_ALIGN - 1)) + +/* We ask for this much memory each time we create a chunk which is to + hold small objects. */ + +#define CHUNK_SIZE (4096 - 32) + +/* A request for this amount or more is just passed through to malloc. */ + +#define BIG_REQUEST (512) + +/* Create an objalloc structure. */ + +struct objalloc * +objalloc_create () +{ + struct objalloc *ret; + struct objalloc_chunk *chunk; + + ret = (struct objalloc *) malloc (sizeof *ret); + if (ret == NULL) + return NULL; + + ret->chunks = (PTR) malloc (CHUNK_SIZE); + if (ret->chunks == NULL) + { + free (ret); + return NULL; + } + + chunk = (struct objalloc_chunk *) ret->chunks; + chunk->next = NULL; + chunk->current_ptr = NULL; + + ret->current_ptr = (char *) chunk + CHUNK_HEADER_SIZE; + ret->current_space = CHUNK_SIZE - CHUNK_HEADER_SIZE; + + return ret; +} + +/* Allocate space from an objalloc structure. */ + +PTR +_objalloc_alloc (o, len) + struct objalloc *o; + unsigned long len; +{ + /* We avoid confusion from zero sized objects by always allocating + at least 1 byte. */ + if (len == 0) + len = 1; + + len = (len + OBJALLOC_ALIGN - 1) &~ (OBJALLOC_ALIGN - 1); + + if (len <= o->current_space) + { + o->current_ptr += len; + o->current_space -= len; + return (PTR) (o->current_ptr - len); + } + + if (len >= BIG_REQUEST) + { + char *ret; + struct objalloc_chunk *chunk; + + ret = (char *) malloc (CHUNK_HEADER_SIZE + len); + if (ret == NULL) + return NULL; + + chunk = (struct objalloc_chunk *) ret; + chunk->next = (struct objalloc_chunk *) o->chunks; + chunk->current_ptr = o->current_ptr; + + o->chunks = (PTR) chunk; + + return (PTR) (ret + CHUNK_HEADER_SIZE); + } + else + { + struct objalloc_chunk *chunk; + + chunk = (struct objalloc_chunk *) malloc (CHUNK_SIZE); + if (chunk == NULL) + return NULL; + chunk->next = (struct objalloc_chunk *) o->chunks; + chunk->current_ptr = NULL; + + o->current_ptr = (char *) chunk + CHUNK_HEADER_SIZE; + o->current_space = CHUNK_SIZE - CHUNK_HEADER_SIZE; + + o->chunks = (PTR) chunk; + + return objalloc_alloc (o, len); + } +} + +/* Free an entire objalloc structure. */ + +void +objalloc_free (o) + struct objalloc *o; +{ + struct objalloc_chunk *l; + + l = (struct objalloc_chunk *) o->chunks; + while (l != NULL) + { + struct objalloc_chunk *next; + + next = l->next; + free (l); + l = next; + } + + free (o); +} + +/* Free a block from an objalloc structure. This also frees all more + recently allocated blocks. */ + +void +objalloc_free_block (o, block) + struct objalloc *o; + PTR block; +{ + struct objalloc_chunk *p, *small; + char *b = (char *) block; + + /* First set P to the chunk which contains the block we are freeing, + and set Q to the last small object chunk we see before P. */ + small = NULL; + for (p = (struct objalloc_chunk *) o->chunks; p != NULL; p = p->next) + { + if (p->current_ptr == NULL) + { + if (b > (char *) p && b < (char *) p + CHUNK_SIZE) + break; + small = p; + } + else + { + if (b == (char *) p + CHUNK_HEADER_SIZE) + break; + } + } + + /* If we can't find the chunk, the caller has made a mistake. */ + if (p == NULL) + abort (); + + if (p->current_ptr == NULL) + { + struct objalloc_chunk *q; + struct objalloc_chunk *first; + + /* The block is in a chunk containing small objects. We can + free every chunk through SMALL, because they have certainly + been allocated more recently. After SMALL, we will not see + any chunks containing small objects; we can free any big + chunk if the current_ptr is greater than or equal to B. We + can then reset the new current_ptr to B. */ + + first = NULL; + q = (struct objalloc_chunk *) o->chunks; + while (q != p) + { + struct objalloc_chunk *next; + + next = q->next; + if (small != NULL) + { + if (small == q) + small = NULL; + free (q); + } + else if (q->current_ptr > b) + free (q); + else if (first == NULL) + first = q; + + q = next; + } + + if (first == NULL) + first = p; + o->chunks = (PTR) first; + + /* Now start allocating from this small block again. */ + o->current_ptr = b; + o->current_space = ((char *) p + CHUNK_SIZE) - b; + } + else + { + struct objalloc_chunk *q; + char *current_ptr; + + /* This block is in a large chunk by itself. We can free + everything on the list up to and including this block. We + then start allocating from the next chunk containing small + objects, setting current_ptr from the value stored with the + large chunk we are freeing. */ + + current_ptr = p->current_ptr; + p = p->next; + + q = (struct objalloc_chunk *) o->chunks; + while (q != p) + { + struct objalloc_chunk *next; + + next = q->next; + free (q); + q = next; + } + + o->chunks = (PTR) p; + + while (p->current_ptr != NULL) + p = p->next; + + o->current_ptr = current_ptr; + o->current_space = ((char *) p + CHUNK_SIZE) - current_ptr; + } +} |