#ifndef __VDU_H #define __VDU_H typedef ino64_t KEY; typedef KEY *PKEY; typedef struct stat64 VAL; typedef VAL *PVAL; static inline unsigned int HASH(PKEY key){ return (int) *key; } static inline unsigned int // boolean EQUAL(PKEY key1, PKEY key2){ return *key1 == *key2; } #ifndef MIN #define MIN(x,y) (((x)<(y))?(x):(y)) #endif // MIN #ifndef MAX #define MAX(x,y) (((x)>(y))?(x):(y)) #endif // MAX //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// // hash table support for efficient lookup of duplicate inodes */ //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// #define Multiplier (0x9e3779b9) #define MaxLogBuckets (((sizeof (unsigned long))*8) - 2) #define MaxBuckets (1< 2*MinDensity; *) // to avoid excessive probes, we must try to keep MaxDensity low. *) // Divide by 100 before using #define MaxDensity 75 /* max numEntries/NUMBER(buckets) */ #define MinDensity 20 /* min numEntries/NUMBER(buckets) */ #define IdealDensity 50 #define BITSIZE(x) (sizeof(x)*8) #define NEW(type,num) ((type*)malloc(sizeof(type)*num)) #define DISPOSE(ptr) (free((void*)ptr)) //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// // Generic Hash Entry Type //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// typedef struct VoidList { struct VoidList *tail; } VoidList, *PVoidList; typedef struct HashTable { PVoidList *buckets; unsigned int numBuckets; // number of buckets unsigned int minLogBuckets; // minimum value for Log_2(initial size) unsigned int logBuckets; // CEILING(Log2(NUMBER(buckets^))) unsigned int maxEntries; // maximum number of entries unsigned int minEntries; // minimum number of entries unsigned int numEntries; // current num of entries in table PVoidList cache; // cache of removed elements int cacheSize; // current size of the cache int maxCacheSize; // maximum size, -1 means unbounded, 0 no cache } HashTable, *PHashTable; //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// // Hash Prototypes //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// PHashTable Init(PHashTable tbl, unsigned int n, int maxCacheSize); void Dispose(PHashTable tbl); unsigned int Log_2(unsigned int x); void NewBuckets(PHashTable tbl, unsigned int logBuckets); // // Generic Hash Table support // PHashTable Init(PHashTable tbl, unsigned int n, int maxCacheSize){ int idealBuckets; int minBuckets; idealBuckets = MIN(((n*100)/IdealDensity),MaxBuckets); minBuckets = MAX(MinBuckets, idealBuckets); tbl->minLogBuckets = Log_2(minBuckets); NewBuckets(tbl, tbl->minLogBuckets); tbl->numEntries = 0; tbl->maxCacheSize = maxCacheSize; tbl->cacheSize = 0; tbl->cache = 0; return tbl; } // Init() // // Internal procedures // unsigned int Log_2(unsigned int x){ // return CEILING(LOG_2(x)) unsigned int log = 0; unsigned int n= 1; assert(x != 0); while ((log < MaxLogBuckets) && (x > n)){ log++; n += n; } return log; } // Log_2() void NewBuckets(PHashTable tbl, unsigned int logBuckets){ // Allocate "2^logBuckets" buckets. unsigned int numBuckets = 1 << logBuckets; PVoidList *b; unsigned int i; tbl->buckets = NEW(PVoidList, numBuckets); tbl->numBuckets = numBuckets; b = tbl->buckets; for (i=0; inumBuckets; i++){ b[i] = NULL; } tbl->logBuckets = logBuckets; tbl->maxEntries = MaxDensity * numBuckets / 100; tbl->minEntries = MinDensity * numBuckets / 100; } // NewBuckets() #ifndef NULL #define NULL (void*)0 #endif // !NULL #ifndef TRUE #define TRUE 1 #endif // !TRUE #ifndef FALSE #define FALSE 0 #endif // !FALSE //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// // Type specific hash entry //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// typedef struct EntryList { struct EntryList *tail; KEY key; VAL val; }EntryList, *PEntryList; //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// // Type specific Hash implementation functions //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// static void Rehash(PHashTable tbl, unsigned int logBuckets) { // Reallocate "2^logBuckets" buckets, and rehash the entries into // the new table. PVoidList *oldBucketPointer; PEntryList *ob, obi; PEntryList *nb, *nbh; PEntryList that, tail; unsigned int index; unsigned int i; unsigned int oldNumBuckets; return; assert(logBuckets <= MaxLogBuckets); assert(logBuckets >= tbl->minLogBuckets); oldBucketPointer = tbl->buckets; ob = (PEntryList*)tbl->buckets; oldNumBuckets = tbl->numBuckets; NewBuckets(tbl, logBuckets); nb = (PEntryList*)tbl->buckets; for(i=0;ikey))*Multiplier)>>(BITSIZE(unsigned long)-tbl->logBuckets); nbh = &(nb[index]); tail = that->tail; that->tail = *nbh; *nbh = that; that = tail; } } DISPOSE (oldBucketPointer); } static inline unsigned int // boolean Get(PHashTable tbl, PKEY key, PVAL *val){ PEntryList that; unsigned int index; index = (HASH(key)*Multiplier)>>(BITSIZE(unsigned long)-tbl->logBuckets); that = (PEntryList)tbl->buckets[index]; while ((that != NULL) && !EQUAL(key,&(that->key))) { that = that->tail; } if (that != NULL){ *val = &that->val; return TRUE; } else { return FALSE; } } // Get() static inline unsigned int // boolean Put(PHashTable tbl, PKEY key, PVAL *val){ PEntryList that; PEntryList *first; unsigned int index; unsigned int res; index = (HASH(key)*Multiplier)>>(BITSIZE(unsigned long)-tbl->logBuckets); first = (PEntryList*)&(tbl->buckets[index]); that = *first; while ((that != NULL) && !EQUAL(key, &(that->key))){ that = that->tail; } // found an entry in the hash table given above key if (that != NULL){ res = TRUE; } else { // check if we can reuse something from the cache if (tbl->cache != NULL) { that = (PEntryList)tbl->cache; tbl->cache = (PVoidList)tbl->cache->tail; that->key = *key; that->tail = *first; *first = that; } else { that = NEW(EntryList,1); that->key = *key; that->tail = *first; *first = that; } that->val = **val; tbl->numEntries++; if ((tbl->logBuckets < MaxLogBuckets) && (tbl->numEntries > tbl->maxEntries)){ Rehash(tbl, tbl->logBuckets + 1); // too crowded } res = FALSE; } *val = &that->val; return res; } // Put() static inline int Delete(PHashTable tbl,PKEY key){ PEntryList that, prev; PEntryList *first; unsigned int index; index = (HASH(key)*Multiplier)>>(BITSIZE(unsigned long)-tbl->logBuckets); first = (PEntryList*)&(tbl->buckets[index]); that = *first; prev = NULL; while ((that != NULL) && !EQUAL(key, &(that->key))){ prev = that; that = that->tail; } if (that != NULL) { if (prev == NULL) { *first = that->tail; } else { prev->tail = that->tail; } if ((tbl->maxCacheSize == -1)||(tbl->cacheSize < tbl->maxCacheSize)) { that->tail = (PEntryList)tbl->cache; tbl->cache = (PVoidList)that; tbl->cacheSize++; } else { DISPOSE (that); } tbl->numEntries--; if (tbl->maxCacheSize == 0) { if ((tbl->logBuckets > tbl->minLogBuckets) && (tbl->numEntries < tbl->minEntries)) { Rehash(tbl, tbl->logBuckets - 1); // too sparse } } return TRUE; } else { return FALSE; } } // Delete() typedef void (*callback)(PKEY key, PVAL val); void Iterate(PHashTable tbl, callback fn) { PVoidList that; unsigned int i; for(i=0;inumBuckets;i++) { that = tbl->buckets[i]; while ( that != (PVoidList)0 ) { PEntryList entry = (PEntryList)that; fn(&entry->key,&entry->val); that = that->tail; } } } void Dispose(PHashTable tbl) { PVoidList that, next; unsigned int i; for(i=0;inumBuckets;i++) { that = tbl->buckets[i]; while( that != NULL) { next = that->tail; DISPOSE (that); tbl->numEntries--; that = next; } } DISPOSE(tbl->buckets); assert(tbl->numEntries = 0); } // Dispose; #endif // __VDU_H