thash_impl.h
Go to the documentation of this file.
1 /* Copyright (C) 2013-2016, The Regents of The University of Michigan.
2 All rights reserved.
3 
4 This software was developed in the APRIL Robotics Lab under the
5 direction of Edwin Olson, ebolson@umich.edu. This software may be
6 available under alternative licensing terms; contact the address above.
7 
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions are met:
10 
11 1. Redistributions of source code must retain the above copyright notice, this
12  list of conditions and the following disclaimer.
13 2. Redistributions in binary form must reproduce the above copyright notice,
14  this list of conditions and the following disclaimer in the documentation
15  and/or other materials provided with the distribution.
16 
17 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
21 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 
28 The views and conclusions contained in the software and documentation are those
29 of the authors and should not be interpreted as representing official policies,
30 either expressed or implied, of the Regents of The University of Michigan.
31 */
32 
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <math.h>
36 
37 /*
38 // beware: The combination of:
39 // 1) a hash function that uses floating point values
40 // 2) that is inlined
41 // 3) and compiled with -Ofast
42 // can result in inconsistent values being computed. You can force the function
43 // NOT to be inlined with __attribute__ ((noinline)).
44 //
45 // It's also tempting to do:
46 // #define TKEYEQUAL(pka, pkb) (!memcmp(pka, pkb, sizeof(my_key_tyep)))
47 //
48 // But this will not work as expected if the structure contains
49 // padding that is not consistently cleared to zero. It appears that
50 // in C99, copying a struct by value does NOT necessarily copy
51 // padding, and so it may be difficult to guarantee that padding is
52 // zero, even when the code otherwise appears sane.
53 //
54 // You can use the "performance" method to evaluate how well your hash
55 // function is doing. Bad hash functions (obviously) are very bad for
56 // performance!
57 
58 #define TNAME sm_points_hash
59 #define TVALTYPE struct sm_points_record
60 #define TKEYTYPE zarray_t*
61 
62 // takes a pointer to the key
63 #define TKEYHASH(pk) ((uint32_t) (pk->level + (a->rad * 100) + (a->meters_per_pixel*100)))
64 
65 // takes a pointer to the value
66 #define TKEYEQUAL(pka, pkb) (!memcmp(pka, pkb, sizeof(struct sm_points_record)))
67 
68 */
69 
70 // when nentries/size is greater than _CRITICAL, we trigger a rehash.
71 #define THASH_FACTOR_CRITICAL 2
72 
73 // when rehashing (or allocating with a target capacity), we use this ratio of nentries/size
74 // should be greater than THASH_FACTOR_CRITICAL
75 #define THASH_FACTOR_REALLOC 4
76 
77 #define TRRFN(root, suffix) root ## _ ## suffix
78 #define TRFN(root, suffix) TRRFN(root, suffix)
79 #define TFN(suffix) TRFN(TNAME, suffix)
80 
81 #define TTYPENAME TFN(t)
82 
83 struct TFN(_entry)
84 {
85  // XXX Better to store the hash value and to reserve a special
86  // hash value to mean invalid?
87  uint8_t valid;
88  TKEYTYPE key;
89  TVALTYPE value;
90 };
91 
92 typedef struct TTYPENAME TTYPENAME;
93 struct TTYPENAME
94 {
95  struct TFN(_entry) *entries;
96  int nentries;
97  int size; // always a power of two.
98 };
99 
100 // will allocate enough room so that size can grow to 'capacity'
101 // without rehashing.
102 static inline TTYPENAME *TFN(create_capacity)(int capacity)
103 {
104  // must be this large to not trigger rehash
105  int _nentries = THASH_FACTOR_REALLOC*capacity;
106  if (_nentries < 8)
107  _nentries = 8;
108 
109  // but must also be a power of 2
110  int nentries = _nentries;
111  if ((nentries & (nentries - 1)) != 0) {
112  nentries = 8;
113  while (nentries < _nentries)
114  nentries *= 2;
115  }
116 
117  assert((nentries & (nentries-1)) == 0);
118  TTYPENAME *hash = calloc(1, sizeof(TTYPENAME));
119  hash->nentries = nentries;
120  hash->entries = calloc(hash->nentries, sizeof(struct TFN(_entry)));
121  return hash;
122 }
123 
124 static inline TTYPENAME *TFN(create)()
125 {
126  return TFN(create_capacity)(8);
127 }
128 
129 static inline void TFN(destroy)(TTYPENAME *hash)
130 {
131  if (!hash)
132  return;
133 
134  free(hash->entries);
135  free(hash);
136 }
137 
138 static inline int TFN(size)(TTYPENAME *hash)
139 {
140  return hash->size;
141 }
142 
143 static inline void TFN(clear)(TTYPENAME *hash)
144 {
145  // could just clear the 'valid' flag.
146  memset(hash->entries, 0, hash->nentries * sizeof(struct TFN(_entry)));
147  hash->size = 0;
148 }
149 
150 // examine the performance of the hashing function by looking at the distribution of bucket->size
151 static inline void TFN(performance)(TTYPENAME *hash)
152 {
153  int runs_sz = 32;
154  int runs[runs_sz];
155  int cnt = 0;
156  int max_run = 0;
157  int min_run = hash->size;
158  int run1 = 0;
159  int run2 = 0;
160 
161  memset(runs, 0, sizeof(runs));
162 
163  for (int entry_idx = 0; entry_idx < hash->nentries; entry_idx++) {
164  if (!hash->entries[entry_idx].valid)
165  continue;
166 
167  int this_run = 0;
168  while (hash->entries[(entry_idx+this_run) & (hash->nentries - 1)].valid)
169  this_run++;
170  if (this_run < runs_sz)
171  runs[this_run]++;
172  if (this_run < min_run)
173  min_run = this_run;
174  if (this_run > max_run)
175  max_run = this_run;
176 
177  run1 += this_run;
178  run2 += this_run*this_run;
179  cnt++;
180  }
181 
182  double Ex1 = 1.0 * run1 / cnt;
183  double Ex2 = 1.0 * run2 / cnt;
184 
185 #define strr(s) #s
186 #define str(s) strr(s)
187  printf("%s: size %8d, nentries: %8d, min %3d, max %3d, mean %6.3f, stddev %6.3f\n",
188  str(TNAME),
189  hash->size, hash->nentries, min_run, max_run, Ex1, sqrt(Ex2 - Ex1*Ex1));
190 }
191 
192 static inline int TFN(get_volatile)(TTYPENAME *hash, TKEYTYPE *key, TVALTYPE **value)
193 {
194  uint32_t code = TKEYHASH(key);
195  uint32_t entry_idx = code & (hash->nentries - 1);
196 
197  while (hash->entries[entry_idx].valid) {
198  if (TKEYEQUAL(key, &hash->entries[entry_idx].key)) {
199  *value = &hash->entries[entry_idx].value;
200  return 1;
201  }
202 
203  entry_idx = (entry_idx + 1) & (hash->nentries - 1);
204  }
205 
206  return 0;
207 }
208 
209 static inline int TFN(get)(TTYPENAME *hash, TKEYTYPE *key, TVALTYPE *value)
210 {
211  // XXX see implementation in zhash.c (implement in terms of
212  // get_volatile)
213 
214  uint32_t code = TKEYHASH(key);
215  uint32_t entry_idx = code & (hash->nentries - 1);
216 
217  while (hash->entries[entry_idx].valid) {
218  if (TKEYEQUAL(key, &hash->entries[entry_idx].key)) {
219  *value = hash->entries[entry_idx].value;
220  return 1;
221  }
222 
223  entry_idx = (entry_idx + 1) & (hash->nentries - 1);
224  }
225 
226  return 0;
227 }
228 
229 static inline int TFN(put)(TTYPENAME *hash, TKEYTYPE *key, TVALTYPE *value, TKEYTYPE *oldkey, TVALTYPE *oldvalue)
230 {
231  uint32_t code = TKEYHASH(key);
232  uint32_t entry_idx = code & (hash->nentries - 1);
233 
234  while (hash->entries[entry_idx].valid) {
235  if (TKEYEQUAL(key, &hash->entries[entry_idx].key)) {
236  if (oldkey)
237  *oldkey = hash->entries[entry_idx].key;
238  if (oldvalue)
239  *oldvalue = hash->entries[entry_idx].value;
240  hash->entries[entry_idx].key = *key;
241  hash->entries[entry_idx].value = *value;
242  return 1;
243  }
244 
245  entry_idx = (entry_idx + 1) & (hash->nentries - 1);
246  }
247 
248  hash->entries[entry_idx].valid = 1;
249  hash->entries[entry_idx].key = *key;
250  hash->entries[entry_idx].value = *value;
251  hash->size++;
252 
253  if (hash->nentries < THASH_FACTOR_CRITICAL*hash->size) {
254 // printf("rehash: \n before: ");
255 // TFN(performance)(hash);
256 
257  // rehash!
258  TTYPENAME *newhash = TFN(create_capacity)(hash->size + 1);
259 
260  for (int entry_idx = 0; entry_idx < hash->nentries; entry_idx++) {
261  if (hash->entries[entry_idx].valid) {
262 
263  if (TFN(put)(newhash, &hash->entries[entry_idx].key, &hash->entries[entry_idx].value, NULL, NULL))
264  assert(0); // shouldn't already be present.
265  }
266  }
267 
268  // play switch-a-roo. We become 'newhash' and free the old one.
269  TTYPENAME tmp;
270  memcpy(&tmp, hash, sizeof(TTYPENAME));
271  memcpy(hash, newhash, sizeof(TTYPENAME));
272  memcpy(newhash, &tmp, sizeof(TTYPENAME));
273  TFN(destroy)(newhash);
274 
275 // printf(" after : ");
276 // TFN(performance)(hash);
277  }
278 
279  return 0;
280 }
281 
282 static inline int TFN(remove)(TTYPENAME *hash, TKEYTYPE *key, TKEYTYPE *oldkey, TVALTYPE *oldvalue)
283 {
284  uint32_t code = TKEYHASH(key);
285  uint32_t entry_idx = code & (hash->nentries - 1);
286 
287  while (hash->entries[entry_idx].valid) {
288  if (TKEYEQUAL(key, &hash->entries[entry_idx].key)) {
289 
290  if (oldkey)
291  *oldkey = hash->entries[entry_idx].key;
292  if (oldvalue)
293  *oldvalue = hash->entries[entry_idx].value;
294 
295  hash->entries[entry_idx].valid = 0;
296  hash->size--;
297 
298  // re-put following entries
299  entry_idx = (entry_idx + 1) & (hash->nentries - 1);
300  while (hash->entries[entry_idx].valid) {
301  TKEYTYPE key = hash->entries[entry_idx].key;
302  TVALTYPE value = hash->entries[entry_idx].value;
303  hash->entries[entry_idx].valid = 0;
304  hash->size--;
305 
306  if (TFN(put)(hash, &key, &value, NULL, NULL)) {
307  assert(0);
308  }
309 
310  entry_idx = (entry_idx + 1) & (hash->nentries - 1);
311  }
312 
313  return 1;
314  }
315 
316  entry_idx = (entry_idx + 1) & (hash->nentries - 1);
317  }
318 
319  return 0;
320 }
321 
322 static inline TTYPENAME *TFN(copy)(TTYPENAME *hash)
323 {
324  TTYPENAME *newhash = TFN(create_capacity)(hash->size);
325 
326  for (int entry_idx = 0; entry_idx < hash->nentries; entry_idx++) {
327  if (hash->entries[entry_idx].valid) {
328  if (TFN(put)(newhash, &hash->entries[entry_idx].key, &hash->entries[entry_idx].value, NULL, NULL))
329  assert(0); // shouldn't already be present.
330  }
331  }
332 
333  return newhash;
334 }
335 
336 typedef struct TFN(iterator) TFN(iterator_t);
337 struct TFN(iterator)
338 {
339  TTYPENAME *hash;
340  int last_entry; // points to last entry returned by _next
341 };
342 
343 static inline void TFN(iterator_init)(TTYPENAME *hash, TFN(iterator_t) *iter)
344 {
345  iter->hash = hash;
346  iter->last_entry = -1;
347 }
348 
349 static inline int TFN(iterator_next)(TFN(iterator_t) *iter, TKEYTYPE *outkey, TVALTYPE *outval)
350 {
351  TTYPENAME *hash = iter->hash;
352 
353  while(1) {
354  if (iter->last_entry+1 >= hash->nentries)
355  return 0;
356 
357  iter->last_entry++;
358 
359  if (hash->entries[iter->last_entry].valid) {
360  if (outkey)
361  *outkey = hash->entries[iter->last_entry].key;
362  if (outval)
363  *outval = hash->entries[iter->last_entry].value;
364  return 1;
365  }
366  }
367 }
368 
369 static inline void TFN(iterator_remove)(TFN(iterator_t) *iter)
370 {
371  TTYPENAME *hash = iter->hash;
372 
373  hash->entries[iter->last_entry].valid = 0;
374 
375  // have to reinsert any consecutive entries that follow.
376  int entry_idx = (iter->last_entry + 1) & (hash->nentries - 1);
377  while (hash->entries[entry_idx].valid) {
378  TKEYTYPE key = hash->entries[entry_idx].key;
379  TVALTYPE value = hash->entries[entry_idx].value;
380  hash->entries[entry_idx].valid = 0;
381  hash->size--;
382 
383  if (TFN(put)(hash, &key, &value, NULL, NULL)) {
384  assert(0);
385  }
386 
387  entry_idx = (entry_idx + 1) & (hash->nentries - 1);
388  }
389 
390  hash->size--;
391 }
static void TFN() performance(TTYPENAME *hash)
Definition: thash_impl.h:151
static int TFN() put(TTYPENAME *hash, TKEYTYPE *key, TVALTYPE *value, TKEYTYPE *oldkey, TVALTYPE *oldvalue)
Definition: thash_impl.h:229
int size
Definition: thash_impl.h:97
static TTYPENAME *TFN() create()
Definition: thash_impl.h:124
static int TFN() iterator_next(TFN(iterator_t)*iter, TKEYTYPE *outkey, TVALTYPE *outval)
Definition: thash_impl.h:349
#define str(s)
int nentries
Definition: thash_impl.h:96
static void TFN() destroy(TTYPENAME *hash)
Definition: thash_impl.h:129
static TTYPENAME *TFN() create_capacity(int capacity)
Definition: thash_impl.h:102
static int TFN() size(TTYPENAME *hash)
Definition: thash_impl.h:138
static int TFN() get_volatile(TTYPENAME *hash, TKEYTYPE *key, TVALTYPE **value)
Definition: thash_impl.h:192
static void TFN() iterator_init(TTYPENAME *hash, TFN(iterator_t)*iter)
Definition: thash_impl.h:343
static void TFN() iterator_remove(TFN(iterator_t)*iter)
Definition: thash_impl.h:369
#define THASH_FACTOR_REALLOC
Definition: thash_impl.h:75
#define THASH_FACTOR_CRITICAL
Definition: thash_impl.h:71
static void TFN() clear(TTYPENAME *hash)
Definition: thash_impl.h:143
#define TFN(suffix)
Definition: thash_impl.h:79
static TTYPENAME *TFN() copy(TTYPENAME *hash)
Definition: thash_impl.h:322
#define TNAME
Definition: doubles.h:25


apriltags2
Author(s): Danylo Malyuta
autogenerated on Fri Oct 19 2018 04:02:32