Skip to content

Instantly share code, notes, and snippets.

@eloraiby
Last active May 22, 2018 13:38
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eloraiby/f84c169c6d60cf733b8baf42e38a9cbe to your computer and use it in GitHub Desktop.
Save eloraiby/f84c169c6d60cf733b8baf42e38a9cbe to your computer and use it in GitHub Desktop.
HashTable implementation using 2 buffers (1 for pointers and 1 for elements). It uses the linked list approach.
//
// Copyright (c) 2018 Wael El Oraiby
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. The name of the author may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
#include <malloc.h>
#include "HashTable.h"
HashTable*
HashTable_init(HashTable* ht, KeyHash kh, KeyCompare eq, KeyDestructor kDes, ValueDestructor vDes) {
ht->pointers = 0;
ht->elements = 0;
ht->capacity = 0;
ht->count = 0;
ht->freeList.raw = HT_END_PTR;
ht->keyHash = kh;
ht->keyEq = eq;
ht->keyDestructor = kDes;
ht->valDestructor = vDes;
return ht;
}
void
HashTable_releaseElements(HashTable* ht) {
if( ht->pointers ) {
for( uint64_t i = 0; i < ht->capacity; ++i ) {
if( ht->pointers[i].s.isUsed ) {
EPtr ptr = ht->pointers[i];
while( ptr.raw != HT_END_PTR ) {
ht->keyDestructor(ht->elements[ptr.s.ptr].key);
ht->valDestructor(ht->elements[ptr.s.ptr].value);
ptr = ht->elements[ptr.s.ptr].next;
}
}
}
free(ht->pointers);
}
if( ht->elements ) {
free(ht->elements);
}
ht->pointers = 0;
ht->elements = 0;
}
static inline
Element*
HashTable_allocateBuckets(uint64_t size) {
Element* buckets = calloc(size, sizeof(Element));
for (uint64_t i = 0; i < size; ++i) {
buckets[i].next .s.ptr = i + 1;
}
buckets[size - 1].next.raw = HT_END_PTR;
return buckets;
}
static inline
EPtr*
HashTable_allocatePointers(uint64_t size) {
EPtr* pointers = calloc(size, sizeof(EPtr));
for (uint64_t i = 0; i < size; ++i) {
pointers[i].raw = HT_END_PTR;
}
return pointers;
}
EPtr
HashTable_findElement(HashTable* ht, KEY key) {
if( ht->capacity ) {
uint64_t hash = ht->keyHash(key) & PTR_MASK;
uint64_t ptrPosition = hash % ht->capacity;
EPtr oldPtr = ht->pointers[ptrPosition];
while( oldPtr.raw != HT_END_PTR && !ht->keyEq(ht->elements[oldPtr.s.ptr].key, key) ) {
oldPtr = ht->elements[oldPtr.s.ptr].next;
}
return oldPtr;
} else {
EPtr p = { .raw = HT_END_PTR };
return p;
}
}
VALUE*
HashTable_getValue(HashTable* ht, KEY key) {
EPtr elemPos = HashTable_findElement(ht, key);
if( elemPos.raw != HT_END_PTR ) {
return &ht->elements[elemPos.s.ptr].value;
} else {
return 0;
}
}
void
HashTable_rehash(HashTable* ht, uint64_t newSize) {
Element* prevElements = ht->elements;
EPtr* prevPointers = ht->pointers;
uint64_t prevCapacity = ht->capacity;
ht->elements = HashTable_allocateBuckets(newSize);
ht->pointers = HashTable_allocatePointers(newSize);
ht->freeList.raw = 0;
ht->count = 0;
ht->capacity = newSize;
for( uint64_t p = 0; p < prevCapacity; ++p ) {
if( prevPointers[p].s.isUsed ) {
EPtr ptr = prevPointers[p];
while( ptr.raw != HT_END_PTR ) {
HashTable_insert(ht, prevElements[ptr.s.ptr].key, prevElements[ptr.s.ptr].value);
ptr = prevElements[ptr.s.ptr].next;
}
}
}
free(prevElements);
free(prevPointers);
}
void
HashTable_insert(HashTable* ht, KEY key, VALUE value) {
EPtr elementFound = HashTable_findElement(ht, key);
if( elementFound.raw == HT_END_PTR && ht->freeList.raw == HT_END_PTR ) {
uint64_t newCapacity = ht->capacity ? ht->capacity * 2 : INITIAL_HASH_ELEMENT_COUNT;
HashTable_rehash(ht, newCapacity);
}
uint64_t hash = ht->keyHash(key) & PTR_MASK;
uint64_t ptrPosition = hash % ht->capacity;
EPtr oldPtr = ht->pointers[ptrPosition];
uint64_t currentFree = (elementFound.raw != HT_END_PTR) ? elementFound.s.ptr : ht->freeList.s.ptr;
if( elementFound.raw == HT_END_PTR ) { /* new element */
ht->freeList = ht->elements[ht->freeList.s.ptr].next;
ht->elements[currentFree].next = oldPtr;
ht->elements[currentFree].key = key;
++ht->count;
} else {
/* remove the old element */
ht->valDestructor(ht->elements[elementFound.s.ptr].value);
}
ht->elements[currentFree].value = value;
ht->pointers[ptrPosition].s.ptr = currentFree;
ht->pointers[ptrPosition].s.isUsed = 1;
}
void
HashTable_remove(HashTable* ht, KEY key) {
uint64_t hash = ht->keyHash(key) & PTR_MASK;
uint64_t ptrPosition = hash % ht->capacity;
EPtr oldPtr = ht->pointers[ptrPosition];
EPtr prevPos = { .raw = HT_END_PTR };
while( oldPtr.raw != HT_END_PTR && !ht->keyEq(ht->elements[oldPtr.s.ptr].key, key) ) {
prevPos = oldPtr;
oldPtr = ht->elements[prevPos.s.ptr].next;
}
if( oldPtr.raw != HT_END_PTR ) {
EPtr next = ht->elements[oldPtr.s.ptr].next;
if( prevPos.raw != HT_END_PTR ) {
next.s.isUsed = 1;
ht->elements[prevPos.s.ptr].next = next;
}
if( prevPos.raw == HT_END_PTR ) {
ht->pointers[ptrPosition] = next;
}
ht->keyDestructor((void*)ht->elements[oldPtr.s.ptr].key);
ht->valDestructor((void*)ht->elements[oldPtr.s.ptr].value);
ht->elements[oldPtr.s.ptr].key = EMPTY_KEY;
ht->elements[oldPtr.s.ptr].value = EMPTY_VALUE;
ht->elements[oldPtr.s.ptr].next = ht->freeList;
ht->freeList = oldPtr;
--ht->count;
}
}
#ifndef HASHTABLE_H
#define HASHTABLE_H
//
// Copyright (c) 2018 Wael El Oraiby
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 3. The name of the author may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
#include <stdint.h>
#include <stdbool.h>
#define HT_END_PTR ((uint64_t)(-1))
#define KEY void*
#define VALUE void*
#define EMPTY_KEY 0
#define EMPTY_VALUE 0
#define PTR_MASK 0x7FFFFFFFFFFFFFFF
typedef union {
uint64_t raw;
struct {
uint64_t isUsed : 1;
uint64_t ptr : 63;
} s;
} EPtr;
typedef struct {
EPtr next;
KEY key;
VALUE value;
} Element;
typedef uint64_t (*KeyHash)(KEY);
typedef bool (*KeyCompare)(KEY, KEY);
typedef void (*KeyDestructor)(KEY);
typedef void (*ValueDestructor)(VALUE);
typedef struct {
EPtr* pointers;
Element* elements;
uint64_t capacity;
uint64_t count;
EPtr freeList;
KeyHash keyHash;
KeyCompare keyEq;
KeyDestructor keyDestructor;
ValueDestructor valDestructor;
} HashTable;
enum {
INITIAL_HASH_ELEMENT_COUNT = 16,
};
HashTable* HashTable_init(HashTable* ht, KeyHash kh, KeyCompare eq, KeyDestructor kDes, ValueDestructor vDes);
EPtr HashTable_findElement(HashTable* ht, KEY key);
VALUE* HashTable_getValue(HashTable* ht, KEY key);
void HashTable_rehash(HashTable* ht, uint64_t newSize);
void HashTable_insert(HashTable* ht, KEY key, VALUE value);
void HashTable_remove(HashTable* ht, KEY key);
void HashTable_releaseElements(HashTable* ht);
#endif // HASHTABLE_H
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment