Sfoglia il codice sorgente

Squashed commit of the following:

commit 9383adf370df9408a2244c53f6ba08b22e995895
Author: Thilo Schulz <[email protected]>
Date:   Tue Jun 28 00:45:11 2016 +0200

    Incorporate Arthur O'Dwyer's suggestions

commit 1494286e16f72b4fa61c4f8195235d038495b3bb
Merge: fa99052 6130b77
Author: Thilo Schulz <[email protected]>
Date:   Mon Jun 27 23:51:46 2016 +0200

    Merge branch 'master' of https://github.com/troydhanson/uthash

    Conflicts:
    	doc/userguide.txt
    	src/uthash.h

commit fa99052d768c0aee2dc2c069eefc507eec85f305
Author: Thilo Schulz <[email protected]>
Date:   Fri Jun 17 14:32:29 2016 +0200

    Better to have no separate target for generation of .gitignore. Check of .gitignore existence and generation of it is always done ad-hoc now.

commit 5cf17157c74fe8648ada71eb15ccd17e7beedf1e
Author: Thilo Schulz <[email protected]>
Date:   Tue Jun 14 22:39:51 2016 +0200

    Uh... just a typo fixed.

commit b0b67951b5460c3af6f366d39a67a5807bc031b5
Author: Thilo Schulz <[email protected]>
Date:   Tue Jun 14 22:28:52 2016 +0200

    Add .gitignore and autogenerate .gitignore in tests/ directory so as to prevent tests from clogging git output

commit e9ec721a19e3c3388b72839d5183166eb0e2aefe
Author: Thilo Schulz <[email protected]>
Date:   Tue Jun 14 22:28:23 2016 +0200

    Shut up a few unused function compiler warnings

commit 682a45a230ea9a40f6e7c808908f68a2bcc575cc
Author: Thilo Schulz <[email protected]>
Date:   Tue Jun 14 22:08:26 2016 +0200

    Update userguide with HASH_SADD*() macro family

commit 9809d8901db6a7be4308887e57ff630e992fc220
Author: Thilo Schulz <[email protected]>
Date:   Tue Jun 14 16:54:19 2016 +0200

    This way round is ANSI-C

commit 5eec55af912aaca665062c493dccf4e0468a3449
Author: Thilo Schulz <[email protected]>
Date:   Tue Jun 14 16:47:22 2016 +0200

    Make sure we conform to ANSI C in variable declaration

commit cdaa14cc6e3dd6e21cdf61736db37f868e7ce343
Author: Thilo Schulz <[email protected]>
Date:   Tue Jun 14 16:36:48 2016 +0200

    Add test case for SADD* macros

commit 24e3ee3373aa5422f8bc4f50b52bbae5f7e4e6b8
Author: Thilo Schulz <[email protected]>
Date:   Tue Jun 14 16:32:50 2016 +0200

    Introduce two separate macros for SADD/ADD as going over checking whether compare function pointer is NULL is cumbersome and prone to throw warnings. Also, this is faster.

commit ae67050122fd24575fd7bf47cb94253365966e1d
Author: Thilo Schulz <[email protected]>
Date:   Tue Jun 14 15:48:46 2016 +0200

    Use proper prototype for compare function

commit b18c6d1dcb98aee23c8935b1f6e29160b4db1a88
Author: Thilo Schulz <[email protected]>
Date:   Tue Jun 14 15:19:31 2016 +0200

    Need to move setting of table pointer as head pointer may change.
    There's no need to set it if the table is newly created, this happens in HASH_MAKE_TABLE() already.

commit d05dc94c5eefa6ea2532b622299b18021a61ccfe
Author: Thilo Schulz <[email protected]>
Date:   Mon Jun 13 19:54:44 2016 +0200

    Remove accidental semicolon

commit 851c803db87b457c3230cb5ec1d985e43a87415b
Author: Thilo Schulz <[email protected]>
Date:   Mon Jun 13 19:45:57 2016 +0200

    - Add HASH_SADD* macro family so new hash table entries can be added to the linked list in an ordered fashion
    - Enclose macro parameters in parenthesis in HASH_REPLACE()
Troy D. Hanson 9 anni fa
parent
commit
650214c076
11 ha cambiato i file con 557 aggiunte e 295 eliminazioni
  1. 3 0
      .gitignore
  2. 220 195
      doc/userguide.html
  3. 50 19
      doc/userguide.txt
  4. 96 56
      src/uthash.h
  5. 8 2
      tests/Makefile
  6. 1 0
      tests/README
  7. 0 5
      tests/test32.c
  8. 0 5
      tests/test34.c
  9. 0 13
      tests/test65.c
  10. 104 0
      tests/test87.ans
  11. 75 0
      tests/test87.c

