librnr  1.14.5
RoadNarrows Robotics Common Library 1
example_hash.c
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // Package: RoadNarrows Robotics Common Library 1
4 //
5 // File: example_hash.c
6 //
7 /*! \file
8  *
9  * $LastChangedDate: 2011-11-18 13:30:34 -0700 (Fri, 18 Nov 2011) $
10  * $Rev: 1577 $
11  *
12  * \brief Example of using a librnr hash table.
13  *
14  * \author Robin Knight (robin.knight@roadnarrows.com)
15  *
16  * \pkgcopyright{2007-2018,RoadNarrows LLC.,http://www.roadnarrows.com}
17  */
18 // Permission is hereby granted, without written agreement and without
19 // license or royalty fees, to use, copy, modify, and distribute this
20 // software and its documentation for any purpose, provided that
21 // (1) The above copyright notice and the following two paragraphs
22 // appear in all copies of the source code and (2) redistributions
23 // including binaries reproduces these notices in the supporting
24 // documentation. Substantial modifications to this software may be
25 // copyrighted by their authors and need not follow the licensing terms
26 // described here, provided that the new terms are clearly indicated in
27 // all files where they apply.
28 //
29 // IN NO EVENT SHALL THE AUTHOR, ROADNARROWS LLC, OR ANY MEMBERS/EMPLOYEES
30 // OF ROADNARROW LLC OR DISTRIBUTORS OF THIS SOFTWARE BE LIABLE TO ANY
31 // PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
32 // DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION,
33 // EVEN IF THE AUTHORS OR ANY OF THE ABOVE PARTIES HAVE BEEN ADVISED OF
34 // THE POSSIBILITY OF SUCH DAMAGE.
35 //
36 // THE AUTHOR AND ROADNARROWS LLC SPECIFICALLY DISCLAIM ANY WARRANTIES,
37 // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
38 // FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN
39 // "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO
40 // PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
41 //
42 ////////////////////////////////////////////////////////////////////////////////
43 
44 #include <stdio.h>
45 #include <string.h>
46 #include <ctype.h>
47 #include <stdarg.h>
48 
49 #include "rnr/rnrconfig.h"
50 #include "rnr/new.h"
51 #include "rnr/log.h"
52 #include "rnr/hash.h"
53 
54 /*!
55  * \brief Working input buffer.
56  */
57 typedef char input_t[256];
58 
59 /*!
60  * \brief Tokenize input
61  *
62  * \param string Input string to parse.
63  * \param [out] ... Variable argument list of char** arguments terminated
64  * by a last NULL argument.
65  *
66  * \return Number of tokens.
67  */
68 static int tokenize(char *string, ...)
69 {
70  char **tokptr;
71  va_list arglist;
72  int tokcount = 0;
73 
74  va_start(arglist, string);
75  tokptr = va_arg(arglist, char **);
76  while(tokptr)
77  {
78  while(*string && isspace((int)*string))
79  {
80  string++;
81  }
82  if(!*string)
83  {
84  break;
85  }
86  *tokptr = string;
87  while(*string && !isspace((int)*string))
88  {
89  string++;
90  }
91  tokptr = va_arg(arglist, char **);
92  tokcount++;
93  if(!*string)
94  {
95  break;
96  }
97  *string++ = 0;
98  }
99  va_end(arglist);
100 
101  return tokcount;
102 }
103 
104 /*!
105  * \brief Delete node data - both key and value are dynamically allocated.
106  *
107  * \param key Data key.
108  * \param val Data value.
109  */
110 static void del_node_data(void *key, void *val)
111 {
112  delete(key);
113  delete(val);
114 }
115 
116 /*!
117  * \brief Colorado's 14,000+ foot mountains.
118  *
119  * N.B. there are 53 of them, I'm stopping at the highest 16.
120  */
121 static char *Colorado14ers[][2] =
122 {
123  {"mt_elbert", "14,433"},
124  {"mt_massive", "14,421"},
125  {"mt_harvard", "14,420"},
126  {"blanca_peak", "14,345"},
127  {"la_plata_peak", "14,336"},
128  {"uncompahgre_peak", "14,309"},
129  {"crestone_peak", "14,294"},
130  {"mt_lincoln", "14,286"},
131  {"grays_peak", "14,270"},
132  {"mt_antero", "14,269"},
133  {"torreys_peak", "14,267"},
134  {"castle_peak", "14,265"},
135  {"quandary_peak", "14,265"},
136  {"mt_evans", "14,264"},
137  {"longs_peak", "14,255"},
138  {"mt_wilson", "14,246"}
139 };
140 
141 /*!
142  * \brief Make random keys unique.
143  */
144 static int MtnCounter = 0;
145 
146 /*!
147  * \brief Force table to grow by feeding the table random entries.
148  *
149  * \param h Pointer to hash table.
150  */
151 static void force_grow(hash_t *h)
152 {
153  hashcount_t count, size;
154  size_t num_mtns = arraysize(Colorado14ers);
155  int mtn;
156  char buf[64];
157  char *key, *val;
158 
159  count = hash_count(h);
160  size = hash_size(h);
161 
162  printf(" starting count=%lu, table size=%lu\n", count, size);
163 
164  // tables grows at highwater mark
165  size = h->highmark;
166 
167  while( count++ < size )
168  {
169  mtn = (int)(random() % (long int)num_mtns);
170  sprintf(buf, "%s(%d)", Colorado14ers[mtn][0], MtnCounter++);
171  key = new_strdup(buf);
172  val = new_strdup(Colorado14ers[mtn][1]);
173 
174  if( !hash_insert(h, key, val) )
175  {
176  puts("hash_insert failed");
177  del_node_data(key, val);
178  return;
179  }
180  else
181  {
182  printf(" added %s -> %s\n", key, val);
183  }
184  }
185 
186  count = hash_count(h);
187  size = hash_size(h);
188 
189  printf(" ending count=%lu, table size=%lu\n", count, size);
190 }
191 
192 /*!
193  * \brief Force table to shink by deleting table random entries.
194  *
195  * \param h Pointer to hash table.
196  */
197 static void force_shrink(hash_t *h)
198 {
199  hashcount_t count, size;
200  hscan_t hs;
201  hnode_t *hn;
202  char *key, *val;
203 
204  count = hash_count(h);
205  size = hash_size(h);
206 
207  printf(" starting count=%lu, table size=%lu\n", count, size);
208 
209  // tables shrinks at lowwater mark
210  size = h->lowmark;
211 
212  hash_scan_begin(&hs, h);
213 
214  // delete first count nodes
215  while( ((hn = hash_scan_next(&hs)) != NULL) && (count-- > size) )
216  {
217  key = (char *)hnode_getkey(hn);
218  val = (char *)hnode_get(hn);
219  printf(" deleted %s -> %s\n", key, val);
220  hash_node_delete(h, hn);
221  }
222 
223  count = hash_count(h);
224  size = hash_size(h);
225 
226  printf(" ending count=%lu, table size=%lu\n", count, size);
227 }
228 
229 /*!
230  * \brief Example main.
231  *
232  * \param argc Command-line argument count.
233  * \param argv Command-line argument list.
234  *
235  * \par Exit Status:
236  * Program exits with 0 success, \h_gt 0 on failure.
237  */
238 int main(int argc, char *argv[])
239 {
240  input_t in;
241  hash_t *h = NULL;
242  hnode_t *hn;
243  hscan_t hs;
244  char *tok1, *tok2;
245  char *key, *val;
246  int n;
247 
248  char *help =
249  "n create new empty hash table\n"
250  "x delete entire hash table\n"
251  "a <key> <val> add value to hash table\n"
252  "d <key> delete value from hash table\n"
253  "l <key> lookup value in hash table\n"
254  "c show counts: number of entries and table size\n"
255  "t dump whole hash table\n"
256  "+ force increase hash table (by auto-adding)\n"
257  "- force decrease hash table (by auto-deleting)\n"
258  "v <level> set logging verbosity level 0-4\n"
259  "q quit";
260 
261 
262  puts("Hashing example (enter '?' for list of commands)");
263 
264  // process user commands
265  for(;;)
266  {
267  printf("hash> ");
268 
269  // read user input
270  if( !fgets(in, (int)sizeof(input_t), stdin) )
271  {
272  break;
273  }
274 
275  // execute command
276  switch( in[0] )
277  {
278  // help
279  case '?':
280  puts(help);
281  break;
282 
283  // create new hash table
284  case 'n':
285  if( h != NULL )
286  {
287  printf("table exists, delete first\n");
288  }
289  else
290  {
291  h = hash_table_create(
292  true, // dynamic table sizing
293  (hashcount_t)4, // minimum size
294  HASHCOUNT_T_MAX, // maximum size
295  NULL, // use default comparator function
296  NULL, // use default hashing function
297  del_node_data); // hash node data deletion function
298 
299  hash_set_self_verify(true); // off by default
300  }
301  break;
302 
303  // delete hash table and all user data
304  case 'x':
305  if( h == NULL )
306  {
307  printf("no table\n");
308  }
309  else
310  {
312  h = NULL;
313  }
314  break;
315 
316  // add hash table entry
317  case 'a':
318  if( h == NULL )
319  {
320  printf("no hash table\n");
321  }
322  else if(tokenize(in+1, &tok1, &tok2, (char **) 0) != 2)
323  {
324  puts("what?");
325  }
326  else
327  {
328  // new key and value
329  key = new_strdup(tok1);
330  val = new_strdup(tok2);
331 
332  // insert into hash table, automatically allocating hnode_t
333  if( !hash_insert(h, key, val) )
334  {
335  puts("hash_insert failed");
336  del_node_data(key, val);
337  }
338  }
339  break;
340 
341  // delete hash table entry
342  case 'd':
343  if( h == NULL )
344  {
345  printf("no hash table\n");
346  }
347  else if(tokenize(in+1, &tok1, (char **)0) != 1)
348  {
349  puts("what?");
350  }
351  else if( (hn = hash_lookup(h, tok1)) == NULL )
352  {
353  puts("hash_lookup failed");
354  }
355  else
356  {
357  key = (char *)hnode_getkey(hn);
358  val = (char *)hnode_get(hn);
359  printf("deleted %s -> %s\n", key, val);
360  hash_node_delete(h, hn);
361  }
362  break;
363 
364  // lookup hash table entry
365  case 'l':
366  if( h == NULL )
367  {
368  printf("no hash table\n");
369  }
370  else if(tokenize(in+1, &tok1, (char **) 0) != 1)
371  {
372  puts("what?");
373  }
374  else if( (hn = hash_lookup(h, tok1)) == NULL )
375  {
376  puts("hash_lookup failed");
377  }
378  else
379  {
380  val = hnode_get(hn);
381  printf("%s -> %s\n", tok1, val);
382  }
383  break;
384 
385  // get current number of hash table entries and current table size
386  case 'c':
387  if( h == NULL )
388  {
389  printf("no hash table\n");
390  }
391  else
392  {
393  printf("count=%lu, size=%lu\n",
394  (unsigned long)hash_count(h), (unsigned long)hash_size(h));
395  }
396  break;
397 
398  // dump hash table entries
399  case 't':
400  if( h == NULL )
401  {
402  printf("no hash table\n");
403  }
404  else
405  {
406  hash_scan_begin(&hs, h);
407  while( (hn = hash_scan_next(&hs)) != NULL )
408  {
409  printf("%s -> %s\n", (char *)hnode_getkey(hn),
410  (char *)hnode_get(hn));
411  }
412  }
413  break;
414 
415  // force hash table to grow
416  case '+':
417  if( h == NULL )
418  {
419  printf("no hash table\n");
420  }
421  else
422  {
423  force_grow(h);
424  }
425  break;
426 
427  // force hash table to shrink
428  case '-':
429  if( h == NULL )
430  {
431  printf("no hash table\n");
432  }
433  else
434  {
435  force_shrink(h);
436  }
437  break;
438 
439  // logging level
440  case 'v':
441  if( tokenize(in+1, &tok1, (char **)0) != 1 )
442  {
443  puts("what?");
444  }
445  else
446  {
447  n = atoi(tok1);
449  }
450  break;
451 
452  // quit
453  case 'q':
454  exit(0);
455  break;
456 
457  // null command
458  case '\0':
459  case '\n':
460  break;
461 
462  // input error
463  default:
464  puts("?");
465  break;
466  }
467  }
468 
469  return 0;
470 }
int main(int argc, char *argv[])
Example main.
Definition: example_hash.c:238
#define hnode_getkey(N)
Get hash node hash key.
Definition: hash.h:409
static void force_grow(hash_t *h)
Force table to grow by feeding the table random entries.
Definition: example_hash.c:151
#define HASHCOUNT_T_MAX
Maximum maximum hash table size.
Definition: hash.h:81
hnode_t * hash_scan_next(hscan_t *scan)
Retrieve the next node from the hash table.
Definition: hash.c:998
char * new_strdup(const char *s)
Duplicate a string.
Definition: new.c:176
static void del_node_data(void *key, void *val)
Delete node data - both key and value are dynamically allocated.
Definition: example_hash.c:110
#define NULL
null pointer
Definition: rnrconfig.h:199
General purpose hash data and function declarations.
char input_t[256]
Working input buffer.
Definition: example_hash.c:57
int LOG_SET_THRESHOLD(int nLevel)
Set new logging threshold level.
Definition: log.c:96
static int MtnCounter
Make random keys unique.
Definition: example_hash.c:144
Memory allocation and deallocation declarations.
hnode_t * hash_lookup(hash_t *hash, const void *key)
Find a node in the hash table and return a pointer to it.
Definition: hash.c:1246
#define hash_size(H)
Returns size of hash table.
Definition: hash.h:397
hashcount_t highmark
Note 4.
Definition: hash.h:273
void hash_set_self_verify(bool_t selfverify)
Enable (disable) automatic self-checks during hash table modification operatations.
Definition: hash.c:1331
#define arraysize(array)
array size, i.e. number of array entries
Definition: rnrconfig.h:259
unsigned long hashcount_t
maximum hash table size type
Definition: hash.h:94
static void force_shrink(hash_t *h)
Force table to shink by deleting table random entries.
Definition: example_hash.c:197
hashcount_t lowmark
Note 5.
Definition: hash.h:274
void hash_scan_begin(hscan_t *scan, hash_t *hash)
Reset the hash scanner (iterator).
Definition: hash.c:937
RoadNarrows Robotics common configuration file.
Hash scanner structure.
Definition: hash.h:302
#define hnode_get(N)
Get hash node user data.
Definition: hash.h:403
hash_t * hash_table_create(bool_t isdynamic, hashcount_t minsize, hashcount_t maxsize, hash_comp_t compfun, hash_fun_t hashfun, hnode_data_free_t freedatafun)
Create a dynamic hash table.
Definition: hash.c:691
Hash table control structure.
Definition: hash.h:267
Hash chain node structure.
Definition: hash.h:149
void hash_node_delete(hash_t *hash, hnode_t *node)
Unlink and delete a hash node from the hash table.
Definition: hash.c:1217
#define hash_count(H)
Returns number of entires in hash table.
Definition: hash.h:391
Logger declarations.
bool_t hash_insert(hash_t *hash, void *key, void *data)
Insert user data with the given key into the hash table.
Definition: hash.c:1283
static char * Colorado14ers[][2]
Colorado&#39;s 14,000+ foot mountains.
Definition: example_hash.c:121
void hash_table_destroy(hash_t *hash)
Delete the hash table, all of its entries, and all of the user data.
Definition: hash.c:750
static int tokenize(char *string,...)
Tokenize input.
Definition: example_hash.c:68