This repository was archived by the owner on May 20, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 92b9623
Showing
7 changed files
with
670 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
Copyright 2018 Brandon Pfeifer | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
#ifndef BNP_COMMON_H | ||
#define BNP_COMMON_H | ||
|
||
#ifdef _MSC_VER | ||
// signed | ||
typedef __int8 bnp_int8; | ||
typedef __int16 bnp_int16; | ||
typedef __int32 bnp_int32; | ||
typedef __int64 bnp_int64; | ||
// unsigned | ||
typedef unsigned __int8 bnp_uint8; | ||
typedef unsigned __int16 bnp_uint16; | ||
typedef unsigned __int32 bnp_uint32; | ||
typedef unsigned __int64 bnp_uint64; | ||
// special | ||
typedef bnp_uint8 bnp_byte; | ||
#else | ||
#include <stdint.h> | ||
// signed | ||
typedef int8_t bnp_int8; | ||
typedef int16_t bnp_int16; | ||
typedef int32_t bnp_int32; | ||
typedef int64_t bnp_int64; | ||
// unsigned | ||
typedef uint8_t bnp_uint8; | ||
typedef uint16_t bnp_uint16; | ||
typedef uint32_t bnp_uint32; | ||
typedef uint64_t bnp_uint64; | ||
// special | ||
typedef bnp_uint8 bnp_byte; | ||
#endif | ||
|
||
#if _WIN32 || _WIN64 | ||
#ifdef _WIN64 | ||
typedef bnp_uint64 bnp_size; | ||
#else | ||
typedef bnp_uint32 bnp_size; | ||
#endif | ||
#else | ||
#ifdef __x86_64 | ||
typedef bnp_uint64 bnp_size; | ||
#else | ||
typedef bnp_uint32 bnp_size; | ||
#endif | ||
#endif | ||
|
||
// includes function implementations | ||
#ifdef BNP_IMPLEMENTATION | ||
#define BNP_COLLECTION_IMPLEMENTATION | ||
#define BNP_SPATIAL_IMPLEMENTATION | ||
#endif | ||
|
||
// includes function debugging (assert) | ||
#ifdef BNP_DEBUG | ||
#define BNP_COLLECTION_DEBUG | ||
#define BNP_SPATIAL_DEBUG | ||
#endif | ||
|
||
// enables implementations of collections | ||
#ifdef BNP_COLLECTION_IMPLEMENTATION | ||
#define BNPC_HASHMAP_IMPLEMENTATION // completed | ||
#define BNPC_LIST_IMPLEMENTATION // not-started | ||
#define BNPC_QUEUE_IMPLEMENTATION // unneeded | ||
#define BNPC_STACK_IMPLEMENTATION // unneeded | ||
#define BNPC_VECTOR_IMPLEMENTATION // completed | ||
#endif | ||
|
||
// enables debugging for collections | ||
#ifdef BNP_COLLECTION_DEBUG | ||
#define BNPC_HASHMAP_DEBUG // unneeded | ||
#define BNPC_LIST_DEBUG // not-implemented | ||
#define BNPC_QUEUE_DEBUG // unneeded | ||
#define BNPC_STACK_DEBUG // unneeded | ||
#define BNPC_VECTOR_DEBUG // completed | ||
#endif | ||
|
||
// force-inline | ||
#define BNP_FORCE_INLINE __attribute__ ((always_inline)) inline | ||
|
||
#include <assert.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
#include <stdio.h> | ||
BNP_FORCE_INLINE void* bnp_alloc(const bnp_size size) { | ||
void* pointer = malloc(size); | ||
assert(pointer); | ||
return pointer; | ||
} | ||
|
||
BNP_FORCE_INLINE void* bnp_realloc( | ||
void* src, | ||
const bnp_size old_size, | ||
const bnp_size new_size) { | ||
void* dst = malloc(new_size); | ||
assert(dst); | ||
memcpy(dst, src, (old_size < new_size) ? old_size : new_size); | ||
free(src); | ||
return dst; | ||
} | ||
|
||
#if !defined(BNP_ALLOC) ||\ | ||
!defined(BNP_REALLOC) ||\ | ||
!defined(BNP_FREE) | ||
#define BNP_ALLOC(S) bnp_alloc(S) | ||
#define BNP_REALLOC(P,O,N) bnp_realloc(P,O,N) | ||
#define BNP_FREE(P) free(P) | ||
#endif | ||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,241 @@ | ||
#ifndef BNPC_HASHMAP_H | ||
#define BNPC_HASHMAP_H | ||
|
||
#include "bnp_common.h" | ||
#include "bnpc_vector.h" | ||
#include "bnpc_list.h" | ||
|
||
struct bnpc_hashmap { | ||
struct bnpc_vector buckets; | ||
bnp_size k_size; // key size | ||
bnp_size v_size; // value size | ||
bnp_size element_count; // element count | ||
bnp_size reserved; // reserved space | ||
bnp_size (*func_hash)(void* key); // hashing function | ||
bnp_int32 (*func_comp)(void* key_a, void* key_b); // comparison function | ||
}; | ||
|
||
void bnpc_hashmap_init (struct bnpc_hashmap* hashmap, bnp_size k_size, bnp_size v_size, bnp_size reserved, bnp_size (*func_hash)(void* key), bnp_int32 (*func_comp)(void* key_a, void* key_b)); | ||
void bnpc_hashmap_free (struct bnpc_hashmap* hashmap); | ||
void* bnpc_hashmap_getp (struct bnpc_hashmap* hashmap, void* key); | ||
void bnpc_hashmap__insert (struct bnpc_hashmap* hashmap, void* key, void* value); | ||
bnp_int32 bnpc_hashmap__remove (struct bnpc_hashmap* hashmap, void* key, void* value); | ||
bnp_int32 bnpc_hashmap__erase (struct bnpc_hashmap* hashmap, void* key); | ||
void bnpc_hashmap__resize (struct bnpc_hashmap* hashmap); | ||
struct bnpc_list* bnpc_hashmap__getBucket (struct bnpc_hashmap* hashmap, void* key); | ||
void bnpc_hashmap__initBuckets (struct bnpc_vector* buckets, bnp_size size, bnp_size capacity); | ||
void bnpc_hashmap__freeBuckets (struct bnpc_vector* buckets); | ||
|
||
BNP_FORCE_INLINE void bnpc_hashmap_insert(struct bnpc_hashmap* hashmap, void* key, void* value) { | ||
// bnpc_hashmap_* are the 'public' facing functions. These functions may | ||
// resize the container. bnpc_hashmap__resize relies on bnpc_hashmap__* | ||
// functions; therefore none of the bnpc_hashmap__* functions can call | ||
// bnpc_hashmap__resize. | ||
bnpc_hashmap__resize(hashmap); | ||
bnpc_hashmap__insert(hashmap, key, value); | ||
} | ||
|
||
BNP_FORCE_INLINE bnp_int32 bnpc_hashmap_remove(struct bnpc_hashmap* hashmap, void* key, void* value) { | ||
// bnpc_hashmap_* are the 'public' facing functions. These functions may | ||
// resize the container. bnpc_hashmap__resize relies on bnpc_hashmap__* | ||
// functions; therefore none of the bnpc_hashmap__* functions can call | ||
// bnpc_hashmap__resize. | ||
bnpc_hashmap__resize(hashmap); | ||
return bnpc_hashmap__remove(hashmap, key, value); | ||
} | ||
|
||
BNP_FORCE_INLINE bnp_int32 bnpc_hashmap_erase(struct bnpc_hashmap* hashmap, void* key) { | ||
// bnpc_hashmap_* are the 'public' facing functions. These functions may | ||
// resize the container. bnpc_hashmap__resize relies on bnpc_hashmap__* | ||
// functions; therefore none of the bnpc_hashmap__* functions can call | ||
// bnpc_hashmap__resize. | ||
bnpc_hashmap__resize(hashmap); | ||
return bnpc_hashmap__erase(hashmap, key); | ||
} | ||
|
||
#ifdef BNPC_HASHMAP_IMPLEMENTATION | ||
#define BNPC_HASHMAP_LOADMAX 0.7 | ||
#define BNPC_HASHMAP_LOADMIN 0.3 | ||
#define BNPC_HASHMAP_KEY_OFFSET(H) 0 | ||
#define BNPC_HASHMAP_VAL_OFFSET(H) H->k_size | ||
#define BNPC_HASHMAP_ELEMENT_SIZE(H) H->k_size + H->v_size | ||
|
||
void bnpc_hashmap_init( | ||
struct bnpc_hashmap* hashmap, | ||
bnp_size k_size, | ||
bnp_size v_size, | ||
bnp_size reserved, | ||
bnp_size (*func_hash)(void* key), | ||
bnp_int32 (*func_comp)(void* key_a, void* key_b)) { | ||
hashmap->element_count = 0; // no elements | ||
hashmap->k_size = k_size; // key size | ||
hashmap->v_size = v_size; // value size | ||
hashmap->reserved = reserved; // reserved buckets | ||
hashmap->func_hash = func_hash; // hashing function | ||
hashmap->func_comp = func_comp; // comparison function | ||
bnp_size size = BNPC_HASHMAP_ELEMENT_SIZE(hashmap); | ||
// initializes the buckets | ||
bnpc_hashmap__initBuckets(&hashmap->buckets, size, hashmap->reserved); | ||
} | ||
|
||
void bnpc_hashmap_free(struct bnpc_hashmap* hashmap) { | ||
// releases the buckets | ||
bnpc_hashmap__freeBuckets(&hashmap->buckets); | ||
} | ||
|
||
void* bnpc_hashmap_getp(struct bnpc_hashmap* hashmap, void* key) { | ||
struct bnpc_list* bucket = bnpc_hashmap__getBucket(hashmap, key); | ||
for (bnp_size i = 0; i < bucket->count; i++) { | ||
struct bnpc_node* node = bnpc_list_getp(bucket, i); | ||
if (!hashmap->func_comp(node->elem, key)) { | ||
return node->elem + hashmap->k_size; | ||
} | ||
} | ||
return NULL; | ||
} | ||
|
||
bnp_int32 bnpc_hashmap_contains(struct bnpc_hashmap* hashmap, void* key) { | ||
struct bnpc_list* bucket = bnpc_hashmap__getBucket(hashmap, key); | ||
for (bnp_size i = 0; i < bucket->count; i++) { | ||
struct bnpc_node* node = bnpc_list_getp(bucket, i); | ||
if (!hashmap->func_comp(node->elem, key)) { | ||
return 1; | ||
} | ||
} | ||
return 0; | ||
} | ||
|
||
bnp_int32 bnpc_hashmap__remove(struct bnpc_hashmap* hashmap, void* key, void* value) { | ||
struct bnpc_list* bucket = bnpc_hashmap__getBucket(hashmap, key); | ||
for (bnp_size i = 0; i < bucket->count; i++) { | ||
struct bnpc_node* node = bnpc_list_getp(bucket, i); | ||
if (!hashmap->func_comp(node->elem, key)) { | ||
memcpy(value, node->elem + hashmap->k_size, hashmap->v_size); | ||
bnpc_list_remove(bucket, node); | ||
hashmap->element_count--; | ||
return 1; | ||
} | ||
} | ||
return 0; | ||
} | ||
|
||
bnp_int32 bnpc_hashmap__erase(struct bnpc_hashmap* hashmap, void* key) { | ||
struct bnpc_list* bucket = bnpc_hashmap__getBucket(hashmap, key); | ||
for (bnp_size i = 0; i < bucket->count; i++) { | ||
struct bnpc_node* node = bnpc_list_getp(bucket, i); | ||
if (!hashmap->func_comp(node->elem, key)) { | ||
bnpc_list_remove(bucket, node); | ||
hashmap->element_count--; | ||
return 1; | ||
} | ||
} | ||
return 0; | ||
} | ||
|
||
struct bnpc_list* bnpc_hashmap__getBucket(struct bnpc_hashmap* hashmap, void* key) { | ||
// Ensures that the bucket selected by hashmap->func_hash(key) is | ||
// within the limits of the actual vector. | ||
bnp_size index = hashmap->func_hash(key) % hashmap->buckets.count; | ||
return bnpc_vector_getp(&hashmap->buckets, index); | ||
} | ||
|
||
void bnpc_hashmap__initBuckets(struct bnpc_vector* buckets, bnp_size size, bnp_size capacity) { | ||
// The hashmap contains buckets and those buckets contain elements. | ||
// Instead of linked-lists, vectors are used to improve cache | ||
// efficiency. bnpc_vector* is used for the vector implementation. | ||
bnpc_vector_init(buckets, sizeof(struct bnpc_list), capacity); | ||
for (bnp_size i = 0; i < capacity; i++) { | ||
// initializes the bucket | ||
struct bnpc_list bucket; | ||
bnpc_list_init(&bucket, size); | ||
bnpc_vector_push(buckets, &bucket); | ||
} | ||
} | ||
|
||
void bnpc_hashmap__freeBuckets(struct bnpc_vector* buckets) { | ||
// The hashmap contains buckets and those buckets contain elements. | ||
// Instead of linked-lists, vectors are used to improve cache | ||
// efficiency. bnpc_vector* is used for the vector implementation. | ||
for (bnp_size i = 0; i < buckets->count; i++) { | ||
// releases the buckets | ||
bnpc_list_free(bnpc_vector_getp(buckets, i)); | ||
} | ||
bnpc_vector_free(buckets); | ||
} | ||
|
||
void bnpc_hashmap__insert(struct bnpc_hashmap* hashmap, void* key, void* value) { | ||
// Keys and values are sequentially packed into a vector. This approach | ||
// attempts to keep cache-misses to a minimum by avoiding linked-lists; | ||
// however, a buffer is needed to transfer the keys and values into | ||
// the hashmap. | ||
bnp_byte* buffer = BNP_ALLOC(BNPC_HASHMAP_ELEMENT_SIZE(hashmap)); | ||
memcpy(buffer + BNPC_HASHMAP_KEY_OFFSET(hashmap), key , hashmap->k_size); | ||
memcpy(buffer + BNPC_HASHMAP_VAL_OFFSET(hashmap), value, hashmap->v_size); | ||
|
||
struct bnpc_list* bucket = bnpc_hashmap__getBucket(hashmap, key); | ||
// iterates through the elements | ||
for (bnp_size i = 0; i < bucket->count; i++) { | ||
struct bnpc_node* node = bnpc_list_getp(bucket, i); | ||
if (!hashmap->func_comp(node->elem, key)) { | ||
// An element with the same key has been located. In these cases, | ||
// the element is updated with the new value; this should be the | ||
// same as with C++'s STL. Since the buffer is already built, the | ||
// buffer is copied over the element. | ||
memcpy(node->elem, buffer, BNPC_HASHMAP_ELEMENT_SIZE(hashmap)); | ||
// releases the buffer | ||
free(buffer); | ||
return; | ||
} | ||
} | ||
// inserts the element | ||
bnpc_list_insert(bucket, buffer); | ||
hashmap->element_count++; | ||
// releases the buffer | ||
free(buffer); | ||
} | ||
|
||
void bnpc_hashmap__resize(struct bnpc_hashmap* hashmap) { | ||
// In the best-case scenario, each element has a unique bucket; | ||
// therefore, we calculate the load-factor by comparing the amount of | ||
// buckets and the amount of elements. There isn't a defined optimal | ||
// time to increase/decrease the quantity of bucket. | ||
double load_factor = | ||
(double)hashmap->element_count / | ||
(double)hashmap->buckets.count; | ||
bnp_int32 increase = (load_factor >= BNPC_HASHMAP_LOADMAX); | ||
bnp_int32 decrease = (load_factor <= BNPC_HASHMAP_LOADMIN) && (hashmap->buckets.count > hashmap->reserved); | ||
|
||
if (increase || decrease) { | ||
struct bnpc_vector buckets; | ||
// temporarily copy the elements outside the hashmap | ||
memcpy(&buckets, &hashmap->buckets, sizeof buckets); | ||
// Since the elements are temporarily stored outside of the hashmap, | ||
// hashmap's element_count is incorrect; therefore, we reset it to | ||
// zero. Once all elements have been inserted, element_count will | ||
// reflect the accurate count. | ||
hashmap->element_count = 0; | ||
|
||
bnp_size size = BNPC_HASHMAP_ELEMENT_SIZE(hashmap); | ||
bnp_size capacity = decrease | ||
? hashmap->buckets.capacity >> 1 // decreases the capacity | ||
: hashmap->buckets.capacity << 1; // increases the capacity | ||
bnpc_hashmap__initBuckets(&hashmap->buckets, size, capacity); | ||
|
||
for (bnp_size i = 0; i < buckets.count; i++) { | ||
struct bnpc_vector* bucket = bnpc_vector_getp(&buckets, i); | ||
for (bnp_size j = 0; j < bucket->count; j++) { | ||
// Retrieves a pointer to the element; since keys and values are | ||
// packed contiguously, the keys and values can be found by | ||
// offseting the element pointer. | ||
bnp_byte* element = bnpc_vector_getp(bucket, j); | ||
void* key = element + BNPC_HASHMAP_KEY_OFFSET(hashmap); | ||
void* value = element + BNPC_HASHMAP_VAL_OFFSET(hashmap); | ||
bnpc_hashmap__insert(hashmap, key, value); | ||
} | ||
} | ||
// releases the buckets | ||
bnpc_hashmap__freeBuckets(&buckets); | ||
} | ||
} | ||
#endif | ||
#endif |
Oops, something went wrong.