+ 3 - 0
.gitignore

@@ -0,0 +1,3 @@
+*~
+*.out
+keystat.*

File diff suppressed because it is too large
+ 220 - 195
doc/userguide.html


+ 50 - 19
doc/userguide.txt

@@ -1141,6 +1141,33 @@ always used with the `users_by_name` hash table).
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
 
 
+Sorted insertion of new items
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+If you would like to maintain a sorted hash you have two options. The first
+option is to use the HASH_SRT() macro, which will sort any unordered list in
+'O(n log(n))'. This is the best strategy if you're just filling up a hash
+table with items in random order with a single final HASH_SRT() operation
+when all is done. Obviously, this won't do what you want if you need
+the list to be in an ordered state at times between insertion of
+items. You can use HASH_SRT() after every insertion operation, but that will
+yield a computational complexity of 'O(n^2 log(n)'.
+
+The second route you can take is via the sorted add macros.
+The HASH_SADD*() macros work just like their HASH_ADD*() counterparts, but
+with an additional compare function argument:
+
+  int name_sort(struct my_struct *a, struct my_struct *b) {
+    return strcmp(a->name,b->name);
+  }
+
+  HASH_SADD_KEYPTR(hh, items, &item->name, strlen(item->name), item, name_sort);
+
+New items are sorted at insertion time in 'O(n)', thus resulting in a
+total computational complexity of 'O(n^2)' for the creation of the hash
+table with all items.
+For sorted add to work, the list must be in an ordered state before
+insertion of the new item.
+
 Several sort orders
 Several sort orders
 ~~~~~~~~~~~~~~~~~~~
 ~~~~~~~~~~~~~~~~~~~
 It comes as no surprise that two hash tables can have different sort orders, but
 It comes as no surprise that two hash tables can have different sort orders, but
@@ -1500,7 +1527,7 @@ ineffective at reducing the chain lengths.
 Imagine a very bad hash function which always puts every item in bucket 0. No
 Imagine a very bad hash function which always puts every item in bucket 0. No
 matter how many times the number of buckets is doubled, the chain length of
 matter how many times the number of buckets is doubled, the chain length of
 bucket 0 stays the same. In a situation like this, the best behavior is to 
 bucket 0 stays the same. In a situation like this, the best behavior is to 
-stop expanding, and accept O(n) lookup performance. This is what uthash
+stop expanding, and accept 'O(n)' lookup performance. This is what uthash
 does. It degrades gracefully if the hash function is ill-suited to the keys.
 does. It degrades gracefully if the hash function is ill-suited to the keys.
 
 
 If two consecutive bucket expansions yield `ideal%` values below 50%, uthash
 If two consecutive bucket expansions yield `ideal%` values below 50%, uthash
@@ -1712,23 +1739,27 @@ than `hh`, or if your key's data type isn't `int` or `char[]`.
 .General macros
 .General macros
 [width="90%",cols="10m,30m",grid="none",options="header"]
 [width="90%",cols="10m,30m",grid="none",options="header"]
 |===============================================================================
 |===============================================================================
