浅析uthash系列之三:打印uthash工具

打印uthash

uthash版本 :2.0.2
作者:jafon.tian
转载请注明出处:https://blog.csdn.net/JT_Notes

为了能直观的看到hash表的情况,下面提供一种将hash表详情打印到标准输出或文件的方式。

  • 整套打印采用宏定义的方式,对外提供两个API函数
名称 参数
PRINT_UTHASH (head)
基于PRINT_UTHASH_HH的宏
PRINT_UTHASH_HH (hh, head)
hh为句柄成员名称
head为hash表指针
  • hash表内容打印到标准输出或文件

在包含printHashTable.h之前定义PRINT_FILE_ENABLE使能输出到文件,通过PRINT_FILE_FULLPATH定义文件全路径,缺省输出到”/tmp/print_uthash_table.txt”。

  • 使用tests/test2.c举例演示使用方式
#include "uthash.h"
#include 
#include    /* malloc */
#include     /* printf */

//使能打印到文件 !!!
#define PRINT_FILE_ENABLE
//指定打印文件全路径,默认路径是"/tmp/print_uthash_table.txt" !!!
#define PRINT_FILE_FULLPATH "./print_uthash_table.txt"
#include "printHashTable.h"
//添加头文件 !!!
#include "printHashTable.h"

typedef struct example_user_t {
    int id;
    int cookie;
    UT_hash_handle hh;
} example_user_t;

int main(int argc,char *argv[])
{
    int i;
    example_user_t *user, *tmp, *users=NULL;

    // 打印hash表 !!!
    PRINT_UTHASH(users); 

    /* create elements */
    for(i=0; i<10; i++) {
        user = (example_user_t*)malloc(sizeof(example_user_t));
        if (user == NULL) {
            exit(-1);
        }
        user->id = i;
        user->cookie = i*i;
        HASH_ADD_INT(users,id,user);
    }

    // 打印hash表 !!!
    PRINT_UTHASH(users);

    /* find each even ID */
    for(i=0; i<10; i+=2) {
        HASH_FIND_INT(users,&i,tmp);
        if (tmp != NULL) {
            printf("user id %d found, cookie %d\n", tmp->id, tmp->cookie);
        } else {
            printf("user id %d not found\n", i);
        }
    }
    return 0;
}

输出如下内容

invalid arg head 
PRINT_UTHASH fail, ret = -100
============================================== TABLE INFORMATION ==============================================
Address    ideal num_buckets log2_num_buckets num_items  hho ideal_cmax nonideal_items ineff_ex noex overhead  
---------- ----- ----------- ---------------- --------- ---- ---------- -------------- -------- ---- ----------
0x1338060   100%          32                5        10    8          0              0       ok   ok 1136      

************** BUCKET(     1) INFORMATION : Address:0x13380c0          count:1               expand_mult:0          
hashv:74401341    | key:9         | keylen:   4

************** BUCKET(     4) INFORMATION : Address:0x13380f0          count:1               expand_mult:0          
hashv:B68EF0E4    | key:3         | keylen:   4

************** BUCKET(     5) INFORMATION : Address:0x1338100          count:3               expand_mult:0          
hashv:0DB3B8E5    | key:4         | keylen:   4 <===> 
hashv:6217F7A5    | key:1         | keylen:   4 <===> 
hashv:2A333225    | key:0         | keylen:   4

************** BUCKET(    14) INFORMATION : Address:0x1338190          count:1               expand_mult:0          
hashv:45F2400E    | key:6         | keylen:   4

************** BUCKET(    17) INFORMATION : Address:0x13381c0          count:1               expand_mult:0          
hashv:46FD8A31    | key:5         | keylen:   4

************** BUCKET(    18) INFORMATION : Address:0x13381d0          count:1               expand_mult:0          
hashv:9D13C292    | key:8         | keylen:   4

************** BUCKET(    26) INFORMATION : Address:0x1338250          count:1               expand_mult:0          
hashv:B33656FA    | key:7         | keylen:   4

************** BUCKET(    28) INFORMATION : Address:0x1338270          count:1               expand_mult:0          
hashv:6392F77C    | key:2         | keylen:   4

