/**********************************************************************/
/*                                                                    */
/*	CRISP - Programmable editor                                   */
/*	===========================                                   */
/*                                                                    */
/*  File:          stype.c                                            */
/*  Author:        P. D. Fox                                          */
/*  Created:       24 Apr 1991                     		      */
/*                                                                    */
/*  Copyright (c) 1990, 1991 Paul Fox                                 */
/*                All Rights Reserved.                                */
/*                                                                    */
/*                                                                    */
/*--------------------------------------------------------------------*/
/*  Description:  Self-organising integer data structure      	      */
/*                                                                    */
/*   This  is  a  library  of  routines for storing a data structure  */
/*   where  the  primary key is an integer. We want to keep overhead  */
/*   to  an  absolute minimum but have a pretty good lookup time. We  */
/*   do  this  by  maintaining  an  array  of integer/pointer pairs,  */
/*   which  gets  sorted  into order every when necessary. This also  */
/*   allows us to access the data in numerical order.		      */
/*   								      */
/*   Lists  have  slow  access,  whereas  splay  trees  have a large  */
/*   space overhead for items.					      */
/**********************************************************************/

/* static char sccs_id[] = "%Z% %M% %R%.%L%"; */

# include	<stdio.h>
# include	"chkalloc.h"
# include	"stype.h"

# if !defined(TRUE)
#	define	TRUE	1
#	define	FALSE	0
# endif

void	qsort();
char	*bsearch();
/**********************************************************************/
/*   Allocate a new stype object.				      */
/**********************************************************************/
stype_t *
st_alloc()
{
	stype_t *sp = (stype_t *) chk_alloc(sizeof *sp);
	
	if (sp == NULL)
		return NULL;
	sp->st_need_sort = FALSE;
	sp->st_bsize = 1;
	sp->st_bitmaps = (sbitmap_t *) chk_alloc(sizeof(sbitmap_t));
	memset(sp->st_bitmaps, 0, sizeof(sbitmap_t));
	sp->st_size = SINT_INCR;
	sp->st_used = 0;
	sp->st_block = (sentry_t *) chk_alloc(SINT_INCR * sizeof(sentry_t));
	return sp;
}
/**********************************************************************/
/*   Free an allocated block.					      */
/**********************************************************************/
void
st_free(sp)
stype_t	*sp;
{
	chk_free((char *) sp->st_bitmaps);
	chk_free((char *) sp->st_block);
	chk_free((char *) sp);
}
/**********************************************************************/
/*   Function  to  add  key to the bitmap in the stype structure. If  */
/*   we  don't  have  a block for this range, then we allocate a new  */
/*   entry.							      */
/**********************************************************************/
void
st_bitmap(sp, key)
stype_t	*sp;
unsigned long	key;
{	sbitmap_t	*bp;
	unsigned long start;
	int	i;

	start = SB_RANGE(key);
	for (i = 0, bp = sp->st_bitmaps; i < sp->st_bsize; i++, bp++) {
		if (bp->sb_start == start)
			break;
		}
	if (i >= sp->st_bsize) {
		sp->st_bitmaps = (sbitmap_t *) 
			chk_realloc((char *) sp->st_bitmaps, 
				++sp->st_bsize * sizeof(sbitmap_t));
		bp = &sp->st_bitmaps[sp->st_bsize - 1];
		memset(bp, 0, sizeof(sbitmap_t));
		bp->sb_start = start;
		}
	key -= start;
	SB_SET(key, bp);
}
/**********************************************************************/
/*   Add  a  new  entry  to  the  object.  No  validation  done  for  */
/*   duplicates.						      */
/**********************************************************************/
void
st_insert(sp, key, ptr)
stype_t	*sp;
unsigned long key;
char	*ptr;
{	sentry_t	*sep;

	st_bitmap(sp, key);

	if (sp->st_used >= sp->st_size) {
		sp->st_size += SINT_INCR;
		sp->st_block = (sentry_t *) chk_realloc((char *) sp->st_block,
			sp->st_size * sizeof (sentry_t));
		}
	sep = &sp->st_block[sp->st_used++];
	sep->se_key = key;
	sep->se_ptr = ptr;
	/***********************************************/
	/*   Only  need  to  force a sort if adding a  */
	/*   value   which   comes  before  the  last  */
	/*   entry in the table.		       */
	/***********************************************/
	if (sp->st_used > 1 && key < sep[-1].se_key)
		sp->st_need_sort = TRUE;
}
/**********************************************************************/
/*   Function to sort the data structure if necessary.		      */
/**********************************************************************/
static int
st_sort_fn(p1, p2)
sentry_t *p1, *p2;
{
	return (int) (p1->se_key - p2->se_key);
}
void
st_sort(sp)
stype_t *sp;
{
	if (sp->st_need_sort == FALSE)
		return;
	sp->st_need_sort = FALSE;
	qsort(sp->st_block, sp->st_used, sizeof(sentry_t), st_sort_fn);
}
 
/**********************************************************************/
/*   Function  to  lookup a key and return the value associated with  */
/*   it.							      */
/**********************************************************************/
static int
st_lookup_fn(key, p)
unsigned long key;
sentry_t *p;
{
	return (int) (key - p->se_key);
}
sentry_t *
st_lookup(sp, key)
stype_t	*sp;
unsigned long key;
{	register sbitmap_t	*bp;
	register int	i;
	int	start, k;
	
	/***********************************************/
	/*   Scan  the  bitmap  quickly  to see if we  */
	/*   can  avoid  doing  a sort and a bsearch.  */
	/*   Most  of  the  time  we're expecting not  */
	/*   to  find  the  thing  we're  looking  up  */
	/*   during the insert phase.		       */
	/***********************************************/
	start = SB_RANGE(key);
	for (bp = sp->st_bitmaps, i = 0; i < sp->st_bsize; i++, bp++) {
		if (bp->sb_start == start) {
			k = key - start;
			if (!SB_ISSET(k, bp))
				return NULL;
			break;
			}
		}
	if (i >= sp->st_bsize)
		return NULL;
	if (sp->st_need_sort)
		st_sort(sp);
	return (sentry_t *) bsearch(key, sp->st_block, sp->st_used,
			sizeof(sentry_t), st_lookup_fn);
}
/**********************************************************************/
/*   Function  to  replace  an  entry in the table. This should only  */
/*   be  called  if you've already done an st_lookup() and know that  */
/*   you are not going to end up with an out of order table.	      */
/**********************************************************************/
void
st_replace(sp, sep, val)
stype_t	*sp;
sentry_t *sep;
char	*val;
{
	sep->se_ptr = val;
}
