Skip to content

Commit a7ba083

Browse files
committed
test: switch to catch2 unit tests and add some functions
Added: - hashmap_empty() - hashmap_contains() - hashmap_iter_find()
1 parent 3591e7c commit a7ba083

10 files changed

Lines changed: 590 additions & 656 deletions

File tree

.github/workflows/ci.yml

Lines changed: 30 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@ name: CI
22

33
on:
44
push:
5-
branches: [ master ]
5+
branches: [master]
66
pull_request:
7-
branches: [ master ]
7+
branches: [master]
8+
9+
env:
10+
BUILD_DIR: ${{ github.workspace }}/build
811

912
jobs:
1013
build:
@@ -14,35 +17,29 @@ jobs:
1417
matrix:
1518
os: [ubuntu-latest]
1619
compiler: [gcc, clang]
17-
build_type: [debug, release]
20+
build_type: [Debug, Release]
21+
1822
steps:
19-
- uses: actions/checkout@v4
20-
- name: install-clang
21-
if: matrix.compiler == 'clang'
22-
run: |
23-
sudo apt-get install -y clang
24-
sudo update-alternatives --remove-all cc
25-
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang 14
26-
- name: configure-debug
27-
if: matrix.build_type == 'debug'
28-
run: |
29-
mkdir build
30-
cd build
31-
cmake -DCMAKE_BUILD_TYPE=Debug -DHASHMAP_BUILD_TESTS=ON -DHASHMAP_BUILD_EXAMPLES=ON ${GITHUB_WORKSPACE}
32-
- name: configure-release
33-
if: matrix.build_type == 'release'
34-
run: |
35-
mkdir build
36-
cd build
37-
cmake -DCMAKE_BUILD_TYPE=Release -DHASHMAP_BUILD_TESTS=ON -DHASHMAP_BUILD_EXAMPLES=ON ${GITHUB_WORKSPACE}
38-
- name: make
39-
run: |
40-
make VERBOSE=1 -C build
41-
- name: install
42-
run: |
43-
cd build
44-
sudo make install
45-
- name: test
46-
run: |
47-
cd build
48-
ctest --output-on-failure
23+
- uses: actions/checkout@v4
24+
25+
- name: Create build directory
26+
run: mkdir -p ${{ env.BUILD_DIR }}
27+
28+
- name: Install Clang
29+
if: matrix.compiler == 'clang'
30+
run: |
31+
sudo apt-get install -y clang
32+
sudo update-alternatives --remove-all cc
33+
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang 15
34+
35+
- name: Configure CMake
36+
run: cmake -B ${{ env.BUILD_DIR }} -S ${{ github.workspace }} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DHASHMAP_BUILD_TESTS=ON -DHASHMAP_BUILD_EXAMPLES=ON
37+
38+
- name: Build
39+
run: cmake --build ${{ env.BUILD_DIR }} --config ${{ matrix.build_type }}
40+
41+
- name: Install
42+
run: sudo cmake --install ${{ env.BUILD_DIR }} --config ${{ matrix.build_type }}
43+
44+
- name: Test
45+
run: ctest --output-on-failure --test-dir ${{ env.BUILD_DIR }}

CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
cmake_minimum_required(VERSION 3.5)
22
project(hashmap VERSION 2.0.0 LANGUAGES C)
33

4-
set(CMAKE_C_STANDARD 99)
4+
set(CMAKE_C_STANDARD 11)
55

66
##############################################
77
# Build options
@@ -108,7 +108,7 @@ export(PACKAGE HashMap)
108108

109109
if(HASHMAP_BUILD_TESTS)
110110
enable_testing()
111-
add_subdirectory(test)
111+
add_subdirectory(tests)
112112
endif()
113113

114114
##############################################

examples/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@ cmake_minimum_required(VERSION 3.5)
22

33
# Hashmap example
44
add_executable(hashmap_example hashmap_example.c)
5-
target_compile_options(hashmap_example PRIVATE $<$<C_COMPILER_ID:GNU>:-Wall -Werror>)
5+
target_compile_options(hashmap_example PRIVATE -Wall -Werror)
66
target_link_libraries(hashmap_example PRIVATE HashMap::HashMap)
77

