Skip to content

Commit

Permalink
added HASH_REPLACE macros
Browse files Browse the repository at this point in the history
  • Loading branch information
nickva committed Jan 16, 2013
1 parent 16ce98c commit c760770
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 12 deletions.
44 changes: 33 additions & 11 deletions doc/userguide.txt
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,11 @@ Why doesn't uthash check key uniqueness for you? It saves the cost of a hash
lookup for those programs which don't need it- for example, programs whose keys
are generated by an incrementing, non-repeating counter.

However, if replacement is a common operation, it is possible to use HASH_REPLACE
macro(s). This macro, before adding the item, will try to find an item with the
same key and delete it first. It also returns a pointer to the replace item so
that user has a chance to de-allocate its memory.

Passing the hash pointer into functions
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In the example above `users` is a global variable, but what if the caller wanted
Expand Down Expand Up @@ -276,6 +281,14 @@ The reason it's necessary to deal with a pointer to the hash pointer is simple:
the hash macros modify it (in other words, they modify the 'pointer itself' not
just what it points to).


Replacing An Item
~~~~~~~~~~~~~~~~~
HASH_REPLACE macros are equivalent to HASH_ADD macros except they attempt
to find and delete the item first. If it find and deletes and item, it will
also return that items pointer as an output parameter.


Find item
~~~~~~~~~
To look up a structure in a hash, you need its key. Then call `HASH_FIND`.
Expand Down Expand Up @@ -322,7 +335,9 @@ uthash never frees your structure
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Deleting a structure just removes it from the hash table-- it doesn't `free`
it. The choice of when to free your structure is entirely up to you; uthash
will never free your structure.
will never free your structure. For example when using HASH_REPLACE macros,
a replaced output argument is used in order to return back a pointer to the
replaced item, in order to make it possible for the user to de-allocate it.

