/*
 * nasd_cheops_cache.c
 *
 * Cache module for the CHEOPS (storage manager and client clerk)
 *
 * Author: Khalil Amiri, CMU SCS/ECE, July 18 1997
 */
/*
 * Copyright (c) of Carnegie Mellon University, 1996,1997,1998,1999.
 *
 * Permission to reproduce, use, and prepare derivative works of
 * this software for internal use is granted provided the copyright
 * and "No Warranty" statements are included with all reproductions
 * and derivative works. This software may also be redistributed
 * without charge provided that the copyright and "No Warranty"
 * statements are included in all redistributions.
 *
 * NO WARRANTY. THIS SOFTWARE IS FURNISHED ON AN "AS IS" BASIS.
 * CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED AS TO THE MATTER INCLUDING, BUT NOT LIMITED
 * TO: WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY
 * OF RESULTS OR RESULTS OBTAINED FROM USE OF THIS SOFTWARE. CARNEGIE
 * MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT
 * TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
 */

#include <nasd/nasd_options.h>
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <stdlib.h>

#include <sys/errno.h>
#include <nasd/nasd_types.h>
#include <nasd/nasd_mem.h>
#include <nasd/nasd_threadstuff.h>
#include <nasd/nasd_cheops_cache.h>
#include <nasd/nasd_cheops_common.h>
#include <nasd/nasd_cheops_locks.h>

#define KEY_EQ(key1, key2)                                              \
	 ( ( (key1).object_id == (key2).object_id ) &&                        \
	   ( (key1).record_id == (key2).record_id ) )

#define _NASD_CHEOPS_CACHE_DEBUG 4

/* prototypes (forward declarations) */
int _nasd_cheops_cache_compute_htab_size(
  int                             csize);
int _nasd_cheops_cache_hash_key(
  _nasd_cheops_cache_t           *cp,
  _nasd_cheops_cache_key_t        key);
void _nasd_cheops_cache_print_key(
  _nasd_cheops_cache_key_t        key);
nasd_boolean_t _nasd_cheops_cache_lookup_ent(
  _nasd_cheops_cache_t           *cp, 
  _nasd_cheops_cache_key_t        key,
  caddr_t                         re,
  nasd_boolean_t                  miss_allocate);
int _nasd_cheops_cache_get_ent(
  _nasd_cheops_cache_t           *cp,
  _nasd_cheops_cache_ent_t      **ep);
int _nasd_cheops_cache_init_ent(
  _nasd_cheops_cache_t           *cp,
  _nasd_cheops_cache_key_t        key,
  int                             flags,
  _nasd_cheops_cache_ent_t       *ep);
char *_nasd_cheops_cache_type_string(
  _nasd_cheops_cache_type_t       ctype);

/* deallocate cache object */
int 
_nasd_cheops_cache_destroy(
  _nasd_cheops_cache_t           *cp)
{
  int i;

  for (i = 0; i < cp->csize; i++) {
    nasd_mutex_destroy(&cp->ents[i].m);
    nasd_cond_destroy(&cp->ents[i].c);
  }
  nasd_mutex_destroy(&cp->m);
  nasd_mutex_destroy(&cp->stats_m);
  NASD_Free(cp->buckets, cp->nbuckets*sizeof(_nasd_cheops_cache_ent_t));
  NASD_Free(cp->ents, cp->csize*sizeof(_nasd_cheops_cache_ent_t));
  NASD_Free(cp->bufs, cp->csize*cp->bsize );
	NASD_Free(cp, sizeof(_nasd_cheops_cache_t));
  return 0;
}

