/* #ident "@(#)smail/src:RELEASE-3_2_0_121:alloc.c,v 1.31 2005/07/20 20:33:45 woods Exp" */ /* * Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll * Copyright (C) 1992 Ronald S. Karr * * See the file COPYING, distributed with smail, for restriction * and warranty information. */ /* * alloc.c: * storage allocation with panics on errors * The functions in this file may be replaced in the future when * daemon mode is implemented, to make it easier to reclaim * storage. * * block storage allocation * This allows a given storage allocation to be associated * with a group of other storage allocations. It is * possible to free or test for existence of the class. * * A block a pointer to a chain of segments. Each segment * refers to one storage allocation. A block also contains * the total number of bytes allocated. If this number is * zero, then no storage is associated with the block. * * external functions: xmalloc, xrealloc, priv_xfree, * balloc, brealloc, bfree, * malloc_block, realloc_block, free_block */ #include "defs.h" #include #include #include #ifdef STDC_HEADERS # include # include #else # ifdef HAVE_STDLIB_H # include # endif #endif #ifdef HAVE_STRING_H # if !defined(STDC_HEADERS) && defined(HAVE_MEMORY_H) # include # endif # include #endif #ifdef HAVE_STRINGS_H # include #endif #if defined(HAVE_UNISTD_H) # include #endif #ifdef USE_VMALLOC # include #endif #include "smail.h" #include "exitcodes.h" #include "addr.h" /* for "log.h" */ #include "log.h" #include "alloc.h" #include "smailport.h" /* the master block of blocks */ static struct block master = {(struct bseg *)0, 0}; /* default block for 0 based life time */ static struct block default_block = {(struct bseg *)0, 0}; static struct block *def_block = &default_block; /* variable exported for the X_CHECK macro */ int x_dont_panic = TRUE; /* if TRUE, X_CHECK won't call panic */ /* functions imported by this file */ #if !defined(HAVE_STDLIB_H) && !defined(HAVE_MALLOC_H) && !defined(USE_VMALLOC) extern char *malloc(); extern char *realloc(); extern void free(); /* int on SunOS */ #endif /* * xmalloc, priv_xrealloc, priv_xfree - per message malloc and realloc and free * with aborts on error * * The following three routines are an interface to malloc(), realloc(), and * free(). The first two detect errors and panic the application, which means * calling functions don't having to worry about whether a memory allocation * error occurs or not. There is seldom anything graceful to do when such * errors happen anyway, and besides they happen very rarely on machines with a * reasonable VM space, so just panic about the problem and don't bother * returning. * * These functions also reserve space for a magic canary number, and then check * for its presense to help diagnose any double-free attempts, attempts to free * non-allocated storage, etc. See "alloc.h" for the rest of the details. * Because of this xfree() must always be used to release storage that was * allocated by xalloc() or xrealloc(). * * Note there are xrealloc() and xfree() macros defined in "alloc.h" which * provide wrappers to the priv_*() routines. These wrappers also call the * X_CHECK() macro (and for xfree() check that the region pointer is not NULL) * and so can provide the line number of the original call instead of always * just pointing into this source file. The priv_*() functions also call the * X_CHECK() macro in order to allow the application to pass pointers to these * functions off to other libraries which might want to use the same allocator * as the common application, but which are not themselves compiled with * "alloc.h". * * In the future, the x-style allocation routines will be used for * allocation of spaces which will be reclaimed after processing one * message -- i.e. through the block allocator. */ char * xmalloc(size) size_t size; /* size of region to allocate */ { register char *ret; /* region returned by malloc */ ret = malloc(size + sizeof(ALIGNED_TYPE)); if (!ret) { panic(EX_SOFTWARE, "malloc(%d) failed: %s.", size, strerror(errno)); /*NOTREACHED*/ } ((ALIGNED_TYPE *) ret)[0] = X_MAGIC; return ret + sizeof(ALIGNED_TYPE); } char * priv_xrealloc(region, size) char *region; /* region to be realloc'd */ size_t size; /* realloc size */ { register char *ret; /* region returned by realloc */ if (X_CHECK(region) == FAIL) { /* what to do with realloc that is reasonable to avoid a panic? */ /* copy the region to an entirely new malloc'd area */ ret = xmalloc(size); memcpy(ret, region, (size_t) size); return ret; } ((ALIGNED_TYPE *) region)[-1] = 0; /* clear the magic number */ ret = realloc(region - sizeof(ALIGNED_TYPE), size + sizeof(ALIGNED_TYPE)); if (!ret) { panic(EX_SOFTWARE, "realloc(%p, %u) failed: %s.", (POINTER_TYPE) region, size, strerror(errno)); /*NOTREACHED*/ } ((ALIGNED_TYPE *) ret)[0] = X_MAGIC; /* put the magic number back */ return ret + sizeof(ALIGNED_TYPE); } void priv_xfree(region) char *region; { if (region == NULL) { panic(EX_SOFTWARE, "xfree(NULL) called! Please trace under a debugger!"); /*NOTREACHED*/ } if (X_CHECK(region) == FAIL) { /* * we must permit double free()'s when x_dont_panic is set and hope the * paniclog reveals the problem soon enough */ return; } ((ALIGNED_TYPE *)region)[-1] = 0; free(region - sizeof(ALIGNED_TYPE)); return; } /* * bmalloc - allocate memory and associate it with a block * * input: * size - number of bytes to allocate * block - the block to associate it with, 0 ==> master * * output: * returns a pointer to the allocated storage */ char * bmalloc(size, block) size_t size; /* number of bytes to allocate */ struct block *block; /* block that data belongs to */ { struct bseg *newseg; /* the new segment for this alloc */ /* a zero block implies master block */ if (block == 0) { block = def_block; } /* add a new segment to the block */ if ((newseg = (struct bseg *)malloc(sizeof(struct bseg))) == NULL) { panic(EX_SOFTWARE, "bmalloc: malloc of bseg [%d] failed: %s.", sizeof(struct bseg), strerror(errno)); /* NOTREACHED */ } newseg->next = block->next; block->next = newseg; /* allocate the storage */ if ((newseg->data = malloc(size)) == NULL) { panic(EX_SOFTWARE, "bmalloc: malloc of %u bytes failed: %s.", size, strerror(errno)); /* NOTREACHED */ } ++block->cnt; /* return the pointer */ return newseg->data; } /* * brealloc - reallocate storage associated with a block * * Resize a storagg from a block, and perform all needed * accounting adjustments. * * input: * data - pointer to data being realloced * size - the new size * block - the block that data belongs * * output: * returns a pointer to the re-allocated storage */ char * brealloc(data, size, block) char *data; /* data being re-allocated */ size_t size; /* number of bytes to re-allocate */ struct block *block; /* block that data belongs to */ { struct bseg *oldseg; /* old segment associated with data */ /* a zero block implies master block */ if (block == 0) { block = def_block; } /* find the segment */ for (oldseg=block->next; oldseg != NULL && oldseg->data != data; oldseg=oldseg->next) { ; /* NO-OP */ } if (oldseg == NULL) { panic(EX_SOFTWARE, "brealloc: seg (at %p) not in block (at %p, cnt:%d)", (POINTER_TYPE) oldseg, (POINTER_TYPE) block, block->cnt); /* NOTREACHED */ } /* reallocate */ if ((oldseg->data = realloc(data, size)) == NULL) { panic(EX_SOFTWARE, "brealloc: realloc to %d bytes (at %p) failed: %s.", size, (POINTER_TYPE) data, strerror(errno)); /* NOTREACHED */ } /* return the pointer */ return oldseg->data; } /* * bfree - free a signle segment from a block * * Free a storage from a block, and perform all needed accounting adjustments. * * input: * data - pointer to data being freed * block - the block that data belongs */ void bfree(data, block) char *data; /* data being freed */ struct block *block; /* block that data belongs to */ { struct bseg *oldseg; /* old segment associated with data */ struct bseg *lastseg; /* the segment before the deleted segment */ /* firewall */ if (block == NULL) { panic(EX_SOFTWARE, "bfree: block is NULL"); /* NOTREACHED */ } /* * delete the segment from the block */ /* first find the segment */ for (lastseg = NULL, oldseg = block->next; oldseg != NULL && oldseg->data != data; lastseg = oldseg, oldseg = oldseg->next) { ; /* NO-OP */ } if (oldseg == NULL) { panic(EX_SOFTWARE, "bfree: data (at %p) not in block (at %p, cnt: %d)", (POINTER_TYPE) data, (POINTER_TYPE) block, block->cnt); /* NOTREACHED */ } /* remove the segment from the block */ if (lastseg == NULL) { block->next = oldseg->next; } else { lastseg->next = oldseg->next; } /* do the accounting */ --block->cnt; /* free the data and segment */ free(oldseg->data); free((char *) oldseg); return; } /* * malloc_block - form a new storage block * * Start a new storage block with no storage allociated with it. * * NOTE: the block structure becomes a segment in the master block * * input: * none * * output: * returns a pointer to the new block */ struct block * malloc_block() { struct block *block; /* ptr to the new block */ /* allocate the block */ block = (struct block *) bmalloc(sizeof(struct block), &master); block->cnt = 0; block->next = NULL; /* return the new block */ return block; } /* * realloc_block - move a segment from one block to another * * input: * data - the data being moved * oldblock - the block where the segment currently resides * newblock - the block that will hold the new segment */ void realloc_block(data, oldblock, newblock) char *data; /* the data to move */ struct block *oldblock; /* the old block */ struct block *newblock; /* the new block */ { struct bseg *oldseg; /* old segment associated with data */ struct bseg *lastseg; /* the segment before the deleted segment */ /* firewall */ if (oldblock == NULL && newblock == NULL) { panic(EX_SOFTWARE, "realloc_block: oldblock or newblock is NULL"); /* NOTREACHED */ } /* * delete the segment from the oldblock */ /* first find the segment */ for (lastseg = NULL, oldseg = oldblock->next; oldseg != NULL && oldseg->data != data; lastseg = oldseg, oldseg = oldseg->next) { ; /* NO-OP */ } if (oldseg == NULL) { panic(EX_SOFTWARE, "realloc_block: data (at %p) not in block (at %p, cnt: %d)", (POINTER_TYPE) data, (POINTER_TYPE) oldblock, oldblock->cnt); /* NOTREACHED */ } /* remove the segment from the old block */ if (lastseg == NULL) { oldblock->next = oldseg->next; } else { lastseg->next = oldseg->next; } --oldblock->cnt; /* add the segment to the new block */ oldseg->next = newblock->next; newblock->next = oldseg; ++newblock->cnt; return; } /* * free_block - free everything in a block * * input: * block - the block to free all segments on */ void free_block(block) struct block *block; /* the block to free */ { struct bseg *curseg; if (block == NULL) { panic(EX_SOFTWARE, "free_block: block is NULL"); /* NOTREACHED */ } /* free the chain */ curseg = block->next; while (curseg != NULL) { struct bseg *nextseg; /* find the next entry so we can free the current one */ nextseg = curseg->next; /* do the accounting */ --block->cnt; /* free the data */ free(curseg->data); /* free the segemnt */ free((char *) curseg); /* go on to the next */ curseg = nextseg; } /* double-check the balance sheet */ if (block->cnt != 0) { panic(EX_SOFTWARE, "free_block: freed block (at %p) has cnt: %d", (POINTER_TYPE) block, block->cnt); /* NOTREACHED */ } /* free the block segment from the master block */ bfree((char *) block, &master); return; } /* * Local Variables: * c-file-style: "smail" * End: */