GFXprim
2D bitmap graphics library with emphasis on speed and correctness
Loading...
Searching...
No Matches
gp_htable2.h
1//SPDX-License-Identifier: LGPL-2.0-or-later
2
3/*
4
5 Simple hash table implementation - generic part.
6
7 These functions are basic building blocks for a hash table.
8
9 Copyright (c) 2014-2021 Cyril Hrubis <metan@ucw.cz>
10
11 */
12
13#ifndef GP_HTABLE2_H
14#define GP_HTABLE2_H
15
16#include <stddef.h>
17#include <string.h>
18#include <core/gp_debug.h>
19#include <utils/gp_htable.h>
20
21/* Returns new size for table based on number of used records. */
22size_t gp_htable_tsize(size_t used);
23
24static inline void gp_htable_put_(struct gp_htable_rec *recs,
25 size_t (*hash)(const void *key, size_t htable_size),
26 size_t htable_size,
27 void *val, char *key)
28{
29 unsigned int h = hash(key, htable_size);
30
31 while (recs[h].key)
32 h = (h+1) % htable_size;
33
34 recs[h].val = val;
35 recs[h].key = key;
36}
37
38static inline void gp_htable_rehash(gp_htable *self, size_t new_size,
39 size_t (*hash)(const void *key, size_t htable_size))
40{
41 size_t i;
42
43 GP_DEBUG(1, "Rehashing from %zu to %zu", self->size, new_size);
44
45 struct gp_htable_rec *recs = malloc(sizeof(struct gp_htable_rec) * new_size);
46
47 if (!recs) {
48 GP_WARN("Malloc failed :-(");
49 return;
50 }
51
52 memset(recs, 0, sizeof(struct gp_htable_rec) * new_size);
53
54 for (i = 0; i < self->size; i++) {
55 if (!self->recs[i].key)
56 continue;
57
58 gp_htable_put_(recs, hash, new_size, self->recs[i].val, self->recs[i].key);
59 }
60
61 free(self->recs);
62 self->recs = recs;
63 self->size = new_size;
64}
65
66static inline void gp_htable_put2(gp_htable *self,
67 size_t (*hash)(const void *key, size_t htable_size),
68 void *val, void *key)
69{
70 if (++self->used > self->size/2)
71 gp_htable_rehash(self, gp_htable_tsize(self->used), hash);
72
73 gp_htable_put_(self->recs, hash, self->size, val, key);
74}
75
76static inline void *gp_htable_get2(gp_htable *self,
77 size_t (*hash)(const void *key, size_t htable_size),
78 int (*cmp)(const void *key1, const void *key2),
79 const void *key)
80{
81 if (!self)
82 return NULL;
83
84 size_t h = hash(key, self->size);
85
86 while (self->recs[h].key) {
87 if (cmp(self->recs[h].key, key))
88 return self->recs[h].val;
89 h = (h+1) % self->size;
90 }
91
92 return NULL;
93}
94
95static inline void *gp_htable_rem2_(gp_htable *self,
96 size_t (*hash)(const void *key, size_t htable_size),
97 size_t h)
98{
99 void *ret;
100
101 if (self->flags & GP_HTABLE_FREE_KEY)
102 free(self->recs[h].key);
103
104 ret = self->recs[h].val;
105
106 self->recs[h].key = NULL;
107 self->recs[h].val = NULL;
108
109 if (--self->used < self->size/8) {
110 gp_htable_rehash(self, gp_htable_tsize(self->used), hash);
111 return ret;
112 }
113
114 size_t i = h;
115 size_t j = h;
116
117 for (;;) {
118 i = (i+1)%self->size;
119
120 if (!self->recs[i].key)
121 break;
122
123 h = hash(self->recs[i].key, self->size);
124
125 /* record at i can't be moved to the empty slot j */
126 if (i >= h && h > j)
127 continue;
128
129 /* the same but i has overflown over self->size */
130 if (i < j && h <= i)
131 continue;
132
133 if (i < j && h > j)
134 continue;
135
136 self->recs[j] = self->recs[i];
137
138 self->recs[i].key = NULL;
139 self->recs[i].val = NULL;
140
141 j = i;
142 }
143
144 return ret;
145}
146
147static inline void *gp_htable_rem2(gp_htable *self,
148 size_t (*hash)(const void *key, size_t htable_size),
149 int (*cmp)(const void *key1, const void *key2),
150 const void *key)
151{
152 size_t h = hash(key, self->size);
153
154 while (self->recs[h].key) {
155 if (cmp(self->recs[h].key, key))
156 return gp_htable_rem2_(self, hash, h);
157
158 h = (h+1) % self->size;
159 }
160
161 return NULL;
162}
163
164/*
165 * Iterates over all hash table records, calls trim() on each once. If trim()
166 * returns non-zero record is removed from the table.
167 *
168 * We cannot remove elements from the table in the middle of the loop, since
169 * removal shuffles them around the table. Instead we build a list of to be
170 * deleted elements reusing the val pointer in record and do the removal once
171 * we evaluated all records.
172 */
173static inline void gp_htable_trim2(gp_htable *self,
174 size_t (*hash)(const void *key, size_t htable_size),
175 int (*cmp)(const void *key1, const void *key2),
176 int (*trim)(void *val),
177 void (*free_val)(void *val))
178{
179 size_t i = 0;
180 void *key = NULL;
181
182 for (i = 0; i < self->size; i++) {
183 if (!self->recs[i].key)
184 continue;
185
186 if (!trim(self->recs[i].val))
187 continue;
188
189 if (free_val)
190 free_val(self->recs[i].val);
191
192 self->recs[i].val = key;
193 key = self->recs[i].key;
194 }
195
196 while (key)
197 key = gp_htable_rem2(self, hash, cmp, key);
198}
199
200#endif /* GP_HTABLE2_H */
A debug message layer.
#define GP_WARN(...)
A debug WARN printf-like macro.
Definition gp_debug.h:104
#define GP_DEBUG(level,...)
A debug printf-like macro.
Definition gp_debug.h:88
Simple hash table implementation.
@ GP_HTABLE_FREE_KEY
Definition gp_htable.h:28
A hash table record.
Definition gp_htable.h:34
A hash table.
Definition gp_htable.h:40
size_t used
Number of used slots in the hash table.
Definition gp_htable.h:46
enum gp_htable_flags flags
Flags.
Definition gp_htable.h:48
size_t size
Hash table record array size.
Definition gp_htable.h:44
struct gp_htable_rec * recs
Array for the hash table records.
Definition gp_htable.h:42