-|macro                        | arguments
-|HASH_ADD                     | (hh_name, head, keyfield_name, key_len, item_ptr)
-|HASH_ADD_BY_HASH_VALUE       | (hh_name, head, keyfield_name, key_len, key_hash, item_ptr)
-|HASH_ADD_KEYPTR              | (hh_name, head, key_ptr, key_len, item_ptr)
-|HASH_ADD_KEYPTR_BY_HASH_VALUE| (hh_name, head, key_ptr, key_len, key_hash, item_ptr)
-|HASH_REPLACE                 | (hh_name, head, keyfield_name, key_len, item_ptr, replaced_item_ptr)
-|HASH_REPLACE_BY_HASH_VALUE   | (hh_name, head, keyfield_name, key_len, key_hash, item_ptr, replaced_item_ptr)
-|HASH_FIND                    | (hh_name, head, key_ptr, key_len, item_ptr)
-|HASH_FIND_BY_HASH_VALUE      | (hh_name, head, key_ptr, key_len, key_hash, item_ptr)
-|HASH_DELETE                  | (hh_name, head, item_ptr)
-|HASH_VALUE                   | (key_ptr, key_len, key_hash)
-|HASH_SRT                     | (hh_name, head, cmp)
-|HASH_CNT                     | (hh_name, head)
-|HASH_CLEAR                   | (hh_name, head)
-|HASH_SELECT                  | (dst_hh_name, dst_head, src_hh_name, src_head, condition)
-|HASH_ITER                    | (hh_name, head, item_ptr, tmp_item_ptr)
-|HASH_OVERHEAD                | (hh_name, head)
+|macro                    | arguments
+|HASH_ADD                 | (hh_name, head, keyfield_name, key_len, item_ptr)
+|HASH_ADD_BY_HVAL         | (hh_name, head, keyfield_name, key_len, key_hash, item_ptr)
+|HASH_ADD_KEYPTR          | (hh_name, head, key_ptr, key_len, item_ptr)
+|HASH_ADD_KEYPTR_BY_HVAL  | (hh_name, head, key_ptr, key_len, key_hash, item_ptr)
+|HASH_SADD                | (hh_name, head, keyfield_name, key_len, item_ptr, cmp)
+|HASH_SADD_BY_HVAL        | (hh_name, head, keyfield_name, key_len, key_hash, item_ptr, cmp)
+|HASH_SADD_KEYPTR         | (hh_name, head, key_ptr, key_len, item_ptr, cmp)
+|HASH_SADD_KEYPTR_BY_HVAL | (hh_name, head, key_ptr, key_len, key_hash, item_ptr, cmp)
+|HASH_REPLACE             | (hh_name, head, keyfield_name, key_len, item_ptr, replaced_item_ptr)
+|HASH_REPLACE_BY_HVAL     | (hh_name, head, keyfield_name, key_len, key_hash, item_ptr, replaced_item_ptr)
+|HASH_FIND                | (hh_name, head, key_ptr, key_len, item_ptr)
+|HASH_FIND_BY_HVAL        | (hh_name, head, key_ptr, key_len, key_hash, item_ptr)
+|HASH_DELETE              | (hh_name, head, item_ptr)
+|HASH_VALUE               | (key_ptr, key_len, key_hash)
+|HASH_SRT                 | (hh_name, head, cmp)
+|HASH_CNT                 | (hh_name, head)
+|HASH_CLEAR               | (hh_name, head)
+|HASH_SELECT              | (dst_hh_name, dst_head, src_hh_name, src_head, condition)
+|HASH_ITER                | (hh_name, head, item_ptr, tmp_item_ptr)
+|HASH_OVERHEAD            | (hh_name, head)
 |===============================================================================
 |===============================================================================
 
 
 [NOTE]
 [NOTE]
@@ -1759,7 +1790,7 @@ key_ptr::
     `HASH_ADD_KEYPTR`, this is the address of the key of the item being added.
     `HASH_ADD_KEYPTR`, this is the address of the key of the item being added.
 key_hash::
 key_hash::
     the hash value of the provided key. This is an input parameter for the
     the hash value of the provided key. This is an input parameter for the
-    `..._BY_HASH_VALUE` macros, and an output parameter for `HASH_VALUE`.
+    `..._BY_HVAL` macros, and an output parameter for `HASH_VALUE`.
     Reusing a cached hash value can be a performance optimization if
     Reusing a cached hash value can be a performance optimization if
     you're going to do repeated lookups for the same key.
     you're going to do repeated lookups for the same key.
 item_ptr::
 item_ptr::

+ 96 - 56
src/uthash.h

@@ -102,6 +102,8 @@ typedef unsigned char uint8_t;
 
 
 /* calculate the element whose hash handle address is hhe */
 /* calculate the element whose hash handle address is hhe */
 #define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho)))
 #define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho)))
+/* calculate the hash handle from element address elp */
+#define HH_FROM_ELMT(tbl,elp) ((UT_hash_handle *)(((char*)(elp)) + ((tbl)->hho)))
 
 
 #define HASH_VALUE(keyptr,keylen,hashv)                                          \
 #define HASH_VALUE(keyptr,keylen,hashv)                                          \
 do {                                                                             \
 do {                                                                             \
@@ -122,7 +124,7 @@ do {
   }                                                                              \
   }                                                                              \
 } while (0)
 } while (0)
 
 
