Skip to content

Commit f806e95

Browse files
committed
Adding some hashtable examples
1 parent 3ba12c3 commit f806e95

12 files changed

+502
-0
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ enable_testing()
1212

1313
add_subdirectory(src)
1414
add_subdirectory(test)
15+
add_subdirectory(examples)

examples/CMakeLists.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
cmake_minimum_required(VERSION 3.5)
2+
3+
set(CMAKE_DISABLE_IN_SOURCE_BUILD ON)
4+
5+
set(CFLAGS "-Wall -Werror")
6+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CFLAGS}")
7+
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CFLAGS}")
8+
9+
add_subdirectory(hashtable)

examples/README.md

Whitespace-only changes.

examples/hashtable/CMakeLists.txt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
cmake_minimum_required(VERSION 3.5)
2+
project(collectc_hashtable_examples)
3+
4+
include_directories(${PROJECT_SOURCE_DIR}/include ${collectc_INCLUDE_DIRS})
5+
6+
add_executable(string_keys initialization/string_keys.c)
7+
target_link_libraries(string_keys collectc)
8+
9+
add_executable(custom_key_types initialization/custom_key_types.c)
10+
target_link_libraries(custom_key_types collectc)
11+
12+
add_executable(pointer_keys initialization/pointer_keys.c)
13+
target_link_libraries(pointer_keys collectc)
14+
15+
add_executable(configuration initialization/configuration.c)
16+
target_link_libraries(configuration collectc)
17+
18+
add_executable(iterating_over_pairs operations/iterating_over_pairs.c)
19+
target_link_libraries(iterating_over_pairs collectc)
20+
21+
add_executable(adding_key_value_pairs operations/adding_key_value_pairs.c)
22+
target_link_libraries(adding_key_value_pairs collectc)
23+
24+
add_executable(removing_key_value_pairs operations/removing_key_value_pairs.c)
25+
target_link_libraries(removing_key_value_pairs collectc)
26+
27+
add_executable(getting_info_about_the_table_and_pairs operations/getting_info_about_the_table_and_pairs.c)
28+
target_link_libraries(getting_info_about_the_table_and_pairs collectc)
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/* Example of performance tuning a table by setting it's capacity
2+
and load factor. */
3+
4+
5+
#include <hashtable.h>
6+
7+
8+
int main(int argc, char **argv)
9+
{
10+
HashTableConf config;
11+
hashtable_conf_init(&config);
12+
13+
/* As explained in the string_key example if we don't specifically set
14+
the key options they'll default to string.
15+
16+
************************************************************
17+
18+
Set the initial capacity of the internal array. Note that
19+
the value here is a power of 2. Non 2^n values can be passed,
20+
but they'll be rounded to the nearest upper 2^n value for performance
21+
reasons (12 would round to 16, etc.).
22+
23+
Note that a hashtable can hold an unlimited (limited only by
24+
memory) number key/value pairs. This value is better viewed
25+
not as an absolute capacity, but as a performant capacity.
26+
Anything beyond this number is going to see a sharp decrease in
27+
performance. Of course, by default, the table resizes before
28+
this point is reached. However, this default behavior can be
29+
changed by changing the load_factor. */
30+
config.initial_capacity = 32;
31+
32+
/*
33+
This value controls the rate of expansion of the internal array.
34+
It is usually set between 0 and 1. Although values above 1 are valid,
35+
there's little value in setting them.
36+
37+
The load factor represents the percentage of fullness of the internal
38+
array (capacity set above) at which the internal array resize is triggered.
39+
In this case, since we've set the initial capacity to 32 and the load
40+
factor to 0.5, the resize will happen as soon as we add the 16th pair.
41+
The rule here is capacity * load_factor, which translates to 32 * 0.5
42+
in our case.
43+
44+
So why set this value?
45+
46+
By setting this value (and the capacity) we can control the performance
47+
of the table. If space is abundant and we don't care about it that much,
48+
we can set the load factor to a smaller value to keep the table sparsely
49+
populated and thus bring the performance close to O(1) by avoiding
50+
hash collisions (collision happen fairly often because of hash truncation).
51+
On the other hand, if the space is a problem we can set the load factor
52+
to a higher value and keep the table tightly packed at the expense of some
53+
speed.
54+
55+
In short:
56+
capacity up + load_factor down = fast table but space wasteful
57+
capacity down + load_factor up = slow table but space conserving
58+
*/
59+
config.load_factor = 0.5;
60+
61+
62+
HashTable *table;
63+
hashtable_new_conf(&config, &table);
64+
hashtable_destroy(table);
65+
return 0;
66+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/* Example of a table using the data behind the pointer as keys.
2+
3+
(check the pointer key example for direct pointer keys) */
4+
5+
#include <hashtable.h>
6+
#include <stdio.h>
7+
8+
9+
/* Structure that we will use as a key. */
10+
struct Point {
11+
int x;
12+
int y;
13+
};
14+
15+
16+
/* We need to create a custom comparator function for our
17+
Point structure. If keys are equal the function should
18+
return 0. */
19+
int point_compare(const void *key1, const void *key2)
20+
{
21+
struct Point p1 = *((struct Point*) key1);
22+
struct Point p2 = *((struct Point*) key2);
23+
return !(p1.x == p2.x && p1.y == p2.y);
24+
}
25+
26+
27+
int main(int argc, char **argv)
28+
{
29+
/* Define the config structure (for more details check the
30+
configuration example) */
31+
HashTableConf config;
32+
33+
/* While it's not necessary, it's always a good idea to initialize the
34+
config structure to default values and then override whichever
35+
value we need. */
36+
hashtable_conf_init(&config);
37+
38+
/* First we need to set the key length to match the length of
39+
our Point structure. */
40+
config.key_length = sizeof(struct Point);
41+
42+
/* Next, we set the hash function. The library provides a general
43+
hash function we can use. */
44+
config.hash = GENERAL_HASH;
45+
46+
/* Finally we need to set the key comparator function. */
47+
config.key_compare = point_compare;
48+
49+
/* We can define a new table */
50+
HashTable *table;
51+
52+
/* Create a new hashtable that Point structures as keys and assign to
53+
*table*. The return value indicates the success or failure of the
54+
operation. */
55+
enum cc_stat status = hashtable_new_conf(&config, &table);
56+
57+
/* It's always a good idea to check whether the allocation of a new
58+
structure was successful or not. */
59+
if (status != CC_OK) {
60+
/* Do some error handling */
61+
if (status == CC_ERR_ALLOC) {
62+
/* This is the only kind of error hashtable_new can return.
63+
It means that the allocation has failed. */
64+
}
65+
}
66+
67+
/* ************************************************************
68+
Adding keys
69+
************************************************************/
70+
71+
struct Point point;
72+
point.x = 8;
73+
point.y = 10;
74+
75+
/* Add a new key value pair. */
76+
hashtable_add(table, (void*) &point, "foo");
77+
78+
/* Destroy the table structure */
79+
hashtable_destroy(table);
80+
return 0;
81+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/* An example of directly using pointers as keys. */
2+
3+
4+
#include <hashtable.h>
5+
#include <stdlib.h>
6+
#include <stdio.h>
7+
8+
/* In order to use pointers as keys directly as opposed to
9+
the data behind the pointer, we need to define a function
10+
that check for pointer equality. */
11+
int pointer_equality(const void *k1, const void *k2)
12+
{
13+
return !(k1 == k2);
14+
}
15+
16+
17+
int main(int argc, char **argv)
18+
{
19+
/* ************************************************************
20+
Config strcture
21+
************************************************************/
22+
23+
24+
/* Define the config structure (for more details check the
25+
configuration example) */
26+
HashTableConf config;
27+
28+
/* While it's not necessary, it's always a good idea to initialize the
29+
config structure to default values and then override whichever
30+
value we need. */
31+
hashtable_conf_init(&config);
32+
33+
/* The length of our key should be equal to the length of a pointer. */
34+
config.key_length = sizeof(void*);
35+
36+
/* Next, we set the hash function. The library provides a hash function
37+
that hashes the pointer itself rather than the data behind it. */
38+
config.hash = POINTER_HASH;
39+
40+
/* Finally, we set our comparator function. */
41+
config.key_compare = pointer_equality;
42+
43+
/* ************************************************************
44+
Creating a new table.
45+
************************************************************/
46+
47+
/* Define a new table pointer */
48+
HashTable *table;
49+
50+
/* Create a new hashtable that accepts pointers as keys and assign to
51+
*table*. The return value indicates the success or failure of the
52+
operation. */
53+
enum cc_stat status = hashtable_new_conf(&config, &table);
54+
55+
/* It's always a good idea to check whether the allocation of a new
56+
structure was successful or not.*/
57+
if (status != CC_OK) {
58+
/* Do some error handling */
59+
if (status == CC_ERR_ALLOC) {
60+
/* This is the only kind of error hashtable_new can return.
61+
It means that the allocation has failed. */
62+
}
63+
}
64+
65+
/* ************************************************************
66+
Adding keys
67+
************************************************************/
68+
69+
/* We can now add pointer keys to our table. */
70+
void *pointer = malloc(16);
71+
hashtable_add(table, (void*) pointer, "bar");
72+
73+
/* We can even hackishly use it for direct integer keys. */
74+
uintptr_t number = 23123;
75+
hashtable_add(table, (void*) number, "foo");
76+
77+
/* After we're done using the table, we can destroy it with. This
78+
only destroys the structure, not the actual data inside of it. */
79+
hashtable_destroy(table);
80+
return 0;
81+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/* An example of creating a hash table that works with string keys. */
2+
3+
#include <hashtable.h>
4+
5+
6+
int main(int argc, char **argv)
7+
{
8+
/* Define a new table pointer */
9+
HashTable *string_table;
10+
11+
/* Create a new hashtable that accepts strings as keys and assign to
12+
*string_pointer*. The return value indicates success or failure
13+
of the operation.
14+
15+
A thing to note here is that when hashtable_new is used and no
16+
additional configuration is passed, the default behavior is to
17+
create a string key table.
18+
19+
This can be changed by using a different constructor that accepts
20+
a configuration structure as a parameter. (more details below) */
21+
enum cc_stat status = hashtable_new(&string_table);
22+
23+
/* It's always a good idea to check whether the allocation of a new
24+
structure was successful or not. */
25+
if (status != CC_OK) {
26+
/* Do some error handling */
27+
28+
if (status == CC_ERR_ALLOC) {
29+
/* This is the only kind of error hashtable_new can return.
30+
It means that the allocation has failed. */
31+
}
32+
}
33+
34+
/* After we're done using the table, we can destroy it with. This
35+
only destroys the structure, not the actual data inside of it. */
36+
hashtable_destroy(string_table);
37+
38+
/************************************************************
39+
Alternatively a string key table can be constructed using the
40+
config structure
41+
************************************************************/
42+
43+
/* Define the config structure (for more details check the
44+
configuration example) */
45+
HashTableConf config;
46+
47+
/* While it's not necessary, it's always a good idea to initialize the
48+
config structure to default values and then override whichever
49+
value we need. */
50+
hashtable_conf_init(&config);
51+
52+
/* The hash function needs to know the length of the key and since we
53+
are using strings, the key length will be variable. */
54+
config.key_length = KEY_LENGTH_VARIABLE;
55+
56+
/* Next, we set the hash function. The library already provides a
57+
string hashing function. */
58+
config.hash = STRING_HASH;
59+
60+
/* Finally, we need to set the key comparator function. The library
61+
provides a default comparator. */
62+
config.key_compare = CC_CMP_STRING;
63+
64+
/* Now we can create a new table described by the config structure */
65+
status = hashtable_new_conf(&config, &string_table);
66+
67+
68+
hashtable_destroy(string_table);
69+
return 0;
70+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#include <hashtable.h>
2+
3+
4+
int main(int argc, char **argv)
5+
{
6+
/* Make a new string key table */
7+
HashTable *table;
8+
hashtable_new(&table);
9+
10+
/* Assign value "one" to key "1" inside the table "table" */
11+
enum cc_stat status = hashtable_add(table, (void*) "1", (void*) "one");
12+
13+
/* Checking the status of the operation is alwasy a good idea since
14+
memory allocation can fail (lack of space, etc.). */
15+
if (status == CC_ERR_ALLOC) {
16+
/* Internal allocation error. This is the only type of error
17+
hashtable_add can return. If everything went well CC_OK is
18+
returned instead. */
19+
}
20+
21+
hashtable_destroy(table);
22+
}

0 commit comments

Comments
 (0)