uthash 是C的比较优秀的开源代码,它实现了常见的hash操作函数,例如查找、插入、删除等。该套开源代码采用宏的方式实现hash函数的相关功能,支持C语言的任意数据结构最为key值,甚至可以采用多个值作为key,无论是自定义的struct还是基本数据类型,需要注意的是不同类型的key其操作接口方式略有不通。

使用uthash代码时只需要包含头文件"uthash.h"即可。由于该代码采用宏的方式实现,所有的实现代码都在uthash.h文件中,因此只需要在自己的代码中包含该头文件即可。

源代码地址:

https://github.com/troydhanson/uthash

源码如下:

 /*
 Copyright (c) 2003-2016, Troy D. Hanson     http://troydhanson.github.com/uthash/
 All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are met:

     * Redistributions of source code must retain the above copyright
       notice, this list of conditions and the following disclaimer.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

 #ifndef UTHASH_H
 #define UTHASH_H

 #define UTHASH_VERSION 2.0.1

 #include <string.h>   /* memcmp,strlen */
 #include <stddef.h>   /* ptrdiff_t */
 #include <stdlib.h>   /* exit() */

 /* These macros use decltype or the earlier __typeof GNU extension.
    As decltype is only available in newer compilers (VS2010 or gcc 4.3+
    when compiling c++ source) this code uses whatever method is needed
    or, for VS2008 where neither is available, uses casting workarounds. */
 #if defined(_MSC_VER)   /* MS compiler */
 #if _MSC_VER >= 1600 && defined(__cplusplus)  /* VS2010 or newer in C++ mode */
 #define DECLTYPE(x) (decltype(x))
 #else                   /* VS2008 or older (or VS2010 in C mode) */
 #define NO_DECLTYPE
 #define DECLTYPE(x)
 #endif
 #elif defined(__BORLANDC__) || defined(__LCC__) || defined(__WATCOMC__)
 #define NO_DECLTYPE
 #define DECLTYPE(x)
 #else                   /* GNU, Sun and other compilers */
 #define DECLTYPE(x) (__typeof(x))
 #endif

 #ifdef NO_DECLTYPE
 #define DECLTYPE_ASSIGN(dst,src)                                                 \
 do {                                                                             \
   char **_da_dst = (char**)(&(dst));                                             \
   *_da_dst = (char*)(src);                                                       \
 } )
 #else
 #define DECLTYPE_ASSIGN(dst,src)                                                 \
 do {                                                                             \
   (dst) = DECLTYPE(dst)(src);                                                    \
 } )
 #endif

 /* a number of the hash function use uint32_t which isn't defined on Pre VS2010 */
 #if defined(_WIN32)
 #if defined(_MSC_VER) && _MSC_VER >= 1600
 #include <stdint.h>
 #elif defined(__WATCOMC__) || defined(__MINGW32__) || defined(__CYGWIN__)
 #include <stdint.h>
 #else
 typedef unsigned int uint32_t;
 typedef unsigned char uint8_t;
 #endif
 #elif defined(__GNUC__) && !defined(__VXWORKS__)
 #include <stdint.h>
 #else
 typedef unsigned int uint32_t;
 typedef unsigned char uint8_t;
 #endif

 #ifndef uthash_fatal
 #define uthash_fatal(msg) exit(-1)        /* fatal error (out of memory,etc) */
 #endif
 #ifndef uthash_malloc
 #define uthash_malloc(sz) malloc(sz)      /* malloc fcn                      */
 #endif
 #ifndef uthash_free
 #define uthash_free(ptr,sz) free(ptr)     /* free fcn                        */
 #endif
 #ifndef uthash_strlen
 #define uthash_strlen(s) strlen(s)
 #endif
 #ifndef uthash_memcmp
 #define uthash_memcmp(a,b,n) memcmp(a,b,n)
 #endif

 #ifndef uthash_noexpand_fyi
 #define uthash_noexpand_fyi(tbl)          /* can be defined to log noexpand  */
 #endif
 #ifndef uthash_expand_fyi
 #define uthash_expand_fyi(tbl)            /* can be defined to log expands   */
 #endif

 /* initial number of buckets */
 #define HASH_INITIAL_NUM_BUCKETS 32U     /* initial number of buckets        */
 #define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets */
 #define HASH_BKT_CAPACITY_THRESH 10U     /* expand when bucket count reaches */

 /* calculate the element whose hash handle address is hhp */
 #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)                                          \
 do {                                                                             \
   HASH_FCN(keyptr, keylen, hashv);                                               \
 } )

 #define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out)                 \
 do {                                                                             \
   (out) = NULL;                                                                  \
   if (head) {                                                                    \
     unsigned _hf_bkt;                                                            \
     HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt);                  \
     ) {                         \
       HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], keyptr, keylen, hashval, out); \
     }                                                                            \
   }                                                                              \
 } )

 #define HASH_FIND(hh,head,keyptr,keylen,out)                                     \
 do {                                                                             \
   unsigned _hf_hashv;                                                            \
   HASH_VALUE(keyptr, keylen, _hf_hashv);                                         \
   HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out);               \
 } )

 #ifdef HASH_BLOOM
 #define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM)
 #define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL)
 #define HASH_BLOOM_MAKE(tbl)                                                     \
 do {                                                                             \
   (tbl)->bloom_nbits = HASH_BLOOM;                                               \
   (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN);                 \
   if (!((tbl)->bloom_bv))  { uthash_fatal( "out of memory"); }                   \
   memset((tbl)->bloom_bv, , HASH_BLOOM_BYTELEN);                                \
   (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE;                                       \
 } )

 #define HASH_BLOOM_FREE(tbl)                                                     \
 do {                                                                             \
   uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN);                              \
 } )

 #define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8U] |= (1U << ((idx)%8U)))
 #define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8U] & (1U << ((idx)%8U)))

 #define HASH_BLOOM_ADD(tbl,hashv)                                                \
   HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1U)))

 #define HASH_BLOOM_TEST(tbl,hashv)                                               \
   HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1U)))

 #else
 #define HASH_BLOOM_MAKE(tbl)
 #define HASH_BLOOM_FREE(tbl)
 #define HASH_BLOOM_ADD(tbl,hashv)
 #define HASH_BLOOM_TEST(tbl,hashv) (1)
 #define HASH_BLOOM_BYTELEN 0U
 #endif

 #define HASH_MAKE_TABLE(hh,head)                                                 \
 do {                                                                             \
   (head)->hh.tbl = (UT_hash_table*)uthash_malloc(                                \
                   sizeof(UT_hash_table));                                        \
   if (!((head)->hh.tbl))  { uthash_fatal( "out of memory"); }                    \
   memset((head)->hh.tbl, , sizeof(UT_hash_table));                              \
   (head)->hh.tbl->tail = &((head)->hh);                                          \
   (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS;                        \
   (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2;              \
   (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head);                    \
   (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc(                      \
           HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket));               \
   if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); }             \
   memset((head)->hh.tbl->buckets, ,                                             \
           HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket));               \
   HASH_BLOOM_MAKE((head)->hh.tbl);                                               \
   (head)->hh.tbl->signature = HASH_SIGNATURE;                                    \
 } )

 #define HASH_REPLACE_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,replaced,cmpfcn) \
 do {                                                                             \
   (replaced) = NULL;                                                             \
   HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \
   if (replaced) {                                                                \
      HASH_DELETE(hh, head, replaced);                                            \
   }                                                                              \
   HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn); \
 } )

 #define HASH_REPLACE_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \
 do {                                                                             \
   (replaced) = NULL;                                                             \
   HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \
   if (replaced) {                                                                \
      HASH_DELETE(hh, head, replaced);                                            \
   }                                                                              \
   HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add); \
 } )

 #define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced)                   \
 do {                                                                             \
   unsigned _hr_hashv;                                                            \
   HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv);                         \
   HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced); \
 } )

 #define HASH_REPLACE_INORDER(hh,head,fieldname,keylen_in,add,replaced,cmpfcn)    \
 do {                                                                             \
   unsigned _hr_hashv;                                                            \
   HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv);                         \
   HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced, cmpfcn); \
 } )

 #define HASH_APPEND_LIST(hh, head, add)                                          \
 do {                                                                             \
   (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);                                           \
 } )

 #define HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh,head,keyptr,keylen_in,hashval,add,cmpfcn) \
 do {                                                                             \
   unsigned _ha_bkt;                                                              \
   (add)->hh.hashv = (hashval);                                                   \
   (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);                                                   \
   } else {                                                                       \
     struct UT_hash_handle *_hs_iter = &(head)->hh;                               \
     (add)->hh.tbl = (head)->hh.tbl;                                              \
     do {                                                                         \
       ) \
         break;                                                                   \
     } while ((_hs_iter = _hs_iter->next));                                       \
     if (_hs_iter) {                                                              \
       (add)->hh.next = _hs_iter;                                                 \
       if (((add)->hh.prev = _hs_iter->prev)) {                                   \
         HH_FROM_ELMT((head)->hh.tbl, _hs_iter->prev)->next = (add);              \
       } else {                                                                   \
         (head) = (add);                                                          \
       }                                                                          \
       _hs_iter->prev = (add);                                                    \
     } else {                                                                     \
       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, hashval);                                       \
   HASH_EMIT_KEY(hh, head, keyptr, keylen_in);                                    \
   HASH_FSCK(hh, head);                                                           \
 } )

 #define HASH_ADD_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,cmpfcn)             \
 do {                                                                             \
   unsigned _hs_hashv;                                                            \
   HASH_VALUE(keyptr, keylen_in, _hs_hashv);                                      \
   HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hs_hashv, add, cmpfcn); \
 } )

 #define HASH_ADD_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,cmpfcn) \
   HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn)

 #define HASH_ADD_INORDER(hh,head,fieldname,keylen_in,add,cmpfcn)                 \
   HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, cmpfcn)

 #define HASH_ADD_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add)        \
 do {                                                                             \
   unsigned _ha_bkt;                                                              \
   (add)->hh.hashv = (hashval);                                                   \
   (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);                                                   \
   } else {                                                                       \
     (add)->hh.tbl = (head)->hh.tbl;                                              \
     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, hashval);                                       \
   HASH_EMIT_KEY(hh, head, keyptr, keylen_in);                                    \
   HASH_FSCK(hh, head);                                                           \
 } )

 #define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add)                            \
 do {                                                                             \
   unsigned _ha_hashv;                                                            \
   HASH_VALUE(keyptr, keylen_in, _ha_hashv);                                      \
   HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, add);      \
 } )

 #define HASH_ADD_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add)            \
   HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add)

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

 #define HASH_TO_BKT(hashv,num_bkts,bkt)                                          \
 do {                                                                             \
   bkt = ((hashv) & ((num_bkts) - 1U));                                           \
 } )

 /* delete "delptr" from the hash table.
  * "the usual" patch-up process for the app-order doubly-linked-list.
  * The use of _hd_hh_del below deserves special explanation.
  * These used to be expressed using (delptr) but that led to a bug
  * if someone used the same symbol for the head and deletee, like
  *  HASH_DELETE(hh,users,users);
  * We want that to work, but by changing the head (users) below
  * we were forfeiting our ability to further refer to the deletee (users)
  * in the patch-up process. Solution: use scratch space to
  * copy the deletee pointer, then the latter references are via that
  * scratch pointer rather than through the repointed (users) symbol.
  */
 #define HASH_DELETE(hh,head,delptr)                                              \
 do {                                                                             \
     struct UT_hash_handle *_hd_hh_del;                                           \
     if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) )  {         \
         uthash_free((head)->hh.tbl->buckets,                                     \
                     (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \
         HASH_BLOOM_FREE((head)->hh.tbl);                                         \
         uthash_free((head)->hh.tbl, sizeof(UT_hash_table));                      \
         head = NULL;                                                             \
     } else {                                                                     \
         unsigned _hd_bkt;                                                        \
         _hd_hh_del = &((delptr)->hh);                                            \
         if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) {     \
             (head)->hh.tbl->tail =                                               \
                 (UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) +               \
                 (head)->hh.tbl->hho);                                            \
         }                                                                        \
         if ((delptr)->hh.prev != NULL) {                                         \
             ((UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) +                  \
                     (head)->hh.tbl->hho))->next = (delptr)->hh.next;             \
         } else {                                                                 \
             DECLTYPE_ASSIGN(head,(delptr)->hh.next);                             \
         }                                                                        \
         if (_hd_hh_del->next != NULL) {                                          \
             ((UT_hash_handle*)((ptrdiff_t)_hd_hh_del->next +                     \
                     (head)->hh.tbl->hho))->prev =                                \
                     _hd_hh_del->prev;                                            \
         }                                                                        \
         HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt);   \
         HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del);        \
         (head)->hh.tbl->num_items--;                                             \
     }                                                                            \
     HASH_FSCK(hh,head);                                                          \
 } )

 /* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */
 #define HASH_FIND_STR(head,findstr,out)                                          \
     HASH_FIND(hh,head,findstr,(unsigned)uthash_strlen(findstr),out)
 #define HASH_ADD_STR(head,strfield,add)                                          \
     HASH_ADD(hh,head,strfield[],(unsigned)uthash_strlen(add->strfield),add)
 #define HASH_REPLACE_STR(head,strfield,add,replaced)                             \
     HASH_REPLACE(hh,head,strfield[],(unsigned)uthash_strlen(add->strfield),add,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,replaced)                             \
     HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced)
 #define HASH_DEL(head,delptr)                                                    \
     HASH_DELETE(hh,head,delptr)

 /* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined.
  * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined.
  */
 #ifdef HASH_DEBUG
 #define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0)
 #define HASH_FSCK(hh,head)                                                       \
 do {                                                                             \
     struct UT_hash_handle *_thh;                                                 \
     if (head) {                                                                  \
         unsigned _bkt_i;                                                         \
         unsigned _count;                                                         \
         char *_prev;                                                             \
         _count = ;                                                              \
         ; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) {       \
             unsigned _bkt_count = ;                                             \
             _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head;                      \
             _prev = NULL;                                                        \
             while (_thh) {                                                       \
                if (_prev != (char*)(_thh->hh_prev)) {                            \
                    HASH_OOPS("invalid hh_prev %p, actual %p\n",                  \
                     _thh->hh_prev, _prev );                                      \
                }                                                                 \
                _bkt_count++;                                                     \
                _prev = (char*)(_thh);                                            \
                _thh = _thh->hh_next;                                             \
             }                                                                    \
             _count += _bkt_count;                                                \
             if ((head)->hh.tbl->buckets[_bkt_i].count !=  _bkt_count) {          \
                HASH_OOPS("invalid bucket count %u, actual %u\n",                 \
                 (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count);              \
             }                                                                    \
         }                                                                        \
         if (_count != (head)->hh.tbl->num_items) {                               \
             HASH_OOPS("invalid hh item count %u, actual %u\n",                   \
                 (head)->hh.tbl->num_items, _count );                             \
         }                                                                        \
         /* traverse hh in app order; check next/prev integrity, count */         \
         _count = ;                                                              \
         _prev = NULL;                                                            \
         _thh =  &(head)->hh;                                                     \
         while (_thh) {                                                           \
            _count++;                                                             \
            if (_prev !=(char*)(_thh->prev)) {                                    \
               HASH_OOPS("invalid prev %p, actual %p\n",                          \
                     _thh->prev, _prev );                                         \
            }                                                                     \
            _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh);                    \
            _thh = ( _thh->next ?  (UT_hash_handle*)((char*)(_thh->next) +        \
                                   (head)->hh.tbl->hho) : NULL );                 \
         }                                                                        \
         if (_count != (head)->hh.tbl->num_items) {                               \
             HASH_OOPS("invalid app item count %u, actual %u\n",                  \
                 (head)->hh.tbl->num_items, _count );                             \
         }                                                                        \
     }                                                                            \
 } )
 #else
 #define HASH_FSCK(hh,head)
 #endif

 /* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to
  * the descriptor to which this macro is defined for tuning the hash function.
  * The app can #include <unistd.h> to get the prototype for write(2). */
 #ifdef HASH_EMIT_KEYS
 #define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)                                   \
 do {                                                                             \
     unsigned _klen = fieldlen;                                                   \
     write(HASH_EMIT_KEYS, &_klen, sizeof(_klen));                                \
     write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen);                      \
 } )
 #else
 #define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)
 #endif

 /* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */
 #ifdef HASH_FUNCTION
 #define HASH_FCN HASH_FUNCTION
 #else
 #define HASH_FCN HASH_JEN
 #endif

 /* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */
 #define HASH_BER(key,keylen,hashv)                                               \
 do {                                                                             \
   unsigned _hb_keylen=(unsigned)keylen;                                          \
   const unsigned char *_hb_key=(const unsigned char*)(key);                      \
   (hashv) = ;                                                                   \
   while (_hb_keylen-- != 0U) {                                                   \
       (hashv) = (((hashv) << ) + (hashv)) + *_hb_key++;                         \
   }                                                                              \
 } )

 /* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at
  * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */
 #define HASH_SAX(key,keylen,hashv)                                               \
 do {                                                                             \
   unsigned _sx_i;                                                                \
   const unsigned char *_hs_key=(const unsigned char*)(key);                      \
   hashv = ;                                                                     \
   ; _sx_i < keylen; _sx_i++) {                                        \
       hashv ^= (hashv << ) + (hashv >> ) + _hs_key[_sx_i];                     \
   }                                                                              \
 } )
 /* FNV-1a variation */
 #define HASH_FNV(key,keylen,hashv)                                               \
 do {                                                                             \
   unsigned _fn_i;                                                                \
   const unsigned char *_hf_key=(const unsigned char*)(key);                      \
   hashv = 2166136261U;                                                           \
   ; _fn_i < keylen; _fn_i++) {                                        \
       hashv = hashv ^ _hf_key[_fn_i];                                            \
       hashv = hashv * 16777619U;                                                 \
   }                                                                              \
 } )

 #define HASH_OAT(key,keylen,hashv)                                               \
 do {                                                                             \
   unsigned _ho_i;                                                                \
   const unsigned char *_ho_key=(const unsigned char*)(key);                      \
   hashv = ;                                                                     \
   ; _ho_i < keylen; _ho_i++) {                                        \
       hashv += _ho_key[_ho_i];                                                   \
       hashv += (hashv << );                                                    \
       hashv ^= (hashv >> );                                                     \
   }                                                                              \
   hashv += (hashv << );                                                         \
   hashv ^= (hashv >> );                                                        \
   hashv += (hashv << );                                                        \
 } )

 #define HASH_JEN_MIX(a,b,c)                                                      \
 do {                                                                             \
   a -= b; a -= c; a ^= ( c >>  );                                              \
   b -= c; b -= a; b ^= ( a <<  );                                               \
   c -= a; c -= b; c ^= ( b >>  );                                              \
   a -= b; a -= c; a ^= ( c >>  );                                              \
   b -= c; b -= a; b ^= ( a <<  );                                              \
   c -= a; c -= b; c ^= ( b >>  );                                               \
   a -= b; a -= c; a ^= ( c >>  );                                               \
   b -= c; b -= a; b ^= ( a <<  );                                              \
   c -= a; c -= b; c ^= ( b >>  );                                              \
 } )

 #define HASH_JEN(key,keylen,hashv)                                               \
 do {                                                                             \
   unsigned _hj_i,_hj_j,_hj_k;                                                    \
   unsigned const char *_hj_key=(unsigned const char*)(key);                      \
   hashv = 0xfeedbeefu;                                                           \
   _hj_i = _hj_j = 0x9e3779b9u;                                                   \
   _hj_k = (unsigned)(keylen);                                                    \
   while (_hj_k >= 12U) {                                                         \
     _hj_i +=    (_hj_key[] + ( (unsigned)_hj_key[] <<  )                      \
         + ( (unsigned)_hj_key[] <<  )                                         \
         + ( (unsigned)_hj_key[] <<  ) );                                      \
     _hj_j +=    (_hj_key[] + ( (unsigned)_hj_key[] <<  )                      \
         + ( (unsigned)_hj_key[] <<  )                                         \
         + ( (unsigned)_hj_key[] <<  ) );                                      \
     hashv += (_hj_key[] + ( (unsigned)_hj_key[] <<  )                         \
         + ( (unsigned)_hj_key[] <<  )                                        \
         + ( (unsigned)_hj_key[] <<  ) );                                     \
                                                                                  \
      HASH_JEN_MIX(_hj_i, _hj_j, hashv);                                          \
                                                                                  \
      _hj_key += ;                                                              \
      _hj_k -= 12U;                                                               \
   }                                                                              \
   hashv += (unsigned)(keylen);                                                   \
   switch ( _hj_k ) {                                                             \
      : hashv += ( (unsigned)_hj_key[] <<  ); /* FALLTHROUGH */        \
      : hashv += ( (unsigned)_hj_key[] <<  );  /* FALLTHROUGH */        \
      :  hashv += ( (unsigned)_hj_key[] <<  );   /* FALLTHROUGH */        \
      :  _hj_j += ( (unsigned)_hj_key[] <<  );  /* FALLTHROUGH */        \
      :  _hj_j += ( (unsigned)_hj_key[] <<  );  /* FALLTHROUGH */        \
      :  _hj_j += ( (unsigned)_hj_key[] <<  );   /* FALLTHROUGH */        \
      :  _hj_j += _hj_key[];                      /* FALLTHROUGH */        \
      :  _hj_i += ( (unsigned)_hj_key[] <<  );  /* FALLTHROUGH */        \
      :  _hj_i += ( (unsigned)_hj_key[] <<  );  /* FALLTHROUGH */        \
      :  _hj_i += ( (unsigned)_hj_key[] <<  );   /* FALLTHROUGH */        \
      :  _hj_i += _hj_key[];                                               \
   }                                                                              \
   HASH_JEN_MIX(_hj_i, _hj_j, hashv);                                             \
 } )

 /* The Paul Hsieh hash function */
 #undef get16bits
 #if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__)             \
   || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
 #define get16bits(d) (*((const uint16_t *) (d)))
 #endif

 #if !defined (get16bits)
 #define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)             \
                        +(uint32_t)(((]) )
 #endif
 #define HASH_SFH(key,keylen,hashv)                                               \
 do {                                                                             \
   unsigned const char *_sfh_key=(unsigned const char*)(key);                     \
   uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen;                                \
                                                                                  \
   unsigned _sfh_rem = _sfh_len & 3U;                                             \
   _sfh_len >>= ;                                                                \
   hashv = 0xcafebabeu;                                                           \
                                                                                  \
   /* Main loop */                                                                \
   for (;_sfh_len > 0U; _sfh_len--) {                                             \
     hashv    += get16bits (_sfh_key);                                            \
     _sfh_tmp  = ((uint32_t)(get16bits (_sfh_key+)) << ) ^ hashv;              \
     hashv     = (hashv << ) ^ _sfh_tmp;                                        \
     _sfh_key += 2U*sizeof (uint16_t);                                            \
     hashv    += hashv >> ;                                                     \
   }                                                                              \
                                                                                  \
   /* Handle end cases */                                                         \
   switch (_sfh_rem) {                                                            \
     : hashv += get16bits (_sfh_key);                                       \
             hashv ^= hashv << ;                                                \
             hashv ^= (uint32_t)(_sfh_key[;              \
             hashv += hashv >> ;                                                \
             break;                                                               \
     : hashv += get16bits (_sfh_key);                                       \
             hashv ^= hashv << ;                                                \
             hashv += hashv >> ;                                                \
             break;                                                               \
     : hashv += *_sfh_key;                                                  \
             hashv ^= hashv << ;                                                \
             hashv += hashv >> ;                                                 \
   }                                                                              \
                                                                                  \
     /* Force "avalanching" of final 127 bits */                                  \
     hashv ^= hashv << ;                                                         \
     hashv += hashv >> ;                                                         \
     hashv ^= hashv << ;                                                         \
     hashv += hashv >> ;                                                        \
     hashv ^= hashv << ;                                                        \
     hashv += hashv >> ;                                                         \
 } )

 #ifdef HASH_USING_NO_STRICT_ALIASING
 /* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads.
  * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error.
  * MurmurHash uses the faster approach only on CPU's where we know it's safe.
  *
  * Note the preprocessor built-in defines can be emitted using:
  *
  *   gcc -m64 -dM -E - < /dev/null                  (on gcc)
  *   cc -## a.c (where a.c is a simple test file)   (Sun Studio)
  */
 #if (defined(__i386__) || defined(__x86_64__)  || defined(_M_IX86))
 #define MUR_GETBLOCK(p,i) p[i]
 #else /* non intel */
 #define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 3UL) == 0UL)
 #define MUR_PLUS1_ALIGNED(p) (((unsigned long)p & 3UL) == 1UL)
 #define MUR_PLUS2_ALIGNED(p) (((unsigned long)p & 3UL) == 2UL)
 #define MUR_PLUS3_ALIGNED(p) (((unsigned long)p & 3UL) == 3UL)
 #define WP(p) ((uint32_t*)((unsigned long)(p) & ~3UL))
 #if (defined(__BIG_ENDIAN__) || defined(SPARC) || defined(__ppc__) || defined(__ppc64__))
 #define MUR_THREE_ONE(p) ((((*WP(p))&0x00ffffff) << 8) | (((*(WP(p)+1))&0xff000000) >> 24))
 #define MUR_TWO_TWO(p)   ((((*WP(p))&0x0000ffff) <<16) | (((*(WP(p)+1))&0xffff0000) >> 16))
 #define MUR_ONE_THREE(p) ((((*WP(p))&0x000000ff) <<24) | (((*(WP(p)+1))&0xffffff00) >>  8))
 #else /* assume little endian non-intel */
 #define MUR_THREE_ONE(p) ((((*WP(p))&0xffffff00) >> 8) | (((*(WP(p)+1))&0x000000ff) << 24))
 #define MUR_TWO_TWO(p)   ((((*WP(p))&0xffff0000) >>16) | (((*(WP(p)+1))&0x0000ffff) << 16))
 #define MUR_ONE_THREE(p) ((((*WP(p))&0xff000000) >>24) | (((*(WP(p)+1))&0x00ffffff) <<  8))
 #endif
 #define MUR_GETBLOCK(p,i) (MUR_PLUS0_ALIGNED(p) ? ((p)[i]) :           \
                             (MUR_PLUS1_ALIGNED(p) ? MUR_THREE_ONE(p) : \
                              (MUR_PLUS2_ALIGNED(p) ? MUR_TWO_TWO(p) :  \
                                                       MUR_ONE_THREE(p))))
 #endif
 #define MUR_ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r))))
 #define MUR_FMIX(_h) \
 do {                 \
   _h ^= _h >> ;    \
   _h *= 0x85ebca6bu; \
   _h ^= _h >> ;    \
   _h *= 0xc2b2ae35u; \
   _h ^= _h >> ;    \
 } )

 #define HASH_MUR(key,keylen,hashv)                                     \
 do {                                                                   \
   const uint8_t *_mur_data = (const uint8_t*)(key);                    \
   ;                          \
   uint32_t _mur_h1 = 0xf88D5353u;                                      \
   uint32_t _mur_c1 = 0xcc9e2d51u;                                      \
   uint32_t _mur_c2 = 0x1b873593u;                                      \
   uint32_t _mur_k1 = ;                                                \
   const uint8_t *_mur_tail;                                            \
   )); \
   int _mur_i;                                                          \
   ; _mur_i++) {                   \
     _mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i);                        \
     _mur_k1 *= _mur_c1;                                                \
     _mur_k1 = MUR_ROTL32(_mur_k1,);                                  \
     _mur_k1 *= _mur_c2;                                                \
                                                                        \
     _mur_h1 ^= _mur_k1;                                                \
     _mur_h1 = MUR_ROTL32(_mur_h1,);                                  \
     _mur_h1 = (_mur_h1*5U) + 0xe6546b64u;                              \
   }                                                                    \
   _mur_tail = ());          \
   _mur_k1=;                                                           \
   switch((keylen) & 3U) {                                              \
     : _mur_k1 ^= (uint32_t)_mur_tail[] << ; /* FALLTHROUGH */ \
     : _mur_k1 ^= (uint32_t)_mur_tail[] << ;  /* FALLTHROUGH */ \
     : _mur_k1 ^= (uint32_t)_mur_tail[];                         \
     _mur_k1 *= _mur_c1;                                                \
     _mur_k1 = MUR_ROTL32(_mur_k1,);                                  \
     _mur_k1 *= _mur_c2;                                                \
     _mur_h1 ^= _mur_k1;                                                \
   }                                                                    \
   _mur_h1 ^= (uint32_t)(keylen);                                       \
   MUR_FMIX(_mur_h1);                                                   \
   hashv = _mur_h1;                                                     \
 } )
 #endif  /* HASH_USING_NO_STRICT_ALIASING */

 /* iterate over items in a known bucket to find desired item */
 #define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,hashval,out)               \
 do {                                                                             \
   if ((head).hh_head != NULL) {                                                  \
     DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (head).hh_head));                     \
   } else {                                                                       \
     (out) = NULL;                                                                \
   }                                                                              \
   while ((out) != NULL) {                                                        \
     if ((out)->hh.hashv == (hashval) && (out)->hh.keylen == (keylen_in)) {       \
       ) {                \
         break;                                                                   \
       }                                                                          \
     }                                                                            \
     if ((out)->hh.hh_next != NULL) {                                             \
       DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (out)->hh.hh_next));                \
     } else {                                                                     \
       (out) = NULL;                                                              \
     }                                                                            \
   }                                                                              \
 } )

 /* add an item to a bucket  */
 #define HASH_ADD_TO_BKT(head,addhh)                                              \
 do {                                                                             \
  head.count++;                                                                   \
  (addhh)->hh_next = head.hh_head;                                                \
  (addhh)->hh_prev = NULL;                                                        \
  if (head.hh_head != NULL) { (head).hh_head->hh_prev = (addhh); }                \
  (head).hh_head=addhh;                                                           \
  if ((head.count >= ((head.expand_mult+1U) * HASH_BKT_CAPACITY_THRESH))          \
      && ((addhh)->tbl->noexpand != 1U)) {                                        \
        HASH_EXPAND_BUCKETS((addhh)->tbl);                                        \
  }                                                                               \
 } )

 /* remove an item from a given bucket */
 #define HASH_DEL_IN_BKT(hh,head,hh_del)                                          \
     (head).count--;                                                              \
     if ((head).hh_head == hh_del) {                                              \
       (head).hh_head = hh_del->hh_next;                                          \
     }                                                                            \
     if (hh_del->hh_prev) {                                                       \
         hh_del->hh_prev->hh_next = hh_del->hh_next;                              \
     }                                                                            \
     if (hh_del->hh_next) {                                                       \
         hh_del->hh_next->hh_prev = hh_del->hh_prev;                              \
     }

 /* Bucket expansion has the effect of doubling the number of buckets
  * and redistributing the items into the new buckets. Ideally the
  * items will distribute more or less evenly into the new buckets
  * (the extent to which this is true is a measure of the quality of
  * the hash function as it applies to the key domain).
  *
  * With the items distributed into more buckets, the chain length
  * (item count) in each bucket is reduced. Thus by expanding buckets
  * the hash keeps a bound on the chain length. This bounded chain
  * length is the essence of how a hash provides constant time lookup.
  *
  * The calculation of tbl->ideal_chain_maxlen below deserves some
  * explanation. First, keep in mind that we're calculating the ideal
  * maximum chain length based on the *new* (doubled) bucket count.
  * In fractions this is just n/b (n=number of items,b=new num buckets).
  * Since the ideal chain length is an integer, we want to calculate
  * ceil(n/b). We don't depend on floating point arithmetic in this
  * hash, so to calculate ceil(n/b) with integers we could write
  *
  *      ceil(n/b) = (n/b) + ((n%b)?1:0)
  *
  * and in fact a previous version of this hash did just that.
  * But now we have improved things a bit by recognizing that b is
  * always a power of two. We keep its base 2 log handy (call it lb),
  * so now we can write this with a bit shift and logical AND:
  *
  *      ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0)
  *
  */
 #define HASH_EXPAND_BUCKETS(tbl)                                                 \
 do {                                                                             \
     unsigned _he_bkt;                                                            \
     unsigned _he_bkt_i;                                                          \
     struct UT_hash_handle *_he_thh, *_he_hh_nxt;                                 \
     UT_hash_bucket *_he_new_buckets, *_he_newbkt;                                \
     _he_new_buckets = (UT_hash_bucket*)uthash_malloc(                            \
              2UL * tbl->num_buckets * sizeof(struct UT_hash_bucket));            \
     if (!_he_new_buckets) { uthash_fatal( "out of memory"); }                    \
     memset(_he_new_buckets, ,                                                   \
             2UL * tbl->num_buckets * sizeof(struct UT_hash_bucket));             \
     tbl->ideal_chain_maxlen =                                                    \
        (tbl->num_items >> (tbl->log2_num_buckets+1U)) +                          \
        (((tbl->num_items & ((tbl->num_buckets*2U)-1U)) != 0U) ? 1U : 0U);        \
     tbl->nonideal_items = ;                                                     \
     ; _he_bkt_i < tbl->num_buckets; _he_bkt_i++)                \
     {                                                                            \
         _he_thh = tbl->buckets[ _he_bkt_i ].hh_head;                             \
         while (_he_thh != NULL) {                                                \
            _he_hh_nxt = _he_thh->hh_next;                                        \
            HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2U, _he_bkt);           \
            _he_newbkt = &(_he_new_buckets[ _he_bkt ]);                           \
            if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) {                \
              tbl->nonideal_items++;                                              \
              _he_newbkt->expand_mult = _he_newbkt->count /                       \
                                         tbl->ideal_chain_maxlen;                 \
            }                                                                     \
            _he_thh->hh_prev = NULL;                                              \
            _he_thh->hh_next = _he_newbkt->hh_head;                               \
            if (_he_newbkt->hh_head != NULL) { _he_newbkt->hh_head->hh_prev =     \
                 _he_thh; }                                                       \
            _he_newbkt->hh_head = _he_thh;                                        \
            _he_thh = _he_hh_nxt;                                                 \
         }                                                                        \
     }                                                                            \
     uthash_free( tbl->buckets, tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \
     tbl->num_buckets *= 2U;                                                      \
     tbl->log2_num_buckets++;                                                     \
     tbl->buckets = _he_new_buckets;                                              \
     tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> )) ?         \
         (tbl->ineff_expands+1U) : 0U;                                            \
     if (tbl->ineff_expands > 1U) {                                               \
         tbl->noexpand=;                                                         \
         uthash_noexpand_fyi(tbl);                                                \
     }                                                                            \
     uthash_expand_fyi(tbl);                                                      \
 } )

 /* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */
 /* Note that HASH_SORT assumes the hash handle name to be hh.
  * HASH_SRT was added to allow the hash handle name to be passed in. */
 #define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn)
 #define HASH_SRT(hh,head,cmpfcn)                                                 \
 do {                                                                             \
   unsigned _hs_i;                                                                \
   unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize;               \
   struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail;            \
   if (head != NULL) {                                                            \
       _hs_insize = ;                                                            \
       _hs_looping = ;                                                           \
       _hs_list = &((head)->hh);                                                  \
       while (_hs_looping != 0U) {                                                \
           _hs_p = _hs_list;                                                      \
           _hs_list = NULL;                                                       \
           _hs_tail = NULL;                                                       \
           _hs_nmerges = ;                                                       \
           while (_hs_p != NULL) {                                                \
               _hs_nmerges++;                                                     \
               _hs_q = _hs_p;                                                     \
               _hs_psize = ;                                                     \
               ; _hs_i  < _hs_insize; _hs_i++ ) {                  \
                   _hs_psize++;                                                   \
                   _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ?              \
                           ((void*)((char*)(_hs_q->next) +                        \
                           (head)->hh.tbl->hho)) : NULL);                         \
                   if (! (_hs_q) ) { break; }                                     \
               }                                                                  \
               _hs_qsize = _hs_insize;                                            \
               while ((_hs_psize > 0U) || ((_hs_qsize > 0U) && (_hs_q != NULL))) {\
                   if (_hs_psize == 0U) {                                         \
                       _hs_e = _hs_q;                                             \
                       _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ?          \
                               ((void*)((char*)(_hs_q->next) +                    \
                               (head)->hh.tbl->hho)) : NULL);                     \
                       _hs_qsize--;                                               \
                   } else if ( (_hs_qsize == 0U) || (_hs_q == NULL) ) {           \
                       _hs_e = _hs_p;                                             \
                       if (_hs_p != NULL){                                        \
                         _hs_p = (UT_hash_handle*)((_hs_p->next != NULL) ?        \
                                 ((void*)((char*)(_hs_p->next) +                  \
                                 (head)->hh.tbl->hho)) : NULL);                   \
                        }                                                         \
                       _hs_psize--;                                               \
                   } else if ((                                                   \
                       cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \
                              DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \
                              ) <= ) {                                           \
                       _hs_e = _hs_p;                                             \
                       if (_hs_p != NULL){                                        \
                         _hs_p = (UT_hash_handle*)((_hs_p->next != NULL) ?        \
                                ((void*)((char*)(_hs_p->next) +                   \
                                (head)->hh.tbl->hho)) : NULL);                    \
                        }                                                         \
                       _hs_psize--;                                               \
                   } else {                                                       \
                       _hs_e = _hs_q;                                             \
                       _hs_q = (UT_hash_handle*)((_hs_q->next != NULL) ?          \
                               ((void*)((char*)(_hs_q->next) +                    \
                               (head)->hh.tbl->hho)) : NULL);                     \
                       _hs_qsize--;                                               \
                   }                                                              \
                   if ( _hs_tail != NULL ) {                                      \
                       _hs_tail->next = ((_hs_e != NULL) ?                        \
                             ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL);          \
                   } else {                                                       \
                       _hs_list = _hs_e;                                          \
                   }                                                              \
                   if (_hs_e != NULL) {                                           \
                   _hs_e->prev = ((_hs_tail != NULL) ?                            \
                      ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL);              \
                   }                                                              \
                   _hs_tail = _hs_e;                                              \
               }                                                                  \
               _hs_p = _hs_q;                                                     \
           }                                                                      \
           if (_hs_tail != NULL){                                                 \
             _hs_tail->next = NULL;                                               \
           }                                                                      \
           if ( _hs_nmerges <= 1U ) {                                             \
               _hs_looping=;                                                     \
               (head)->hh.tbl->tail = _hs_tail;                                   \
               DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list));      \
           }                                                                      \
           _hs_insize *= 2U;                                                      \
       }                                                                          \
       HASH_FSCK(hh,head);                                                        \
  }                                                                               \
 } )

 /* This function selects items from one hash into another hash.
  * The end result is that the selected items have dual presence
  * in both hashes. There is no copy of the items made; rather
  * they are added into the new hash through a secondary hash
  * hash handle that must be present in the structure. */
 #define HASH_SELECT(hh_dst, dst, hh_src, src, cond)                              \
 do {                                                                             \
   unsigned _src_bkt, _dst_bkt;                                                   \
   void *_last_elt=NULL, *_elt;                                                   \
   UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL;                         \
   ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst));                 \
   if (src != NULL) {                                                             \
     ; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) {     \
       for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head;                \
           _src_hh != NULL;                                                       \
           _src_hh = _src_hh->hh_next) {                                          \
           _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh);                       \
           if (cond(_elt)) {                                                      \
             _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho);               \
             _dst_hh->key = _src_hh->key;                                         \
             _dst_hh->keylen = _src_hh->keylen;                                   \
             _dst_hh->hashv = _src_hh->hashv;                                     \
             _dst_hh->prev = _last_elt;                                           \
             _dst_hh->next = NULL;                                                \
             if (_last_elt_hh != NULL) { _last_elt_hh->next = _elt; }             \
             if (dst == NULL) {                                                   \
               DECLTYPE_ASSIGN(dst,_elt);                                         \
               HASH_MAKE_TABLE(hh_dst,dst);                                       \
             } else {                                                             \
               _dst_hh->tbl = (dst)->hh_dst.tbl;                                  \
             }                                                                    \
             HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt);    \
             HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh);            \
             (dst)->hh_dst.tbl->num_items++;                                      \
             _last_elt = _elt;                                                    \
             _last_elt_hh = _dst_hh;                                              \
           }                                                                      \
       }                                                                          \
     }                                                                            \
   }                                                                              \
   HASH_FSCK(hh_dst,dst);                                                         \
 } )

 #define HASH_CLEAR(hh,head)                                                      \
 do {                                                                             \
   if (head != NULL) {                                                            \
     uthash_free((head)->hh.tbl->buckets,                                         \
                 (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket));      \
     HASH_BLOOM_FREE((head)->hh.tbl);                                             \
     uthash_free((head)->hh.tbl, sizeof(UT_hash_table));                          \
     (head)=NULL;                                                                 \
   }                                                                              \
 } )

 #define HASH_OVERHEAD(hh,head)                                                   \
  ((head != NULL) ? (                                                             \
  (size_t)(((head)->hh.tbl->num_items   * sizeof(UT_hash_handle))   +             \
           ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket))   +             \
            sizeof(UT_hash_table)                                   +             \
            (HASH_BLOOM_BYTELEN))) : 0U)

 #ifdef NO_DECLTYPE
 #define HASH_ITER(hh,head,el,tmp)                                                \
 for(((el)=(head)), ((*(char**)(&(tmp)))=(char*)((head!=NULL)?(head)->hh.next:NULL)); \
   (el) != NULL; ((el)=(tmp)), ((*(char**)(&(tmp)))=(char*)((tmp!=NULL)?(tmp)->hh.next:NULL)))
 #else
 #define HASH_ITER(hh,head,el,tmp)                                                \
 for(((el)=(head)), ((tmp)=DECLTYPE(el)((head!=NULL)?(head)->hh.next:NULL));      \
   (el) != NULL; ((el)=(tmp)), ((tmp)=DECLTYPE(el)((tmp!=NULL)?(tmp)->hh.next:NULL)))
 #endif

 /* obtain a count of items in the hash */
 #define HASH_COUNT(head) HASH_CNT(hh,head)
 #define HASH_CNT(hh,head) ((head != NULL)?((head)->hh.tbl->num_items):0U)

 typedef struct UT_hash_bucket {
    struct UT_hash_handle *hh_head;
    unsigned count;

    /* expand_mult is normally set to 0. In this situation, the max chain length
     * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If
     * the bucket's chain exceeds this length, bucket expansion is triggered).
     * However, setting expand_mult to a non-zero value delays bucket expansion
     * (that would be triggered by additions to this particular bucket)
     * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH.
     * (The multiplier is simply expand_mult+1). The whole idea of this
     * multiplier is to reduce bucket expansions, since they are expensive, in
     * situations where we know that a particular bucket tends to be overused.
     * It is better to let its chain length grow to a longer yet-still-bounded
     * value, than to do an O(n) bucket expansion too often.
     */
    unsigned expand_mult;

 } UT_hash_bucket;

 /* random signature used only to find hash tables in external analysis */
 #define HASH_SIGNATURE 0xa0111fe1u
 #define HASH_BLOOM_SIGNATURE 0xb12220f2u

 typedef struct UT_hash_table {
    UT_hash_bucket *buckets;
    unsigned num_buckets, log2_num_buckets;
    unsigned num_items;
    struct UT_hash_handle *tail; /* tail hh in app order, for fast append    */
    ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */

    /* in an ideal situation (all buckets used equally), no bucket would have
     * more than ceil(#items/#buckets) items. that's the ideal chain length. */
    unsigned ideal_chain_maxlen;

    /* nonideal_items is the number of items in the hash whose chain position
     * exceeds the ideal chain maxlen. these items pay the penalty for an uneven
     * hash distribution; reaching them in a chain traversal takes >ideal steps */
    unsigned nonideal_items;

    /* ineffective expands occur when a bucket doubling was performed, but
     * afterward, more than half the items in the hash had nonideal chain
     * positions. If this happens on two consecutive expansions we inhibit any
     * further expansion, as it's not helping; this happens when the hash
     * function isn't a good fit for the key domain. When expansion is inhibited
     * the hash will still work, albeit no longer in constant time. */
    unsigned ineff_expands, noexpand;

    uint32_t signature; /* used only to find hash tables in external analysis */
 #ifdef HASH_BLOOM
    uint32_t bloom_sig; /* used only to test bloom exists in external analysis */
    uint8_t *bloom_bv;
    uint8_t bloom_nbits;
 #endif

 } UT_hash_table;

 typedef struct UT_hash_handle {
    struct UT_hash_table *tbl;
    void *prev;                       /* prev element in app order      */
    void *next;                       /* next element in app order      */
    struct UT_hash_handle *hh_prev;   /* previous hh in bucket order    */
    struct UT_hash_handle *hh_next;   /* next hh in bucket order        */
    void *key;                        /* ptr to enclosing struct's key  */
    unsigned keylen;                  /* enclosing struct's key len     */
    unsigned hashv;                   /* result of hash-fcn(key)        */
 } UT_hash_handle;

 #endif /* UTHASH_H */