-#define HASH_FIND_BY_HASH_VALUE(hh,head,keyptr,keylen,_hf_hashv,out)             \
+#define HASH_FIND_BY_HVAL(hh,head,keyptr,keylen,_hf_hashv,out)                   \
 do {                                                                             \
 do {                                                                             \
   out=NULL;                                                                      \
   out=NULL;                                                                      \
   if (head != NULL) {                                                            \
   if (head != NULL) {                                                            \
@@ -188,79 +190,117 @@ do {
   (head)->hh.tbl->signature = HASH_SIGNATURE;                                    \
   (head)->hh.tbl->signature = HASH_SIGNATURE;                                    \
 } while(0)
 } while(0)
 
 
-#define HASH_ADD(hh,head,fieldname,keylen_in,add)                                \
-        HASH_ADD_KEYPTR(hh,head,&((add)->fieldname),keylen_in,add)
+#define HASH_SADD(hh,head,fieldname,keylen_in,add,cmpfcn)                        \
+    HASH_SADD_KEYPTR(hh,(head),&((add)->fieldname),(keylen_in),(add),cmpfcn)
+
+#define HASH_SADD_BY_HVAL(hh,head,fieldname,keylen_in,hashval,add,cmpfcn)        \
+    HASH_SADD_KEYPTR_BY_HVAL(hh,(head),&((add)->fieldname),                      \
+                             (keylen_in),(add),cmpfcn)
 
 
-#define HASH_ADD_BY_HASH_VALUE(hh,head,fieldname,keylen_in,hashval,add)          \
-        HASH_ADD_KEYPTR_BY_HASH_VALUE(hh,head,&((add)->fieldname),keylen_in,hashval,add)
+#define HASH_ADD_BY_HVAL(hh,head,fieldname,keylen_in,hashval,add)                \
+        HASH_ADD_KEYPTR_BY_HVAL(hh,head,&((add)->fieldname),                     \
+                                      keylen_in,hashval,add)
 
 
 #define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced)                   \
 #define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced)                   \
 do {                                                                             \
 do {                                                                             \
   unsigned _hr_hashv;                                                            \
   unsigned _hr_hashv;                                                            \
-  HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv);                         \
-  HASH_REPLACE_BY_HASH_VALUE(hh,head,fieldname,keylen_in,_hr_hashv,add,replaced);\
+  HASH_VALUE(&((add)->fieldname), (keylen_in), _hr_hashv);                       \
+  HASH_REPLACE_BY_HVAL(hh,(head),fieldname,(keylen_in),                          \
+                             _hr_hashv,(add),(replaced));                        \
 } while(0)
 } while(0)
 
 
-#define HASH_REPLACE_BY_HASH_VALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \
+#define HASH_REPLACE_BY_HVAL(hh,head,fieldname,keylen_in,hashval,add,replaced)   \
 do {                                                                             \
 do {                                                                             \
   replaced=NULL;                                                                 \
   replaced=NULL;                                                                 \
-  HASH_FIND_BY_HASH_VALUE(hh,head,&((add)->fieldname),keylen_in,hashval,replaced); \
-  if (replaced!=NULL) {                                                          \
-     HASH_DELETE(hh,head,replaced);                                              \
+  HASH_FIND_BY_HVAL(hh,(head),&((add)->fieldname),                               \
+                          (keylen_in),(hashval),(replaced));                     \
+  if ((replaced)!=NULL) {                                                        \
+     HASH_DELETE(hh,(head),(replaced));                                          \
   }                                                                              \
   }                                                                              \
-  HASH_ADD_BY_HASH_VALUE(hh,head,fieldname,keylen_in,hashval,add);               \
+  HASH_ADD_BY_HVAL(hh,(head),fieldname,(keylen_in),(hashval),(add));             \
 } while(0)
 } while(0)
 
 
-#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add)                            \
+#define HASH_APPEND_LIST(hh, head, add)                                          \
 do {                                                                             \
 do {                                                                             \
- unsigned _ha_bkt;                                                               \
- (add)->hh.next = NULL;                                                          \
- (add)->hh.key = (char*)(keyptr);                                                \
- (add)->hh.keylen = (unsigned)(keylen_in);                                       \
- if (!(head)) {                                                                  \
-    head = (add);                                                                \
-    (head)->hh.prev = NULL;                                                      \
-    HASH_MAKE_TABLE(hh,head);                                                    \
- } else {                                                                        \
-    (head)->hh.tbl->tail->next = (add);                                          \
-    (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail);         \
-    (head)->hh.tbl->tail = &((add)->hh);                                         \
- }                                                                               \
- (head)->hh.tbl->num_items++;                                                    \
- (add)->hh.tbl = (head)->hh.tbl;                                                 \
- HASH_FCN(keyptr, keylen_in, (add)->hh.hashv);                                   \
- HASH_TO_BKT((add)->hh.hashv, (head)->hh.tbl->num_buckets, _ha_bkt);             \
- HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh);                   \
- HASH_BLOOM_ADD((head)->hh.tbl,(add)->hh.hashv);                                 \
- HASH_EMIT_KEY(hh,head,keyptr,keylen_in);                                        \
- HASH_FSCK(hh,head);                                                             \
+  (add)->hh.next = NULL;                                                         \
+  (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail);           \
+  (head)->hh.tbl->tail->next = (add);                                            \
+  (head)->hh.tbl->tail = &((add)->hh);                                           \
 } while(0)
 } while(0)
 
 
-#define HASH_ADD_KEYPTR_BY_HASH_VALUE(hh,head,keyptr,keylen_in,hashval,add)      \
-do {                                                                             \
- unsigned _ha_bkt;                                                               \
- (add)->hh.next = NULL;                                                          \
- (add)->hh.key = (char*)(keyptr);                                                \
- (add)->hh.keylen = (unsigned)(keylen_in);                                       \
- if (!(head)) {                                                                  \
-    head = (add);                                                                \
-    (head)->hh.prev = NULL;                                                      \
+
+#define HASH_ADD_PROLOGUE(hh,head,keyptr,keylen_in,add)                          \
+  add->hh.key = (char*) keyptr;                                                  \
+  add->hh.keylen = (unsigned) keylen_in;                                         \
+  if (!head) {                                                                   \
+    add->hh.next = NULL;                                                         \
+    add->hh.prev = NULL;                                                         \
+    head = add;                                                                  \
     HASH_MAKE_TABLE(hh,head);                                                    \
     HASH_MAKE_TABLE(hh,head);                                                    \
- } else {                                                                        \
-    (head)->hh.tbl->tail->next = (add);                                          \
-    (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail);         \
-    (head)->hh.tbl->tail = &((add)->hh);                                         \
- }                                                                               \
- (head)->hh.tbl->num_items++;                                                    \
- (add)->hh.tbl = (head)->hh.tbl;                                                 \
- (add)->hh.hashv = hashval;                                                      \
- HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt);                     \
- HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh);                   \
- HASH_BLOOM_ADD((head)->hh.tbl,hashval);                                         \
- HASH_EMIT_KEY(hh,head,keyptr,keylen_in);                                        \
- HASH_FSCK(hh,head);                                                             \
+  } else {
+
+#define HASH_ADD_EPILOGUE(hh,head,keyptr,keylen_in,hashval,add)                  \
+    HASH_APPEND_LIST(hh, head, add);                                             \
+  }                                                                              \
+  head->hh.tbl->num_items++;                                                     \
+  HASH_TO_BKT((hashval), (head)->hh.tbl->num_buckets, _ha_bkt);                  \
+  HASH_ADD_TO_BKT(head->hh.tbl->buckets[_ha_bkt],&add->hh);                      \
+  HASH_BLOOM_ADD(head->hh.tbl,add->hh.hashv);                                    \
+  HASH_EMIT_KEY(hh,head,keyptr,keylen_in);                                       \
+  HASH_FSCK(hh,head);                                                            \
 } while(0)
 } while(0)
 
 