附:printHashTable.h源代码

/*
Copyright (c) 2018, jafon.tian  https://blog.csdn.net/JT_Notes
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 __PRINT_HASH_TABLE__
#define __PRINT_HASH_TABLE__

#include "uthash.h"

#ifdef PRINT_FILE_ENABLE
#define P_TO_FILE // enable print table to file, undefine this to enable print to stdout
#endif

#ifdef PRINT_FILE_FULLPATH
#define DEFAULT_FILE PRINT_FILE_FULLPATH
#else
#define DEFAULT_FILE "/tmp/print_uthash_table.txt"
#endif

#ifdef P_TO_FILE
static FILE *_g_printfile = NULL;
#define PRINT_START(filename, ret)                           \
    do                                                       \
    {                                                        \
        _g_printfile = fopen(filename, "a");                 \
        if (!_g_printfile)                                   \
        {                                                    \
            printf("open file \"%s\" fail\n", DEFAULT_FILE); \
            ret = -1;                                        \
        }                                                    \
    } while (0)

#define PRINT_END()               \
    do                            \
    {                             \
        if (_g_printfile)         \
        {                         \
            fclose(_g_printfile); \
        }                         \
    } while (0)

#define PRINT_INFO(...)                         \
    do                                          \
    {                                           \
        if (_g_printfile)                       \
        {                                       \
            fprintf(_g_printfile, __VA_ARGS__); \
        }                                       \
    } while (0)
#else
#define PRINT_START(filename, ret)
#define PRINT_END()
#define PRINT_INFO(...)      \
    do                       \
    {                        \
        printf(__VA_ARGS__); \
    } while (0)
#endif

#define PRINT_RET(ret) \
    if (ret)           \
    break
/*
============================================== TABLE INFORMATION ==============================================
Address    ideal num_buckets log2_num_buckets num_items  hho ideal_cmax nonideal_items ineff_ex noex overhead  
---------- ----- ----------- ---------------- --------- ---- ---------- -------------- -------- ---- ----------
0x1338060   100%          32                5        10    8          0              0       ok   ok 1136      
*/
#define PRINT_TABLE_HH(hh, head, ret)                                                                                                    \
    do                                                                                                                                   \
    {                                                                                                                                    \
        int inx;                                                                                                                         \
        UT_hash_table *tbl = NULL;                                                                                                       \
        size_t overhead;                                                                                                                 \
        if (!head)                                                                                                                       \
        {                                                                                                                                \
            PRINT_INFO("invalid arg head \n");                                                                                           \
            ret = -100;                                                                                                                  \
            PRINT_RET(ret);                                                                                                              \
        }                                                                                                                                \
        tbl = head->hh.tbl;                                                                                                              \
        if (!tbl)                                                                                                                        \
        {                                                                                                                                \
            PRINT_INFO("invalid arg tbl \n");                                                                                            \
            ret = -200;                                                                                                                  \
            PRINT_RET(ret);                                                                                                              \
        }                                                                                                                                \
        overhead = HASH_OVERHEAD(hh, head);                                                                                              \
        PRINT_INFO("============================================== TABLE INFORMATION ==============================================\n"); \
        PRINT_INFO("Address    ideal num_buckets log2_num_buckets num_items  hho ideal_cmax nonideal_items ineff_ex noex overhead  \n"); \
        PRINT_INFO("---------- ----- ----------- ---------------- --------- ---- ---------- -------------- -------- ---- ----------\n"); \
        PRINT_INFO("%-10p %4.0f%% %11u %16u %9u %4u %10u %14u %8s %4s %-10u\n\n",                                                        \
                   (void *)tbl,                                                                                                          \
                   (tbl->num_items - tbl->nonideal_items) * 100.0 / tbl->num_items,                                                      \
                   tbl->num_buckets,                                                                                                     \
                   tbl->log2_num_buckets,                                                                                                \
                   tbl->num_items,                                                                                                       \
                   tbl->hho,                                                                                                             \
                   tbl->ideal_chain_maxlen,                                                                                              \
                   tbl->nonideal_items,                                                                                                  \
                   tbl->ineff_expands ? "IE" : "ok",                                                                                     \
                   tbl->noexpand ? "NX" : "ok",                                                                                          \
                   overhead);                                                                                                            \
        for (inx = 0; inx < tbl->num_buckets; inx++)                                                                                     \
        {                                                                                                                                \
            PRINT_BUCKET(&tbl->buckets[inx], inx, ret);                                                                                  \
            PRINT_RET(ret);                                                                                                              \
        }                                                                                                                                \
        PRINT_RET(ret);                                                                                                                  \
    } while (0)