/* create cache of size csize with entries of size bsize    */
int 
_nasd_cheops_cache_create(
  int                             csize,
  int                             bsize,
  _nasd_cheops_cache_type_t       ctype,
  _nasd_cheops_cache_t          **cpp)
{
  _nasd_cheops_cache_t      *cp;
	int                      hsize;
	int                      i, rc;
	_nasd_cheops_cache_ent_t  *p, *q, *bu;
	caddr_t                  buf;
	_nasd_cheops_cache_key_t  zkey;
	
	zkey.object_id = 0;
	zkey.record_id = 0;

	/* verify arguments */
	if ((csize <= 1) || 
	    ((ctype != _NASD_CHEOPS_CACHE_TYPE_SMAP) &&
       (ctype != _NASD_CHEOPS_CACHE_TYPE_GMAP) &&
	     (ctype != _NASD_CHEOPS_CACHE_TYPE_DMAP) &&
       (ctype != _NASD_CHEOPS_CACHE_TYPE_IMAP)))
	  return EINVAL;
	
	if (csize > _NASD_CHEOPS_MAX_CACHE_SIZE)
	  return EDOM;
	
	/* allocate memory for cache data strutures */
	NASD_Malloc(cp, sizeof(_nasd_cheops_cache_t), (_nasd_cheops_cache_t *));
	if (cp == NULL) {
	  return ENOMEM;
	}
	bzero((char *)cp, sizeof(_nasd_cheops_cache_t));
	*cpp = cp;
	cp->ctype = ctype;
	cp->victim = 0;
	
	cp->csize = csize;
	NASD_Malloc(cp->ents, csize*sizeof(_nasd_cheops_cache_ent_t),
              (_nasd_cheops_cache_ent_t *));
	if ( cp->ents == NULL) {
	  NASD_Free(cp, sizeof(_nasd_cheops_cache_t));
	  *cpp = NULL;
	  return ENOMEM;
	}

	/* set up hash table */
	hsize = _nasd_cheops_cache_compute_htab_size(csize);
	cp->nbuckets = hsize;
	NASD_Malloc(cp->buckets, cp->nbuckets*sizeof(_nasd_cheops_cache_ent_t),
              (_nasd_cheops_cache_ent_t *));
	if (cp->buckets == NULL) {
	  NASD_Free(cp, sizeof(_nasd_cheops_cache_t));
	  *cpp = NULL;
	  return ENOMEM;
	}
	bzero((char*)cp->buckets, cp->nbuckets*sizeof(_nasd_cheops_cache_ent_t));
	
	for(i=0;i<cp->nbuckets;i++) {
	  bu = &cp->buckets[i];
	  bu->bnext = bu->bprev = bu;
	}

	/* put all entries on freelist */
	for (i=0; i<csize; i++) {
	  p = &(cp->ents[i]);
	  if (i==0) {
	    cp->free_list = p;
	    q = p;
	  } else {
	    q->fnext = p;
	    p->fnext = NULL;
	    q = p;
	  }
	  
	  rc = nasd_mutex_init(&cp->ents[i].m);
	  if (rc) {
	    NASD_Free(cp->ents, cp->csize*sizeof(_nasd_cheops_cache_ent_t));
	    NASD_Free(cp, sizeof(_nasd_cheops_cache_t));
	    *cpp = NULL;
	    return (ENOMEM);
	  }
	  /* FIX -- should we bzero entries ? */
	  cp->ents[i].flags = _NASD_CHEOPS_CACHE_ENT_FREE;
	  cp->ents[i].hold_wait_refcnt = 0;
	  cp->ents[i].key = zkey;	  
	  cp->ents[i].data = NULL;
	}

	/* buffers */
	cp->bsize = bsize;
	NASD_Malloc( buf, csize*bsize, (caddr_t ));
	if (buf==NULL) {
	  NASD_Free(cp->buckets, cp->nbuckets*sizeof(_nasd_cheops_cache_ent_t));
	  NASD_Free(cp->ents, cp->csize*sizeof(_nasd_cheops_cache_ent_t));
	  NASD_Free(cp, sizeof(_nasd_cheops_cache_t));
	  *cpp = NULL;
	  return ENOMEM;
	}

	for (i=0;i<csize;i++)
	  cp->ents[i].data = buf + i*bsize;	
	cp->bufs = buf;

	/* init cache lock */
	_nasd_cheops_lockInit(&cp->lock);

	/* init statistics */
  rc = nasd_mutex_init(&cp->stats_m);
	if (rc) {
	  NASD_Free(cp->buckets, cp->nbuckets*sizeof(_nasd_cheops_cache_ent_t));
	  NASD_Free(cp->ents, cp->csize*sizeof(_nasd_cheops_cache_ent_t));
	  NASD_Free(cp->bufs, csize*bsize);
	  NASD_Free(cp, sizeof(_nasd_cheops_cache_t));	  
	  *cpp = NULL;
	  return ENOMEM;                      /* XXX bogus return code */
	}
	cp->stats.misses = cp->stats.hits = cp->stats.updates =
    cp->stats.invalidates = 0;

	/* init lru and dirty */
	cp->lru.lnext = cp->lru.lprev = &cp->lru;
	cp->dirty.dnext = cp->dirty.dprev = &cp->dirty;

	return 0;
}

