mirror of
https://github.com/CrazyRedMachine/popnhax.git
synced 2024-11-27 23:40:50 +01:00
470 lines
25 KiB
C
470 lines
25 KiB
C
/*
|
|
* C-Ware License
|
|
*
|
|
* Copyright (c) 2022, C-Ware
|
|
* All rights reserved.
|
|
*
|
|
* 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. Redistributions of modified source code must append a copyright notice in
|
|
* the form of 'Copyright <YEAR> <NAME>' to each modified source file's
|
|
* copyright notice, and the standalone license file if one exists.
|
|
*
|
|
* A "redistribution" can be constituted as any version of the source code
|
|
* that is intended to comprise some other derivative work of this code. A
|
|
* fork created for the purpose of contributing to any version of the source
|
|
* does not constitute a truly "derivative work" and does not require listing.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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.
|
|
*/
|
|
|
|
#ifndef CWARE_LIBCHASH_H
|
|
#define CWARE_LIBCHASH_H
|
|
|
|
#define CWARE_LIBCHASH_VERSION "2.0.0"
|
|
|
|
/* How big heap-allocated hashtables are by default */
|
|
#ifndef CHASH_INITIAL_SIZE
|
|
#define CHASH_INITIAL_SIZE 10
|
|
#elif CHASH_INITIAL_SIZE <= 0
|
|
"chash_init: default length must be greater than 0"
|
|
#endif
|
|
|
|
/* Calculates the next size of the hashtable. */
|
|
#ifndef CHASH_RESIZE
|
|
#define CHASH_RESIZE(size) \
|
|
((size) * 1.3)
|
|
#endif
|
|
|
|
/* The threshold that, when passed, will cause a resize */
|
|
#ifndef CHASH_LOAD_THRESHOLD
|
|
#define CHASH_LOAD_THRESHOLD 0.8
|
|
#endif
|
|
|
|
/* The type that is used for counters; useful for aligning hashtable
|
|
* length and capacity fields so type casting warnings do not appear */
|
|
#ifndef CHASH_COUNTER_TYPE
|
|
#define CHASH_COUNTER_TYPE int
|
|
#endif
|
|
|
|
/* The name of the key field */
|
|
#ifndef CHASH_KEY_FIELD
|
|
#define CHASH_KEY_FIELD key
|
|
#endif
|
|
|
|
/* The name of the value field */
|
|
#ifndef CHASH_VALUE_FIELD
|
|
#define CHASH_VALUE_FIELD value
|
|
#endif
|
|
|
|
/* The name of the state field */
|
|
#ifndef CHASH_STATE_FIELD
|
|
#define CHASH_STATE_FIELD state
|
|
#endif
|
|
|
|
/* The name of the buckets field */
|
|
#ifndef CHASH_BUCKETS_FIELD
|
|
#define CHASH_BUCKETS_FIELD buckets
|
|
#endif
|
|
|
|
/* The name of the length field */
|
|
#ifndef CHASH_LENGTH_FIELD
|
|
#define CHASH_LENGTH_FIELD length
|
|
#endif
|
|
|
|
/* The name of the capacity field */
|
|
#ifndef CHASH_CAPACITY_FIELD
|
|
#define CHASH_CAPACITY_FIELD capacity
|
|
#endif
|
|
|
|
/* State enums */
|
|
#define CHASH_UNFILLED 0
|
|
#define CHASH_FILLED 1
|
|
#define CHASH_TOMBSTONE 2
|
|
|
|
/* Built-ins */
|
|
|
|
#define chash_string_hash(key, hash) \
|
|
5031; \
|
|
do { \
|
|
int __CHASH_HINDEX = 0; \
|
|
\
|
|
for(__CHASH_HINDEX = 0; (key)[__CHASH_HINDEX] != '\0'; \
|
|
__CHASH_HINDEX++) { \
|
|
(hash) = (((hash) << 1) + (hash)) + (key)[__CHASH_HINDEX]; \
|
|
} \
|
|
} while(0)
|
|
|
|
#define chash_string_compare(cmp_a, cmp_b) \
|
|
(strcmp((cmp_a), (cmp_b)) == 0)
|
|
|
|
#define chash_default_init(bucket, _key, _value) \
|
|
(bucket).CHASH_KEY_FIELD = (_key); \
|
|
(bucket).CHASH_VALUE_FIELD = _value
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* utility macros */
|
|
|
|
#define __chash_abs(x) \
|
|
((x) < 0 ? (x) * - 1 : (x))
|
|
|
|
#define __chash_hash(mod, _key, namespace) \
|
|
__CHASH_HASH = namespace ## _HASH((_key), __CHASH_HASH); \
|
|
__CHASH_HASH = __CHASH_HASH % (mod); \
|
|
__CHASH_HASH = __chash_abs(__CHASH_HASH);
|
|
|
|
#define __chash_probe(hashtable, _key, namespace) \
|
|
while(__CHASH_INDEX < (hashtable)->CHASH_CAPACITY_FIELD) { \
|
|
if((hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH].CHASH_STATE_FIELD == \
|
|
CHASH_UNFILLED) \
|
|
break; \
|
|
\
|
|
if((namespace ## _COMPARE((_key), \
|
|
(hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH].CHASH_KEY_FIELD)) == 1) { \
|
|
\
|
|
__CHASH_INDEX = -1; \
|
|
break; \
|
|
} \
|
|
\
|
|
__CHASH_HASH = (__CHASH_HASH + 1) % (hashtable)->CHASH_CAPACITY_FIELD; \
|
|
__CHASH_INDEX++; \
|
|
} \
|
|
|
|
#define __chash_probe_to_unfilled(mod, _key, buffer, namespace) \
|
|
while(1) { \
|
|
if(buffer[__CHASH_HASH].CHASH_STATE_FIELD != CHASH_FILLED) \
|
|
break; \
|
|
\
|
|
if((namespace ## _COMPARE((_key), buffer[__CHASH_HASH].CHASH_KEY_FIELD)) \
|
|
== 1) \
|
|
break; \
|
|
\
|
|
__CHASH_HASH = (__CHASH_HASH + 1) % mod; \
|
|
} \
|
|
|
|
#define __chash_resize(hashtable, namespace) \
|
|
do { \
|
|
CHASH_COUNTER_TYPE __CHASH_INDEX = 0; \
|
|
namespace ## _BUCKET *__CHASH_BUCKETS = NULL; \
|
|
CHASH_COUNTER_TYPE __CHASH_NEXT_SIZE = (CHASH_COUNTER_TYPE) \
|
|
CHASH_RESIZE((hashtable)->CHASH_CAPACITY_FIELD); \
|
|
\
|
|
if((namespace ## _HEAP) == 0) { \
|
|
if((hashtable)->CHASH_LENGTH_FIELD != \
|
|
(hashtable)->CHASH_CAPACITY_FIELD) { \
|
|
break; \
|
|
} \
|
|
\
|
|
fprintf(stderr, "__chash_resize: hashtable is full. could not resize" \
|
|
" (%s:%i)\n", __FILE__, __LINE__); \
|
|
abort(); \
|
|
} \
|
|
\
|
|
if((float) (hashtable)->CHASH_LENGTH_FIELD / \
|
|
(float) (hashtable)->CHASH_CAPACITY_FIELD < CHASH_LOAD_THRESHOLD) \
|
|
break; \
|
|
\
|
|
__CHASH_BUCKETS = (namespace ## _BUCKET *)malloc((size_t) (__CHASH_NEXT_SIZE \
|
|
* ((CHASH_COUNTER_TYPE) \
|
|
sizeof(namespace ## _BUCKET)))); \
|
|
memset(__CHASH_BUCKETS, 0, ((size_t) (__CHASH_NEXT_SIZE \
|
|
* ((CHASH_COUNTER_TYPE) \
|
|
sizeof(namespace ## _BUCKET))))); \
|
|
\
|
|
for(__CHASH_INDEX = 0; __CHASH_INDEX < (hashtable)->CHASH_CAPACITY_FIELD; \
|
|
__CHASH_INDEX++) { \
|
|
namespace ## _BUCKET __CHASH_NEW_KEY_BUCKET; \
|
|
memset(&__CHASH_NEW_KEY_BUCKET, 0, sizeof(namespace ## _BUCKET)); \
|
|
namespace ## _INIT(__CHASH_NEW_KEY_BUCKET, \
|
|
(hashtable)->CHASH_BUCKETS_FIELD[__CHASH_INDEX].CHASH_KEY_FIELD, \
|
|
(hashtable)->CHASH_BUCKETS_FIELD[__CHASH_INDEX].CHASH_VALUE_FIELD); \
|
|
\
|
|
if((hashtable)->CHASH_BUCKETS_FIELD[__CHASH_INDEX].CHASH_STATE_FIELD \
|
|
!= CHASH_FILLED) \
|
|
continue; \
|
|
\
|
|
__chash_hash(__CHASH_NEXT_SIZE, __CHASH_NEW_KEY_BUCKET.CHASH_KEY_FIELD, \
|
|
namespace); \
|
|
__chash_probe_to_unfilled(__CHASH_NEXT_SIZE, \
|
|
(hashtable)->CHASH_BUCKETS_FIELD[__CHASH_INDEX].CHASH_KEY_FIELD, \
|
|
__CHASH_BUCKETS, namespace) \
|
|
\
|
|
__CHASH_BUCKETS[__CHASH_HASH] = __CHASH_NEW_KEY_BUCKET; \
|
|
__CHASH_BUCKETS[__CHASH_HASH].CHASH_STATE_FIELD = CHASH_FILLED; \
|
|
__CHASH_HASH = 0; \
|
|
} \
|
|
\
|
|
free((hashtable)->CHASH_BUCKETS_FIELD); \
|
|
(hashtable)->CHASH_BUCKETS_FIELD = __CHASH_BUCKETS; \
|
|
(hashtable)->CHASH_CAPACITY_FIELD = __CHASH_NEXT_SIZE; \
|
|
__CHASH_HASH = 0; \
|
|
} while(0)
|
|
|
|
#define __chash_assert_nonnull(func, ptr) \
|
|
do { \
|
|
if((ptr) == NULL) { \
|
|
fprintf(stderr, #func ": " #ptr " cannot be null (%s:%i)\n", \
|
|
__FILE__, __LINE__); \
|
|
abort(); \
|
|
} \
|
|
} while(0)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* operations */
|
|
#define chash_init(hashtable, namespace) \
|
|
NULL; \
|
|
\
|
|
(hashtable) = (namespace ## _TYPE *) malloc(sizeof((*(hashtable)))); \
|
|
(hashtable)->CHASH_LENGTH_FIELD = 0; \
|
|
(hashtable)->CHASH_CAPACITY_FIELD = CHASH_INITIAL_SIZE; \
|
|
(hashtable)->CHASH_BUCKETS_FIELD = (namespace ## _BUCKET *) malloc(CHASH_INITIAL_SIZE \
|
|
* sizeof(*((hashtable)->CHASH_BUCKETS_FIELD))); \
|
|
memset((hashtable)->CHASH_BUCKETS_FIELD, 0, \
|
|
sizeof(*((hashtable)->CHASH_BUCKETS_FIELD)) * CHASH_INITIAL_SIZE)
|
|
|
|
#define chash_init_stack(hashtable, buffer, _length, namespace) \
|
|
(*(hashtable)); \
|
|
\
|
|
if((_length) <= 0) { \
|
|
fprintf(stderr, "chash_init_stack: hashtable cannot have a maximum " \
|
|
"length of 0 or less (%s:%i)\n", __FILE__, __LINE__); \
|
|
abort(); \
|
|
} \
|
|
\
|
|
__chash_assert_nonnull(chash_init_stack, buffer); \
|
|
\
|
|
(hashtable)->CHASH_LENGTH_FIELD = 0; \
|
|
(hashtable)->CHASH_CAPACITY_FIELD = _length; \
|
|
(hashtable)->CHASH_BUCKETS_FIELD = buffer
|
|
|
|
#define chash_assign(hashtable, _key, _value, namespace) \
|
|
do { \
|
|
long __CHASH_HASH = 0; \
|
|
namespace ## _BUCKET __CHASH_KEY_BUCKET; \
|
|
memset(&__CHASH_KEY_BUCKET, 0, sizeof(namespace ## _BUCKET)); \
|
|
namespace ## _INIT(__CHASH_KEY_BUCKET, _key, _value); \
|
|
\
|
|
__chash_assert_nonnull(chash_assign, hashtable); \
|
|
__chash_assert_nonnull(chash_assign, (hashtable)->CHASH_BUCKETS_FIELD); \
|
|
__chash_resize(hashtable, namespace); \
|
|
__chash_hash((hashtable)->CHASH_CAPACITY_FIELD, _key, namespace); \
|
|
__chash_probe_to_unfilled((hashtable)->CHASH_CAPACITY_FIELD, \
|
|
(_key), (hashtable)->CHASH_BUCKETS_FIELD, namespace) \
|
|
\
|
|
if((hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH].CHASH_STATE_FIELD == \
|
|
CHASH_FILLED) { \
|
|
namespace ## _FREE_VALUE( \
|
|
(hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH].CHASH_VALUE_FIELD); \
|
|
} else { \
|
|
(hashtable)->CHASH_LENGTH_FIELD++; \
|
|
} \
|
|
\
|
|
(hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH] = __CHASH_KEY_BUCKET; \
|
|
(hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH].CHASH_STATE_FIELD = \
|
|
CHASH_FILLED; \
|
|
} while(0)
|
|
|
|
#define chash_lookup(hashtable, _key, storage, namespace) \
|
|
(void)storage; \
|
|
\
|
|
do { \
|
|
int __CHASH_INDEX = 0; \
|
|
long __CHASH_HASH = 0; \
|
|
namespace ## _BUCKET __CHASH_KEY_BUCKET; \
|
|
memset(&__CHASH_KEY_BUCKET, 0, sizeof(namespace ## _BUCKET)); \
|
|
namespace ## _INIT(__CHASH_KEY_BUCKET, _key, \
|
|
__CHASH_KEY_BUCKET.CHASH_VALUE_FIELD); \
|
|
\
|
|
(void) __CHASH_KEY_BUCKET; \
|
|
\
|
|
__chash_assert_nonnull(chash_lookup, hashtable); \
|
|
__chash_assert_nonnull(chash_lookup, (hashtable)->CHASH_BUCKETS_FIELD); \
|
|
__chash_hash((hashtable)->CHASH_CAPACITY_FIELD, _key, namespace); \
|
|
__chash_probe(hashtable, _key, namespace) \
|
|
\
|
|
if(((hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH].CHASH_STATE_FIELD != \
|
|
CHASH_FILLED) || __CHASH_INDEX != -1) { \
|
|
fprintf(stderr, "chash_lookup: failed to find key in hashtable (%s:%i)" \
|
|
"\n", __FILE__, __LINE__); \
|
|
abort(); \
|
|
} \
|
|
\
|
|
storage = (hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH].CHASH_VALUE_FIELD; \
|
|
} while(0)
|
|
|
|
#define chash_delete(hashtable, _key, namespace) \
|
|
do { \
|
|
int __CHASH_INDEX = 0; \
|
|
long __CHASH_HASH = 0; \
|
|
\
|
|
__chash_assert_nonnull(chash_delete, hashtable); \
|
|
__chash_assert_nonnull(chash_delete, (hashtable)->CHASH_BUCKETS_FIELD); \
|
|
__chash_hash((hashtable)->CHASH_CAPACITY_FIELD, _key, namespace); \
|
|
__chash_probe(hashtable, _key, namespace) \
|
|
\
|
|
if(((hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH].CHASH_STATE_FIELD != \
|
|
CHASH_FILLED) || __CHASH_INDEX != -1) { \
|
|
fprintf(stderr, "chash_delete: failed to find key in hashtable (%s:%i)" \
|
|
"\n", __FILE__, __LINE__); \
|
|
abort(); \
|
|
} \
|
|
\
|
|
namespace ## _FREE_KEY((hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH] \
|
|
.CHASH_KEY_FIELD); \
|
|
namespace ## _FREE_VALUE( \
|
|
(hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH].CHASH_VALUE_FIELD); \
|
|
(hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH].CHASH_STATE_FIELD = \
|
|
CHASH_TOMBSTONE; \
|
|
(hashtable)->CHASH_LENGTH_FIELD--; \
|
|
} while(0)
|
|
|
|
#define chash_contains(hashtable, _key, storage, namespace) \
|
|
1; \
|
|
\
|
|
do { \
|
|
int __CHASH_INDEX = 0; \
|
|
long __CHASH_HASH = 0; \
|
|
\
|
|
__chash_assert_nonnull(chash_contents, hashtable); \
|
|
__chash_assert_nonnull(chash_contents, (hashtable)->CHASH_BUCKETS_FIELD); \
|
|
__chash_hash((hashtable)->CHASH_CAPACITY_FIELD, _key, namespace); \
|
|
__chash_probe(hashtable, _key, namespace) \
|
|
\
|
|
if(((hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH].CHASH_STATE_FIELD != \
|
|
CHASH_FILLED) || __CHASH_INDEX != -1) { \
|
|
storage = 0; \
|
|
} \
|
|
} while(0)
|
|
|
|
#define chash_lookup_bucket(hashtable, _key, storage, namespace) \
|
|
storage; \
|
|
\
|
|
do { \
|
|
CHASH_COUNTER_TYPE __CHASH_INDEX = 0; \
|
|
long __CHASH_HASH = 0; \
|
|
namespace ## _BUCKET __CHASH_KEY_BUCKET; \
|
|
memset(&__CHASH_KEY_BUCKET, 0, sizeof(namespace ## _BUCKET)); \
|
|
namespace ## _INIT(__CHASH_KEY_BUCKET, _key, \
|
|
__CHASH_KEY_BUCKET.CHASH_VALUE_FIELD); \
|
|
\
|
|
(void) __CHASH_KEY_BUCKET; \
|
|
\
|
|
__chash_assert_nonnull(chash_lookup_bucket, hashtable); \
|
|
__chash_assert_nonnull(chash_lookup_bucket, \
|
|
(hashtable)->CHASH_BUCKETS_FIELD); \
|
|
__chash_hash((hashtable)->CHASH_CAPACITY_FIELD, _key, namespace); \
|
|
__chash_probe(hashtable, _key, namespace) \
|
|
\
|
|
if(((hashtable)->CHASH_BUCKETS_FIELD[__CHASH_HASH].CHASH_STATE_FIELD != \
|
|
CHASH_FILLED) || __CHASH_INDEX != -1) { \
|
|
fprintf(stderr, "chash_lookup_bucket: failed to find key in hashtable" \
|
|
"(%s:%i) \n", __FILE__, __LINE__); \
|
|
abort(); \
|
|
} \
|
|
\
|
|
storage = ((hashtable)->CHASH_BUCKETS_FIELD + __CHASH_HASH); \
|
|
} while(0)
|
|
|
|
#define chash_free(hashtable, namespace) \
|
|
do { \
|
|
__chash_assert_nonnull(chash_free, hashtable); \
|
|
__chash_assert_nonnull(chash_free, (hashtable)->CHASH_BUCKETS_FIELD); \
|
|
(hashtable)->CHASH_CAPACITY_FIELD--; \
|
|
\
|
|
while((hashtable)->CHASH_CAPACITY_FIELD != -1) { \
|
|
if((hashtable)->CHASH_BUCKETS_FIELD[(hashtable)->CHASH_CAPACITY_FIELD] \
|
|
.CHASH_STATE_FIELD != CHASH_FILLED) { \
|
|
(hashtable)->CHASH_CAPACITY_FIELD--; \
|
|
continue; \
|
|
} \
|
|
\
|
|
namespace ##_FREE_KEY( \
|
|
(hashtable)->CHASH_BUCKETS_FIELD[(hashtable)->CHASH_CAPACITY_FIELD] \
|
|
.CHASH_KEY_FIELD); \
|
|
namespace ##_FREE_VALUE( \
|
|
(hashtable)->CHASH_BUCKETS_FIELD[(hashtable)->CHASH_CAPACITY_FIELD] \
|
|
.CHASH_VALUE_FIELD); \
|
|
(hashtable)->CHASH_CAPACITY_FIELD--; \
|
|
(hashtable)->CHASH_LENGTH_FIELD--; \
|
|
} \
|
|
\
|
|
if((namespace ## _HEAP) == 1) { \
|
|
free((hashtable)->CHASH_BUCKETS_FIELD); \
|
|
free((hashtable)); \
|
|
} \
|
|
} while(0);
|
|
|
|
#define chash_is_full(hashtable, namespace) \
|
|
(((hashtable)->CHASH_LENGTH_FIELD) == ((hashtable)->CHASH_CAPACITY_FIELD))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Iterator logic */
|
|
#define chash_iter(hashtable, i) \
|
|
for((i) = 0; (i) < (hashtable)->CHASH_CAPACITY_FIELD; (i)++)
|
|
|
|
#define chash_get(hashtable, key, value, i) \
|
|
if((hashtable)->CHASH_BUCKETS_FIELD[(i)].CHASH_STATE_FIELD != CHASH_FILLED) \
|
|
continue; \
|
|
\
|
|
(key) = (hashtable)->CHASH_BUCKETS_FIELD[(i)].CHASH_KEY_FIELD; \
|
|
(value) = (hashtable)->CHASH_BUCKETS_FIELD[(i)].CHASH_VALUE_FIELD
|
|
|
|
#endif
|