/* * 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 #include #include #include #include #include #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; itype) { 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) { 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; }