/* read an entry from the cache, copy it out into user buffer */
int
_nasd_cheops_cache_read(
  _nasd_cheops_cache_t          *cp,
  _nasd_cheops_cache_key_t       key,
  caddr_t                        re)
{
  nasd_boolean_t found, inserted;
  _nasd_cheops_cache_ent_t      *e, *q;
  _nasd_cheops_cache_hash_ent_t *hp;
  int rc;
  int flags;
 /* allocate space and read in entry into the cache on miss */
  nasd_boolean_t miss_allocate;

#if _NASD_CHEOPS_CACHE_DEBUG > 0
  (cheops_Msg "\ncache: received read for object %d ->", key.record_id);
#endif /* _NASD_CHEOPS_CACHE_DEBUG > 0 */

  /* try to look for the entry with a shared lock */
  miss_allocate = NASD_FALSE;
  _nasd_cheops_lockShared(&cp->lock);
  found = _nasd_cheops_cache_lookup_ent(cp, key, re, miss_allocate);
  _nasd_cheops_unlockShared(&cp->lock);
  if (found) {
#if _NASD_CHEOPS_CACHE_DEBUG > 0
    (cheops_Msg "cache: found object %d\n", key.record_id);
#endif /* _NASD_CHEOPS_CACHE_DEBUG > 0 */
    _NASD_CHEOPS_CACHE_INC_STAT(cp, hits);
    return 0;
  } else {
    miss_allocate = NASD_TRUE;
    _nasd_cheops_lockExclusive(&cp->lock);
    inserted = _nasd_cheops_cache_lookup_ent(cp, key, re, miss_allocate);
#if _NASD_CHEOPS_CACHE_DEBUG > 0
    (cheops_Msg "cache: brought in object %d\n", key.record_id);
#endif /* _NASD_CHEOPS_CACHE_DEBUG > 0 */
    _nasd_cheops_unlockExclusive(&cp->lock);
    if (!inserted) {
      return ENOMEM;
    }
    else
      return 0;
  }	 	 
}

/* invalidate an entry */
int 
_nasd_cheops_cache_mark_invalid(
  _nasd_cheops_cache_t          *cp,
  _nasd_cheops_cache_key_t       key)
{
  nasd_boolean_t  found = NASD_FALSE;
	_nasd_cheops_cache_hash_ent_t *hp;
	_nasd_cheops_cache_ent_t      *e;
	_nasd_cheops_cache_ent_t      *enext, *eprev;

  /* XXX write this */

	_NASD_CHEOPS_CACHE_INC_STAT(cp, invalidates);

	return 0;

}

