Implemented separate functions for uint32_t and uint64_t hash sets.
This commit is contained in:
parent
e8603e7fd6
commit
00764536ca
8
Makefile
8
Makefile
@ -1,7 +1,7 @@
|
|||||||
SUBDIRS := libhashset hashset
|
SUBDIRS := libhashset hashset
|
||||||
|
|
||||||
BUILD_ALL := $(patsubst %,build_rule\:%,$(SUBDIRS))
|
BUILD_ALL := $(patsubst %,build\:%,$(SUBDIRS))
|
||||||
CLEAN_ALL := $(patsubst %,clean_rule\:%,$(SUBDIRS))
|
CLEAN_ALL := $(patsubst %,clean\:%,$(SUBDIRS))
|
||||||
|
|
||||||
.PHONY: all clean $(BUILD_ALL) $(CLEAN_ALL)
|
.PHONY: all clean $(BUILD_ALL) $(CLEAN_ALL)
|
||||||
|
|
||||||
@ -10,7 +10,7 @@ all: $(BUILD_ALL)
|
|||||||
clean: $(CLEAN_ALL)
|
clean: $(CLEAN_ALL)
|
||||||
|
|
||||||
$(BUILD_ALL):
|
$(BUILD_ALL):
|
||||||
$(MAKE) -C $(patsubst build_rule:%,%,$@)
|
$(MAKE) -C $(patsubst build:%,%,$@)
|
||||||
|
|
||||||
$(CLEAN_ALL):
|
$(CLEAN_ALL):
|
||||||
$(MAKE) -C $(patsubst clean_rule:%,%,$@) clean
|
$(MAKE) -C $(patsubst clean:%,%,$@) clean
|
||||||
|
35
README.md
35
README.md
@ -3,7 +3,7 @@ Introduction
|
|||||||
|
|
||||||
**LibHashSet** is a simple *hash set* implementation for C99. It uses open addressing and double hashing.
|
**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 😏
|
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;
|
uintptr_t cursor = 0U;
|
||||||
|
|
||||||
/* create new hash set instance */
|
/* 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)
|
if (!hash_set)
|
||||||
{
|
{
|
||||||
fputs("Allocation has failed!\n", stderr);
|
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 */
|
/* add a number of items to the hash set, the set will grow as needed */
|
||||||
while (have_more_items())
|
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)
|
if (error)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Insert operation has failed! (error: %d)\n", 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 */
|
/* 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!");
|
puts("Set contains item!");
|
||||||
|
|
||||||
/* remove the existing item from the hash set */
|
/* 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!");
|
puts("Item has been removed!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* print total number of items in the hash set*/
|
/* 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 */
|
/* 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);
|
printf("Item: %016llX\n", value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* destroy the hash set, when it is no longer needed! */
|
/* destroy the hash set, when it is no longer needed! */
|
||||||
hash_set_destroy(hash_set);
|
hash_set_destroy64(hash_set);
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -74,6 +74,8 @@ API Reference
|
|||||||
|
|
||||||
This section describes the LibHashSet programming interface, as declared in the `<hash_set.h>` header file.
|
This section describes the LibHashSet programming interface, as declared in the `<hash_set.h>` 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 `<hash_set.h>`! This is **not** required or allowed when using the "static" library.
|
***Note:*** On Microsoft Windows, when using LibHashSet as a "shared" library (DLL), the macro `HASHSET_DLL` must be defined *before* including `<hash_set.h>`! This is **not** required or allowed when using the "static" library.
|
||||||
|
|
||||||
Types
|
Types
|
||||||
@ -164,7 +166,7 @@ Tries to insert the given value into the hash set. The operation fails, if the s
|
|||||||
```C
|
```C
|
||||||
errno_t hash_set_insert(
|
errno_t hash_set_insert(
|
||||||
hash_set_t *const instance,
|
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.
|
A pointer to the hash set instance to be modified, as returned by the [hash_set_create()](#hash_set_create) function.
|
||||||
|
|
||||||
* `value`
|
* `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
|
#### Return value
|
||||||
|
|
||||||
@ -201,7 +203,7 @@ Tries to remove the given value from the hash set. The operation fails, if the s
|
|||||||
```C
|
```C
|
||||||
errno_t hash_set_remove(
|
errno_t hash_set_remove(
|
||||||
hash_set_t *const instance,
|
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.
|
A pointer to the hash set instance to be modified, as returned by the [hash_set_create()](#hash_set_create) function.
|
||||||
|
|
||||||
* `value`
|
* `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
|
#### Return value
|
||||||
|
|
||||||
@ -260,7 +262,7 @@ Tests whether the hash set contains a value. The operation fails, if the set doe
|
|||||||
```C
|
```C
|
||||||
errno_t hash_set_contains(
|
errno_t hash_set_contains(
|
||||||
const hash_set_t *const instance,
|
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.
|
A pointer to the hash set instance to be examined, as returned by the [hash_set_create()](#hash_set_create) function.
|
||||||
|
|
||||||
* `value`
|
* `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
|
#### 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(
|
errno_t hash_set_iterate(
|
||||||
const hash_set_t *const instance,
|
const hash_set_t *const instance,
|
||||||
uintptr_t *const cursor,
|
uintptr_t *const cursor,
|
||||||
uint64_t *const value
|
VALUE_TYPE *const value
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -311,9 +313,8 @@ errno_t hash_set_iterate(
|
|||||||
This variable **must** be initialized to the value `0U`, by the calling application, prior to the the *first* invocation!
|
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.
|
Each invocation will update the value of `*cursor`; the value **shall not** be altered by the application.
|
||||||
|
|
||||||
|
|
||||||
* `value`
|
* `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.
|
The content of the variable should be considered *undefined*, if the invocation has failed.
|
||||||
|
|
||||||
#### Return value
|
#### Return value
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
DUMPMACHINE := $(shell $(CC) -dumpmachine)
|
DUMPMACHINE := $(shell $(CC) -dumpmachine)
|
||||||
|
|
||||||
ifneq ($(SANITIZE_ADDRESS),1)
|
ifneq ($(SANITIZE_ADDRESS),1)
|
||||||
XCFLAGS = -O3 -DNDEBUG
|
XCFLAGS = -Ofast -DNDEBUG -s -static
|
||||||
ifneq ($(firstword $(filter x86_64-%,$(DUMPMACHINE))),)
|
ifneq ($(firstword $(filter x86_64-%,$(DUMPMACHINE))),)
|
||||||
XCFLAGS += -march=x86-64 -mtune=nocona
|
XCFLAGS += -march=x86-64 -mtune=nocona
|
||||||
else ifneq ($(firstword $(filter i686-%,$(DUMPMACHINE))),)
|
else ifneq ($(firstword $(filter i686-%,$(DUMPMACHINE))),)
|
||||||
@ -22,19 +22,22 @@ CFLAGS = -std=c99 -D_DEFAULT_SOURCE -Wpedantic -I../libhashset/include $(XCFLAGS
|
|||||||
|
|
||||||
SRC_PATH := src
|
SRC_PATH := src
|
||||||
BIN_PATH := bin
|
BIN_PATH := bin
|
||||||
|
ALL_PATH := $(SRC_PATH) $(BIN_PATH)
|
||||||
|
|
||||||
BIN_FILE := $(BIN_PATH)/hashset-test$(EXE_SUFFIX)
|
BIN_FILE := $(BIN_PATH)/hashset-test$(EXE_SUFFIX)
|
||||||
SRC_FILE := $(SRC_PATH)/main.c
|
SRC_FILE := $(SRC_PATH)/main.c
|
||||||
LIB_FILE := ../libhashset/lib/libhashset-1.a
|
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)
|
build: $(ALL_PATH) $(BIN_FILE)
|
||||||
$(CC) $(CFLAGS) $< -o $@ $(LIB_FILE)
|
|
||||||
|
|
||||||
$(SRC_PATH) $(BIN_PATH):
|
$(BIN_FILE): $(SRC_FILE) $(LIB_FILE)
|
||||||
|
$(CC) $(CFLAGS) $^ -o $@
|
||||||
|
|
||||||
|
$(ALL_PATH):
|
||||||
mkdir -p $@
|
mkdir -p $@
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
@ -64,7 +64,7 @@ static INLINE uint64_t random_next(random_t *const rnd)
|
|||||||
|
|
||||||
#define PRINT_SET_INFO(X) do \
|
#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); \
|
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
|
#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;
|
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))
|
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)
|
if (error)
|
||||||
{
|
{
|
||||||
printf("Insert operation has failed! (error: %d)\n", 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);
|
PRINT_SET_INFO(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hash_set_size(hash_set) != MAXIMUM - 4U)
|
if (hash_set_size64(hash_set) != MAXIMUM - 4U)
|
||||||
{
|
{
|
||||||
puts("Invalid size!");
|
puts("Invalid size!");
|
||||||
return EXIT_FAILURE;
|
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))
|
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))
|
if (error != ((i != 185903U) ? EEXIST : 0))
|
||||||
{
|
{
|
||||||
printf("Insert operation has failed! (error: %d)\n", error);
|
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!");
|
puts("Invalid size!");
|
||||||
return EXIT_FAILURE;
|
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)
|
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)
|
if (error != ((i != 3167U) && (i != 9887U) && (i != 387083U)) ? 0 : ENOENT)
|
||||||
{
|
{
|
||||||
printf("Contains operation has failed! (error: %d)\n", error);
|
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))
|
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)
|
if (error)
|
||||||
{
|
{
|
||||||
printf("Remove operation has failed! (error: %d)\n", 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);
|
PRINT_SET_INFO(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hash_set_size(hash_set) != 1U)
|
if (hash_set_size64(hash_set) != 1U)
|
||||||
{
|
{
|
||||||
puts("Invalid size!");
|
puts("Invalid size!");
|
||||||
return EXIT_FAILURE;
|
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)
|
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))
|
if (error != ((i != 216263U) ? ENOENT : 0))
|
||||||
{
|
{
|
||||||
printf("Contains operation has failed! (error: %d)\n", error);
|
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!");
|
puts("Final remove operation has failed!");
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hash_set_remove(hash_set, 216263U))
|
if (hash_set_remove64(hash_set, 216263U))
|
||||||
{
|
{
|
||||||
puts("Final remove operation has failed!");
|
puts("Final remove operation has failed!");
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hash_set_size(hash_set) != 0U)
|
if (hash_set_size64(hash_set) != 0U)
|
||||||
{
|
{
|
||||||
puts("Invalid size!");
|
puts("Invalid size!");
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
@ -193,7 +193,7 @@ static int test_function_1(hash_set_t *const hash_set)
|
|||||||
|
|
||||||
#define ARRSIZE 14867U
|
#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;
|
size_t capacity, valid, deleted, limit;
|
||||||
uint64_t value;
|
uint64_t value;
|
||||||
@ -221,7 +221,7 @@ static int test_function_2(hash_set_t *const hash_set)
|
|||||||
{
|
{
|
||||||
if (test[j])
|
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)
|
if (error)
|
||||||
{
|
{
|
||||||
printf("Insert operation has failed! (error: %d)\n", 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);
|
PRINT_SET_INFO(2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (!hash_set_iterate(hash_set, &cursor, &value))
|
while (!hash_set_iterate64(hash_set, &cursor, &value))
|
||||||
{
|
{
|
||||||
if (!test[value])
|
if (!test[value])
|
||||||
{
|
{
|
||||||
@ -242,7 +242,7 @@ static int test_function_2(hash_set_t *const hash_set)
|
|||||||
{
|
{
|
||||||
if (test[j])
|
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)
|
if (error)
|
||||||
{
|
{
|
||||||
printf("Remove operation has failed! (error: %d)\n", 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);
|
test[j] = UINT8_C(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (hash_set_size(hash_set) != 0U)
|
if (hash_set_size64(hash_set) != 0U)
|
||||||
{
|
{
|
||||||
puts("Invalid size!");
|
puts("Invalid size!");
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
@ -269,7 +269,7 @@ static int test_function_2(hash_set_t *const hash_set)
|
|||||||
/* TEST #3 */
|
/* 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;
|
size_t capacity, valid, deleted, limit;
|
||||||
uint8_t spinner = 0U;
|
uint8_t spinner = 0U;
|
||||||
@ -283,7 +283,7 @@ static int test_function_3(hash_set_t *const hash_set)
|
|||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
const uint64_t rnd = random_next(&random) & UINT64_C(0x3FFFFFFFFFFFFFF);
|
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)
|
||||||
{
|
{
|
||||||
if (error != EEXIST)
|
if (error != EEXIST)
|
||||||
@ -311,7 +311,7 @@ static int test_function_3(hash_set_t *const hash_set)
|
|||||||
|
|
||||||
PRINT_SET_INFO(3);
|
PRINT_SET_INFO(3);
|
||||||
|
|
||||||
if (hash_set_clear(hash_set))
|
if (hash_set_clear64(hash_set))
|
||||||
{
|
{
|
||||||
puts("Clear operation has failed!");
|
puts("Clear operation has failed!");
|
||||||
return EXIT_FAILURE;
|
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)
|
#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;
|
size_t capacity, valid, deleted, limit;
|
||||||
uint8_t spinner = 0U;
|
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)
|
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)
|
if (error)
|
||||||
{
|
{
|
||||||
PRINT_SET_INFO(4);
|
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)
|
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)
|
if (error)
|
||||||
{
|
{
|
||||||
PRINT_SET_INFO(4);
|
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!");
|
puts("Invalid size!");
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
@ -397,7 +397,7 @@ int main(void)
|
|||||||
printf("LibHashSet Test v%" PRIu16 ".%" PRIu16 ".%" PRIu16 " [%s]\n\n",
|
printf("LibHashSet Test v%" PRIu16 ".%" PRIu16 ".%" PRIu16 " [%s]\n\n",
|
||||||
HASHSET_VERSION_MAJOR, HASHSET_VERSION_MINOR, HASHSET_VERSION_PATCH, HASHSET_BUILD_DATE);
|
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)
|
if (!hash_set)
|
||||||
{
|
{
|
||||||
puts("Allocation has failed!");
|
puts("Allocation has failed!");
|
||||||
@ -424,12 +424,12 @@ int main(void)
|
|||||||
goto failure;
|
goto failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
hash_set_destroy(hash_set);
|
hash_set_destroy64(hash_set);
|
||||||
puts("Test completed successfully.");
|
puts("Test completed successfully.");
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
|
|
||||||
failure:
|
failure:
|
||||||
hash_set_destroy(hash_set);
|
hash_set_destroy64(hash_set);
|
||||||
puts("Something went wrong !!!");
|
puts("Something went wrong !!!");
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
DUMPMACHINE := $(shell $(CC) -dumpmachine)
|
DUMPMACHINE := $(shell $(CC) -dumpmachine)
|
||||||
|
|
||||||
ifneq ($(SANITIZE_ADDRESS),1)
|
ifneq ($(SANITIZE_ADDRESS),1)
|
||||||
XCFLAGS = -O3 -DNDEBUG
|
XCFLAGS = -Ofast -DNDEBUG
|
||||||
ifneq ($(firstword $(filter x86_64-%,$(DUMPMACHINE))),)
|
ifneq ($(firstword $(filter x86_64-%,$(DUMPMACHINE))),)
|
||||||
XCFLAGS += -march=x86-64 -mtune=nocona
|
XCFLAGS += -march=x86-64 -mtune=nocona
|
||||||
else ifneq ($(firstword $(filter i686-%,$(DUMPMACHINE))),)
|
else ifneq ($(firstword $(filter i686-%,$(DUMPMACHINE))),)
|
||||||
@ -16,22 +16,26 @@ CFLAGS = -std=c99 -D_DEFAULT_SOURCE -Wpedantic -Iinclude $(XCFLAGS)
|
|||||||
SRC_PATH := src
|
SRC_PATH := src
|
||||||
OBJ_PATH := obj
|
OBJ_PATH := obj
|
||||||
LIB_PATH := lib
|
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
|
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)
|
build: $(ALL_PATH) $(LIB_FILE)
|
||||||
$(AR) rcs $@ $<
|
|
||||||
|
|
||||||
$(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 $@
|
$(CC) $(CFLAGS) -c $(SRC_PATH)/$(patsubst %.o,%.c,$(notdir $@)) -o $@
|
||||||
|
|
||||||
$(SRC_PATH) $(OBJ_PATH) $(LIB_PATH):
|
$(ALL_PATH):
|
||||||
mkdir -p $@
|
mkdir -p $@
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(LIB_FILE) $(OBJ_PATH)/*.o
|
rm -vf $(LIB_FILE) $(OBJ_PATH)/*.o
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
/* This work has been released under the CC0 1.0 Universal license! */
|
/* This work has been released under the CC0 1.0 Universal license! */
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
#ifndef _INC_HASHSET_H
|
#ifndef _LIBHASHSET_INCLUDED
|
||||||
#define _INC_HASHSET_H
|
#define _LIBHASHSET_INCLUDED
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@ -27,9 +27,6 @@ extern "C" {
|
|||||||
typedef int errno_t;
|
typedef int errno_t;
|
||||||
#endif
|
#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_MAJOR;
|
||||||
HASHSET_API const uint16_t HASHSET_VERSION_MINOR;
|
HASHSET_API const uint16_t HASHSET_VERSION_MINOR;
|
||||||
HASHSET_API const uint16_t HASHSET_VERSION_PATCH;
|
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_DATE;
|
||||||
HASHSET_API const char *const HASHSET_BUILD_TIME;
|
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);
|
struct _hash_set32;
|
||||||
HASHSET_API void hash_set_destroy(hash_set_t *const instance);
|
struct _hash_set64;
|
||||||
|
|
||||||
HASHSET_API errno_t hash_set_insert(hash_set_t *const instance, const uint64_t value);
|
typedef struct _hash_set32 hash_set32_t;
|
||||||
HASHSET_API errno_t hash_set_remove(hash_set_t *const instance, const uint64_t value);
|
typedef struct _hash_set64 hash_set64_t;
|
||||||
HASHSET_API errno_t hash_set_clear(hash_set_t *const instance);
|
|
||||||
|
|
||||||
HASHSET_API errno_t hash_set_contains(const hash_set_t *const instance, const uint64_t value);
|
HASHSET_API hash_set32_t *hash_set_create32(const size_t initial_capacity, const double load_factor);
|
||||||
HASHSET_API errno_t hash_set_iterate(const hash_set_t *const instance, uintptr_t *const cursor, uint64_t *const value);
|
HASHSET_API hash_set64_t* hash_set_create64(const size_t initial_capacity, const double load_factor);
|
||||||
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 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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -40,9 +40,13 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="include\hash_set.h" />
|
<ClInclude Include="include\hash_set.h" />
|
||||||
|
<ClInclude Include="src\common.h" />
|
||||||
|
<ClInclude Include="src\generic.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="src\hash_set.c" />
|
<ClCompile Include="src\hash_set_32.c" />
|
||||||
|
<ClCompile Include="src\hash_set_64.c" />
|
||||||
|
<ClCompile Include="src\version.c" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Globals">
|
<PropertyGroup Label="Globals">
|
||||||
<VCProjectVersion>16.0</VCProjectVersion>
|
<VCProjectVersion>16.0</VCProjectVersion>
|
||||||
|
@ -18,9 +18,21 @@
|
|||||||
<ClInclude Include="include\hash_set.h">
|
<ClInclude Include="include\hash_set.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="src\common.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="src\generic.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="src\hash_set.c">
|
<ClCompile Include="src\hash_set_64.c">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="src\version.c">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="src\hash_set_32.c">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
122
libhashset/src/common.h
Normal file
122
libhashset/src/common.h
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
/******************************************************************************/
|
||||||
|
/* HashSet for C99, by LoRd_MuldeR <MuldeR2@GMX.de> */
|
||||||
|
/* This work has been released under the CC0 1.0 Universal license! */
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
#ifndef _LIBHASHSET_COMMON_INCLUDED
|
||||||
|
#define _LIBHASHSET_COMMON_INCLUDED
|
||||||
|
|
||||||
|
/* CRT */
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <float.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
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
|
@ -3,120 +3,44 @@
|
|||||||
/* This work has been released under the CC0 1.0 Universal license! */
|
/* This work has been released under the CC0 1.0 Universal license! */
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
#include "hash_set.h"
|
#ifndef _LIBHASHSET_COMMON_INCLUDED
|
||||||
|
#error File "common.h" must be included first!
|
||||||
/* CRT */
|
|
||||||
#include <string.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <float.h>
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
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
|
#endif
|
||||||
|
|
||||||
const uint16_t HASHSET_VERSION_MAJOR = UINT16_C(1);
|
#ifndef NAME_SUFFIX
|
||||||
const uint16_t HASHSET_VERSION_MINOR = UINT16_C(0);
|
#error NAME_SUFFIX must be defined!
|
||||||
const uint16_t HASHSET_VERSION_PATCH = UINT16_C(0);
|
#endif
|
||||||
|
|
||||||
const char *const HASHSET_BUILD_DATE = __DATE__;
|
#ifndef SET_TYPE
|
||||||
const char *const HASHSET_BUILD_TIME = __TIME__;
|
#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;
|
uint8_t *used, *deleted;
|
||||||
size_t capacity;
|
size_t capacity;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _hash_set
|
struct MAKE_NAME(_hash_set)
|
||||||
{
|
{
|
||||||
double load_factor;
|
double load_factor;
|
||||||
size_t valid, deleted, limit;
|
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 */
|
/* Hash function */
|
||||||
/* ------------------------------------------------- */
|
/* ------------------------------------------------- */
|
||||||
@ -131,7 +55,7 @@ static FORCE_INLINE void hash_update(uint64_t *const hash, uint64_t value)
|
|||||||
while (value >>= CHAR_BIT);
|
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);
|
uint64_t hash = UINT64_C(14695981039346656037);
|
||||||
hash_update(&hash, i);
|
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));
|
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)
|
if (!data->values)
|
||||||
{
|
{
|
||||||
return FALSE;
|
return FALSE;
|
||||||
@ -177,7 +101,7 @@ static INLINE bool_t alloc_data(struct _hash_set_data *const data, const size_t
|
|||||||
return TRUE;
|
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)
|
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 */
|
/* Set functions */
|
||||||
/* ------------------------------------------------- */
|
/* ------------------------------------------------- */
|
||||||
|
|
||||||
#define INDEX(X) ((size_t)((X) % data->capacity))
|
#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;
|
uint64_t loop = 0U;
|
||||||
bool_t is_saved = FALSE;
|
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;
|
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;
|
data->values[index] = value;
|
||||||
if (reusing)
|
if (reusing)
|
||||||
@ -277,9 +182,9 @@ static size_t INLINE compute_limit(const size_t capacity, const double load_fact
|
|||||||
return limit;
|
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;
|
size_t index, k;
|
||||||
|
|
||||||
if (new_capacity < instance->valid)
|
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))
|
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))
|
if (find_slot(&temp, value, &index, NULL))
|
||||||
{
|
{
|
||||||
free_data(&temp);
|
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 */
|
/* 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)
|
if (!instance)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -338,17 +243,17 @@ hash_set_t *hash_set_create(const size_t initial_capacity, const double load_fac
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
void hash_set_destroy(hash_set_t *instance)
|
void MAKE_NAME(hash_set_destroy)(SET_TYPE *instance)
|
||||||
{
|
{
|
||||||
if (instance)
|
if (instance)
|
||||||
{
|
{
|
||||||
free_data(&instance->data);
|
free_data(&instance->data);
|
||||||
zero_memory(instance, 1U, sizeof(hash_set_t));
|
zero_memory(instance, 1U, sizeof(SET_TYPE));
|
||||||
SAFE_FREE(instance);
|
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;
|
size_t index;
|
||||||
bool_t slot_reused;
|
bool_t slot_reused;
|
||||||
@ -387,7 +292,7 @@ errno_t hash_set_insert(hash_set_t *const instance, const uint64_t value)
|
|||||||
return 0;
|
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))
|
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;
|
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;
|
size_t index;
|
||||||
|
|
||||||
@ -417,13 +322,13 @@ errno_t hash_set_remove(hash_set_t *const instance, const uint64_t value)
|
|||||||
|
|
||||||
if (!instance->valid)
|
if (!instance->valid)
|
||||||
{
|
{
|
||||||
return hash_set_clear(instance);
|
return MAKE_NAME(hash_set_clear)(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (instance->deleted > (instance->limit / 2U))
|
if (instance->deleted > (instance->limit / 2U))
|
||||||
{
|
{
|
||||||
const size_t min_capacity = next_pow2(round_sz(safe_incr(instance->valid) / instance->load_factor));
|
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))
|
if (error && (error != ENOMEM))
|
||||||
{
|
{
|
||||||
return error;
|
return error;
|
||||||
@ -433,7 +338,7 @@ errno_t hash_set_remove(hash_set_t *const instance, const uint64_t value)
|
|||||||
return 0;
|
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))
|
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);
|
const size_t count = div_ceil(instance->data.capacity, 8U);
|
||||||
instance->valid = instance->deleted = 0U;
|
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));
|
zero_memory(instance->data.deleted, count, sizeof(uint8_t));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -464,7 +369,7 @@ errno_t hash_set_clear(hash_set_t *const instance)
|
|||||||
return 0;
|
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;
|
size_t index;
|
||||||
|
|
||||||
@ -490,12 +395,12 @@ errno_t hash_set_iterate(const hash_set_t *const instance, uintptr_t *const curs
|
|||||||
return ENOENT;
|
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;
|
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))
|
if ((!instance) || (!instance->data.values))
|
||||||
{
|
{
|
13
libhashset/src/hash_set_32.c
Normal file
13
libhashset/src/hash_set_32.c
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/******************************************************************************/
|
||||||
|
/* HashSet for C99, by LoRd_MuldeR <MuldeR2@GMX.de> */
|
||||||
|
/* This work has been released under the CC0 1.0 Universal license! */
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
#include <hash_set.h>
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#define NAME_SUFFIX 32
|
||||||
|
#define SET_TYPE hash_set32_t
|
||||||
|
#define VALUE_TYPE uint32_t
|
||||||
|
|
||||||
|
#include "generic.h"
|
13
libhashset/src/hash_set_64.c
Normal file
13
libhashset/src/hash_set_64.c
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/******************************************************************************/
|
||||||
|
/* HashSet for C99, by LoRd_MuldeR <MuldeR2@GMX.de> */
|
||||||
|
/* This work has been released under the CC0 1.0 Universal license! */
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
#include <hash_set.h>
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#define NAME_SUFFIX 64
|
||||||
|
#define SET_TYPE hash_set64_t
|
||||||
|
#define VALUE_TYPE uint64_t
|
||||||
|
|
||||||
|
#include "generic.h"
|
13
libhashset/src/version.c
Normal file
13
libhashset/src/version.c
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/******************************************************************************/
|
||||||
|
/* HashSet for C99, by LoRd_MuldeR <MuldeR2@GMX.de> */
|
||||||
|
/* This work has been released under the CC0 1.0 Universal license! */
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
#include <hash_set.h>
|
||||||
|
|
||||||
|
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__;
|
Loading…
Reference in New Issue
Block a user