Files
epics-base/src/libCom/bucketLib.c
Jeff Hill 39edab9046 cosmetic
1998-02-05 21:29:14 +00:00

601 lines
13 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* Author: Jeffrey O. Hill
* hill@atdiv.lanl.gov
* (505) 665 1831
* Date: 9-93
*
* Experimental Physics and Industrial Control System (EPICS)
*
* Copyright 1991, the Regents of the University of California,
* and the University of Chicago Board of Governors.
*
* This software was produced under U.S. Government contracts:
* (W-7405-ENG-36) at the Los Alamos National Laboratory,
* and (W-31-109-ENG-38) at Argonne National Laboratory.
*
* Initial development by:
* The Controls and Automation Group (AT-8)
* Ground Test Accelerator
* Accelerator Technology Division
* Los Alamos National Laboratory
*
* Co-developed with
* The Controls and Computing Group
* Accelerator Systems Division
* Advanced Photon Source
* Argonne National Laboratory
*
* Modification Log:
* -----------------
* .01 091493 joh fixed overzealous parameter check
* .02 121693 joh added bucketFree()
*
* NOTES:
* .01 Storage for identifier must persist until an item is deleted
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <math.h>
#include <time.h>
#define epicsExportSharedSymbols
#include "epicsAssert.h"
#include "freeList.h" /* bucketLib uses freeListLib inside the DLL */
#include "bucketLib.h"
#ifndef TRUE
#define TRUE 1
#endif /* TRUE */
#ifndef FALSE
#define FALSE 0
#endif /* FALSE */
#ifndef LOCAL
#define LOCAL static
#endif /* LOCAL */
#ifndef max
#define max(A,B) ((A)>(B)?(A):(B))
#endif /* max */
/*
* these data type dependent routines are
* provided in the bucketLib.c
*/
typedef BUCKETID bucketHash(BUCKET *pb, const void *pId);
typedef ITEM **bucketCompare(ITEM **ppi, const void *pId);
LOCAL bucketCompare bucketUnsignedCompare;
LOCAL bucketCompare bucketPointerCompare;
LOCAL bucketCompare bucketStringCompare;
LOCAL bucketHash bucketUnsignedHash;
LOCAL bucketHash bucketPointerHash;
LOCAL bucketHash bucketStringHash;
typedef struct {
bucketHash *pHash;
bucketCompare *pCompare;
buckTypeOfId type;
}bucketSET;
LOCAL bucketSET BSET[] = {
{bucketUnsignedHash, bucketUnsignedCompare, bidtUnsigned},
{bucketPointerHash, bucketPointerCompare, bidtPointer},
{bucketStringHash, bucketStringCompare, bidtString}
};
LOCAL int bucketAddItem(BUCKET *prb, bucketSET *pBSET,
const void *pId, const void *pApp);
LOCAL int bucketRemoveItem (BUCKET *prb, bucketSET *pBSET, const void *pId);
LOCAL void *bucketLookupItem(BUCKET *pb, bucketSET *pBSET, const void *pId);
/*
* bucket id bit width
*/
#define BUCKETID_BIT_WIDTH (sizeof(BUCKETID)*CHAR_BIT)
/*
* Maximum bucket size
*/
#define BUCKET_MAX_WIDTH 12
#ifdef DEBUG
main()
{
unsigned id1;
unsigned id2;
char *pValSave1;
char *pValSave2;
int s;
BUCKET *pb;
char *pVal;
unsigned i;
clock_t start, finish;
double duration;
const int LOOPS = 500000;
pb = bucketCreate(8);
if(!pb){
return -1;
}
id1 = 0x1000a432;
pValSave1 = "fred";
s = bucketAddItemUnsignedId(pb, &id1, pValSave1);
assert (s == S_bucket_success);
pValSave2 = "jane";
id2 = 0x0000a432;
s = bucketAddItemUnsignedId(pb, &id2, pValSave2);
assert (s == S_bucket_success);
start = clock();
for(i=0; i<LOOPS; i++){
pVal = bucketLookupItemUnsignedId(pb, &id1);
assert(pVal == pValSave1);
pVal = bucketLookupItemUnsignedId(pb, &id1);
assert(pVal == pValSave1);
pVal = bucketLookupItemUnsignedId(pb, &id1);
assert(pVal == pValSave1);
pVal = bucketLookupItemUnsignedId(pb, &id1);
assert(pVal == pValSave1);
pVal = bucketLookupItemUnsignedId(pb, &id1);
assert(pVal == pValSave1);
pVal = bucketLookupItemUnsignedId(pb, &id1);
assert(pVal == pValSave1);
pVal = bucketLookupItemUnsignedId(pb, &id1);
assert(pVal == pValSave1);
pVal = bucketLookupItemUnsignedId(pb, &id1);
assert(pVal == pValSave1);
pVal = bucketLookupItemUnsignedId(pb, &id1);
assert(pVal == pValSave1);
pVal = bucketLookupItemUnsignedId(pb, &id2);
assert(pVal == pValSave2);
}
finish = clock();
duration = finish-start;
duration = duration/CLOCKS_PER_SEC;
printf("It took %15.10f total sec\n", duration);
duration = duration/LOOPS;
duration = duration/10;
duration = duration * 1e6;
printf("It took %15.10f u sec per hash lookup\n", duration);
bucketShow(pb);
return S_bucket_success;
}
#endif
/*
* bucketUnsignedCompare()
*/
LOCAL ITEM **bucketUnsignedCompare (ITEM **ppi, const void *pId)
{
unsigned id;
unsigned *pItemId;
ITEM *pi;
id = * (unsigned *) pId;
while ( (pi = *ppi) ) {
if (bidtUnsigned == pi->type) {
pItemId = (unsigned *) pi->pId;
if (id == *pItemId) {
return ppi;
}
}
ppi = &pi->pItem;
}
return NULL;
}
/*
* bucketPointerCompare()
*/
LOCAL ITEM **bucketPointerCompare (ITEM **ppi, const void *pId)
{
void *ptr;
void **pItemId;
ITEM *pi;
ptr = * (void **) pId;
while ( (pi = *ppi) ) {
if (bidtPointer == pi->type ) {
pItemId = (void **) pi->pId;
if (ptr == *pItemId) {
return ppi;
}
}
ppi = &pi->pItem;
}
return NULL;
}
/*
* bucketStringCompare ()
*/
LOCAL ITEM **bucketStringCompare (ITEM **ppi, const void *pId)
{
const char *pStr = pId;
ITEM *pi;
int status;
while ( (pi = *ppi) ) {
if (bidtString == pi->type) {
status = strcmp (pStr, (char *)pi->pId);
if (status == '\0') {
return ppi;
}
}
ppi = &pi->pItem;
}
return NULL;
}
/*
* bucketUnsignedHash ()
*/
LOCAL BUCKETID bucketUnsignedHash (BUCKET *pb, const void *pId)
{
const unsigned *pUId = pId;
unsigned src;
BUCKETID hashid;
src = *pUId;
hashid = src;
src = src >> pb->hashIdNBits;
while (src) {
hashid = hashid ^ src;
src = src >> pb->hashIdNBits;
}
hashid = hashid & pb->hashIdMask;
return hashid;
}
/*
* bucketPointerHash ()
*/
LOCAL BUCKETID bucketPointerHash (BUCKET *pb, const void *pId)
{
void * const *ppId = (void * const *) pId;
unsigned long src;
BUCKETID hashid;
/*
* This makes the assumption that
* a pointer will fit inside of a long
* (this assumption may not port to all
* CPU architectures)
*/
src = (unsigned long) *ppId;
hashid = src;
src = src >> pb->hashIdNBits;
while(src){
hashid = hashid ^ src;
src = src >> pb->hashIdNBits;
}
hashid = hashid & pb->hashIdMask;
return hashid;
}
/*
* bucketStringHash ()
*/
LOCAL BUCKETID bucketStringHash (BUCKET *pb, const void *pId)
{
const char *pStr = pId;
BUCKETID hashid;
unsigned i;
hashid = 0;
i = 1;
while(*pStr){
hashid += *pStr * i;
pStr++;
i++;
}
hashid = hashid % (pb->hashIdMask+1);
return hashid;
}
/*
* bucketCreate()
*/
epicsShareFunc BUCKET * epicsShareAPI bucketCreate (unsigned nHashTableEntries)
{
BUCKETID mask;
unsigned nbits;
BUCKET *pb;
/*
* no absurd sized buckets
*/
if (nHashTableEntries<=1) {
fprintf (stderr, "Tiny bucket create failed\n");
return NULL;
}
/*
* count the number of bits in the bucket id
*/
for (nbits=0; nbits<BUCKETID_BIT_WIDTH; nbits++) {
mask = (1<<nbits) - 1;
if ( ((nHashTableEntries-1) & ~mask) == 0){
break;
}
}
/*
* indexWidth must be specified at least one
* bit less than the bit size of type BUCKETID
*/
if (nbits>=BUCKETID_BIT_WIDTH) {
fprintf (
stderr,
"%s at %d: Requested index width=%d to large. max=%ld\n",
__FILE__,
__LINE__,
nbits,
(long)(BUCKETID_BIT_WIDTH-1));
return NULL;
}
pb = (BUCKET *) calloc(1, sizeof(*pb));
if (!pb) {
return pb;
}
pb->hashIdMask = mask;
pb->hashIdNBits = nbits;
freeListInitPvt(&pb->freeListPVT, sizeof(ITEM), 1024);
pb->pTable = (ITEM **) calloc (mask+1, sizeof(*pb->pTable));
if (!pb->pTable) {
freeListCleanup(pb->freeListPVT);
free (pb);
return NULL;
}
return pb;
}
/*
* bucketFree()
*/
epicsShareFunc int epicsShareAPI bucketFree (BUCKET *prb)
{
/*
* deleting a bucket with entries in use
* will cause memory leaks and is not allowed
*/
assert (prb->nInUse==0);
/*
* free the free list
*/
freeListCleanup(prb->freeListPVT);
free (prb->pTable);
free (prb);
return S_bucket_success;
}
/*
* bucketAddItem()
*/
epicsShareFunc int epicsShareAPI
bucketAddItemUnsignedId(BUCKET *prb, const unsigned *pId, const void *pApp)
{
return bucketAddItem(prb, &BSET[bidtUnsigned], pId, pApp);
}
epicsShareFunc int epicsShareAPI
bucketAddItemPointerId(BUCKET *prb, void * const *pId, const void *pApp)
{
return bucketAddItem(prb, &BSET[bidtPointer], pId, pApp);
}
epicsShareFunc int epicsShareAPI
bucketAddItemStringId(BUCKET *prb, const char *pId, const void *pApp)
{
return bucketAddItem(prb, &BSET[bidtString], pId, pApp);
}
LOCAL int bucketAddItem(BUCKET *prb, bucketSET *pBSET, const void *pId, const void *pApp)
{
BUCKETID hashid;
ITEM **ppi;
ITEM **ppiExists;
ITEM *pi;
/*
* try to get it off the free list first. If
* that fails then malloc()
*/
pi = (ITEM *) freeListMalloc(prb->freeListPVT);
if (!pi) {
return S_bucket_noMemory;
}
/*
* create the hash index
*/
hashid = (*pBSET->pHash) (prb, pId);
pi->pApp = pApp;
pi->pId = pId;
pi->type = pBSET->type;
assert ((hashid & ~prb->hashIdMask) == 0);
ppi = &prb->pTable[hashid];
/*
* Dont reuse a resource id !
*/
ppiExists = (*pBSET->pCompare) (ppi, pId);
if (ppiExists) {
freeListFree(prb->freeListPVT,pi);
return S_bucket_idInUse;
}
pi->pItem = *ppi;
prb->pTable[hashid] = pi;
prb->nInUse++;
return S_bucket_success;
}
/*
* bucketRemoveItem()
*/
epicsShareFunc int epicsShareAPI
bucketRemoveItemUnsignedId (BUCKET *prb, const unsigned *pId)
{
return bucketRemoveItem(prb, &BSET[bidtUnsigned], pId);
}
epicsShareFunc int epicsShareAPI
bucketRemoveItemPointerId (BUCKET *prb, void * const *pId)
{
return bucketRemoveItem(prb, &BSET[bidtPointer], pId);
}
epicsShareFunc int epicsShareAPI
bucketRemoveItemStringId (BUCKET *prb, const char *pId)
{
return bucketRemoveItem(prb, &BSET[bidtString], pId);
}
LOCAL int bucketRemoveItem (BUCKET *prb, bucketSET *pBSET, const void *pId)
{
BUCKETID hashid;
ITEM **ppi;
ITEM *pi;
/*
* create the hash index
*/
hashid = (*pBSET->pHash) (prb, pId);
assert((hashid & ~prb->hashIdMask) == 0);
ppi = &prb->pTable[hashid];
ppi = (*pBSET->pCompare) (ppi, pId);
if(!ppi){
return S_bucket_uknId;
}
prb->nInUse--;
pi = *ppi;
*ppi = pi->pItem;
/*
* stuff it on the free list
*/
freeListFree(prb->freeListPVT, pi);
return S_bucket_success;
}
/*
* bucketLookupItem()
*/
epicsShareFunc void * epicsShareAPI
bucketLookupItemUnsignedId (BUCKET *prb, const unsigned *pId)
{
return bucketLookupItem(prb, &BSET[bidtUnsigned], pId);
}
epicsShareFunc void * epicsShareAPI
bucketLookupItemPointerId (BUCKET *prb, void * const *pId)
{
return bucketLookupItem(prb, &BSET[bidtPointer], pId);
}
epicsShareFunc void * epicsShareAPI
bucketLookupItemStringId (BUCKET *prb, const char *pId)
{
return bucketLookupItem(prb, &BSET[bidtString], pId);
}
LOCAL void *bucketLookupItem (BUCKET *pb, bucketSET *pBSET, const void *pId)
{
BUCKETID hashid;
ITEM **ppi;
/*
* create the hash index
*/
hashid = (*pBSET->pHash) (pb, pId);
assert((hashid & ~pb->hashIdMask) == 0);
/*
* at the bottom level just
* linear search for it.
*/
ppi = (*pBSET->pCompare) (&pb->pTable[hashid], pId);
if(ppi){
return (void *) (*ppi)->pApp;
}
return NULL;
}
/*
* bucketShow()
*/
epicsShareFunc int epicsShareAPI bucketShow(BUCKET *pb)
{
ITEM **ppi;
ITEM *pi;
unsigned nElem;
double X;
double XX;
double mean;
double stdDev;
unsigned count;
unsigned maxEntries;
printf( "Bucket entries in use = %d bytes in use = %ld\n",
pb->nInUse,
(long) (sizeof(*pb)+(pb->hashIdMask+1)*
sizeof(ITEM *)+pb->nInUse*sizeof(ITEM)));
ppi = pb->pTable;
nElem = pb->hashIdMask+1;
X = 0.0;
XX = 0.0;
maxEntries = 0;
while (ppi < &pb->pTable[nElem]) {
pi = *ppi;
count = 0;
while (pi) {
count++;
pi = pi->pItem;
}
X += count;
XX += count*count;
maxEntries = max (count, maxEntries);
ppi++;
}
mean = X/nElem;
stdDev = sqrt(XX/nElem - mean*mean);
printf( "Bucket entries/hash id - mean = %f std dev = %f max = %d\n",
mean,
stdDev,
maxEntries);
return S_bucket_success;
}