节点的结构体(自定义的结构体必须包含)

 typedef struct UT_hash_handle {
    struct UT_hash_table *tbl;
    void *prev;                       /* prev element in app order      */
    void *next;                       /* next element in app order      */
    struct UT_hash_handle *hh_prev;   /* previous hh in bucket order    */
    struct UT_hash_handle *hh_next;   /* next hh in bucket order        */
    void *key;                        /* ptr to enclosing struct's key  */
    unsigned keylen;                  /* enclosing struct's key len     */
    unsigned hashv;                   /* result of hash-fcn(key)        */
 } UT_hash_handle;

example

 #include <stdio.h>
 #include "uthash.h" // 包含头文件

 typedef struct HASH_DEMO_ST
 {
     int key;
     int value;
     UT_hash_handle hh; // 必须包含
 }HASH_DEMO_ST;

 int main()
 {
     HASH_DEMO_ST *hashHead = NULL; // hash表头,必须初始化NULL
     HASH_DEMO_ST *out = NULL;
     HASH_DEMO_ST *tmp = NULL;
     HASH_DEMO_ST *current = NULL;

     // 添加节点
     ;
     ; i < ; i++)
     {
         HASH_DEMO_ST *new = (HASH_DEMO_ST *)malloc(sizeof(HASH_DEMO_ST));
         new->key = i;
         new->value = i;

         HASH_ADD_INT(hashHead, key, new);
     }

     // 打印节点个数
     printf("hash count=%d\n", HASH_COUNT(hashHead));

     // 通过key查找节点
     ; i < ; i++)
     {
         HASH_FIND_INT(hashHead, &i, out);
         if (out == NULL)
         {
             printf("the key(%d) is not exist in hash\n", i);
             continue;
         }
         else
         {
             printf("find key:%d, value:%d\n", out->key, out->value);
         }
     }

     // 不安全遍历,不能在中间删除节点
     for(current = hashHead; current != NULL; current = (HASH_DEMO_ST *)(current->hh.next))
     {
         printf("[unsafe traversal]key:%d, value:%d\n", current->key, current->value); 

         //HASH_DEL(hashHead, current);
         //free(current);
         //current = NULL;
     }

     // 安全遍历,可以在中间删除节点
     HASH_ITER(hh, hashHead, current, tmp)
     {
         printf("[safe traversal]key:%d, value:%d\n", current->key, current->value); 

         HASH_DEL(hashHead, current);
         free(current);
         current = NULL;
     }

     // 打印节点个数
     printf("hash count=%d\n", HASH_COUNT(hashHead));

     ;
 }