+#define HASH_GHV_DIRECT(hh, add, hashval)                                        \
+  (add)->hh.hashv = (hashval)
+#define HASH_GHV_COMPUTE(hh, keyptr, keylen_in, add)                             \
+  HASH_FCN((keyptr), (keylen_in), (add)->hh.hashv)
+
+#define HASH_ADD_SORTED(hh,head,keyptr,keylen_in,hashval,add,cmpfcn)             \
+    HASH_ADD_PROLOGUE(hh,(head),(keyptr),(keylen_in),(add))                      \
+    struct UT_hash_handle *iter = &(head)->hh;                                   \
+    (add)->hh.tbl = (head)->hh.tbl;                                              \
+    do {                                                                         \
+      if(cmpfcn(DECLTYPE(head) ELMT_FROM_HH((head)->hh.tbl, iter), add) > 0)     \
+        break;                                                                   \
+    } while((iter = iter->next));                                                \
+    if(iter) {                                                                   \
+      (add)->hh.next = iter;                                                     \
+      if(((add)->hh.prev = iter->prev))                                          \
+        HH_FROM_ELMT((head)->hh.tbl, iter->prev)->next = add;                    \
+      else                                                                       \
+        (head) = (add);                                                          \
+      iter->prev = add;                                                          \
+    }                                                                            \
+    else                                                                         \
+      HASH_ADD_EPILOGUE(hh,(head),(keyptr),(keylen_in),(hashval),(add))
+
+#define HASH_SADD_KEYPTR_BY_HVAL(hh,head,keyptr,keylen_in,hashval,add,cmpfcn)    \
+do { unsigned _ha_bkt;                                                           \
+    HASH_GHV_DIRECT(hh, add, hashval);                                           \
+    HASH_ADD_SORTED(hh,head,keyptr,keylen_in,hashval,add,cmpfcn)                 \
+
+#define HASH_SADD_KEYPTR(hh,head,keyptr,keylen_in,add,cmpfcn)                    \
+do { unsigned _ha_bkt;                                                           \
+    HASH_GHV_COMPUTE(hh, keyptr, keylen_in, add);                                \
+    HASH_ADD_SORTED(hh,head,keyptr,keylen_in,(add)->hh.hashv,add,cmpfcn)
+
+#define HASH_ADD(hh,head,fieldname,keylen_in,add)                                \
+    HASH_ADD_KEYPTR(hh,(head),&((add)->fieldname),(keylen_in),(add))
+
+#define HASH_ADD_KEYPTR_BY_HVAL(hh,head,keyptr,keylen_in,hashval,add)            \
+do { unsigned _ha_bkt;                                                           \
+    HASH_GHV_DIRECT(hh, add, hashval);                                           \
+    HASH_ADD_PROLOGUE(hh,(head),(keyptr),(keylen_in),(add))                      \
+    (add)->hh.tbl = (head)->hh.tbl;                                              \
+    HASH_ADD_EPILOGUE(hh,(head),(keyptr),(keylen_in),hashval,(add))
+
+#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add)                            \
+do { unsigned _ha_bkt;                                                           \
+    HASH_GHV_COMPUTE(hh, keyptr, keylen_in, add);                                \
+    HASH_ADD_PROLOGUE(hh,(head),(keyptr),(keylen_in),(add))                      \
+    (add)->hh.tbl = (head)->hh.tbl;                                              \
+    HASH_ADD_EPILOGUE(hh,(head),(keyptr),(keylen_in),(add)->hh.hashv,(add))
+
 #define HASH_TO_BKT( hashv, num_bkts, bkt )                                      \
 #define HASH_TO_BKT( hashv, num_bkts, bkt )                                      \
 do {                                                                             \
 do {                                                                             \
   bkt = ((hashv) & ((num_bkts) - 1U));                                           \
   bkt = ((hashv) & ((num_bkts) - 1U));                                           \

+ 8 - 2
tests/Makefile

@@ -13,7 +13,7 @@ PROGS = test1 test2 test3 test4 test5 test6 test7 test8 test9   \
         test58 test59 test60 test61 test62 test63 test64 test65 \
         test58 test59 test60 test61 test62 test63 test64 test65 \
         test66 test67 test68 test69 test70 test71 test72 test73 \
         test66 test67 test68 test69 test70 test71 test72 test73 \
         test74 test75 test76 test77 test78 test79 test80 test81 \
         test74 test75 test76 test77 test78 test79 test80 test81 \
-        test82 test83 test84 test85 test86
+        test82 test83 test84 test85 test86 test87
 CFLAGS += -I$(HASHDIR)
 CFLAGS += -I$(HASHDIR)
 #CFLAGS += -DHASH_BLOOM=16
 #CFLAGS += -DHASH_BLOOM=16
 #CFLAGS += -O2
 #CFLAGS += -O2
@@ -71,6 +71,9 @@ all: $(PROGS) $(UTILS) $(PLAT_UTILS) $(FUNCS) $(SPECIAL_FUNCS) $(TEST_TARGET)
 
 
 tests_only: $(PROGS) $(TEST_TARGET)
 tests_only: $(PROGS) $(TEST_TARGET)
 
 
+GITIGN = .gitignore
+MKGITIGN = [ -f "$(GITIGN)" ] || echo "$(GITIGN)" > $(GITIGN); grep -q '^\$@$$' $(GITIGN) || echo "$@" >> $(GITIGN)
+
 debug:
 debug:
 	$(MAKE) all HASH_DEBUG=1
 	$(MAKE) all HASH_DEBUG=1
 
 
@@ -85,12 +88,15 @@ example: example.c $(HASHDIR)/uthash.h
 
 
 $(PROGS) $(UTILS) : $(HASHDIR)/uthash.h
 $(PROGS) $(UTILS) : $(HASHDIR)/uthash.h
 	$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $(@).c
 	$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $(@).c
+	@$(MKGITIGN)
 
 
 hashscan : $(HASHDIR)/uthash.h
 hashscan : $(HASHDIR)/uthash.h
 	$(CC) $(CPPFLAGS) $(CFLAGS) $(MUR_CFLAGS) $(LDFLAGS) -o $@ $(@).c
 	$(CC) $(CPPFLAGS) $(CFLAGS) $(MUR_CFLAGS) $(LDFLAGS) -o $@ $(@).c
+	@$(MKGITIGN)
 
 
 sleep_test : $(HASHDIR)/uthash.h 
 sleep_test : $(HASHDIR)/uthash.h 
 	$(CC) $(CPPFLAGS) $(CFLAGS) -DHASH_BLOOM=16 $(LDFLAGS) -o $@ $(@).c
 	$(CC) $(CPPFLAGS) $(CFLAGS) -DHASH_BLOOM=16 $(LDFLAGS) -o $@ $(@).c
+	@$(MKGITIGN)
 
 
 $(FUNCS) : $(HASHDIR)/uthash.h
 $(FUNCS) : $(HASHDIR)/uthash.h
 	$(CC) $(CPPFLAGS) $(CFLAGS) -DHASH_FUNCTION=HASH_$@ \
 	$(CC) $(CPPFLAGS) $(CFLAGS) -DHASH_FUNCTION=HASH_$@ \
@@ -112,5 +118,5 @@ astyle:
 .PHONY: clean astyle
 .PHONY: clean astyle
 
 
 clean:	
 clean:	
-	rm -f $(UTILS) $(PLAT_UTILS) $(PROGS) test*.out keystat.??? example *.exe
+	rm -f $(UTILS) $(PLAT_UTILS) $(PROGS) test*.out keystat.??? example *.exe $(GITIGN)
 	rm -rf *.dSYM
 	rm -rf *.dSYM

+ 1 - 0
tests/README

@@ -88,6 +88,7 @@ test83: test HASH_REPLACE_STR with char[] key
 test84: test HASH_REPLACE_STR with char* key
 test84: test HASH_REPLACE_STR with char* key
 test85: test HASH_OVERHEAD on null and non null hash
 test85: test HASH_OVERHEAD on null and non null hash
 test86: test *_APPEND_ELEM / *_PREPEND_ELEM (Thilo Schulz)
 test86: test *_APPEND_ELEM / *_PREPEND_ELEM (Thilo Schulz)
+test87: test HASH_SADD*() macro family (Thilo Schulz)
 
 
 Other Make targets
 Other Make targets
 ================================================================================
 ================================================================================

+ 0 - 5
tests/test32.c

@@ -10,11 +10,6 @@ typedef struct el {
     struct el *next, *prev;
     struct el *next, *prev;
 } el;
 } el;
 
 
-static int namecmp(el *a, el *b)
-{
-    return strcmp(a->bname,b->bname);
-}
-
 int main(int argc, char *argv[])
 int main(int argc, char *argv[])
 {
 {
     el *name, *tmp;
     el *name, *tmp;

+ 0 - 5
tests/test34.c

@@ -10,11 +10,6 @@ typedef struct el {
     struct el *next, *prev;
     struct el *next, *prev;
 } el;
 } el;
 
 
-static int namecmp(el *a, el *b)
-{
-    return strcmp(a->bname,b->bname);
-}
-
 int main(int argc, char *argv[])
 int main(int argc, char *argv[])
 {
 {
     el *name, *tmp;
     el *name, *tmp;

+ 0 - 13
tests/test65.c

@@ -16,19 +16,6 @@ struct CacheEntry {
 };
 };
 struct CacheEntry *cache = NULL;
 struct CacheEntry *cache = NULL;
 
 
-static char * /*value*/ find_in_cache(const char *key)
-{
-    struct CacheEntry *entry;
-    HASH_FIND_STR(cache, key, entry);
-    if (entry != NULL) {
-        // remove it (so the subsequent add will throw it on the front of the list)
-        HASH_DELETE(hh, cache, entry);
-        HASH_ADD_KEYPTR(hh, cache, entry->key, strlen(entry->key), entry);
-        return entry->value;
-    }
-    return NULL;
-}
-
 static void add_to_cache(const char *key, const char *value)
 static void add_to_cache(const char *key, const char *value)
 {
 {
     struct CacheEntry *entry, *tmp_entry;
     struct CacheEntry *entry, *tmp_entry;

+ 104 - 0
tests/test87.ans

@@ -0,0 +1,104 @@
+1: muh3
+2: muh1
+3: muh5
+5: muh6
+6: muh7
+6: muh9
+8: muh2
+8: muh4
+9: muh10
+10: muh11
+15: muh8
+43: muh12
+###
+1: muh3
+2: muh1
+3: muh5
+5: muh6
+6: muh7
+6: muh9
+8: muh2
+8: muh4
+9: muh10
+10: muh11
+15: muh8
+43: muh12
+###
+1: muh3
+2: muh1
+3: muh5
+5: muh6
+6: muh7
+6: muh9
+8: muh2
+8: muh4
+9: muh10
+10: muh11
+15: muh8
+###
+1: muh3
+3: muh5
+5: muh6
+6: muh7
+6: muh9
+8: muh2
+8: muh4
+9: muh10
+10: muh11
+15: muh8
+###
+1: muh3
+3: muh5
+5: muh6
+6: muh9
+8: muh2
+8: muh4
+9: muh10
+10: muh11
+15: muh8
+###
+3: muh5
+5: muh6
+6: muh9
+8: muh2
+8: muh4
+9: muh10
+10: muh11
+15: muh8
+###
+3: muh5
+5: muh6
+8: muh2
+8: muh4
+9: muh10
+10: muh11
+15: muh8
+###
+3: muh5
+5: muh6
+8: muh4
+9: muh10
+10: muh11
+15: muh8
+###
+3: muh5
+5: muh6
+8: muh4
+9: muh10
+15: muh8
+###
+3: muh5
+5: muh6
+9: muh10
+15: muh8
+###
+3: muh5
+9: muh10
+15: muh8
+###
+9: muh10
+15: muh8
+###
+9: muh10
+###
+###

+ 75 - 0
tests/test87.c

@@ -0,0 +1,75 @@
+#include <stdio.h>
+#include <uthash.h>
+
+typedef struct
+{
+  UT_hash_handle hh;
+
+  char name[32];
+  int weight;
+} hstruct_t;
+
+#define ARRAY_LEN(x) (sizeof(x) / sizeof(*(x)))
+hstruct_t tst[] = {{{}, "muh1", 2}, {{}, "muh2", 8}, {{}, "muh3", 1}, {{}, "muh4", 8}, {{}, "muh5", 3}, {{}, "muh6", 5},
+  {{}, "muh7", 6}, {{}, "muh8", 15}, {{}, "muh9", 6}, {{}, "muh10", 9}, {{}, "muh11", 10}, {{}, "muh12", 43}};
+hstruct_t *hTable;
+
+static int cmpfunc(hstruct_t *s1, hstruct_t *s2)
+{
+  if(s1->weight < s2->weight)
+    return -1;
+  else if(s1->weight > s2->weight)
+    return 1;
+  else
+    return 0;
+}
+
+void printtable(void)
+{
+  hstruct_t *search;
+
+  for(search = hTable; search; search = search->hh.next)
+  {
+    printf("%d: %s\n", search->weight, search->name);
+  }
+  
+  printf("###\n");
+}
+
+void delitem(const char *name)
+{
+  hstruct_t *item;
+  
+  HASH_FIND_STR(hTable, name, item);
+  HASH_DEL(hTable, item);
+  printtable();
+}
+
+int main()
+{
+  int index;
+
+  for(index = 0; index < ARRAY_LEN(tst); index++)
+  {
+    HASH_SADD(hh, hTable, name[0], strlen(tst[index].name), &tst[index], cmpfunc);
+  } 
+
+  printtable();
+  HASH_SORT(hTable, cmpfunc);
+  printtable();
+
+  delitem("muh12");
+  delitem("muh1");
+  delitem("muh7");
+  delitem("muh3");
+  delitem("muh9");
+  delitem("muh2");
+  delitem("muh11");
+  delitem("muh4");
+  delitem("muh6");
+  delitem("muh5");
+  delitem("muh8");
+  delitem("muh10");
+
+  return 0;
+}

Some files were not shown because too many files changed in this diff