int
_nasd_cheops_cache_mark_dirty(
  _nasd_cheops_cache_t          *cp,
  _nasd_cheops_cache_key_t       key)
{
  nasd_boolean_t  found = NASD_FALSE;
	_nasd_cheops_cache_ent_t *bp;
	_nasd_cheops_cache_ent_t *e;
	_nasd_cheops_cache_ent_t *enext, *eprev;

	_NASD_CHEOPS_CACHE_INC_STAT(cp, updates);
	bp = &( cp->buckets[_nasd_cheops_cache_hash_key(cp, key)] );
	_nasd_cheops_lockExclusive( &cp->lock );
	for (e=bp->bnext; e!=bp; e=e->bnext) {
    if (KEY_EQ(key, e->key)) {
      found = NASD_TRUE; 
      break;
    }
	}

	if ( !found ) {
    _nasd_cheops_unlockExclusive( &cp->lock);
    return _NASD_CHEOPS_ERR_NOTFOUND;
	} else {
    cp->writeback(e, cp->bsize); 	 
    _nasd_cheops_unlockExclusive( &cp->lock);
    return 0;
    /* FIX -- put on dirty list and flush asynchronously later?
	     e->flags = e->flags | _NASD_CHEOPS_CACHE_ENT_DIRTY;
	     e->dnext = &cp->dirty;
	     e->dprev = cp->dirty.dprev;;
	     e->dnext->dprev = e;
	     e->dprev->dnext = e;
       return 0;
    */
	}

}

/* show statistics                                     */
void 
_nasd_cheops_cache_show_stats(
  _nasd_cheops_cache_t          *cp)
{
  (cheops_Msg "Cache statistics: \n");
  (cheops_Msg "\t hits: %lu", cp->stats.hits);
  (cheops_Msg "\t misses: %lu", cp->stats.misses);
  (cheops_Msg "\t invalidates: %lu", cp->stats.invalidates);
  (cheops_Msg "\t fixes: %lu\n", cp->stats.fixes);
}

/* */
int
_nasd_cheops_cache_hold_ent(
  _nasd_cheops_cache_t           *cp, 
  _nasd_cheops_cache_key_t        key, 
  _nasd_cheops_cache_ent_t      **ep)
{
  nasd_boolean_t found=NASD_FALSE;
	_nasd_cheops_cache_ent_t *e, *ne;
	_nasd_cheops_cache_ent_t *bp;
	int rc;
	int flags;
	
	_nasd_cheops_lockExclusive( &cp->lock);
	
	bp = &( cp->buckets[_nasd_cheops_cache_hash_key(cp, key)] );
	for (e=bp->bnext; e!=bp; e=e->bnext) {
	  if (KEY_EQ(key, e->key)) {
	    found = NASD_TRUE; 
	    break;
	  }
	}
	
	if (found) {
	  _NASD_CHEOPS_CACHE_ENT_LOCK(e);
	  /* quiesce, so that we do not get junk when we bcopy */
	  while (e->flags&_NASD_CHEOPS_CACHE_ENT_HOLD) {
	    e->hold_wait_refcnt++;
      _NASD_CHEOPS_CACHE_ENT_WAIT(e);
      e->hold_wait_refcnt--;
	  }
	  e->flags |= _NASD_CHEOPS_CACHE_ENT_HOLD;
	  _NASD_CHEOPS_CACHE_ENT_UNLOCK(e);
	  /* remove from LRU */
	  e->lnext->lprev = e->lprev;
	  e->lprev->lnext = e->lnext;
	  /* put at head of LRU */
	  e->lnext = &cp->lru;
	  e->lprev = cp->lru.lprev;
	  e->lnext->lprev = e;
	  e->lprev->lnext = e;
	  *ep = e;
	  _nasd_cheops_unlockExclusive( &cp->lock);
#if _NASD_CHEOPS_CACHE_DEBUG > 0
    fprintf(stderr, "CHEOPS: CACHE DEBUG: hold_ent cache@0x%08lx<%s>"
            " key=<%d,%d> found e@%08lx\n", cp,
            _nasd_cheops_cache_type_string(cp->ctype),
            key.object_id, key.record_id, e);
#endif /* _NASD_CHEOPS_CACHE_DEBUG > 0 */
	  return 0;
  }
		
	/* entry was not found */
	rc = _nasd_cheops_cache_get_ent(cp, &ne);
	if (rc) {
	  _nasd_cheops_unlockExclusive( &cp->lock);
	  return rc;
	}
	flags = 0;
	_nasd_cheops_cache_init_ent(cp, key, flags, ne);
	
	ne->bnext = bp->bnext;
	ne->bprev = ne->bnext->bprev;
	ne->bnext->bprev = ne;
	ne->bprev->bnext = ne;
	
	ne->lnext = &cp->lru;
	ne->lprev = ne->lnext->lprev;
	ne->lnext->lprev = ne;
	ne->lprev->lnext = ne;
	*ep = ne;
  _nasd_cheops_unlockExclusive( &cp->lock);
       
  _NASD_CHEOPS_CACHE_ENT_LOCK(ne);
  while(ne->flags&_NASD_CHEOPS_CACHE_ENT_HOLD) {
    ne->hold_wait_refcnt++;
    _NASD_CHEOPS_CACHE_ENT_WAIT(ne);
    ne->hold_wait_refcnt--;
  }

  ne->flags |= _NASD_CHEOPS_CACHE_ENT_HOLD;
  _NASD_CHEOPS_CACHE_ENT_UNLOCK(ne);       
       
  return 0;
}