C开源hash项目uthash的更多相关文章

  1. C++开源hash项目sparsehash

    源码地址: https://github.com/sparsehash/sparsehash

  2. C++开源代码项目汇总

    Google的C++开源代码项目 v8  -  V8 JavaScript EngineV8 是 Google 的开源 JavaScript 引擎.V8 采用 C++ 编写,可在谷歌浏览器(来自 Go ...

  3. soul开源网关项目搭建学习

    1. soul开源网关项目搭建学习 1.1. 地址 https://gitee.com/shuaiqiyu/soul 1.2. 介绍 官方介绍:这是一个异步的,高性能的,跨语言的,响应式的API网关. ...

  4. 我发起了一个 .Net 开源 数据库 项目 SqlNet

    大家好 , 我发起了一个 .Net 开源 数据库 项目 SqlNet . 项目计划 是 用 C# 写一个 关系数据库 . 可以先参考我之前写的 2 篇文章 : 谈谈数据库原理    https://w ...

  5. Google的C++开源代码项目

    Google的C++开源代码项目 http://www.open-open.com/lib/view/open1413873531356.html v8  -  V8 JavaScript Engin ...

  6. C++开源码项目汇总

    Google的C++开源码项目 v8  -  V8 JavaScript Engine V8 是 Google 的开源 JavaScript 引擎. V8 採用 C++ 编写,可在谷歌浏览器(来自 G ...

  7. 开源 iOS 项目分类索引大全 - 待整理

    开源 iOS 项目分类索引大全 GitHub 上大概600个开源 iOS 项目的分类和介绍,对于你挑选和使用开源项目应该有帮助 系统基础库 Category/Util sstoolkit 一套Cate ...

  8. C#开源资源项目

    一.AOP框架 Encase 是C#编写开发的为.NET平台提供的AOP框架.Encase 独特的提供了把方面(aspects)部署到运行时代码,而其它AOP框架依赖配置文件的方式.这种部署方面(as ...

  9. 开源软件项目管理系统招设计/开发。。。。。Zend Framework2架构 svn://735.ikwb.com/pms

    开源软件项目管理系统招设计/开发.....Zend Framework2架构svn://735.ikwb.com/pms

随机推荐

  1. Page cache和Buffer cache[转1]

    http://www.cnblogs.com/mydomain/archive/2013/02/24/2924707.html Page cache实际上是针对文件系统的,是文件的缓存,在文件层面上的 ...

  2. 001. 为input type=text 时设置默认值

    1. 前端HTML代码 <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Defa ...

  3. Oracle数据库—— PL/SQL进阶编程

    一.涉及内容 1.掌握PL/SQL程序块的结构 2.理解并熟练掌握各种变量的应用. 二.具体操作 1.创建一个表messages,该表只有一个字段results 类型是number(2),编写一个块, ...

  4. postfix

    http://www.postfix.org/ All programmers are optimists -- Frederick P. Brooks, Jr. 所有程序员都是乐天派

  5. make与makefile

    Linux makefile 教程 非常详细,且易懂 make与makefile GNU make体系Linux 环境下的程序员如果不会使用GNU make来构建和管理自己的工程,应该不能算是一个合格 ...

  6. unity, collider/trigger on children

    参考:http://answers.unity3d.com/questions/410711/trigger-in-child-object-calls-ontriggerenter-in-pa.ht ...

  7. SpringMVC+Apache Shiro+JPA(hibernate)

    http://my.oschina.net/moziqi/blog/305412 http://my.oschina.net/miger/blog/283526 spring4.1.0+spring ...

  8. 读取Config文件工具类 PropertiesConfig.java

    package com.util; import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io ...

  9. MySQL 利用SQL线程对Binlog操作(转)

    背景: 对于MySQL的binlog的查看都是用其自带的工具mysqlbinlog进行操作的,其实还有另一个方法来操作binlog,就是Replication中的SQL线程去操作binlog,其实bi ...

  10. Android 使用Telephony API

    Android 使用Telephony API public class TelephonyDemo extends Activity { TextView textOut; TelephonyMan ...