diff --git a/Makefile b/Makefile index d1fedb7..42b43c8 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ SUBDIRS := libhashset hashset -BUILD_ALL := $(patsubst %,build_rule\:%,$(SUBDIRS)) -CLEAN_ALL := $(patsubst %,clean_rule\:%,$(SUBDIRS)) +BUILD_ALL := $(patsubst %,build\:%,$(SUBDIRS)) +CLEAN_ALL := $(patsubst %,clean\:%,$(SUBDIRS)) .PHONY: all clean $(BUILD_ALL) $(CLEAN_ALL) @@ -10,7 +10,7 @@ all: $(BUILD_ALL) clean: $(CLEAN_ALL) $(BUILD_ALL): - $(MAKE) -C $(patsubst build_rule:%,%,$@) + $(MAKE) -C $(patsubst build:%,%,$@) $(CLEAN_ALL): - $(MAKE) -C $(patsubst clean_rule:%,%,$@) clean + $(MAKE) -C $(patsubst clean:%,%,$@) clean diff --git a/README.md b/README.md index 94f9540..1c8782c 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Introduction **LibHashSet** is a simple *hash set* implementation for C99. It uses open addressing and double hashing. -At this time, the *only* type of elements (keys) supported is `uint64_t`. +At this time, the *only* types of elements supported are `uint32_t` and `uint64_t`. This hash set implementation has been tested to *efficiently* handle several billions of items 😏 @@ -23,7 +23,7 @@ int main(int argc, char* argv[]) uintptr_t cursor = 0U; /* create new hash set instance */ - hash_set_t *const hash_set = hash_set_create(0U, -1.0); + hash_set64_t *const hash_set = hash_set_create64(0U, -1.0); if (!hash_set) { fputs("Allocation has failed!\n", stderr); @@ -33,7 +33,7 @@ int main(int argc, char* argv[]) /* add a number of items to the hash set, the set will grow as needed */ while (have_more_items()) { - const errno_t error = hash_set_insert(hash_set, get_next_item()); + const errno_t error = hash_set_insert64(hash_set, get_next_item()); if (error) { fprintf(stderr, "Insert operation has failed! (error: %d)\n", error); @@ -42,28 +42,28 @@ int main(int argc, char* argv[]) } /* test whether hash set contains a specific item */ - if (hash_set_contains(hash_set, 42U) == 0) + if (hash_set_contains64(hash_set, 42U) == 0) { puts("Set contains item!"); /* remove the existing item from the hash set */ - if (hash_set_remove(hash_set, 42U) == 0) + if (hash_set_remove64(hash_set, 42U) == 0) { puts("Item has been removed!"); } } /* print total number of items in the hash set*/ - printf("Total number of items: %zu\n", hash_set_size(hash_set)); + printf("Total number of items: %zu\n", hash_set_size64(hash_set)); /* print all items in the set */ - while (hash_set_iterate(hash_set, &cursor, &value) == 0) + while (hash_set_iterate64(hash_set, &cursor, &value) == 0) { printf("Item: %016llX\n", value); } /* destroy the hash set, when it is no longer needed! */ - hash_set_destroy(hash_set); + hash_set_destroy64(hash_set); return EXIT_SUCCESS; } ``` @@ -74,6 +74,8 @@ API Reference This section describes the LibHashSet programming interface, as declared in the `` header file. +LibHashSet supports sets containing values of type `uint32_t` or `uint64_t`. For each value type, separate functions are provided. The functions for `uint32_t` and `uint64_t` hash sets can be distinguished by the suffix `…32` and `…64` suffix, respectively. In the following, the functions are described in their "generic" form. + ***Note:*** On Microsoft Windows, when using LibHashSet as a "shared" library (DLL), the macro `HASHSET_DLL` must be defined *before* including ``! This is **not** required or allowed when using the "static" library. Types @@ -164,7 +166,7 @@ Tries to insert the given value into the hash set. The operation fails, if the s ```C errno_t hash_set_insert( hash_set_t *const instance, - const uint64_t value + const VALUE_TYPE value ); ``` @@ -174,7 +176,7 @@ errno_t hash_set_insert( A pointer to the hash set instance to be modified, as returned by the [hash_set_create()](#hash_set_create) function. * `value` - The value (key) to be inserted into the hash set. It can be *any* value in the `0U` to `UINT64_MAX` range. + The value (key) to be inserted into the hash set. #### Return value @@ -201,7 +203,7 @@ Tries to remove the given value from the hash set. The operation fails, if the s ```C errno_t hash_set_remove( hash_set_t *const instance, - const uint64_t value + const VALUE_TYPE value ); ``` @@ -211,7 +213,7 @@ errno_t hash_set_remove( A pointer to the hash set instance to be modified, as returned by the [hash_set_create()](#hash_set_create) function. * `value` - The value (key) to be removed from the hash set. It can be *any* value in the `0U` to `UINT64_MAX` range. + The value (key) to be removed from the hash set. #### Return value @@ -260,7 +262,7 @@ Tests whether the hash set contains a value. The operation fails, if the set doe ```C errno_t hash_set_contains( const hash_set_t *const instance, - const uint64_t value + const VALUE_TYPE value ); ``` @@ -270,7 +272,7 @@ errno_t hash_set_contains( A pointer to the hash set instance to be examined, as returned by the [hash_set_create()](#hash_set_create) function. * `value` - The value (key) to be searched in the hash set. It can be *any* value in the `0U` to `UINT64_MAX` range. + The value (key) to be searched in the hash set. #### Return value @@ -297,7 +299,7 @@ This function returns one value at a time. It should be called repeatedly, until errno_t hash_set_iterate( const hash_set_t *const instance, uintptr_t *const cursor, - uint64_t *const value + VALUE_TYPE *const value ); ``` @@ -310,10 +312,9 @@ errno_t hash_set_iterate( A pointer to a variable of type `uintptr_t` where the current iterator state (position) is saved. This variable **must** be initialized to the value `0U`, by the calling application, prior to the the *first* invocation! Each invocation will update the value of `*cursor`; the value **shall not** be altered by the application. - * `value` - A pointer to a variable of type `uint64_t` where the next value in the set is stored on success. + A pointer to a variable of type `uint32_t` or `uint64_t` where the next value in the set is stored on success. The content of the variable should be considered *undefined*, if the invocation has failed. #### Return value diff --git a/hashset/Makefile b/hashset/Makefile index b80712e..db4a5b6 100644 --- a/hashset/Makefile +++ b/hashset/Makefile @@ -1,7 +1,7 @@ DUMPMACHINE := $(shell $(CC) -dumpmachine) ifneq ($(SANITIZE_ADDRESS),1) - XCFLAGS = -O3 -DNDEBUG + XCFLAGS = -Ofast -DNDEBUG -s -static ifneq ($(firstword $(filter x86_64-%,$(DUMPMACHINE))),) XCFLAGS += -march=x86-64 -mtune=nocona else ifneq ($(firstword $(filter i686-%,$(DUMPMACHINE))),) @@ -22,19 +22,22 @@ CFLAGS = -std=c99 -D_DEFAULT_SOURCE -Wpedantic -I../libhashset/include $(XCFLAGS SRC_PATH := src BIN_PATH := bin +ALL_PATH := $(SRC_PATH) $(BIN_PATH) BIN_FILE := $(BIN_PATH)/hashset-test$(EXE_SUFFIX) SRC_FILE := $(SRC_PATH)/main.c LIB_FILE := ../libhashset/lib/libhashset-1.a -.PHONY: all clean +.PHONY: all build clean -all: $(BIN_FILE) +all: clean build -$(BIN_FILE): $(SRC_FILE) $(LIB_FILE) $(BIN_PATH) - $(CC) $(CFLAGS) $< -o $@ $(LIB_FILE) +build: $(ALL_PATH) $(BIN_FILE) -$(SRC_PATH) $(BIN_PATH): +$(BIN_FILE): $(SRC_FILE) $(LIB_FILE) + $(CC) $(CFLAGS) $^ -o $@ + +$(ALL_PATH): mkdir -p $@ clean: diff --git a/hashset/src/main.c b/hashset/src/main.c index f8094d7..8a00b39 100644 --- a/hashset/src/main.c +++ b/hashset/src/main.c @@ -64,7 +64,7 @@ static INLINE uint64_t random_next(random_t *const rnd) #define PRINT_SET_INFO(X) do \ {\ - if (!hash_set_info(hash_set, &capacity, &valid, &deleted, &limit)) \ + if (!hash_set_info64(hash_set, &capacity, &valid, &deleted, &limit)) \ { \ printf("[#%d] capacity: %010zu, valid: %010zu, deleted: %010zu, limit: %010zu\n", (X), capacity, valid, deleted, limit); \ } \ @@ -77,7 +77,7 @@ while(0) #define MAXIMUM 425984U -static int test_function_1(hash_set_t *const hash_set) +static int test_function_1(hash_set64_t *const hash_set) { size_t capacity, valid, deleted, limit; @@ -87,7 +87,7 @@ static int test_function_1(hash_set_t *const hash_set) { if ((i != 3167U) && (i != 9887U) && (i != 185903U) && (i != 387083U)) { - const errno_t error = hash_set_insert(hash_set, i); + const errno_t error = hash_set_insert64(hash_set, i); if (error) { printf("Insert operation has failed! (error: %d)\n", error); @@ -97,7 +97,7 @@ static int test_function_1(hash_set_t *const hash_set) PRINT_SET_INFO(1); } - if (hash_set_size(hash_set) != MAXIMUM - 4U) + if (hash_set_size64(hash_set) != MAXIMUM - 4U) { puts("Invalid size!"); return EXIT_FAILURE; @@ -107,7 +107,7 @@ static int test_function_1(hash_set_t *const hash_set) { if ((i != 3167U) && (i != 9887U) && (i != 387083U)) { - const errno_t error = hash_set_insert(hash_set, i); + const errno_t error = hash_set_insert64(hash_set, i); if (error != ((i != 185903U) ? EEXIST : 0)) { printf("Insert operation has failed! (error: %d)\n", error); @@ -116,7 +116,7 @@ static int test_function_1(hash_set_t *const hash_set) } } - if (hash_set_size(hash_set) != MAXIMUM - 3U) + if (hash_set_size64(hash_set) != MAXIMUM - 3U) { puts("Invalid size!"); return EXIT_FAILURE; @@ -124,7 +124,7 @@ static int test_function_1(hash_set_t *const hash_set) for (uint64_t i = 0; i < MAXIMUM; ++i) { - const errno_t error = hash_set_contains(hash_set, i); + const errno_t error = hash_set_contains64(hash_set, i); if (error != ((i != 3167U) && (i != 9887U) && (i != 387083U)) ? 0 : ENOENT) { printf("Contains operation has failed! (error: %d)\n", error); @@ -136,7 +136,7 @@ static int test_function_1(hash_set_t *const hash_set) { if ((i != 3167U) && (i != 9887U) && (i != 216263U) && (i != 387083U)) { - const errno_t error = hash_set_remove(hash_set, i); + const errno_t error = hash_set_remove64(hash_set, i); if (error) { printf("Remove operation has failed! (error: %d)\n", error); @@ -146,7 +146,7 @@ static int test_function_1(hash_set_t *const hash_set) PRINT_SET_INFO(1); } - if (hash_set_size(hash_set) != 1U) + if (hash_set_size64(hash_set) != 1U) { puts("Invalid size!"); return EXIT_FAILURE; @@ -154,7 +154,7 @@ static int test_function_1(hash_set_t *const hash_set) for (uint64_t i = 0; i < MAXIMUM; ++i) { - const errno_t error = hash_set_contains(hash_set, i); + const errno_t error = hash_set_contains64(hash_set, i); if (error != ((i != 216263U) ? ENOENT : 0)) { printf("Contains operation has failed! (error: %d)\n", error); @@ -162,19 +162,19 @@ static int test_function_1(hash_set_t *const hash_set) } } - if (!hash_set_remove(hash_set, 9887U)) + if (!hash_set_remove64(hash_set, 9887U)) { puts("Final remove operation has failed!"); return EXIT_FAILURE; } - if (hash_set_remove(hash_set, 216263U)) + if (hash_set_remove64(hash_set, 216263U)) { puts("Final remove operation has failed!"); return EXIT_FAILURE; } - if (hash_set_size(hash_set) != 0U) + if (hash_set_size64(hash_set) != 0U) { puts("Invalid size!"); return EXIT_FAILURE; @@ -193,7 +193,7 @@ static int test_function_1(hash_set_t *const hash_set) #define ARRSIZE 14867U -static int test_function_2(hash_set_t *const hash_set) +static int test_function_2(hash_set64_t *const hash_set) { size_t capacity, valid, deleted, limit; uint64_t value; @@ -221,7 +221,7 @@ static int test_function_2(hash_set_t *const hash_set) { if (test[j]) { - const errno_t error = hash_set_insert(hash_set, j); + const errno_t error = hash_set_insert64(hash_set, j); if (error) { printf("Insert operation has failed! (error: %d)\n", error); @@ -230,7 +230,7 @@ static int test_function_2(hash_set_t *const hash_set) PRINT_SET_INFO(2); } } - while (!hash_set_iterate(hash_set, &cursor, &value)) + while (!hash_set_iterate64(hash_set, &cursor, &value)) { if (!test[value]) { @@ -242,7 +242,7 @@ static int test_function_2(hash_set_t *const hash_set) { if (test[j]) { - const errno_t error = hash_set_remove(hash_set, j); + const errno_t error = hash_set_remove64(hash_set, j); if (error) { printf("Remove operation has failed! (error: %d)\n", error); @@ -252,7 +252,7 @@ static int test_function_2(hash_set_t *const hash_set) test[j] = UINT8_C(0); } } - if (hash_set_size(hash_set) != 0U) + if (hash_set_size64(hash_set) != 0U) { puts("Invalid size!"); return EXIT_FAILURE; @@ -269,7 +269,7 @@ static int test_function_2(hash_set_t *const hash_set) /* TEST #3 */ /* ========================================================================= */ -static int test_function_3(hash_set_t *const hash_set) +static int test_function_3(hash_set64_t *const hash_set) { size_t capacity, valid, deleted, limit; uint8_t spinner = 0U; @@ -283,7 +283,7 @@ static int test_function_3(hash_set_t *const hash_set) for (;;) { const uint64_t rnd = random_next(&random) & UINT64_C(0x3FFFFFFFFFFFFFF); - const errno_t error = hash_set_insert(hash_set, rnd); + const errno_t error = hash_set_insert64(hash_set, rnd); if (error) { if (error != EEXIST) @@ -311,7 +311,7 @@ static int test_function_3(hash_set_t *const hash_set) PRINT_SET_INFO(3); - if (hash_set_clear(hash_set)) + if (hash_set_clear64(hash_set)) { puts("Clear operation has failed!"); return EXIT_FAILURE; @@ -330,7 +330,7 @@ static int test_function_3(hash_set_t *const hash_set) #define LIMIT (((uint64_t)UINT32_MAX) >> 2) -static int test_function_4(hash_set_t *const hash_set) +static int test_function_4(hash_set64_t *const hash_set) { size_t capacity, valid, deleted, limit; uint8_t spinner = 0U; @@ -338,7 +338,7 @@ static int test_function_4(hash_set_t *const hash_set) for (uint64_t value = 0U; value < LIMIT; ++value) { - const errno_t error = hash_set_insert(hash_set, value); + const errno_t error = hash_set_insert64(hash_set, value); if (error) { PRINT_SET_INFO(4); @@ -358,7 +358,7 @@ static int test_function_4(hash_set_t *const hash_set) for (uint64_t value = 0U; value < LIMIT; ++value) { - const errno_t error = hash_set_remove(hash_set, value); + const errno_t error = hash_set_remove64(hash_set, value); if (error) { PRINT_SET_INFO(4); @@ -376,7 +376,7 @@ static int test_function_4(hash_set_t *const hash_set) } } - if (hash_set_size(hash_set) != 0U) + if (hash_set_size64(hash_set) != 0U) { puts("Invalid size!"); return EXIT_FAILURE; @@ -397,7 +397,7 @@ int main(void) printf("LibHashSet Test v%" PRIu16 ".%" PRIu16 ".%" PRIu16 " [%s]\n\n", HASHSET_VERSION_MAJOR, HASHSET_VERSION_MINOR, HASHSET_VERSION_PATCH, HASHSET_BUILD_DATE); - hash_set_t *const hash_set = hash_set_create(0U, -1.0); + hash_set64_t *const hash_set = hash_set_create64(0U, -1.0); if (!hash_set) { puts("Allocation has failed!"); @@ -424,12 +424,12 @@ int main(void) goto failure; } - hash_set_destroy(hash_set); + hash_set_destroy64(hash_set); puts("Test completed successfully."); return EXIT_SUCCESS; failure: - hash_set_destroy(hash_set); + hash_set_destroy64(hash_set); puts("Something went wrong !!!"); return EXIT_FAILURE; } diff --git a/libhashset/Makefile b/libhashset/Makefile index 10a8ae9..23d2b5a 100644 --- a/libhashset/Makefile +++ b/libhashset/Makefile @@ -1,7 +1,7 @@ DUMPMACHINE := $(shell $(CC) -dumpmachine) ifneq ($(SANITIZE_ADDRESS),1) - XCFLAGS = -O3 -DNDEBUG + XCFLAGS = -Ofast -DNDEBUG ifneq ($(firstword $(filter x86_64-%,$(DUMPMACHINE))),) XCFLAGS += -march=x86-64 -mtune=nocona else ifneq ($(firstword $(filter i686-%,$(DUMPMACHINE))),) @@ -16,22 +16,26 @@ CFLAGS = -std=c99 -D_DEFAULT_SOURCE -Wpedantic -Iinclude $(XCFLAGS) SRC_PATH := src OBJ_PATH := obj LIB_PATH := lib +ALL_PATH := $(SRC_PATH) $(OBJ_PATH) $(LIB_PATH) +SRC_FILE := $(wildcard $(SRC_PATH)/*.c) +OBJ_FILE := $(addprefix $(OBJ_PATH)/,$(patsubst %.c,%.o,$(notdir $(SRC_FILE)))) LIB_FILE := $(LIB_PATH)/libhashset-1.a -OBJ_FILE := $(OBJ_PATH)/hash_set.o -.PHONY: all clean +.PHONY: all build clean -all: $(LIB_FILE) +all: clean build -$(LIB_FILE): $(OBJ_FILE) $(LIB_PATH) - $(AR) rcs $@ $< +build: $(ALL_PATH) $(LIB_FILE) -$(OBJ_FILE): $(SRC_PATH) $(OBJ_PATH) +$(LIB_FILE): $(OBJ_FILE) + $(AR) rcs $@ $(OBJ_FILE) + +$(OBJ_FILE): $(CC) $(CFLAGS) -c $(SRC_PATH)/$(patsubst %.o,%.c,$(notdir $@)) -o $@ -$(SRC_PATH) $(OBJ_PATH) $(LIB_PATH): +$(ALL_PATH): mkdir -p $@ clean: - rm -f $(LIB_FILE) $(OBJ_PATH)/*.o + rm -vf $(LIB_FILE) $(OBJ_PATH)/*.o diff --git a/libhashset/include/hash_set.h b/libhashset/include/hash_set.h index b3a45ef..56a5253 100644 --- a/libhashset/include/hash_set.h +++ b/libhashset/include/hash_set.h @@ -3,8 +3,8 @@ /* This work has been released under the CC0 1.0 Universal license! */ /******************************************************************************/ -#ifndef _INC_HASHSET_H -#define _INC_HASHSET_H +#ifndef _LIBHASHSET_INCLUDED +#define _LIBHASHSET_INCLUDED #include #include @@ -27,9 +27,6 @@ extern "C" { typedef int errno_t; #endif -struct _hash_set; -typedef struct _hash_set hash_set_t; - HASHSET_API const uint16_t HASHSET_VERSION_MAJOR; HASHSET_API const uint16_t HASHSET_VERSION_MINOR; HASHSET_API const uint16_t HASHSET_VERSION_PATCH; @@ -37,17 +34,38 @@ HASHSET_API const uint16_t HASHSET_VERSION_PATCH; HASHSET_API const char *const HASHSET_BUILD_DATE; HASHSET_API const char *const HASHSET_BUILD_TIME; -HASHSET_API hash_set_t *hash_set_create(const size_t initial_capacity, const double load_factor); -HASHSET_API void hash_set_destroy(hash_set_t *const instance); +struct _hash_set32; +struct _hash_set64; -HASHSET_API errno_t hash_set_insert(hash_set_t *const instance, const uint64_t value); -HASHSET_API errno_t hash_set_remove(hash_set_t *const instance, const uint64_t value); -HASHSET_API errno_t hash_set_clear(hash_set_t *const instance); +typedef struct _hash_set32 hash_set32_t; +typedef struct _hash_set64 hash_set64_t; -HASHSET_API errno_t hash_set_contains(const hash_set_t *const instance, const uint64_t value); -HASHSET_API errno_t hash_set_iterate(const hash_set_t *const instance, uintptr_t *const cursor, uint64_t *const value); -HASHSET_API size_t hash_set_size(const hash_set_t *const instance); -HASHSET_API errno_t hash_set_info(const hash_set_t *const instance, size_t *const capacity, size_t *const valid, size_t *const deleted, size_t *const limit); +HASHSET_API hash_set32_t *hash_set_create32(const size_t initial_capacity, const double load_factor); +HASHSET_API hash_set64_t* hash_set_create64(const size_t initial_capacity, const double load_factor); + +HASHSET_API void hash_set_destroy32(hash_set32_t *const instance); +HASHSET_API void hash_set_destroy64(hash_set64_t *const instance); + +HASHSET_API errno_t hash_set_insert32(hash_set32_t *const instance, const uint32_t value); +HASHSET_API errno_t hash_set_insert64(hash_set64_t *const instance, const uint64_t value); + +HASHSET_API errno_t hash_set_remove32(hash_set32_t *const instance, const uint32_t value); +HASHSET_API errno_t hash_set_remove64(hash_set64_t *const instance, const uint64_t value); + +HASHSET_API errno_t hash_set_clear32(hash_set32_t *const instance); +HASHSET_API errno_t hash_set_clear64(hash_set64_t *const instance); + +HASHSET_API errno_t hash_set_contains32(const hash_set32_t *const instance, const uint32_t value); +HASHSET_API errno_t hash_set_contains64(const hash_set64_t *const instance, const uint64_t value); + +HASHSET_API errno_t hash_set_iterate32(const hash_set32_t *const instance, uintptr_t *const cursor, uint32_t *const value); +HASHSET_API errno_t hash_set_iterate64(const hash_set64_t *const instance, uintptr_t *const cursor, uint64_t *const value); + +HASHSET_API size_t hash_set_size32(const hash_set32_t *const instance); +HASHSET_API size_t hash_set_size64(const hash_set64_t *const instance); + +HASHSET_API errno_t hash_set_info32(const hash_set32_t *const instance, size_t *const capacity, size_t *const valid, size_t *const deleted, size_t *const limit); +HASHSET_API errno_t hash_set_info64(const hash_set64_t *const instance, size_t *const capacity, size_t *const valid, size_t *const deleted, size_t *const limit); #ifdef __cplusplus } diff --git a/libhashset/libhashset.vcxproj b/libhashset/libhashset.vcxproj index 61aa097..60bb7b5 100644 --- a/libhashset/libhashset.vcxproj +++ b/libhashset/libhashset.vcxproj @@ -40,9 +40,13 @@ + + - + + + 16.0 diff --git a/libhashset/libhashset.vcxproj.filters b/libhashset/libhashset.vcxproj.filters index bb9ec5a..a2aba0c 100644 --- a/libhashset/libhashset.vcxproj.filters +++ b/libhashset/libhashset.vcxproj.filters @@ -18,9 +18,21 @@ Header Files + + Header Files + + + Header Files + - + + Source Files + + + Source Files + + Source Files diff --git a/libhashset/src/common.h b/libhashset/src/common.h new file mode 100644 index 0000000..4c8cf4a --- /dev/null +++ b/libhashset/src/common.h @@ -0,0 +1,122 @@ +/******************************************************************************/ +/* HashSet for C99, by LoRd_MuldeR */ +/* This work has been released under the CC0 1.0 Universal license! */ +/******************************************************************************/ + +#ifndef _LIBHASHSET_COMMON_INCLUDED +#define _LIBHASHSET_COMMON_INCLUDED + +/* CRT */ +#include +#include +#include +#include +#include +#include + +typedef int bool_t; +#define TRUE 1 +#define FALSE 0 + +#if defined(__GNUC__) +# define INLINE __inline__ +# define FORCE_INLINE __attribute__((always_inline)) __inline__ +#elif defined(_MSC_VER) +# define INLINE __inline +# define FORCE_INLINE __forceinline +#else +# define INLINE +#endif + +static const size_t MINIMUM_CAPACITY = 128U; +static const size_t DEFAULT_CAPACITY = 8192U; +static const double DEFAULT_LOADFCTR = 0.8; + +/* ------------------------------------------------- */ +/* Utilities */ +/* ------------------------------------------------- */ + +#define IS_VALID(X,Y) (get_flag((X).used, (Y)) && (!get_flag((X).deleted, (Y)))) + +#define BOUND(MIN,VAL,MAX) (((VAL) < (MIN)) ? (MIN) : (((VAL) > (MAX)) ? (MAX) : (VAL))) + +#define SAFE_SET(X,Y) do { if((X)) { *(X) = (Y); } } while(0) + +#define SAFE_FREE(X) do { if ((X)) { free((X)); (X) = NULL; } } while(0) + +#define _NAME_GLUE_HELPER(X,Y) X##Y +#define NAME_GLUE(X,Y) _NAME_GLUE_HELPER(X,Y) + +/* ------------------------------------------------- */ +/* Math */ +/* ------------------------------------------------- */ + +static FORCE_INLINE size_t div_ceil(const size_t value, const size_t divisor) +{ + return (value / divisor) + ((value % divisor != 0U) ? 1U : 0U); +} + +static FORCE_INLINE size_t round_sz(const double d) +{ + + return ((!isnan(d)) && (d >= 0.0)) ? ((d + 0.5 >= ((double)SIZE_MAX)) ? SIZE_MAX : ((size_t)(d + 0.5))) : 0U; +} + +static FORCE_INLINE size_t safe_add(const size_t a, const size_t b) +{ + return ((SIZE_MAX - a) > b) ? (a + b) : SIZE_MAX; +} + +static FORCE_INLINE size_t safe_incr(const size_t value) +{ + return (value < SIZE_MAX) ? (value + 1U) : value; +} + +static FORCE_INLINE size_t safe_decr(const size_t value) +{ + return (value > 0U) ? (value - 1U) : value; +} + +static FORCE_INLINE size_t safe_mult(const size_t a, const size_t b) +{ + const size_t result = a * b; + return ((a == 0U) || (result / a == b)) ? result : SIZE_MAX; +} + +static FORCE_INLINE size_t safe_times2(const size_t value) +{ + return (value <= (SIZE_MAX / 2U)) ? (2U * value) : SIZE_MAX; +} + +static FORCE_INLINE size_t next_pow2(const size_t target) +{ + size_t result = MINIMUM_CAPACITY; + + while (result < target) + { + result = safe_times2(result); + } + + return result; +} + +/* ------------------------------------------------- */ +/* Flags */ +/* ------------------------------------------------- */ + +static INLINE bool_t get_flag(const uint8_t* const flags, const size_t index) +{ + return (flags[index / 8U] >> (index % 8U)) & UINT8_C(1); +} + +static INLINE void set_flag(uint8_t* const flags, const size_t index) +{ + flags[index / 8U] |= UINT8_C(1) << (index % 8U); +} + +static INLINE void clear_flag(uint8_t* const flags, const size_t index) +{ + flags[index / 8U] &= ~(UINT8_C(1) << (index % 8U)); +} + +#endif diff --git a/libhashset/src/hash_set.c b/libhashset/src/generic.h similarity index 58% rename from libhashset/src/hash_set.c rename to libhashset/src/generic.h index 9ca6123..7a5b47b 100644 --- a/libhashset/src/hash_set.c +++ b/libhashset/src/generic.h @@ -3,120 +3,44 @@ /* This work has been released under the CC0 1.0 Universal license! */ /******************************************************************************/ -#include "hash_set.h" - -/* CRT */ -#include -#include -#include -#include -#include -#include - -typedef int bool_t; -#define TRUE 1 -#define FALSE 0 - -#if defined(__GNUC__) -# define INLINE __inline__ -# define FORCE_INLINE __attribute__((always_inline)) __inline__ -#elif defined(_MSC_VER) -# define INLINE __inline -# define FORCE_INLINE __forceinline -#else -# define INLINE +#ifndef _LIBHASHSET_COMMON_INCLUDED +#error File "common.h" must be included first! #endif -const uint16_t HASHSET_VERSION_MAJOR = UINT16_C(1); -const uint16_t HASHSET_VERSION_MINOR = UINT16_C(0); -const uint16_t HASHSET_VERSION_PATCH = UINT16_C(0); +#ifndef NAME_SUFFIX +#error NAME_SUFFIX must be defined! +#endif -const char *const HASHSET_BUILD_DATE = __DATE__; -const char *const HASHSET_BUILD_TIME = __TIME__; +#ifndef SET_TYPE +#error SET_TYPE must be defined! +#endif -struct _hash_set_data +#ifndef VALUE_TYPE +#error VALUE_TYPE must be defined! +#endif + +#define MAKE_NAME(X) NAME_GLUE(X,NAME_SUFFIX) + +/* ------------------------------------------------- */ +/* Data types */ +/* ------------------------------------------------- */ + +#define DATA_STRUCT MAKE_NAME(_hash_data) + +struct DATA_STRUCT { - uint64_t *values; + VALUE_TYPE *values; uint8_t *used, *deleted; size_t capacity; }; -struct _hash_set +struct MAKE_NAME(_hash_set) { double load_factor; size_t valid, deleted, limit; - struct _hash_set_data data; + struct DATA_STRUCT data; }; -static const size_t MINIMUM_CAPACITY = 128U; -static const size_t DEFAULT_CAPACITY = 8192U; -static const double DEFAULT_LOADFCTR = 0.8; - -/* ========================================================================= */ -/* PRIVATE FUNCTIONS */ -/* ========================================================================= */ - -#define IS_VALID(X,Y) (get_flag((X).used, (Y)) && (!get_flag((X).deleted, (Y)))) - -#define BOUND(MIN,VAL,MAX) (((VAL) < (MIN)) ? (MIN) : (((VAL) > (MAX)) ? (MAX) : (VAL))) - -#define SAFE_SET(X,Y) do { if((X)) { *(X) = (Y); } } while(0) - -#define SAFE_FREE(X) do { if ((X)) { free((X)); (X) = NULL; } } while(0) - -/* ------------------------------------------------- */ -/* Math */ -/* ------------------------------------------------- */ - -static FORCE_INLINE size_t div_ceil(const size_t value, const size_t divisor) -{ - return (value / divisor) + ((value % divisor != 0U) ? 1U : 0U); -} - -static FORCE_INLINE size_t round_sz(const double d) -{ - - return ((!isnan(d)) && (d >= 0.0)) ? ((d + 0.5 >= ((double)SIZE_MAX)) ? SIZE_MAX : ((size_t)(d + 0.5))) : 0U; -} - -static FORCE_INLINE size_t safe_add(const size_t a, const size_t b) -{ - return ((SIZE_MAX - a) > b) ? (a + b) : SIZE_MAX; -} - -static FORCE_INLINE size_t safe_incr(const size_t value) -{ - return (value < SIZE_MAX) ? (value + 1U) : value; -} - -static FORCE_INLINE size_t safe_decr(const size_t value) -{ - return (value > 0U) ? (value - 1U) : value; -} - -static FORCE_INLINE size_t safe_mult(const size_t a, const size_t b) -{ - const size_t result = a * b; - return ((a == 0U) || (result / a == b)) ? result : SIZE_MAX; -} - -static FORCE_INLINE size_t safe_times2(const size_t value) -{ - return (value <= (SIZE_MAX / 2U)) ? (2U * value) : SIZE_MAX; -} - -static FORCE_INLINE size_t next_pow2(const size_t target) -{ - size_t result = MINIMUM_CAPACITY; - - while (result < target) - { - result = safe_times2(result); - } - - return result; -} - /* ------------------------------------------------- */ /* Hash function */ /* ------------------------------------------------- */ @@ -131,7 +55,7 @@ static FORCE_INLINE void hash_update(uint64_t *const hash, uint64_t value) while (value >>= CHAR_BIT); } -static INLINE uint64_t hash_compute(const uint64_t i, const uint64_t value) +static INLINE uint64_t hash_compute(const uint64_t i, const VALUE_TYPE value) { uint64_t hash = UINT64_C(14695981039346656037); hash_update(&hash, i); @@ -148,11 +72,11 @@ static INLINE void zero_memory(void *const addr, const size_t count, const size_ memset(addr, 0, safe_mult(count, size)); } -static INLINE bool_t alloc_data(struct _hash_set_data *const data, const size_t capacity) +static INLINE bool_t alloc_data(struct DATA_STRUCT *const data, const size_t capacity) { - zero_memory(data, 1U, sizeof(struct _hash_set_data)); + zero_memory(data, 1U, sizeof(struct DATA_STRUCT)); - data->values = (uint64_t*) calloc(capacity, sizeof(uint64_t)); + data->values = (VALUE_TYPE*) calloc(capacity, sizeof(VALUE_TYPE)); if (!data->values) { return FALSE; @@ -177,7 +101,7 @@ static INLINE bool_t alloc_data(struct _hash_set_data *const data, const size_t return TRUE; } -static INLINE void free_data(struct _hash_set_data *const data) +static INLINE void free_data(struct DATA_STRUCT *const data) { if (data) { @@ -188,32 +112,13 @@ static INLINE void free_data(struct _hash_set_data *const data) } } -/* ------------------------------------------------- */ -/* Flags */ -/* ------------------------------------------------- */ - -static INLINE bool_t get_flag(const uint8_t *const flags, const size_t index) -{ - return (flags[index / 8U] >> (index % 8U)) & UINT8_C(1); -} - -static INLINE void set_flag(uint8_t *const flags, const size_t index) -{ - flags[index / 8U] |= UINT8_C(1) << (index % 8U); -} - -static INLINE void clear_flag(uint8_t *const flags, const size_t index) -{ - flags[index / 8U] &= ~(UINT8_C(1) << (index % 8U)); -} - /* ------------------------------------------------- */ /* Set functions */ /* ------------------------------------------------- */ #define INDEX(X) ((size_t)((X) % data->capacity)) -static INLINE bool_t find_slot(const struct _hash_set_data *const data, const uint64_t value, size_t *const index_out, bool_t *const reused_out) +static INLINE bool_t find_slot(const struct DATA_STRUCT *const data, const VALUE_TYPE value, size_t *const index_out, bool_t *const reused_out) { uint64_t loop = 0U; bool_t is_saved = FALSE; @@ -250,7 +155,7 @@ static INLINE bool_t find_slot(const struct _hash_set_data *const data, const ui return FALSE; } -static INLINE void put_value(struct _hash_set_data *const data, const size_t index, const uint64_t value, const bool_t reusing) +static INLINE void put_value(struct DATA_STRUCT *const data, const size_t index, const VALUE_TYPE value, const bool_t reusing) { data->values[index] = value; if (reusing) @@ -277,9 +182,9 @@ static size_t INLINE compute_limit(const size_t capacity, const double load_fact return limit; } -static INLINE errno_t rebuild_set(hash_set_t *const instance, const size_t new_capacity) +static INLINE errno_t rebuild_set(SET_TYPE *const instance, const size_t new_capacity) { - struct _hash_set_data temp; + struct DATA_STRUCT temp; size_t index, k; if (new_capacity < instance->valid) @@ -296,7 +201,7 @@ static INLINE errno_t rebuild_set(hash_set_t *const instance, const size_t new_c { if (IS_VALID(instance->data, k)) { - const uint64_t value = instance->data.values[k]; + const VALUE_TYPE value = instance->data.values[k]; if (find_slot(&temp, value, &index, NULL)) { free_data(&temp); @@ -318,9 +223,9 @@ static INLINE errno_t rebuild_set(hash_set_t *const instance, const size_t new_c /* PUBLIC FUNCTIONS */ /* ========================================================================= */ -hash_set_t *hash_set_create(const size_t initial_capacity, const double load_factor) +SET_TYPE* MAKE_NAME(hash_set_create)(const size_t initial_capacity, const double load_factor) { - hash_set_t *instance = (hash_set_t*) calloc(1U, sizeof(hash_set_t)); + SET_TYPE* instance = (SET_TYPE*) calloc(1U, sizeof(SET_TYPE)); if (!instance) { return NULL; @@ -338,17 +243,17 @@ hash_set_t *hash_set_create(const size_t initial_capacity, const double load_fac return instance; } -void hash_set_destroy(hash_set_t *instance) +void MAKE_NAME(hash_set_destroy)(SET_TYPE *instance) { if (instance) { free_data(&instance->data); - zero_memory(instance, 1U, sizeof(hash_set_t)); + zero_memory(instance, 1U, sizeof(SET_TYPE)); SAFE_FREE(instance); } } -errno_t hash_set_insert(hash_set_t *const instance, const uint64_t value) +errno_t MAKE_NAME(hash_set_insert)(SET_TYPE *const instance, const VALUE_TYPE value) { size_t index; bool_t slot_reused; @@ -387,7 +292,7 @@ errno_t hash_set_insert(hash_set_t *const instance, const uint64_t value) return 0; } -errno_t hash_set_contains(const hash_set_t *const instance, const uint64_t value) +errno_t MAKE_NAME(hash_set_contains)(const SET_TYPE *const instance, const VALUE_TYPE value) { if ((!instance) || (!instance->data.values)) { @@ -397,7 +302,7 @@ errno_t hash_set_contains(const hash_set_t *const instance, const uint64_t value return (instance->valid && find_slot(&instance->data, value, NULL, NULL)) ? 0 : ENOENT; } -errno_t hash_set_remove(hash_set_t *const instance, const uint64_t value) +errno_t MAKE_NAME(hash_set_remove)(SET_TYPE *const instance, const VALUE_TYPE value) { size_t index; @@ -417,13 +322,13 @@ errno_t hash_set_remove(hash_set_t *const instance, const uint64_t value) if (!instance->valid) { - return hash_set_clear(instance); + return MAKE_NAME(hash_set_clear)(instance); } if (instance->deleted > (instance->limit / 2U)) { const size_t min_capacity = next_pow2(round_sz(safe_incr(instance->valid) / instance->load_factor)); - const errno_t error = rebuild_set(instance, (instance->data.capacity > min_capacity) ? min_capacity : instance->data.capacity); + const errno_t error = rebuild_set(instance, (instance->data.capacity > min_capacity) ? min_capacity : instance->data.capacity); if (error && (error != ENOMEM)) { return error; @@ -433,7 +338,7 @@ errno_t hash_set_remove(hash_set_t *const instance, const uint64_t value) return 0; } -errno_t hash_set_clear(hash_set_t *const instance) +errno_t MAKE_NAME(hash_set_clear)(SET_TYPE *const instance) { if ((!instance) || (!instance->data.values)) { @@ -444,7 +349,7 @@ errno_t hash_set_clear(hash_set_t *const instance) { const size_t count = div_ceil(instance->data.capacity, 8U); instance->valid = instance->deleted = 0U; - zero_memory(instance->data.used, count, sizeof(uint8_t)); + zero_memory(instance->data.used, count, sizeof(uint8_t)); zero_memory(instance->data.deleted, count, sizeof(uint8_t)); } else @@ -464,7 +369,7 @@ errno_t hash_set_clear(hash_set_t *const instance) return 0; } -errno_t hash_set_iterate(const hash_set_t *const instance, uintptr_t *const cursor, uint64_t *const value) +errno_t MAKE_NAME(hash_set_iterate)(const SET_TYPE *const instance, uintptr_t *const cursor, VALUE_TYPE *const value) { size_t index; @@ -490,12 +395,12 @@ errno_t hash_set_iterate(const hash_set_t *const instance, uintptr_t *const curs return ENOENT; } -size_t hash_set_size(const hash_set_t *const instance) +size_t MAKE_NAME(hash_set_size)(const SET_TYPE *const instance) { return instance ? instance->valid : 0U; } -errno_t hash_set_info(const hash_set_t *const instance, size_t *const capacity, size_t *const valid, size_t *const deleted, size_t *const limit) +errno_t MAKE_NAME(hash_set_info)(const SET_TYPE *const instance, size_t *const capacity, size_t *const valid, size_t *const deleted, size_t *const limit) { if ((!instance) || (!instance->data.values)) { diff --git a/libhashset/src/hash_set_32.c b/libhashset/src/hash_set_32.c new file mode 100644 index 0000000..a016d47 --- /dev/null +++ b/libhashset/src/hash_set_32.c @@ -0,0 +1,13 @@ +/******************************************************************************/ +/* HashSet for C99, by LoRd_MuldeR */ +/* This work has been released under the CC0 1.0 Universal license! */ +/******************************************************************************/ + +#include +#include "common.h" + +#define NAME_SUFFIX 32 +#define SET_TYPE hash_set32_t +#define VALUE_TYPE uint32_t + +#include "generic.h" diff --git a/libhashset/src/hash_set_64.c b/libhashset/src/hash_set_64.c new file mode 100644 index 0000000..f9b0e07 --- /dev/null +++ b/libhashset/src/hash_set_64.c @@ -0,0 +1,13 @@ +/******************************************************************************/ +/* HashSet for C99, by LoRd_MuldeR */ +/* This work has been released under the CC0 1.0 Universal license! */ +/******************************************************************************/ + +#include +#include "common.h" + +#define NAME_SUFFIX 64 +#define SET_TYPE hash_set64_t +#define VALUE_TYPE uint64_t + +#include "generic.h" diff --git a/libhashset/src/version.c b/libhashset/src/version.c new file mode 100644 index 0000000..17573c4 --- /dev/null +++ b/libhashset/src/version.c @@ -0,0 +1,13 @@ +/******************************************************************************/ +/* HashSet for C99, by LoRd_MuldeR */ +/* This work has been released under the CC0 1.0 Universal license! */ +/******************************************************************************/ + +#include + +const uint16_t HASHSET_VERSION_MAJOR = UINT16_C(1); +const uint16_t HASHSET_VERSION_MINOR = UINT16_C(1); +const uint16_t HASHSET_VERSION_PATCH = UINT16_C(0); + +const char* const HASHSET_BUILD_DATE = __DATE__; +const char* const HASHSET_BUILD_TIME = __TIME__;