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

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

/*
 *
 *Name
 *
 * falloc_create_pool, falloc_distroy_pool, falloc, falloc_free - a simple
 * fixed sized object storage manager
 *
 *
 * 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 
 * falloc_create_pool fixed sized chunks of storage can be allocated by calls
 * to falloc.  The chunks can be freed by calls to falloc_free and the entire
 * pool can be freed by a call to falloc_distroy_pool.
 * 
 * The big constraint is that the size of the chunks must be specified when
 * the pool is created and cannot be changed.
 * 
 * DIAGNOSTICS
 * 
 * Return codes (except falloc)
 *
 *   -1 -- Unable to perform operation due to resource limitations
 *    0 -- Operation completed normally
 *
 * Return codes for falloc:
 *
 *    0 -- Unable to perform operation due to resource limitations
 *    --not yet checked--
 *    >0 -- address of allocated chunk
 * BUGS
 *
 * Falloc_free does not really return storage to the system, rather it adds it
 * to a free storage list for use for future call to falloc using the same pool.
 *
 */
struct chunk {
    unsigned char   body[1];	/* variable length byte array */
};

typedef struct {
    unsigned int chunk_size;
    struct chunk *free_chunk_list
    unsigned_char *palloc_pool;
    int new_palloc_pool; /* non-zero is a new pallow pool was created for this
        falloc pool */
} pool_type;

int falloc_create_pool (fpool, ppool, chunk_size, expected_number_of_chunks)
    pool_type **fpool;
    char *ppool;
    unsigned int chunk_size;
    unsigned int expected_number_of_chunks;
{
    int rc;			/* return codes */
    int new_pool; /* 0 if ppool is initially non-zero, i.e. true if a new palloc
        is necessary */

    /* allocate a new palloc pool if necessary */
    if (ppool == 0) { /* allocate a palloc pool */
        rc = palloc_create_pool (ppool, chunk_size*expected_number_of_chunks
            + sizeof(pool_type));
	if (rc != 0) { return (rc);}
	new_pool = 1;    
    } else {
	new_pool = 0;
    }

    /* allocate the pool discriptor */
#ifdef debug
    printf ("falloc_create_pool: pool addr %d, chunk size = %d number = %d\n",
        pool, chunk_size, expected_number_of_chunks);
#endif
    *fpool = palloc (ppool, sizeof(pool_type));
    (*fpool)->chunk_size = chunk_size
    (*fpool)->free_chunk_list = 0;
    (*fpool)->palloc_pool = ppool;
    (*fpool)->new_palloc_pool = new_pool;
#ifdef debug
    falloc_print (*pool, "create pool");
#endif
    return (0);
}

unsigned char *falloc (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 ("falloc: pool addr %d, size = %d \n", pool, size);
    falloc_print (pool, "falloc 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
        falloc_print (pool, "falloc, 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
    falloc_print (pool, "falloc after allocation");
#endif
    return (chunk);
}

unsigned int falloc_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
     falloc_print (pool, "falloc_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);
}