/* unfix an entry, so it can be replaced */
int
_nasd_cheops_cache_release_ent(
  _nasd_cheops_cache_t          *cp, 
  _nasd_cheops_cache_ent_t      *ep)
{
	/* decrement refcnt and broadcast condition */
  _NASD_CHEOPS_CACHE_ENT_LOCK(ep);
	ep->flags = ep->flags & ~_NASD_CHEOPS_CACHE_ENT_HOLD;
	if ( (ep->hold_wait_refcnt) )
    /* if there is someone waiting to hold the entry */
	  _NASD_CHEOPS_CACHE_ENT_BCAST(ep);
	_NASD_CHEOPS_CACHE_ENT_UNLOCK(ep);	
	return 0;	
}

/*************************** Internal methods **********************************/

/* lookup an entry in the cache given a key, call with cp->lock held (shared
   or exclusive) */
/* returns 1 if entry is found or successfully brought in, 0 if not */

nasd_boolean_t
_nasd_cheops_cache_lookup_ent(
  _nasd_cheops_cache_t          *cp,
  _nasd_cheops_cache_key_t       key, 
  caddr_t                        re,
  nasd_boolean_t                 miss_allocate)
{
  nasd_boolean_t  found = NASD_FALSE;
	_nasd_cheops_cache_ent_t      *e;
	_nasd_cheops_cache_ent_t      *bp;
	_nasd_cheops_cache_ent_t      *ne;
	int rc, flags;

#if _NASD_CHEOPS_CACHE_DEBUG > 1
  (cheops_Msg "[lookup_ent cp@%08lx key=<%d,%d> re@%08lx ma=%d ...",
   cp, key.object_id, key.record_id, re, miss_allocate);
#endif /* _NASD_CHEOPS_CACHE_DEBUG > 1 */

	bp = &( cp->buckets[_nasd_cheops_cache_hash_key(cp, key)] );
	for (e=bp->bnext; e!=bp; e=e->bnext) {
	  if (KEY_EQ(key, e->key)) {
	    found = NASD_TRUE; 
	    break;
	  }
	}

	if (found) {
#if _NASD_CHEOPS_CACHE_DEBUG > 1
    (cheops_Msg "entry found...");
#endif /* _NASD_CHEOPS_CACHE_DEBUG > 1 */
	  _NASD_CHEOPS_CACHE_ENT_LOCK(e);
#if _NASD_CHEOPS_CACHE_DEBUG > 1
    (cheops_Msg "got lock...");
#endif /* _NASD_CHEOPS_CACHE_DEBUG > 1 */
	  /* quiesce, so that we do not get junk when we bcopy */
	  while(e->flags&_NASD_CHEOPS_CACHE_ENT_HOLD) {
	    e->hold_wait_refcnt++;
	    _NASD_CHEOPS_CACHE_ENT_WAIT(e);
	    e->hold_wait_refcnt--;
	  }
#if _NASD_CHEOPS_CACHE_DEBUG > 1
    (cheops_Msg "quiesced...");
#endif /* _NASD_CHEOPS_CACHE_DEBUG > 1 */
	  bcopy(e->data, re, cp->bsize);
	  _NASD_CHEOPS_CACHE_ENT_UNLOCK(e);
	  /* remove from LRU */
	  e->lnext->lprev = e->lprev;
	  e->lprev->lnext = e->lnext;
	  /* put at head of LRU */
	  e->lnext = &cp->lru;
	  e->lprev = cp->lru.lprev;
	  e->lnext->lprev = e;
	  e->lprev->lnext = e;
#if _NASD_CHEOPS_CACHE_DEBUG > 1
    (cheops_Msg "done]\n");
#endif /* _NASD_CHEOPS_CACHE_DEBUG > 1 */
	} else if (miss_allocate == NASD_FALSE) {
#if _NASD_CHEOPS_CACHE_DEBUG > 1
    (cheops_Msg "not found, returning FALSE]\n");
#endif /* _NASD_CHEOPS_CACHE_DEBUG > 1 */
	  found = NASD_FALSE;
	} else {
#if _NASD_CHEOPS_CACHE_DEBUG > 1
    (cheops_Msg "not found, faulting it in...");
#endif /* _NASD_CHEOPS_CACHE_DEBUG > 1 */
	  /* miss allocate -- assumes cache locked exclusively !! */
	  rc = _nasd_cheops_cache_get_ent(cp, &ne);
	  if (rc) {
#if _NASD_CHEOPS_CACHE_DEBUG > 1
      (cheops_Msg "failed get, rc=%d]\n", rc);
#endif /* _NASD_CHEOPS_CACHE_DEBUG > 1 */
	    found =  NASD_FALSE;
    } else {
#if _NASD_CHEOPS_CACHE_DEBUG > 1
      (cheops_Msg "got fresh ent@%08lx...", ne);
#endif /* _NASD_CHEOPS_CACHE_DEBUG > 1 */
      flags = _NASD_CHEOPS_CACHE_ENT_LRU;
      rc = _nasd_cheops_cache_init_ent(cp, key, flags, ne);
      if (rc) {
#if _NASD_CHEOPS_CACHE_DEBUG > 1
        (cheops_Msg "failed fault-in, rc=%d]\n", rc);
#endif /* _NASD_CHEOPS_CACHE_DEBUG > 1 */
        found = NASD_FALSE;
      } else {
        /* insert into hash chain */
        ne->bnext = bp->bnext;
        ne->bprev = ne->bnext->bprev;
        ne->bnext->bprev = ne;
        ne->bprev->bnext = ne;
        /* insert at head of LRU */
        ne->lnext = &cp->lru;
        ne->lprev = ne->lnext->lprev;
        ne->lnext->lprev = ne;
        ne->lprev->lnext = ne;
        bcopy(ne->data, re, cp->bsize);
#if _NASD_CHEOPS_CACHE_DEBUG > 1
        (cheops_Msg "succeeded]\n");
#endif /* _NASD_CHEOPS_CACHE_DEBUG > 1 */
        found = NASD_TRUE; /* entry has been successfully inserted */
      }
    }
	}

  return found;
}