/*
************** BUCKET(     1) INFORMATION : Address:0x13380c0          count:1               expand_mult:0    
*/
#define PRINT_BUCKET(bkt_in, inx, ret)                                                                           \
    do                                                                                                           \
    {                                                                                                            \
        UT_hash_bucket *bkt = bkt_in;                                                                            \
        UT_hash_handle *lhh;                                                                                     \
        if (!bkt)                                                                                                \
        {                                                                                                        \
            PRINT_INFO("invalid bucket\n");                                                                      \
            ret = -200;                                                                                          \
            PRINT_RET(ret);                                                                                      \
        }                                                                                                        \
        if (bkt->count)                                                                                          \
        {                                                                                                        \
            PRINT_INFO("************** BUCKET(%6u) INFORMATION : Address:%-18p count:%-15u expand_mult:%-11u\n", \
                       inx,                                                                                      \
                       (void *)bkt,                                                                              \
                       bkt->count,                                                                               \
                       bkt->expand_mult);                                                                        \
            lhh = bkt->hh_head;                                                                                  \
            while (lhh)                                                                                          \
            {                                                                                                    \
                PRINT_HH(lhh, ret);                                                                              \
                PRINT_RET(ret);                                                                                  \
                lhh = lhh->hh_next;                                                                              \
            }                                                                                                    \
            PRINT_RET(ret);                                                                                      \
            PRINT_INFO("\n");                                                                                    \
        }                                                                                                        \
    } while (0)

/*
hashv:0DB3B8E5    | key:4         | keylen:   4 <===> 
hashv:6217F7A5    | key:1         | keylen:   4 <===> 
hashv:2A333225    | key:0         | keylen:   4
*/
#define PRINT_HH(hh, ret)                                                                                      \
    do                                                                                                         \
    {                                                                                                          \
        if (!hh)                                                                                               \
        {                                                                                                      \
            PRINT_INFO("invalid hash handle\n");                                                               \
            ret = -300;                                                                                        \
            PRINT_RET(ret);                                                                                    \
        }                                                                                                      \
        if (hh->keylen == sizeof(int))                                                                         \
            PRINT_INFO("hashv:%08X    | key:%-9X | keylen:%4u", hh->hashv, *((int *)hh->key), hh->keylen);     \
        else                                                                                                   \
            PRINT_INFO("hashv:%08X    | key:%-9s | keylen:%4u", hh->hashv, (const char *)hh->key, hh->keylen); \
        if (hh->hh_next)                                                                                       \
            PRINT_INFO(" <===> ");                                                                             \
        PRINT_INFO("\n");                                                                                      \
    } while (0)

#define PRINT_UTHASH_HH(hh, head)                             \
    do                                                        \
    {                                                         \
        int ret = 0;                                          \
        UT_hash_table *tbl = NULL;                            \
        UT_hash_bucket *brks = NULL;                          \
        PRINT_START(DEFAULT_FILE, ret);                       \
        do                                                    \
        {                                                     \
            PRINT_RET(ret);                                   \
            PRINT_TABLE_HH(hh, head, ret);                    \
        } while (0);                                          \
        if (ret)                                              \
            PRINT_INFO("PRINT_UTHASH fail, ret = %d\n", ret); \
        PRINT_END();                                          \
    } while (0)

#define PRINT_UTHASH(head) PRINT_UTHASH_HH(hh, head)

#endif

你可能感兴趣的:(浅析uthash系列之三:打印uthash工具)