include/hashmap.h

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,14 +153,23 @@ extern "C" {
153153
} while (0)
154154

155155
/*
156-
* Return the number of entries in the hash map.
156+
* Return the number of entries in the hashmap.
157157
*
158158
* Parameters:
159159
* const HASHMAP(<key_type>, <data_type>) *h - hashmap pointer
160160
*/
161161
#define hashmap_size(h) \
162162
((typeof((h)->map_base.size))(h)->map_base.size)
163163

164+
/*
165+
* Return true if the hashmap is empty.
166+
*
167+
* Parameters:
168+
* const HASHMAP(<key_type>, <data_type>) *h - hashmap pointer
169+
*/
170+
#define hashmap_empty(h) \
171+
(hashmap_size(h) == 0)
172+
164173
/*
165174
* Set the hashmap's initial allocation size such that no rehashes are
166175
* required to fit the specified number of entries.
@@ -174,6 +183,17 @@ extern "C" {
174183
#define hashmap_reserve(h, capacity) \
175184
hashmap_base_reserve(&(h)->map_base, capacity)
176185

186+
/*
187+
* Get the hashmap's present allocation size.
188+
*
189+
* Parameters:
190+
* HASHMAP(<key_type>, <data_type>) *h - hashmap pointer
191+
*
192+
* Returns 0 on success, or -errno on failure.
193+
*/
194+
#define hashmap_capacity(h) \
195+
((typeof((h)->map_base.table_size))(h)->map_base.table_size)
196+
177197
/*
178198
* Add a new entry to the hashmap. If an entry with a matching key
179199
* already exists -EEXIST is returned.
@@ -205,6 +225,15 @@ extern "C" {
205225
(typeof((h)->map_types->t_data))hashmap_base_get(&(h)->map_base, (const void *)__map_key); \
206226
})
207227

228+
/*
229+
* Return true if the hashmap contains an entry with the specified key.
230+
*
231+
* Parameters:
232+
* const HASHMAP(<key_type>, <data_type>) *h - hashmap pointer
233+
*/
234+
#define hashmap_contains(h, key) \
235+
(hashmap_get(h, key) != NULL)
236+
208237
/*
209238
* Remove an entry with the specified key from the map.
210239
*
@@ -271,6 +300,20 @@ extern "C" {
271300
#define hashmap_iter_next(iter) \
272301
hashmap_base_iter_next((iter)->iter_map, &(iter)->iter_pos)
273302

303+
/*
304+
* Do a constant-time lookup of a hashmap entry and return an iterator to it.
305+
* This provides an efficient way to access and remove an entry without
306+
* performing two lookups.
307+
*
308+
* Parameters:
309+
* HASHMAP(<key_type>, <data_type>) *h - hashmap pointer
310+
* <key_type> *key - pointer to the key to lookup
311+
*
312+
* Returns a valid iterator if the key exists, otherwise an invalid iterator.
313+
*/
314+
#define hashmap_iter_find(h, key) \
315+
((HASHMAP_ITER(*(h))){ &(h)->map_base, hashmap_base_iter_find(&(h)->map_base, key) })
316+
274317
/*
275318
* Remove the hashmap entry pointed to by this iterator and advance the
276319
* iterator to the next entry.

include/hashmap_base.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ void hashmap_base_init(struct hashmap_base *hb,
2727
void hashmap_base_cleanup(struct hashmap_base *hb);
2828

2929
void hashmap_base_set_key_alloc_funcs(struct hashmap_base *hb,
30-
void *(*key_dup_func)(const void *), void (*key_free_func)(void *));
30+
void *(*key_dup_func)(const void *), void (*key_free_func)(void *));
3131

3232
int hashmap_base_reserve(struct hashmap_base *hb, size_t capacity);
3333

@@ -42,6 +42,7 @@ struct hashmap_entry *hashmap_base_iter(const struct hashmap_base *hb,
4242
const struct hashmap_entry *pos);
4343
bool hashmap_base_iter_valid(const struct hashmap_base *hb, const struct hashmap_entry *iter);
4444
bool hashmap_base_iter_next(const struct hashmap_base *hb, struct hashmap_entry **iter);
45+
struct hashmap_entry *hashmap_base_iter_find(const struct hashmap_base *hb, const void *key);
4546
bool hashmap_base_iter_remove(struct hashmap_base *hb, struct hashmap_entry **iter);
4647
const void *hashmap_base_iter_get_key(const struct hashmap_entry *iter);
4748
void *hashmap_base_iter_get_data(const struct hashmap_entry *iter);
@@ -55,4 +56,3 @@ double hashmap_base_collisions_variance(const struct hashmap_base *hb);
5556
size_t hashmap_hash_default(const void *data, size_t len);
5657
size_t hashmap_hash_string(const char *key);
5758
size_t hashmap_hash_string_i(const char *key);
58-

src/hashmap.c

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ static inline size_t hashmap_calc_index(const struct hashmap_base *hb, const voi
7777
static struct hashmap_entry *hashmap_entry_get_populated(const struct hashmap_base *hb,
7878
const struct hashmap_entry *entry)
7979
{
80-
if (hb->size > 0) {
80+
if (hb->size > 0 && entry >= hb->table) {
8181
for (; entry < &hb->table[hb->table_size]; ++entry) {
8282
if (entry->key) {
8383
return (struct hashmap_entry *)entry;
@@ -119,7 +119,7 @@ static struct hashmap_entry *hashmap_entry_find(const struct hashmap_base *hb,
119119

120120
/*
121121
* Removes the specified entry and processes the following entries to
122-
* keep the chain contiguous. This is a required step for hash maps
122+
* keep the chain contiguous. This is a required step for hashmaps
123123
* using linear probing.
124124
*/
125125
static void hashmap_entry_remove(struct hashmap_base *hb, struct hashmap_entry *removed_entry)
@@ -134,6 +134,7 @@ static void hashmap_entry_remove(struct hashmap_base *hb, struct hashmap_entry *
134134
if (hb->key_free) {
135135
hb->key_free(removed_entry->key);
136136
}
137+
137138
--hb->size;
138139

139140
/* Fill the free slot in the chain */
@@ -183,12 +184,8 @@ static int hashmap_rehash(struct hashmap_base *hb, size_t table_size)
183184
hb->table_size = table_size;
184185
hb->table = new_table;
185186

186-
if (!old_table) {
187-
return 0;
188-
}
189-
190187
/* Rehash */
191-
for (entry = old_table; entry < &old_table[old_size]; ++entry) {
188+
for (entry = old_table; entry < old_table + old_size; ++entry) {
192189
if (!entry->key) {
193190
continue;
194191
}
@@ -436,7 +433,7 @@ struct hashmap_entry *hashmap_base_iter(const struct hashmap_base *hb,
436433
*/
437434
bool hashmap_base_iter_valid(const struct hashmap_base *hb, const struct hashmap_entry *iter)
438435
{
439-
return hb && iter && iter->key && iter >= hb->table && iter < &hb->table[hb->table_size];
436+
return hb && iter && iter->key && iter >= hb->table && iter < hb->table + hb->table_size;
440437
}
441438

442439
/*
@@ -451,6 +448,18 @@ bool hashmap_base_iter_next(const struct hashmap_base *hb, struct hashmap_entry
451448
return (*iter = hashmap_entry_get_populated(hb, *iter + 1)) != NULL;
452449
}
453450

451+
/*
452+
* Returns an iterator to the hashmap entry with the specified key.
453+
* Returns NULL if there is no matching entry.
454+
*/
455+
struct hashmap_entry *hashmap_base_iter_find(const struct hashmap_base *hb, const void *key)
456+
{
457+
if (!key) {
458+
return NULL;
459+
}
460+
return hashmap_entry_find(hb, key, false);
461+
}
462+
454463
/*
455464
* Remove the hashmap entry pointed to by this iterator and advance the
456465
* iterator to the next entry.

test/CMakeLists.txt

Lines changed: 0 additions & 10 deletions
This file was deleted.

0 commit comments

Comments
 (0)