/* get an entry, called with cp->lock held in exclusive mode */
int
_nasd_cheops_cache_get_ent(
  _nasd_cheops_cache_t           *cp,
  _nasd_cheops_cache_ent_t      **ep)
{
	_nasd_cheops_cache_ent_t      *e;

	/* take an entry from the free list if one is there  */
	if (cp->free_list != NULL) {
#if _NASD_CHEOPS_CACHE_DEBUG > 2
    (cheops_Msg "[get_ent satisfying request out of free list]\n");
#endif /* _NASD_CHEOPS_CACHE_DEBUG > 2 */
	  e = cp->free_list;
	  cp->free_list = e->fnext;
	  e->fnext = NULL;
	  *ep = e;
	  return 0;
	}
#if _NASD_CHEOPS_CACHE_DEBUG > 2
    (cheops_Msg "[get_ent trying to eject LRU entry]\n");
#endif /* _NASD_CHEOPS_CACHE_DEBUG > 2 */
	/* otherwise, eject an entry from the lru list */
	for(e = cp->lru.lnext; e != &cp->lru; e = e->lnext) {
	  if (e->flags&_NASD_CHEOPS_CACHE_ENT_DIRTY) {
	    continue;
	  }
	  if (e->flags&_NASD_CHEOPS_CACHE_ENT_BUSY) {
	    continue;
	  }
	  if (e->flags&_NASD_CHEOPS_CACHE_ENT_HOLD) {
	    continue;
	  }
	  if (e->hold_wait_refcnt) {
	    continue;
	  }

	  /* found a block */
	  e->lprev->lnext = e->lnext;
	  e->lnext->lprev = e->lprev;
	  e->bnext->bprev = e->bprev;
	  e->bprev->bnext = e->bnext;
	  *ep = e;
	  return 0;
	}
#if _NASD_CHEOPS_CACHE_DEBUG > 2
    (cheops_Msg "[get_ent could not get a free cache ent]\n");
#endif /* _NASD_CHEOPS_CACHE_DEBUG > 2 */
	return ENOMEM;
}

