/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1986
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header:palloc.c 12.0$ */
/* $ACIS:palloc.c 12.0$ */
/* $Source: /ibm/acis/usr/src/ibm/fdb_ca/lib/src/RCS/palloc.c,v $ */

#ifndef lint
static char *rcsid = "$Header:palloc.c 12.0$";
#endif

/* #define debug */
/*
 * NAME
 *
 *palloc, palloc_create_pool, palloc_destroy_pool - simple, 
 *allocate only storage management
 *
 *SYNOPSIS
 *
 *DESCRIPTION
 *
 *The routines implement a very effishent (in both time and storage), but
 *very constrained storage manager.  Once a pool is created by a call to 
 *palloc_create_pool chunks of storage can be allocated by calls to palloc.
 *The big constraint is that only the whole pool can ever be freed (via a 
 *call to palloc_destroy_pool).
 *
 *DIAGNOSTICS
 *
 *Return codes (except palloc)
 *
 *	-1 -- Unable to perform operation due to resource limitations
 *		--not yet checked--
 *	0 -- Operation completed normally
 *
 *Return codes for palloc:
 *
 *	0 -- Unable to perform operation due to resource limitations
 *		--not yet checked--
 *	>0 -- address of allocated chunk
 *
 *
 */
struct block {
    struct block *next_block;
    unsigned char body[1];		/* variable length byte array */
};

typedef struct {
    unsigned int block_size;
    struct block *current_block;
    unsigned char *pos_in_current_block;
    unsigned int space_left_in_current_block;
    struct block first_block;
} pool_type;

#ifdef debug
int palloc_print (pool, msg)
    pool_type *pool;
    char *msg;
{
    printf ("palloc_print: %s, pool addr %d \n", msg, pool);
    if (pool == 0) {
	printf ("    ----no pool---- \n");
    } else {
	printf ("    block_size = %d, current block = %d, pos in current block = %d \n",
	    pool->block_size, pool->current_block, pool->pos_in_current_block);
	printf ("    space left in current block = %d, addr of first block = %d \n",
	    pool->space_left_in_current_block, &(pool->first_block) );
    }
    return (0);
}
#endif

int palloc_create_pool (pool, expected_size)
    pool_type **pool;
    unsigned int expected_size;
{
    /* allocate the pool discriptor including the first block */

#ifdef debug
    printf ("palloc_create_pool: pool addr %d, expected size = %d \n",
        pool, expected_size);
#endif
    *pool = (pool_type *) malloc (sizeof(pool_type)+expected_size);

    /* verify that the allocation was sucessful, return if not */
    if (*pool == 0) {
	return (-1);
    }

     /* initialize the pool descriptor */

    (*pool)->block_size = (expected_size > 4000) ? (expected_size / 4) :
         1000;  /* the minimum additional block is 1000 bytes */

    (*pool)->current_block = &((*pool)->first_block);

    (*pool)->pos_in_current_block = &((*pool)->first_block.body[0]);

    (*pool)->space_left_in_current_block = expected_size;

    /* initialize first block */

    (*pool)->first_block.next_block = 0;
#ifdef debug
    palloc_print (*pool, "create pool");
#endif
    return (0);
}

unsigned char *palloc (pool, size)
    pool_type *pool;
    unsigned int size;
{
    unsigned char *chunk; /* holds address of allocated chunk */

/* force size to be a multiple of 4 for alignment */
    size = (size + 3) & (~3);

#ifdef debug
    printf ("palloc: pool addr %d, size = %d \n", pool, size);
    palloc_print (pool, "palloc before allocation");
#endif
    /* check for available space */
    if (size > pool->space_left_in_current_block) {
	/* not enough space left- go get another block and chain it on */
	if (size > pool->block_size) {
	    pool->block_size = size; /* adjust block size so chunk will just
	        fit. This should not happen, but just in case ... */
	}
	(pool->current_block)->next_block = (struct block *) malloc (
	    sizeof (struct block *) + pool->block_size);
	/* verify that the allocation was sucessful, return is not */
	if ((pool->current_block)->next_block == 0) {
	    return (0);
	}
	pool->current_block = (pool->current_block)->next_block;
	(pool->current_block)->next_block = 0;
        pool->pos_in_current_block = &((pool->current_block)->body[0]);
        pool->space_left_in_current_block = pool->block_size;
#ifdef debug
        palloc_print (pool, "palloc, new block");
#endif
    }
    /* allocate chunk and increment stuff */
    chunk = pool->pos_in_current_block;
    pool->pos_in_current_block += size;
    pool->space_left_in_current_block -= size;
#ifdef debug
    palloc_print (pool, "palloc after allocation");
#endif
    return (chunk);
}

unsigned int palloc_destroy_pool (pool)
    pool_type *pool;
{
    struct block *this_block;
    struct block *next_block;

    /* set up for scan and free pool descriptor which includes the first block */
    this_block = pool->first_block.next_block;
#ifdef debug
     palloc_print (pool, "palloc_destroy_pool");
     printf ("Free descriptor at %d \n", pool);
#endif
    free (pool);

    /* free each of the blocks */
   
    while (this_block != 0) {
	next_block = this_block->next_block;
#ifdef debug
        printf ("Freeing block at %d \n", this_block);
#endif
	free (this_block);
	this_block = next_block;
    }
    return(0);
}
