/*
#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 <sys/types.h>
#include <stdio.h>
#include <errno.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif
#ifdef HAVE_STRING_H
# if !defined(STDC_HEADERS) && defined(HAVE_MEMORY_H)
# include <memory.h>
# endif
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#if defined(HAVE_UNISTD_H)
# include <unistd.h>
#endif
#ifdef USE_VMALLOC
# include <vmalloc.h>
#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:
*/
syntax highlighted by Code2HTML, v. 0.9.1