From 65357c8c46154de4e4eca14075bfe5523bb5fc14 Mon Sep 17 00:00:00 2001 From: Daniel Lange Date: Mon, 7 Dec 2020 10:26:01 +0100 Subject: New upstream version 3.0.3 --- Hashtable.c | 323 ++++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 238 insertions(+), 85 deletions(-) (limited to 'Hashtable.c') diff --git a/Hashtable.c b/Hashtable.c index bb9517a..1beb2bb 100644 --- a/Hashtable.c +++ b/Hashtable.c @@ -1,152 +1,305 @@ /* htop - Hashtable.c (C) 2004-2011 Hisham H. Muhammad -Released under the GNU GPL, see the COPYING file +Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ +#include "config.h" // IWYU pragma: keep + #include "Hashtable.h" -#include "XAlloc.h" -#include #include +#include +#include +#include +#include + +#include "Macros.h" +#include "XUtils.h" + + +#ifndef NDEBUG +static void Hashtable_dump(const Hashtable* this) { + fprintf(stderr, "Hashtable %p: size=%u items=%u owner=%s\n", + (const void*)this, + this->size, + this->items, + this->owner ? "yes" : "no"); -#ifdef DEBUG + unsigned int items = 0; + for (unsigned int i = 0; i < this->size; i++) { + fprintf(stderr, " item %5u: key = %5u probe = %2u value = %p\n", + i, + this->buckets[i].key, + this->buckets[i].probe, + this->buckets[i].value ? (const void*)this->buckets[i].value : "(nil)"); -static bool Hashtable_isConsistent(Hashtable* this) { - int items = 0; - for (int i = 0; i < this->size; i++) { - HashtableItem* bucket = this->buckets[i]; - while (bucket) { + if (this->buckets[i].value) items++; - bucket = bucket->next; - } } - return items == this->items; + + fprintf(stderr, "Hashtable %p: items=%u counted=%u\n", + (const void*)this, + this->items, + items); } -int Hashtable_count(Hashtable* this) { - int items = 0; - for (int i = 0; i < this->size; i++) { - HashtableItem* bucket = this->buckets[i]; - while (bucket) { +static bool Hashtable_isConsistent(const Hashtable* this) { + unsigned int items = 0; + for (unsigned int i = 0; i < this->size; i++) { + if (this->buckets[i].value) + items++; + } + bool res = items == this->items; + if (!res) + Hashtable_dump(this); + return res; +} + +unsigned int Hashtable_count(const Hashtable* this) { + unsigned int items = 0; + for (unsigned int i = 0; i < this->size; i++) { + if (this->buckets[i].value) items++; - bucket = bucket->next; - } } assert(items == this->items); return items; } -#endif +#endif /* NDEBUG */ -static HashtableItem* HashtableItem_new(unsigned int key, void* value) { - HashtableItem* this; +/* https://oeis.org/A014234 */ +static const uint64_t OEISprimes[] = { + 2, 3, 7, 13, 31, 61, 127, 251, 509, 1021, 2039, 4093, 8191, + 16381, 32749, 65521, 131071, 262139, 524287, 1048573, + 2097143, 4194301, 8388593, 16777213, 33554393, + 67108859, 134217689, 268435399, 536870909, 1073741789, + 2147483647, 4294967291, 8589934583, 17179869143, + 34359738337, 68719476731, 137438953447 +}; - this = xMalloc(sizeof(HashtableItem)); - this->key = key; - this->value = value; - this->next = NULL; - return this; +static uint64_t nextPrime(unsigned int n) { + assert(n <= OEISprimes[ARRAYSIZE(OEISprimes) - 1]); + + for (unsigned int i = 0; i < ARRAYSIZE(OEISprimes); i++) { + if (n <= OEISprimes[i]) + return OEISprimes[i]; + } + + return OEISprimes[ARRAYSIZE(OEISprimes) - 1]; } -Hashtable* Hashtable_new(int size, bool owner) { +Hashtable* Hashtable_new(unsigned int size, bool owner) { Hashtable* this; this = xMalloc(sizeof(Hashtable)); this->items = 0; - this->size = size; - this->buckets = (HashtableItem**) xCalloc(size, sizeof(HashtableItem*)); + this->size = size ? nextPrime(size) : 13; + this->buckets = (HashtableItem*) xCalloc(this->size, sizeof(HashtableItem)); this->owner = owner; + assert(Hashtable_isConsistent(this)); return this; } void Hashtable_delete(Hashtable* this) { - assert(Hashtable_isConsistent(this)); - for (int i = 0; i < this->size; i++) { - HashtableItem* walk = this->buckets[i]; - while (walk != NULL) { - if (this->owner) - free(walk->value); - HashtableItem* savedWalk = walk; - walk = savedWalk->next; - free(savedWalk); - } - } + Hashtable_clear(this); + free(this->buckets); free(this); } -void Hashtable_put(Hashtable* this, unsigned int key, void* value) { +void Hashtable_clear(Hashtable* this) { + assert(Hashtable_isConsistent(this)); + + if (this->owner) + for (unsigned int i = 0; i < this->size; i++) + free(this->buckets[i].value); + + memset(this->buckets, 0, this->size * sizeof(HashtableItem)); + this->items = 0; + + assert(Hashtable_isConsistent(this)); +} + +static void insert(Hashtable* this, hkey_t key, void* value) { unsigned int index = key % this->size; - HashtableItem** bucketPtr = &(this->buckets[index]); - while (true) - if (*bucketPtr == NULL) { - *bucketPtr = HashtableItem_new(key, value); + unsigned int probe = 0; +#ifndef NDEBUG + unsigned int origIndex = index; +#endif + + for (;;) { + if (!this->buckets[index].value) { this->items++; - break; - } else if ((*bucketPtr)->key == key) { - if (this->owner) - free((*bucketPtr)->value); - (*bucketPtr)->value = value; - break; - } else - bucketPtr = &((*bucketPtr)->next); + this->buckets[index].key = key; + this->buckets[index].probe = probe; + this->buckets[index].value = value; + return; + } + + if (this->buckets[index].key == key) { + if (this->owner && this->buckets[index].value != value) + free(this->buckets[index].value); + this->buckets[index].value = value; + return; + } + + /* Robin Hood swap */ + if (probe > this->buckets[index].probe) { + HashtableItem tmp = this->buckets[index]; + + this->buckets[index].key = key; + this->buckets[index].probe = probe; + this->buckets[index].value = value; + + key = tmp.key; + probe = tmp.probe; + value = tmp.value; + } + + index = (index + 1) % this->size; + probe++; + + assert(index != origIndex); + } +} + +void Hashtable_setSize(Hashtable* this, unsigned int size) { + assert(Hashtable_isConsistent(this)); + + if (size <= this->items) + return; + + HashtableItem* oldBuckets = this->buckets; + unsigned int oldSize = this->size; + + this->size = nextPrime(size); + this->buckets = (HashtableItem*) xCalloc(this->size, sizeof(HashtableItem)); + this->items = 0; + + /* rehash */ + for (unsigned int i = 0; i < oldSize; i++) { + if (!oldBuckets[i].value) + continue; + + insert(this, oldBuckets[i].key, oldBuckets[i].value); + } + + free(oldBuckets); + + assert(Hashtable_isConsistent(this)); +} + +void Hashtable_put(Hashtable* this, hkey_t key, void* value) { + + assert(Hashtable_isConsistent(this)); + assert(this->size > 0); + assert(value); + + /* grow on load-factor > 0.7 */ + if (10 * this->items > 7 * this->size) + Hashtable_setSize(this, 2 * this->size); + + insert(this, key, value); + + assert(Hashtable_isConsistent(this)); + assert(Hashtable_get(this, key) != NULL); + assert(this->size > this->items); } -void* Hashtable_remove(Hashtable* this, unsigned int key) { +void* Hashtable_remove(Hashtable* this, hkey_t key) { unsigned int index = key % this->size; + unsigned int probe = 0; +#ifndef NDEBUG + unsigned int origIndex = index; +#endif assert(Hashtable_isConsistent(this)); - HashtableItem** bucket; - for (bucket = &(this->buckets[index]); *bucket; bucket = &((*bucket)->next) ) { - if ((*bucket)->key == key) { - void* value = (*bucket)->value; - HashtableItem* next = (*bucket)->next; - free(*bucket); - (*bucket) = next; - this->items--; + void* res = NULL; + + while (this->buckets[index].value) { + if (this->buckets[index].key == key) { if (this->owner) { - free(value); - assert(Hashtable_isConsistent(this)); - return NULL; + free(this->buckets[index].value); } else { - assert(Hashtable_isConsistent(this)); - return value; + res = this->buckets[index].value; + } + + unsigned int next = (index + 1) % this->size; + + while (this->buckets[next].value && this->buckets[next].probe > 0) { + this->buckets[index] = this->buckets[next]; + this->buckets[index].probe -= 1; + + index = next; + next = (index + 1) % this->size; } + + /* set empty after backward shifting */ + this->buckets[index].value = NULL; + this->items--; + + break; } + + if (this->buckets[index].probe < probe) + break; + + index = (index + 1) % this->size; + probe++; + + assert(index != origIndex); } + assert(Hashtable_isConsistent(this)); - return NULL; + assert(Hashtable_get(this, key) == NULL); + + /* shrink on load-factor < 0.125 */ + if (8 * this->items < this->size) + Hashtable_setSize(this, this->size / 2); + + return res; } -inline void* Hashtable_get(Hashtable* this, unsigned int key) { +void* Hashtable_get(Hashtable* this, hkey_t key) { unsigned int index = key % this->size; - HashtableItem* bucketPtr = this->buckets[index]; - while (true) { - if (bucketPtr == NULL) { - assert(Hashtable_isConsistent(this)); - return NULL; - } else if (bucketPtr->key == key) { - assert(Hashtable_isConsistent(this)); - return bucketPtr->value; - } else - bucketPtr = bucketPtr->next; + unsigned int probe = 0; + void* res = NULL; +#ifndef NDEBUG + unsigned int origIndex = index; +#endif + + assert(Hashtable_isConsistent(this)); + + while (this->buckets[index].value) { + if (this->buckets[index].key == key) { + res = this->buckets[index].value; + break; + } + + if (this->buckets[index].probe < probe) + break; + + index = (index + 1) != this->size ? (index + 1) : 0; + probe++; + + assert(index != origIndex); } + + return res; } void Hashtable_foreach(Hashtable* this, Hashtable_PairFunction f, void* userData) { assert(Hashtable_isConsistent(this)); - for (int i = 0; i < this->size; i++) { - HashtableItem* walk = this->buckets[i]; - while (walk != NULL) { + for (unsigned int i = 0; i < this->size; i++) { + HashtableItem* walk = &this->buckets[i]; + if (walk->value) f(walk->key, walk->value, userData); - walk = walk->next; - } } assert(Hashtable_isConsistent(this)); } -- cgit v1.2.3