/* * Copyright (c) 1998-2019 NetApp, Inc. * All rights reserved. */ #include #include "qqsc.h" /* * Searchable Collections: * * Expanding hash tables with a key and data. */ #include "assert.h" #if !defined(USEPRIMESIZES) static int sizes[] = { 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16*1024, 32*1024, 64*1024, 128*1024, 256*1024, 512*1024, 1024*1024, 2*1024*1024, 4*1024*1024, 8*1024*1024, 16*1024*1024 }; #define Hash(a,sc) (QQScHASH(a) & ((sc)->size - 1)) #else static int sizes[] = { 5, 7, 17, 31, 67, 131, 257, 521, 1031, 2053, 4099, 8093, 16193, 32377, 65557, 131071, 262187, 524869, 1048829, 2097223, 4194371, 8388697, 16777291 }; #define Hash(key, sc) (QQScHASH(key) % sc->size) #endif #define MAXFILL(x) (((x) * 17) / 20) /* * Turning this on will cause the package to self-check on every (modifying) * operation. The package runs very slowly when this is enabled. */ #undef DEBUGSC #ifdef DEBUGSC void QQScCheckOut(); #define QQScCHECKOUT(sc) QQScCheckOut(sc) #else #define QQScCHECKOUT(sc) #endif /* Return a new, empty QQSc */ QQSc QQScCreateN(int howbig) { register int i=0; register QQSc sc; sc = (QQSc) malloc(sizeof(QQScRecord)); while (sizes[i] < howbig) { assert((unsigned int)i < sizeof(sizes) / sizeof(sizes[0])); i++; } sc->size = sizes[i]; sc->maxCount = MAXFILL(sc->size); sc->count = 0; sc->table = (QQScTEPtr) malloc((unsigned) sc->size * sizeof(QQScTE)); for (i = 0; i < sc->size; i++) { sc->table[i].key = QQScNIL; } QQScCHECKOUT(sc); return sc; } void QQScDestroy(sc) register QQSc sc; { free((char *)sc->table); free((char *)sc); } /* Expand the hash table. Each element in the table is re-hashed and entered * in the new table. */ static void ExpandHashTable(QQSc sc) { register QQScTE *nh, *oe, *ne; register int oldHashTableSize = sc->size, i=0; register QQScDomainType key; int index; while (sizes[i] <= oldHashTableSize) { assert((unsigned int)i < sizeof(sizes) / sizeof(sizes[0])); i++; } sc->size = sizes[i]; sc->maxCount = MAXFILL(sc->size); nh = (QQScTEPtr) malloc((unsigned)(sc->size * sizeof(QQScTE))); for (i = 0; i < sc->size; i++) { nh[i].key = QQScNIL; } for (i = 0; i < oldHashTableSize; i++) { oe = &sc->table[i]; key = oe->key; if (key == QQScNIL) { continue; } index = Hash(key, sc); while (1) { ne = &nh[index]; if (ne->key == QQScNIL) { ne->key = oe->key; ne->value = oe->value; break; } else { assert(ne->key !=key); index++; if (index >= sc->size) { index = 0; } } } } free((char *)sc->table); sc->table = nh; QQScCHECKOUT(sc); } /* Return the value associated with key in collection sc, or QQScNIL */ QQScRangeType QQScLookup(QQSc sc, QQScDomainType key) { register int index = Hash(key, sc); register QQScTEPtr e; QQScCHECKOUT(sc); while (1) { e = &sc->table[index]; if (e->key == QQScNIL) { /* we did not find it */ return QQScNIL; } else if (QQScCOMPARE(e->key, key)) { return e->value; } if (++index >= sc->size) { index = 0; } } } /* Insert the key, value pair in sc. If the key already exists, change its * value. */ void QQScInsert(sc, key, value) register QQSc sc; register QQScDomainType key; QQScRangeType value; { register int index; register QQScTEPtr e; assert(key != QQScNIL); if (sc->count >= sc->maxCount) { ExpandHashTable(sc); } index = Hash(key, sc); while (1) { e = &sc->table[index]; if (e->key == QQScNIL) { /* put it here */ e->key = key; e->value = value; sc->count++; QQScCHECKOUT(sc); return; } else if (QQScCOMPARE(e->key, key)) { e->value = value; QQScCHECKOUT(sc); return; } if (++index >= sc->size) { index = 0; } } } /* Remove the entry, if it is there */ void QQScDelete(sc, key) register QQSc sc; register QQScDomainType key; { register int index = Hash(key, sc); register QQScRangeType value; register QQScTEPtr e; while (1) { e = &sc->table[index]; if (e->key == QQScNIL) { /* we did not find it */ QQScCHECKOUT(sc); return; } if (QQScCOMPARE(e->key, key)) { /* Found it, now remove it */ sc->count--; e->key = QQScNIL; e->value = QQScNIL; while (1) { /* rehash until we reach nil again */ if (++index >= sc->size) { index = 0; } e = & sc->table[index]; key = e->key; if (key == QQScNIL) { QQScCHECKOUT(sc); return; } /* rehashing is done by removing then reinserting */ value = e->value; e->key = QQScNIL; e->value = QQScNIL; sc->count--; QQScInsert(sc, key, value); } } if (++index >= sc->size) { index = 0; } } } #ifdef DEBUGSC /* DEBUGGING: Print the sc */ void QQScPrint(sc) register QQSc sc; { QQScDomainType key; QQScRangeType value; int index; printf( "\nDump of sc @ 0x%05x, %d entr%s, current max %d\nIndex\tKey\t\tValue\n", (int)sc, sc->count, sc->count == 1 ? "y" : "ies", sc->maxCount); for (index = 0; index < sc->size; index++) { key = sc->table[index].key; value = sc->table[index].value; if (key != QQScNIL) { printf("%3d\t0x%08x\t%08x\n", index, key, value); } } } /* Make sure that the hash table is internally consistent: * every key is findable, * count reflects the number of elements */ void QQScCheckOut(QQSc sc) { register int i; register QQScTEPtr realElement; register int count; count = 0; for (i = 0; i < sc->size; i++) { realElement = &sc->table[i]; if (!QQScIsNIL(realElement->key)) { count++; if (QQScIsNIL(QQScLookup(sc, realElement->key))) { printf("QQSc problem: Key %#x value 0x%x\n", realElement->key, realElement->value); QQScPrint(sc); abort(); } } } if (count != sc->count) { printf("Sc problem: Should have %d entries, but found %d.\n", sc->count, count); QQScPrint(sc); abort(); } } #endif