/*
 * dextab.c - Contains functions that implement a dynamically expandable table.
 * 	      The abstraction includes a data structure with a table of addresses
 *	      which expands in order of two. The table never shrinks. 
 *
 * Copyright (c) 1997 by Procom Technology,Inc.
 *
 * This program can be redistributed or modified under the terms of the 
 * GNU General Public License as published by the Free Software Foundation.
 * This program is distributed without any warranty or implied warranty
 * of merchantability or fitness for a particular purpose.
 *
 * See the GNU General Public License for more details.
 *
 */
 

#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/malloc.h>
#include <linux/netdevice.h>
#include <linux/netbeui.h>


/*
 * Function: dextab_init
 *	Initializes dextab_t structure
 *
 * Parameters:
 *	tbl	: pointer to dextab_t to be initialized
 *	reserved: number of entries to reserve from head of dextab. This value
 *		  instructs other functions not to use those entries.
 *	max_size: maximum number of entries the <tbl> can hold.
 *
 * Returns: none
 *
 * Note:
 *	- There are two ways to initialize a dextab:
 *		1- Staticly initializating when definig dextab_t
 *		2- calling dextab_init
 */

void	
dextab_init( dextab_t *tbl, int reserved, int max_size)
{
	tbl->addr= NULL;
	tbl->size= 0;
	tbl->reserved= reserved;
	tbl->max_size= max_size;
	tbl->count= 0;
}


/*
 * Function: dextab_expand
 *	Expands table array of pointers in order of two. The old entries
 *	contents are safely kept in their place. 
 *
 * Parameters:
 *	tbl	: pointer to dextab_t to be expanded
 *
 * Returns: 
 *	0	: If table expanded successfully.
 *	non-zero: if table entry count has reached maximum size determined 
 *		  in initialization steps and if memory allocation failed.
 */

static int
dextab_expand( dextab_t *tbl)
{
	int new_size= (tbl->size== 0) ? 16 : (tbl->size*2);
	void **new_addr; 

	/* Table size should not exceed maximum table size */
	if (new_size > tbl->max_size)
		{
		if (tbl->size < tbl->max_size)
			new_size= tbl->max_size;
		else
			return -1;    
		}

	new_addr= (void **)kmalloc(new_size * sizeof(void *), GFP_ATOMIC);
	if (!new_addr)
		return -1;

	/* Copy previous data from old area to new one */
	memset(new_addr, 0, new_size * sizeof(void *));
	memcpy(new_addr, tbl->addr, tbl->size * sizeof(void *));
	if (tbl->addr != NULL)
		kfree(tbl->addr);
	tbl->addr= new_addr;
	tbl->size= new_size;
	return 0;
}


/*
 * Function: dextab_add_insert_entry
 *	This is a back-end function which depending on its arguments creates
 *	an entry or puts a pre-allocated entry into dextab. The front ends
 *	are macros defined in header files.
 *
 * Parameters:
 *	tbl	  : pointer to dextab_t to insert element into
 *	entry_size: size of entry to add. if non-zero value passed, the memory
 *		    is allocated with entry_size bytes.
 *	entry	  : address of entry to insert into table
 *
 * Returns: 
 *	>=0	  : uopn successfull insertion into table, this is the index 
 *		    of entry in table.
 * 	<0	  : unsuccessfull insertion 
 *
 * Note:
 *	- The entry_size and entry are exclusive arguments and entry_size 
 *	  percedes entry in decisions.
 *
 *	- Add means allocate and insert entry, which is usually indicated by
 *	  setting entry_size to non-zero value.
 *
 *	- Insert means insert a pre-alocated entry, which is usually indicated 
 *	  by setting entry_szie to zero and passing an address in entry.
 */

int
dextab_add_insert_entry( dextab_t *tbl, int entry_size, void *entry)
{
	int index;

	/* If you can find a free slot in table add entry there */
	for (index= tbl->reserved; index < tbl->size; index++) 
		{
		if (tbl->addr[index] == NULL)
			{
			tbl->addr[index]= (entry_size > 0) ? 
			                  kmalloc(entry_size, GFP_ATOMIC) : 
			                  entry;
			if (tbl->addr[index])
				{
				tbl->count++;
				return index;
				}
			return -1;
			}
		}

	/* Try to expand table then retry again */
	if (dextab_expand(tbl) == 0)
		return dextab_add_insert_entry(tbl, entry_size, entry);

	/* Could not expand table return error */
	return -1;
}


/*
 * Function: dextab_remove_delete_index
 *	This is a back-end function which depending on its arguments removes
 *	destructs an entry from dextab. The front ends are macros defined in
 *	header files.
 *
 * Parameters:
 *	tbl	   : pointer to dextab_t to remove element from
 *	entry_index: index of entry in table 
 *	flag	   : removal or deletion flag. Values are:
 *			- DEXTAB_FREE_ENTRY
 *			- DEXTAB_NOFREE_ENTRY (otherwise)
 *
 * Returns: none
 *	
 * Note:
 *	- The flag value is checked against DEXTAB_FREE_ENTRY
 *
 *	- Remove means delete entry from table and free its memory, This is
 *	  indicated by setting qflag DEXTAB_FREE_ENTRY
 *
 *	- Delete means delete entry from memory, this is indicated by setting
 *	  qflag to everything other than DEXTAB_FREE_ENTRY usually DEXTAB_NOFREE_ENTRY
 */

void	
dextab_remove_delete_index( dextab_t *tbl, int entry_index, int flag)
{
	if ((entry_index < tbl->size) && (entry_index >= tbl->reserved))
		if (tbl->addr[entry_index] != NULL)
			{
			if (flag == DEXTAB_FREE_ENTRY)
				kfree(tbl->addr[entry_index]);
			tbl->addr[entry_index]= NULL;
			tbl->count--;
			}

	return; 
}


/*
 * Function: dextab_entry_index
 *	Returns index of an entry in table using its address.
 *
 * Parameters: 
 *	tbl	   : pointer to dextab_t to find entry in
 *	entry	   : address of entry to find in table
 *
 * Returns: 
 *	>=0	   : index of entry found in table
 *	<0	   : if entry not found in table
 */

int	
dextab_entry_index( dextab_t *tbl, void *entry)
{
	int index;

	for (index= tbl->reserved; index < tbl->size; index++)
		{
		if (tbl->addr[index] == entry)
			return index;
		} 

	return -1;
}


/*
 * Function: dextab_count_entries
 *	returns count of entries in dextab
 *
 * Parameters:
 *	tbl	   : pointer to dextab_t to count its entries 
 *
 * Returns: 
 *	>0	   : count of entries in table
 */

inline int 
dextab_count_entries( dextab_t *tbl)
{
	return tbl->count;
}


/*
 * Function: dextab_destruct
 *	housekees a dextab, deallocates memories, ....
 *
 * Parameters:
 *	tbl	   : pointer to dextab_t to destruct
 *
 * Returns: none
 */

void 	
dextab_destruct( dextab_t *tbl)
{
	if (tbl->addr != NULL)
		kfree(tbl->addr);

	dextab_init(tbl, tbl->reserved, tbl->max_size);    
	return;
}