char *
_nasd_cheops_cache_type_string(
  _nasd_cheops_cache_type_t      ctype)
{
  char *str = "?UNKNOWN CHEOPS CACHE TYPE?";

  switch (ctype) {
  case _NASD_CHEOPS_CACHE_TYPE_SMAP:    str = "SMAP";   break;
  case _NASD_CHEOPS_CACHE_TYPE_GMAP:    str = "GMAP";   break;
  case _NASD_CHEOPS_CACHE_TYPE_DMAP:    str = "DMAP";   break;
  case _NASD_CHEOPS_CACHE_TYPE_IMAP:    str = "IMAP";   break;
  }
  return str;
}

int
_nasd_cheops_cache_init_ent(
  _nasd_cheops_cache_t          *cp,
  _nasd_cheops_cache_key_t       key,
  int                            flags,
  _nasd_cheops_cache_ent_t      *ep)
{
  int rc;

#if _NASD_CHEOPS_CACHE_DEBUG > 1
  (cheops_Msg "[init_ent faulting in <%d,%d> @%08lx<%s>...", key.object_id,
   key.record_id, cp, _nasd_cheops_cache_type_string(cp->ctype));
#endif /* _NASD_CHEOPS_CACHE_DEBUG */
	rc = (cp->handle_read_fault)(key, ep->data);
#if _NASD_CHEOPS_CACHE_DEBUG > 1
  (cheops_Msg " rc=%d...", rc);
#endif /* _NASD_CHEOPS_CACHE_DEBUG > 1 */
  if (!rc) {
    ep->key = key;
    ep->flags = flags;
  }
#if _NASD_CHEOPS_CACHE_DEBUG > 1
  (cheops_Msg " done]\n");
#endif /* _NASD_CHEOPS_CACHE_DEBUG > 1 */
	return rc;
}

int
_nasd_cheops_cache_compute_htab_size(
  int                            csize)
{
  int n, i;

  n = csize / 10;
  for (i=1; i<=n; i++)  
    i = 2*i;	 
  i--;
  return ( (i==0)? 1: i);
}

int
_nasd_cheops_cache_hash_key(
  _nasd_cheops_cache_t          *cp,
  _nasd_cheops_cache_key_t       key)
{
  int part1, part2;
	int bitmask = 0xffaffa;
	int hash;
	int prime1 = 41;

	part1 = key.object_id;
	part2 = key.record_id;       
	/* hash = (part1*prime1) + part2&bitmask); */
	hash = part2;
	hash = hash % cp->nbuckets;
	return hash;
}

void
_nasd_cheops_cache_print_key(
  _nasd_cheops_cache_key_t       key)
{
	(cheops_Msg "(%d,%d)", key.object_id, key.record_id);
}

/* Local Variables:  */
/* indent-tabs-mode: nil */
/* tab-width: 2 */
/* End: */