Delete can change the pointer
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down Expand Up @@ -1647,16 +1662,19 @@ In order to use the convenience macros,
.Convenience macros
[width="90%",cols="10m,30m",grid="none",options="header"]
|===============================================================================
|macro | arguments
|HASH_ADD_INT | (head, keyfield_name, item_ptr)
|HASH_FIND_INT | (head, key_ptr, item_ptr)
|HASH_ADD_STR | (head, keyfield_name, item_ptr)
|HASH_FIND_STR | (head, key_ptr, item_ptr)
|HASH_ADD_PTR | (head, keyfield_name, item_ptr)
|HASH_FIND_PTR | (head, key_ptr, item_ptr)
|HASH_DEL | (head, item_ptr)
|HASH_SORT | (head, cmp)
|HASH_COUNT | (head)
|macro | arguments
|HASH_ADD_INT | (head, keyfield_name, item_ptr)
|HASH_REPLACE_INT | (head, keyfiled_name, item_ptr,replaced_item_ptr)
|HASH_FIND_INT | (head, key_ptr, item_ptr)
|HASH_ADD_STR | (head, keyfield_name, item_ptr)
|HASH_REPLACE_STR | (head,keyfield_name, item_ptr, replaced_item_ptr)
|HASH_FIND_STR | (head, key_ptr, item_ptr)
|HASH_ADD_PTR | (head, keyfield_name, item_ptr)
|HASH_REPLACE_PTR | (head, keyfield_name, item_ptr, replaced_item_ptr)
|HASH_FIND_PTR | (head, key_ptr, item_ptr)
|HASH_DEL | (head, item_ptr)
|HASH_SORT | (head, cmp)
|HASH_COUNT | (head)
|===============================================================================
General macros
Expand All @@ -1672,6 +1690,7 @@ than `hh`, or if your key's data type isn't `int` or `char[]`.
|macro | arguments
|HASH_ADD | (hh_name, head, keyfield_name, key_len, item_ptr)
|HASH_ADD_KEYPTR| (hh_name, head, key_ptr, key_len, item_ptr)
|HASH_REPLACE | (hh_name, head, keyfield_name, key_len, item_ptr, replaced_item_ptr)
|HASH_FIND | (hh_name, head, key_ptr, key_len, item_ptr)
|HASH_DELETE | (hh_name, head, item_ptr)
|HASH_SRT | (hh_name, head, cmp)
Expand Down Expand Up @@ -1713,6 +1732,9 @@ item_ptr::
`HASH_DELETE` macros, and an output parameter for `HASH_FIND` and
`HASH_ITER`. (When using `HASH_ITER` to iterate, `tmp_item_ptr`
is another variable of the same type as `item_ptr`, used internally).
replaced_item_ptr::
used in HASH_REPLACE macros. This is an output parameter that is set to point
to the replaced item (if no item is replaced it is set to NULL).
cmp::
pointer to comparison function which accepts two arguments (pointers to
items to compare) and returns an int specifying whether the first item
Expand Down
16 changes: 16 additions & 0 deletions src/uthash.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,16 @@ do {

#define HASH_ADD(hh,head,fieldname,keylen_in,add) \
HASH_ADD_KEYPTR(hh,head,&((add)->fieldname),keylen_in,add)

#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \
do { \
replaced=NULL; \
HASH_FIND(hh,head,&((add)->fieldname),keylen_in,replaced); \
if (replaced!=NULL) { \
HASH_DELETE(hh,head,replaced); \
}; \
HASH_ADD(hh,head,fieldname,keylen_in,add); \
} while(0)

#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \
do { \
Expand Down Expand Up @@ -242,14 +252,20 @@ do {
HASH_FIND(hh,head,findstr,strlen(findstr),out)
#define HASH_ADD_STR(head,strfield,add) \
HASH_ADD(hh,head,strfield,strlen(add->strfield),add)
#define HASH_REPLACE_STR(head,strfield,add,replaced) \
HASH_REPLACE(hh,head,strfield,strlen(add->strfield),replaced)
#define HASH_FIND_INT(head,findint,out) \
HASH_FIND(hh,head,findint,sizeof(int),out)
#define HASH_ADD_INT(head,intfield,add) \
HASH_ADD(hh,head,intfield,sizeof(int),add)
#define HASH_REPLACE_INT(head,intfield,add,replaced) \
HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced)
#define HASH_FIND_PTR(head,findptr,out) \
HASH_FIND(hh,head,findptr,sizeof(void *),out)
#define HASH_ADD_PTR(head,ptrfield,add) \
HASH_ADD(hh,head,ptrfield,sizeof(void *),add)
#define HASH_REPLACE_PTR(head,ptrfield,add) \
HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced)
#define HASH_DEL(head,delptr) \
HASH_DELETE(hh,head,delptr)

Expand Down
2 changes: 1 addition & 1 deletion tests/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ PROGS = test1 test2 test3 test4 test5 test6 test7 test8 test9 \
test50 test51 test52 test53 test54 test55 test56 test57 \
test58 test59 test60 test61 test62 test63 test64 test65 \
test66 test67 test68 test69 test70 test71 test72 test73 \
test74 test75 test76 test77 test78
test74 test75 test76 test77 test78 test79
CFLAGS = -I$(HASHDIR)
#CFLAGS += -DHASH_BLOOM=16
#CFLAGS += -O2
Expand Down
8 changes: 8 additions & 0 deletions tests/test79.ans
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
added 10 100
id 10, tag 100
added 11 101
id 10, tag 100
id 11, tag 101
replaced 11 that had tag 101 with tag 102
id 10, tag 100
id 11, tag 102
57 changes: 57 additions & 0 deletions tests/test79.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#include <stdlib.h>
#include <stdio.h>
#include "uthash.h"

typedef struct hs_t {
int id;
int tag;
UT_hash_handle hh;
} hs_t;


static void pr(hs_t **hdpp){
hs_t *el, *tmp, *hdp = *hdpp;
HASH_ITER(hh, hdp, el, tmp){
printf("id %d, tag %d\n",el->id,el->tag);
}
}

int main(int argc, char *argv[]) {

hs_t *hs_head=NULL, *tmp, *replaced=NULL;

tmp = (hs_t*)malloc(sizeof(hs_t));
tmp->id = 10;
tmp->tag = 100;
HASH_REPLACE_INT(hs_head,id,tmp,replaced);
if(replaced == NULL)
printf("added %d %d\n",tmp->id,tmp->tag);
else
printf("ERROR, ended up replacing a value, replaced: %p\n",replaced);

pr(&hs_head);

tmp = (hs_t*)malloc(sizeof(hs_t));
tmp->id=11;
tmp->tag = 101;
HASH_REPLACE_INT(hs_head,id,tmp,replaced);
if(replaced == NULL)
printf("added %d %d\n",tmp->id,tmp->tag);
else
printf("ERROR, ended up replacing a value, replaced: %p\n",replaced);

pr(&hs_head);

tmp = (hs_t*)malloc(sizeof(hs_t));
tmp->id=11;
tmp->tag = 102;
HASH_REPLACE_INT(hs_head,id,tmp,replaced);
if(replaced == NULL)
printf("ERROR, exected to replace a value with key: %d\n",tmp->id);
else
printf("replaced %d that had tag %d with tag %d\n",tmp->id,replaced->tag,tmp->tag);

pr(&hs_head);

return 0;
}

0 comments on commit c760770

Please sign in to comment.