/*********************************************************************** * Copyright (C) 1995 Joe English * Freely redistributable *********************************************************************** * * pile.c,v 1.8 1999/02/12 02:50:48 joe Exp" * * Author: Joe English * Created: 20Sep91 * Description: Mark/release style memory allocator. * * 1999/02/12 02:50:48 * 1.8 * * %%% Needs checking, clean-up. Off-by-one bugs probably lurking... * */ #include #include #include "project.h" #include "pile.h" #define DEF_INCREMENT 8000 #define MIN_BIGBLOCK 10 #define PREVBLOCK(b) (*((char **)(b))) /* * the first word of each block is a back-pointer to the previous block. * The pile access structure itself is stored in the first block. */ pile pcreate(void) { unsigned inc = DEF_INCREMENT; pile p; char *block; /* * Allocate pile storage block. */ block = malloc(inc); /* %%% check */ *((char **)(block)) = NULL; p = (pile)(block + sizeof(char **)); p->block = block; p->inc = inc; p->nbig = 0; p->maxbig = 0; /* %%% */ p->bigblocks = NULL; /* %%% */ p->endptr = -1; /* for incrememntal mode */ p->top = sizeof(struct pileRec) + sizeof(char **); return p; } /* * Return a pile mark. This is just the current pile access structure. */ pilemark pmark(pile p) { ASSERT(p->endptr == -1, "Cannot call pmark in incremental mode"); return *p; } int prelease(pile p,pilemark m) { ASSERT(p->endptr == -1, "Cannot call prelease in incremental mode"); ASSERT(p->nbig >= m.nbig,"nbig: mark is higher than current"); /* * See if mark was set in the current block: */ if (m.block == p->block) { /* just need to decrement top */ ASSERT(p->top >= m.top, "top: mark is higher than current"); } else { /* need to free blocks: */ while (p->block && p->block != m.block) { char *prev = PREVBLOCK(p->block); free(p->block); p->block = prev; } ASSERT(p->block, "mark does not belong to this pile"); } /* * Free bigblocks: */ while (m.nbig > p->nbig) free(p->bigblocks[--p->nbig]); /* * Reset top mark: */ p->top = m.top; return 1; } static void pgrow(pile p, int size) { char *newblock; if (size >= p->inc + sizeof(char **)) { abort(); /* %%% allocate big block */ } newblock = malloc(p->inc); /* %%% check */ PREVBLOCK(newblock) = p->block; if (p->endptr > 0) memcpy(newblock+sizeof(char **), p->block+p->top, p->endptr); p->block = newblock; p->top = sizeof(char **); return; } void *palloc(pile p,unsigned short size) { void *ret; ASSERT(p->endptr == -1, "Cannot call palloc in incremental mode"); /* * Round size up to even number: */ size = (size + 3) & ~3; /* * See if there's enough space: */ if (p->top + size > p->inc) { /* Allocate another chunk */ pgrow(p, size); } ret = p->block + p->top; p->top += size; return ret; } /* * pdestroy(pile p) * Destroy the pile data structure and return all memory * to the system pool. */ void pdestroy(pile p) { char *blockptr; /* * Free bigblocks: */ while (p->nbig > 0) free(p->bigblocks[--p->nbig]); /* * Free blocks: * Note that the pile structure itself was allocated * from the first block, so we don't need to free it. */ blockptr = p->block; /* Stomp: */ p->block = (char *)0x239; while (blockptr) { char *prev = PREVBLOCK(blockptr); free(blockptr); blockptr = prev; } } /* * Duplicate string onto pile: */ char *pstrdup(pile p, const char *str) { char *dst = palloc(p, strlen(str) + 1); strcpy(dst, str); return dst; } /* * Incremental mode: */ void pstart(pile p) { ASSERT(p->endptr == -1, "pile already in incremental mode"); p->endptr = 0; } static void pwrite(pile p, void *src, int len) { ASSERT(p->endptr >= 0, "Cannot call pwrite unless in incremental mode"); if (p->top + p->endptr + len >= p->inc) pgrow(p, len); memcpy(p->block + p->top + p->endptr, src, len); p->endptr += len; } void paddstr(pile p, const char *src) { pwrite(p,(void *)src,strlen(src)); } void paddch(pile p, char ch) { pwrite(p,&ch,1); } void *pfinish(pile p) { void *ret; ASSERT(p->endptr != -1, "pfinish: not in incremental mode"); ret = p->block + p->top; p->top += p->endptr; p->endptr = -1; return ret; }