nginx学习——建立hash表的前提条件

在前面两篇博文中分别介绍了nginx中普通hash表和带有通配符的hash表的建立和查找,今天主要是介绍建立hash表的前提条件,并在后面附上一个完整的hash表的实现(该部分代码为转载)。

一、建立hash表的前提条件

(1)为hash表提供数据的数据结构

ngx_hash_keys_arrays_t不负责构造hash表,但是它是使用ngx_hash_init和ngx_hash_wildcard_init的前提条件,也就是说,如果先构造好了ngx_hash_keys_arrays_t就可以简单地创造支持通配符的hash表了。

//哈希key数组
typedef struct
{
    ngx_uint_t        hsize;//hash表桶个数
    ngx_pool_t       *pool;//为nginx中永久存放的hash结构体分配空间的内存
    ngx_pool_t       *temp_pool;//分配临时数据空间的内存池
    ngx_array_t       keys;//保存不含通配符元素中间key的动态数组
    ngx_array_t      *keys_hash;//处理不含通配符元素的简易hash表(动态数组)
    ngx_array_t       dns_wc_head;//保存前置通配符元素中间key的动态数组
    ngx_array_t      *dns_wc_head_hash;//处理前置通配符元素的简易hash表(动态数组)
    ngx_array_t       dns_wc_tail;//保存后置通配符元素中间key的动态数组
    ngx_array_t      *dns_wc_tail_hash;//处理后置通配符元素的简易hash表(动态数组)
} ngx_hash_keys_arrays_t;


(2)哈希key数组的初始化
ngx_hash_keys_arrays_t中有3个动态数组keys、dns_wc_head、dns_wc_tail存放均为ngx_hash_key_t的元素类型,分别保存完全匹配key、带前缀通配符key、带后缀通配符key。同ngx_hash_key_arrays_t建立了3个简易的hash表,这3个hash表用于快速向上述3个动态数组容器插入元素,这该hash表的实质是存放动态数组的固定数组,用keys_hash、dns_wc_head_hash、dns_wc_tail_hash分别指向这3个hash表。
nginx学习——建立hash表的前提条件_第1张图片
//哈希key数组初始化
ngx_int_t ngx_hash_keys_array_init(ngx_hash_keys_arrays_t *ha, ngx_uint_t type)
{
    ngx_uint_t  asize;

    //初始化元素较少
    if (type == NGX_HASH_SMALL)
    {
        asize = 4;
        ha->hsize = 107;

    }
    //初始化元素较多
    else
    {
        asize = NGX_HASH_LARGE_ASIZE;
        ha->hsize = NGX_HASH_LARGE_HSIZE;
    }
    //初始化存放非通配符key元素的动态数组
    if (ngx_array_init(&ha->keys, ha->temp_pool, asize, sizeof(ngx_hash_key_t))!= NGX_OK)
    {
        return NGX_ERROR;
    }
    //初始化存放前缀通配符key元素的动态数组
    if (ngx_array_init(&ha->dns_wc_head, ha->temp_pool, asize, sizeof(ngx_hash_key_t)) != NGX_OK)
    {
        return NGX_ERROR;
    }
    //初始化存放后缀通配符key元素的动态数组
    if (ngx_array_init(&ha->dns_wc_tail, ha->temp_pool, asize, sizeof(ngx_hash_key_t)) != NGX_OK)
    {
        return NGX_ERROR;
    }
    //初始化不含通配符key的简易hash表(实质是存放动态数组的固定数组)
    ha->keys_hash = ngx_pcalloc(ha->temp_pool, sizeof(ngx_array_t) * ha->hsize);
    if (ha->keys_hash == NULL)
    {
        return NGX_ERROR;
    }
    //初始化前缀通配符key的简易hash表(实质是存放动态数组的固定数组)
    ha->dns_wc_head_hash = ngx_pcalloc(ha->temp_pool, sizeof(ngx_array_t) * ha->hsize);
    if (ha->dns_wc_head_hash == NULL)
    {
        return NGX_ERROR;
    }
    //初始化后缀通配符key的简易hash表(实质是存放动态数组的固定数组)
    ha->dns_wc_tail_hash = ngx_pcalloc(ha->temp_pool, sizeof(ngx_array_t) * ha->hsize);
    if (ha->dns_wc_tail_hash == NULL)
    {
        return NGX_ERROR;
    }

    return NGX_OK;
}

(3)向哈希key数组添加key-value对
前面介绍了对于字串"com.yexin.test."、"com.yexin"、"com.example."、"org.example."、"org.yexin"的第一个key字段提取出来,那么不相同的字段只有“com”、"org"。最后存到curr_names数组中的也只有"com"和"org"两个元素。在把key字段添加到动态数组的过程中,为了知道该key字段之前是否已经添加,这里采用了一个简单的hash表,每次向动态数组中添加元素前都要去查hash表,这样添加同名key时,就可以通过hash值获得之前曾经添加过的key,从而判断是否合法或者以及是否进行元素合并操作。
总的流程:字符串检测->字符串处理->判断该key字符串是否在hash表中存在->若不存在则把元素(key-value)放入动态数组,并把key加入hash表
1、字符串检测
nginx中只接受形如"*.example.com"、".example.com"、"www.example.com"、"www.example.*"的字符串。
if (flags & NGX_HASH_WILDCARD_KEY)
{
    //key中“*”出现次数
    n = 0;

    for (i = 0; i < key->len; i++)
    {
        if (key->data[i] == '*')
        {
            //key中“*”超过一个
            if (++n > 1)
            {
                return NGX_DECLINED;
            }
        }

        //key中有两个连续的"."
        if (key->data[i] == '.' && key->data[i + 1] == '.')
        {
            return NGX_DECLINED;
        }
    }

    //以"."开头的key,形如".example.com"
    if (key->len > 1 && key->data[0] == '.')
    {
        skip = 1;
        goto wildcard;
    }

    if (key->len > 2)
    {
        //以"*."开头的key,形如"*.example.com"
        if (key->data[0] == '*' && key->data[1] == '.')
        {
            skip = 2;
            goto wildcard;
        }
        //以".*"结尾的key,形如"www.example.*"
        if (key->data[i - 2] == '.' && key->data[i - 1] == '*')
        {
            skip = 0;
            last -= 2;
            goto wildcard;
        }
    }

    //出现过"*"号,又不满足以上几种情况
    if (n)
    {
        return NGX_DECLINED;
    }
}

2、 字符串处理
if (skip)
{
    //将"*.example.com"转换成"com.example."
    //或将".example.com" 转换成"com.example"
    p = ngx_pnalloc(ha->temp_pool, last);
    if (p == NULL)
    {
        return NGX_ERROR;
    }

    len = 0;
    n = 0;

    for (i = last - 1; i; i--)
    {
        if (key->data[i] == '.')
        {
            ngx_memcpy(&p[n], &key->data[i + 1], len);
            n += len;
            p[n++] = '.';
            len = 0;
            continue;
        }

        len++;
    }

    if (len)
    {
        ngx_memcpy(&p[n], &key->data[1], len);
        n += len;
    }

    p[n] = '\0';
    ......
}
else
{
    //将"www.example.*"转换成"www.example"
    last++;

    p = ngx_pnalloc(ha->temp_pool, last);
    if (p == NULL)
    {
        return NGX_ERROR;
    }

    ngx_cpystrn(p, key->data, last);
    ......
}

3、 判断该key字符串是否在hash表中存在
对于 keys_hash、 dns_wc_head_hash、 dns_wc_tail_hash检测key字符串是否存在处理方式是一样的,这里用keys_hash来举例。
//对不含通配符key的元素的处理
k = 0;

for (i = 0; i < last; i++)
{
    if (!(flags & NGX_HASH_READONLY_KEY)) 
    {
        key->data[i] = ngx_tolower(key->data[i]);
    }
    //计算该key的hash值
    k = ngx_hash(k, key->data[i]);
}

//在不含通配符的简易hash表中查找相应的桶(动态数组)下标
k %= ha->hsize;

name = ha->keys_hash[k].elts;

//该桶(动态数组)已经存在(之前已经初始化并存放过key)
if (name)
{
    //查找桶中是否存在和当前要存放的key相同的key
    for (i = 0; i < ha->keys_hash[k].nelts; i++)
    {
        //长度不一样,一定不冲突
        if (last != name[i].len)
        {
            continue;
        }

        //桶内之前已经存放过相同的key,冲突
        if (ngx_strncmp(key->data, name[i].data, last) == 0)
        {
            return NGX_BUSY;
        }
    }

}
//初始化该元素要存放的桶(动态数组)
else
{
    if (ngx_array_init(&ha->keys_hash[k], ha->temp_pool, 4, sizeof(ngx_str_t)) != NGX_OK)
    {
        return NGX_ERROR;
    }
}

4、 若不存在则把元素(key-value)放入动态数组,并把key加入hash表
这里依然用不含通配符的key存入 keys(动态数组)、 keys_hash(hash表)来举例,前缀key、后缀key同理。
//将key存放到不含通配符的hash表keys_hash中
name = ngx_array_push(&ha->keys_hash[k]);
if (name == NULL)
{
    return NGX_ERROR;
}

*name = *key;

//将元素(key-value对)存放到不含通配符的动态数组keys中
hk = ngx_array_push(&ha->keys);
if (hk == NULL)
{
    return NGX_ERROR;
}

hk->key = *key;
hk->key_hash = ngx_hash_key(key->data, last);
hk->value = value;
return NGX_OK;

(4) 哈希key数组的内存布局
例如将以下key-value对创建 哈希key数组如下图所示。
key = "*..com."         value = "1.1.1.1"
key = "*.baidu.com.cn"  value = "180.97.33.107"
key = "*.baidu.com"     value = "180.97.33.107"
key = ".baidu.com"      value = "180.97.33.107"
key = " www.qq.*"        value = "14.17.42.40"
key = "*.google.com"    value = "93.46.8.89"
key = " www.qq.com"      value = "14.17.42.40"
key = " www.baidu.com"   value = "180.97.33.107"
key = " www.taobao.com"  value = "116.207.117.62"
key = " www.360.cn"      value = "119.84.14.199"
key = " www.google.com"  value = "93.46.8.89"
key = " www.sina.com.cn" value = "121.14.1.189"
key = " www.163.com"     value = "61.136.167.72"
key = " www.sohu.com"    value = "119.97.155.2"
key = "test.yexin.com"  value = "255.255.255.1"
key = ".baidu.com"      value = "180.97.33.107"
key = "test.yexin.*"    value = "255.255.255.255"
key = " www.qq.*"        value = "14.17.42.40"
nginx学习——建立hash表的前提条件_第2张图片
二、完整的hash查找的实现
该部分内容转载自:http://www.cnblogs.com/chengxuyuancc/p/3782808.html
hash_test.h
#ifndef HASH_TEST_H
#define HASH_TEST_H
#include 
#include "ngx_config.h"
#include "ngx_conf_file.h"
#include "nginx.h"
#include "ngx_core.h"
#include "ngx_string.h"
#include "ngx_palloc.h"
#include "ngx_array.h"
#include "ngx_hash.h"
#include "ngx_log.h"
#define DEEPTH 4
#define MAX_NUM 18 
#define MAX_SIZE 1024
#define BUCKET_SIZE 64 

#define MAX_URL_LEN 15
#define MAX_IP_LEN 15
#define MAX_NUM2 3

#define NGX_HASH_ELT_SIZE(name) (sizeof(void *) + ngx_align((name)->key.len + 2, sizeof(void *)))
	
static ngx_str_t urls[MAX_NUM] = {
    ngx_string("*..com."),  //1.1.1.1
	ngx_string("*.baidu.com.cn"),//180.97.33.107
	ngx_string("*.baidu.com"),//180.97.33.107
	ngx_string(".baidu.com"),//180.97.33.107
    ngx_string("www.qq.*"),//14.17.42.40
	ngx_string("*.google.com"),//93.46.8.89
    ngx_string("www.qq.com"),//14.17.42.40
    ngx_string("www.baidu.com"),//180.97.33.107
    ngx_string("www.taobao.com"),//116.207.117.62
    ngx_string("www.360.cn"),//119.84.14.199
    ngx_string("www.google.com"),  //93.46.8.89
    ngx_string("www.sina.com.cn"),  //121.14.1.189
    ngx_string("www.163.com"),  //61.136.167.72
    ngx_string("www.sohu.com"),  //119.97.155.2
    ngx_string("test.yexin.com"),  //255.255.255.1
    ngx_string(".baidu.com"),  //180.97.33.107
    ngx_string("test.yexin.*"),//255.255.255.255
    ngx_string("www.qq.*"),//14.17.42.40
};

static ngx_str_t values[MAX_NUM] = {
    ngx_string("1.1.1.1"),
    ngx_string("180.97.33.107"),
    ngx_string("180.97.33.107"),
    ngx_string("180.97.33.107"),
    ngx_string("14.17.42.40"),
    ngx_string("93.46.8.89"),
    ngx_string("14.17.42.40"),
    ngx_string("180.97.33.107"),
    ngx_string("116.207.117.62"),
    ngx_string("119.84.14.199"),
    ngx_string("93.46.8.89"),
    ngx_string("121.14.1.189"),
    ngx_string("61.136.167.72"),
    ngx_string("119.97.155.2"),
    ngx_string("255.255.255.1"),
    ngx_string("180.97.33.107"),
    ngx_string("255.255.255.255"),
    ngx_string("14.17.42.40")
};

/* for finding test */
static ngx_str_t urls2[MAX_NUM2] = {
    ngx_string("*.xx.xx"),  //60.217.58.79
    ngx_string("www.baidu.com"),  //117.79.157.242
    ngx_string("www.baidu.")  //117.79.157.242
};

void* init_hash(ngx_pool_t *pool, ngx_hash_keys_arrays_t *ha, ngx_hash_combined_t *hash);
void dump_pool(ngx_pool_t* pool);
void dump_hash_array(ngx_array_t* a);
void dump_combined_hash(ngx_hash_combined_t *hash, ngx_hash_keys_arrays_t *array);
void dump_hash(ngx_hash_t *hash, ngx_array_t *array);
void* add_urls_to_array(ngx_pool_t *pool, ngx_hash_keys_arrays_t *ha, ngx_array_t *url, ngx_array_t *value);
void find_test(ngx_hash_combined_t *hash, ngx_str_t addr[], int num);
void dump_hash_wildcard(ngx_hash_wildcard_t *wc_hash, ngx_uint_t deepth);
#endif
hash_test.c
#include "hash_test.h"

volatile ngx_cycle_t  *ngx_cycle;
ngx_log_t ngx_log;
void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, const char *fmt, ...)
{
}

static int ngx_http_cmp_dns_wildcards(const void *one, const void *two)
{
    ngx_hash_key_t  *first, *second;

    first = (ngx_hash_key_t *) one;
    second = (ngx_hash_key_t *) two;

    return ngx_dns_strcmp(first->key.data, second->key.data);
}

int main(/* int argc, char **argv */)
{
    ngx_pool_t *pool = NULL;
    ngx_hash_keys_arrays_t array;
    ngx_hash_combined_t hash;
	ngx_array_t *key, *value;

	hash.wc_head = hash.wc_tail = NULL;
    printf("--------------------------------\n");
    printf("          the pool:\n");
    printf("--------------------------------\n");
    //create a new pool
    pool = ngx_create_pool(1024, &ngx_log);

    dump_pool(pool);

    ngx_int_t loop;
    printf("--------------------------------\n");
    printf("create and add urls to it:\n");
    printf("--------------------------------\n");
	if ((key = ngx_array_create(pool, MAX_NUM, sizeof(ngx_str_t))) == NULL)
	{
		printf("Failed to initialize url!\n");
        return -1;
	}
	if ((value = ngx_array_create(pool, MAX_NUM, sizeof(ngx_str_t))) == NULL)
	{
		printf("Failed to initialize value!\n");
        return -1;
	}

	ngx_str_t *temp;
	//常量字符串是不可修改的,而后面需要修改,所以重新拷贝一份
	for (loop = 0; loop < MAX_NUM; loop++)
	{
		temp = ngx_array_push(key);
		temp->len = urls[loop].len;
		temp->data = ngx_palloc(pool, urls[loop].len);
		ngx_memcpy(temp->data, urls[loop].data, temp->len);
	}
	//由于key-value中的value的地址必须是4对齐的,所以需要重新拷贝一份vaule
	for (loop = 0; loop < MAX_NUM; loop++)
	{
		temp = ngx_array_push(value);
		temp->len = values[loop].len;
		temp->data = ngx_palloc(pool, values[loop].len);
		ngx_memcpy(temp->data, values[loop].data, temp->len);
	}

   	if (add_urls_to_array(pool, &array, key, value) == NULL)
	{
        printf("Failed to initialize array!\n");
        return -1;
    }
    printf("--------------------------------\n");
    printf("         array.keys :\n");
    printf("--------------------------------\n");
    dump_hash_array(&array.keys);
    printf("--------------------------------\n");
    printf("         array.dns_wc_head :\n");
    printf("--------------------------------\n");   
	dump_hash_array(&array.dns_wc_head);
    printf("--------------------------------\n");
    printf("         array.dns_wc_tail :\n");
    printf("--------------------------------\n");	
	dump_hash_array(&array.dns_wc_tail); 
	
    printf("--------------------------------\n");
    printf("         array.keys_hash :\n");
    printf("--------------------------------\n");
    
    for (int i = 0; i < 10007; i++)
    {
            if (array.keys_hash[i].nelts != 0)
            {

                printf("i = %d\n", i);
    	        dump_hash_array(&(array.keys_hash[i]));
            }
    }

    printf("--------------------------------\n");
    printf("         array.dns_wc_head_hash :\n");
    printf("--------------------------------\n");
    
    for (int i = 0; i < 10007; i++)
    {
            if (array.dns_wc_head_hash[i].nelts != 0)
            {

                printf("i = %d\n", i);
    	        dump_hash_array(&(array.dns_wc_head_hash[i]));
            }
    }
    printf("--------------------------------\n");
    printf("         array.dns_wc_tail_hash :\n");
    printf("--------------------------------\n");
    
    for (int i = 0; i < 10007; i++)
    {
            if (array.dns_wc_tail_hash[i].nelts != 0)
            {

                printf("i = %d\n", i);
    	        dump_hash_array(&(array.dns_wc_tail_hash[i]));
            }
    }


    printf("--------------------------------\n");
    printf("          the pool:\n");
    printf("--------------------------------\n");
    dump_pool(pool);

    if (init_hash(pool, &array, &hash) == NULL)
    {
        printf("Failed to initialize hash!\n");
        return -1;
    }
    printf("--------------------------------\n");
    printf("the hash:\n");
    printf("--------------------------------\n");
    
    dump_combined_hash(&hash, &array);
    printf("\n");

    printf("--------------------------------\n");
    printf("the pool:\n");
    printf("--------------------------------\n");
    dump_pool(pool);

    //find test
    printf("--------------------------------\n");
    printf("find test:\n");
    printf("--------------------------------\n");
    find_test(&hash, urls, MAX_NUM);
    printf("\n");

    find_test(&hash, urls2, MAX_NUM2);

    //release
    return 0;
}

void* init_hash(ngx_pool_t *pool, ngx_hash_keys_arrays_t *ha, ngx_hash_combined_t *hash)
{
    ngx_hash_init_t hinit;

    ngx_cacheline_size = 32;  //here this variable for nginx must be defined
    hinit.hash = NULL;  //if hinit.hash is NULL, it will alloc memory for it in ngx_hash_init
    hinit.key = &ngx_hash_key_lc;  //hash function
    hinit.max_size = MAX_SIZE;
    hinit.bucket_size = BUCKET_SIZE;
    hinit.name = "my_hash_sample";
    hinit.pool = pool;  //the hash table exists in the memory pool
	hinit.temp_pool = ha->temp_pool;

    if (ha->keys.nelts) {  //无通配
		hinit.hash = &hash->hash;
        if (ngx_hash_init(&hinit, ha->keys.elts, ha->keys.nelts) != NGX_OK) {
            goto failed;
        }
    }
    if (ha->dns_wc_head.nelts) {  //前缀通配
		hinit.hash = NULL;
        ngx_qsort(ha->dns_wc_head.elts, (size_t) ha->dns_wc_head.nelts,
                  sizeof(ngx_hash_key_t), ngx_http_cmp_dns_wildcards);

		printf("--------------------------------\n");
		printf("  array.dns_wc_head after sort:\n");
		printf("--------------------------------\n"); 
		dump_hash_array(&ha->dns_wc_head);
        
        if (ngx_hash_wildcard_init(&hinit, ha->dns_wc_head.elts,
                                   ha->dns_wc_head.nelts)
            != NGX_OK)
        {
            goto failed;
        }
        hash->wc_head = (ngx_hash_wildcard_t *) hinit.hash;
    }

    if (ha->dns_wc_tail.nelts) {  //后缀通配
        ngx_qsort(ha->dns_wc_tail.elts, (size_t) ha->dns_wc_tail.nelts,
                  sizeof(ngx_hash_key_t), ngx_http_cmp_dns_wildcards);

		printf("--------------------------------\n");
		printf("  array.dns_wc_tail after sort:\n");
		printf("--------------------------------\n"); 
		dump_hash_array(&ha->dns_wc_tail);
		
        hinit.hash = NULL;
        if (ngx_hash_wildcard_init(&hinit, ha->dns_wc_tail.elts,
                                   ha->dns_wc_tail.nelts)
            != NGX_OK)
        {
            goto failed;
        }

        hash->wc_tail = (ngx_hash_wildcard_t *) hinit.hash;
    }

    //ngx_destroy_pool(ha->temp_pool);
	return hash;
failed:
    //ngx_destroy_pool(ha->temp_pool);
    return NULL;
}

void dump_pool(ngx_pool_t* pool)
{
    while (pool)
    {
        printf("pool = %p\n", pool);
        printf("  .d\n");
        printf("    .last = %p\n", pool->d.last);
        printf("    .end = %p\n", pool->d.end);
        printf("    .next = %p\n", pool->d.next);
        printf("    .failed = %d\n", pool->d.failed);
        printf("  .max = %d\n", pool->max);
        printf("  .current = %p\n", pool->current);
        printf("  .chain = %p\n", pool->chain);
        printf("  .large = %p\n", pool->large);
        printf("  .cleanup = %p\n", pool->cleanup);
        printf("  .log = %p\n", pool->log);
        printf("available pool memory = %d\n\n", pool->d.end - pool->d.last);
        pool = pool->d.next;
    }
}

void dump_hash_array(ngx_array_t* a)
{
    char prefix[] = "              ";
	ngx_uint_t i;

    if (a == NULL)
        return;

    printf("array = %p\n", a);
    printf("  .elts = %p\n", a->elts);
    printf("  .nelts = %d\n", a->nelts);
    printf("  .size = %d\n", a->size);
    printf("  .nalloc = %d\n", a->nalloc);
    printf("  .pool = %p\n", a->pool);

    printf("  elements:\n");
    ngx_hash_key_t *ptr = a->elts;
    for (i = 0; i < a->nelts; i++)
    {
    	if (ptr[i].value)
    	{
        	printf("    %p: {key = (\"%.*s\"%.*s, %-2d), key_hash = %-11d , value = \"%.*s\"%.*s}\n", ptr + i, ptr[i].key.len, ptr[i].key.data, MAX_URL_LEN - ptr[i].key.len, prefix, ptr[i].key.len, ptr[i].key_hash, ((ngx_str_t*)ptr[i].value)->len, ((ngx_str_t*)ptr[i].value)->data, MAX_IP_LEN - ((ngx_str_t*)ptr[i].value)->len, prefix);
        }
        else
        {
        	printf("    %p: {key = (\"%.*s\"%.*s, %-2d), key_hash = %-11d }\n", ptr + i, ptr[i].key.len, ptr[i].key.data, MAX_URL_LEN - ptr[i].key.len, prefix, ptr[i].key.len, ptr[i].key_hash);
        }
    }
    printf("\n");
}

void display_str_data(ngx_str_t *str)
{
    int len = str->len;
    for (int i = 0; i < len; i++)
    {
            printf("%c", str->data[i]);
    }
    printf("\0");
}

/**
 * pass array pointer to read elts[i].key_hash, then for getting the position - key
 */
void dump_combined_hash(ngx_hash_combined_t *hash, ngx_hash_keys_arrays_t *array)
{
	dump_hash(&hash->hash, &array->keys);
	dump_hash_wildcard(hash->wc_head, 0);
	dump_hash_wildcard(hash->wc_tail, 0);
}
void dump_hash_wildcard(ngx_hash_wildcard_t *wc_hash, ngx_uint_t deepth)
{
	ngx_uint_t loop;
    char prefix[] = "                         ";
	ngx_hash_wildcard_t *wdc;
	ngx_hash_t *hash = &wc_hash->hash;

	if (wc_hash == NULL)
		return;
	if (wc_hash->value != NULL)
		printf("%.*svalue = \"%.*s\"\n", deepth * DEEPTH, prefix, ((ngx_str_t*)wc_hash->value)->len,
				((ngx_str_t*)wc_hash->value)->data);
	else
		printf("%.*svalue = NULL\n", deepth * DEEPTH, prefix);
    printf("%.*shash = %p: **buckets = %p, size = %d\n", deepth * DEEPTH, prefix, hash, hash->buckets, hash->size);

    for (loop = 0; loop < hash->size; loop++)
    {
        ngx_hash_elt_t *elt = hash->buckets[loop];
        printf("%.*s%p: buckets[%d] = %p\n", deepth * DEEPTH * 2, prefix, &(hash->buckets[loop]), loop, elt);
		if (elt)
		{
			while (elt->value)
			{
				uintptr_t value = (uintptr_t)elt->value;
				if ((value & 3) == 0 || (value & 3) == 1)  //注意位操作与逻辑运算符的优先级
				{
					value &= (uintptr_t)~3;
					//值得参考的是%.*s的输出,通过参数控制输出字符的个数
					printf("%.*sbuckets %d: %p {value = \"%.*s\"%.*s, len = %d, name = \"%.*s\"%.*s}\n", 
						deepth * DEEPTH * 2, prefix, loop, elt, ((ngx_str_t*)value)->len, ((ngx_str_t*)value)->data, MAX_IP_LEN - ((ngx_str_t*)value)->len, prefix, 
						elt->len, elt->len, elt->name, MAX_URL_LEN - elt->len, prefix); 
				}
				else
				{
					wdc = (ngx_hash_wildcard_t *)(value & (uintptr_t)~3);
					printf("%.*sbuckets %d: %p: {value = \"%-16p\", len = %d, name = \"%.*s\"%.*s}\n", 
						deepth * DEEPTH * 2, prefix, loop, elt, wdc, elt->len, elt->len, elt->name, MAX_URL_LEN - elt->len, prefix); 
					dump_hash_wildcard(wdc, deepth + 1);
				}
				elt = (ngx_hash_elt_t *)ngx_align_ptr(&elt->name[0] + elt->len, sizeof(void *));
			}
		}
    }
}
void dump_hash(ngx_hash_t *hash, ngx_array_t *array)
{
    ngx_uint_t loop;
    char prefix[] = "                 ";
    u_short test[MAX_NUM] = {0};
    ngx_uint_t key;
    ngx_hash_key_t* elts;

    if (hash == NULL)
        return;

    printf("hash = %p: **buckets = %p, size = %d\n", hash, hash->buckets, hash->size);

    for (loop = 0; loop < hash->size; loop++)
    {
        ngx_hash_elt_t *elt = hash->buckets[loop];
        printf("  %p: buckets[%d] = %p\n", &(hash->buckets[loop]), loop, elt);
    }
    printf("\n");

    elts = (ngx_hash_key_t*)array->elts;
    for (loop = 0; loop < array->nelts; loop++)
    {
        key = elts[loop].key_hash % hash->size;
        ngx_hash_elt_t *elt = (ngx_hash_elt_t *) ((u_char *) hash->buckets[key] + test[key]);

		//值得参考的是%.*s的输出,通过参数控制输出字符的个数
        printf("  key %-10d: buckets %d: %p: {value = \"%.*s\"%.*s, len = %d, name = \"%.*s\"%.*s}\n", 
            elts[loop].key_hash, key, elt, ((ngx_str_t*)elt->value)->len, ((ngx_str_t*)elt->value)->data, MAX_IP_LEN - ((ngx_str_t*)elt->value)->len, prefix, elt->len,  
            elt->len, elt->name, MAX_URL_LEN - elt->len, prefix); //replace elt->name with url

        test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&elts[loop]));
    }
}


void* add_urls_to_array(ngx_pool_t *pool, ngx_hash_keys_arrays_t *ha, ngx_array_t *url, ngx_array_t *value)
{
	ngx_uint_t loop;
	ngx_int_t	rc;
	ngx_str_t	*strUrl, *strValue;
	
	memset(ha, 0, sizeof(ngx_hash_keys_arrays_t));
	ha->temp_pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, &ngx_log);
	ha->pool = pool;
    if (ngx_hash_keys_array_init(ha, NGX_HASH_LARGE) != NGX_OK) {
        goto failed;
    }
	
	strUrl = url->elts;
	strValue = value->elts;
	for (loop = 0; loop < url->nelts; loop++)
	{
		rc = ngx_hash_add_key(ha, &strUrl[loop], &strValue[loop],
                                  NGX_HASH_WILDCARD_KEY);

            if (rc == NGX_ERROR) {
                goto failed;
            }
	}
    return ha;    

failed:
    ngx_destroy_pool(ha->temp_pool);
    return NULL;
}

void find_test(ngx_hash_combined_t *hash, ngx_str_t addr[], int num)
{
    ngx_uint_t key;
    int loop;
    char prefix[] = "          ";

    for (loop = 0; loop < num; loop++)
    {
        key = ngx_hash_key_lc(addr[loop].data, addr[loop].len);
        ngx_str_t *value = ngx_hash_find_combined(hash, key, addr[loop].data, addr[loop].len);
        if (value)
        {
            printf("(url = \"%s\"%.*s, key = %-11d) found, (ip = \"%.*s%.*s\")\n", 
                addr[loop].data, MAX_URL_LEN - addr[loop].len, prefix, key, value->len, value->data, MAX_IP_LEN - value->len, prefix);
        }
        else
        {
            printf("(url = \"%s\"%.*s, key = %-11d) not found!\n", 
                addr[loop].data, MAX_URL_LEN - addr[loop].len, prefix, key);
        }
    }
}
测试结果:
--------------------------------
          the pool:
--------------------------------
pool = 0x20b7020
  .d
    .last = 0x20b7070
    .end = 0x20b7420
    .next = (nil)
    .failed = 0
  .max = 944
  .current = 0x20b7020
  .chain = (nil)
  .large = (nil)
  .cleanup = (nil)
  .log = 0x608700
available pool memory = 944

--------------------------------
create and add urls to it:
--------------------------------
--------------------------------
         array.keys :
--------------------------------
array = 0x7ffce5196448
  .elts = 0x7f04ed9bc010
  .nelts = 9
  .size = 32
  .nalloc = 16384
  .pool = 0x20b7840
  elements:
    0x7f04ed9bc010: {key = ("www.qq.com"     , 10), key_hash = 203430122   , value = "14.17.42.40"    }
    0x7f04ed9bc030: {key = ("www.baidu.com"  , 13), key_hash = 270263191   , value = "180.97.33.107"  }
    0x7f04ed9bc050: {key = ("www.taobao.com" , 14), key_hash = 123724152   , value = "116.207.117.62" }
    0x7f04ed9bc070: {key = ("www.360.cn"     , 10), key_hash = -1626056701 , value = "119.84.14.199"  }
    0x7f04ed9bc090: {key = ("www.google.com" , 14), key_hash = -702889725  , value = "93.46.8.89"     }
    0x7f04ed9bc0b0: {key = ("www.sina.com.cn", 15), key_hash = 1528635686  , value = "121.14.1.189"   }
    0x7f04ed9bc0d0: {key = ("www.163.com"    , 11), key_hash = -640386838  , value = "61.136.167.72"  }
    0x7f04ed9bc0f0: {key = ("www.sohu.com"   , 12), key_hash = 1313636595  , value = "119.97.155.2"   }
    0x7f04ed9bc110: {key = ("test.yexin.com" , 14), key_hash = -650258232  , value = "255.255.255.1"  }

--------------------------------
         array.dns_wc_head :
--------------------------------
array = 0x7ffce5196478
  .elts = 0x7f04ed93b010
  .nelts = 3
  .size = 32
  .nalloc = 16384
  .pool = 0x20b7840
  elements:
    0x7f04ed93b010: {key = ("cn.com.baidu."  , 13), key_hash = 0           , value = "180.97.33.107"  }
    0x7f04ed93b030: {key = ("com.baidu."     , 10), key_hash = 0           , value = "180.97.33.107"  }
    0x7f04ed93b050: {key = ("com.google."    , 11), key_hash = 0           , value = "93.46.8.89"     }

--------------------------------
         array.dns_wc_tail :
--------------------------------
array = 0x7ffce51964a8
  .elts = 0x7f04ed8ba010
  .nelts = 2
  .size = 32
  .nalloc = 16384
  .pool = 0x20b7840
  elements:
    0x7f04ed8ba010: {key = ("www.qq"         , 6 ), key_hash = 0           , value = "14.17.42.40"    }
    0x7f04ed8ba030: {key = ("test.yexin"     , 10), key_hash = 0           , value = "255.255.255.255"}

--------------------------------
         array.keys_hash :
--------------------------------
i = 474
array = 0x7f04ed40da20
  .elts = 0x20b7bb0
  .nelts = 1
  .size = 16
  .nalloc = 4
  .pool = 0x20b7840
  elements:
    0x20b7bb0: {key = ("www.google.com" , 14), key_hash = 0           , value = NULL}

i = 2741
array = 0x7f04ed423c58
  .elts = 0x20b7bf0
  .nelts = 1
  .size = 16
  .nalloc = 4
  .pool = 0x20b7840
  elements:
    0x20b7bf0: {key = ("www.sina.com.cn", 15), key_hash = 0           , value = NULL}

i = 2876
array = 0x7f04ed425170
  .elts = 0x20b7c70
  .nelts = 1
  .size = 16
  .nalloc = 4
  .pool = 0x20b7840
  elements:
    0x20b7c70: {key = ("www.sohu.com"   , 12), key_hash = 0           , value = NULL}

i = 3339
array = 0x7f04ed4299c8
  .elts = 0x20b7c30
  .nelts = 1
  .size = 16
  .nalloc = 4
  .pool = 0x20b7840
  elements:
    0x20b7c30: {key = ("www.163.com"    , 11), key_hash = 0           , value = NULL}

i = 5437
array = 0x7f04ed43e198
  .elts = 0x20b7b30
  .nelts = 1
  .size = 16
  .nalloc = 4
  .pool = 0x20b7840
  elements:
    0x20b7b30: {key = ("www.taobao.com" , 14), key_hash = 0           , value = NULL}

i = 6447
array = 0x7f04ed447f68
  .elts = 0x20b7ab0
  .nelts = 1
  .size = 16
  .nalloc = 4
  .pool = 0x20b7840
  elements:
    0x20b7ab0: {key = ("www.qq.com"     , 10), key_hash = 0           , value = NULL}

i = 7515
array = 0x7f04ed452648
  .elts = 0x20b7af0
  .nelts = 1
  .size = 16
  .nalloc = 4
  .pool = 0x20b7840
  elements:
    0x20b7af0: {key = ("www.baidu.com"  , 13), key_hash = 0           , value = NULL}

i = 7611
array = 0x7f04ed453548
  .elts = 0x20b7cb0
  .nelts = 1
  .size = 16
  .nalloc = 4
  .pool = 0x20b7840
  elements:
    0x20b7cb0: {key = ("test.yexin.com" , 14), key_hash = 0           , value = NULL}

i = 7759
array = 0x7f04ed454c68
  .elts = 0x20b79a8
  .nelts = 1
  .size = 16
  .nalloc = 4
  .pool = 0x20b7840
  elements:
    0x20b79a8: {key = ("baidu.com"      , 9 ), key_hash = 0           , value = NULL}

i = 9371
array = 0x7f04ed464848
  .elts = 0x20b7b70
  .nelts = 1
  .size = 16
  .nalloc = 4
  .pool = 0x20b7840
  elements:
    0x20b7b70: {key = ("www.360.cn"     , 10), key_hash = 0           , value = NULL}

--------------------------------
         array.dns_wc_head_hash :
--------------------------------
i = 4033
array = 0x7f04ed3ce638
  .elts = 0x20b7900
  .nelts = 1
  .size = 16
  .nalloc = 4
  .pool = 0x20b7840
  elements:
    0x20b7900: {key = ("baidu.com.cn"   , 12), key_hash = 0           , value = NULL}

i = 4603
array = 0x7f04ed3d3f48
  .elts = 0x20b7a60
  .nelts = 1
  .size = 16
  .nalloc = 4
  .pool = 0x20b7840
  elements:
    0x20b7a60: {key = ("google.com"     , 10), key_hash = 0           , value = NULL}

i = 7759
array = 0x7f04ed3f2c68
  .elts = 0x20b7958
  .nelts = 1
  .size = 16
  .nalloc = 4
  .pool = 0x20b7840
  elements:
    0x20b7958: {key = ("baidu.com"      , 9 ), key_hash = 0           , value = NULL}

--------------------------------
         array.dns_wc_tail_hash :
--------------------------------
i = 2198
array = 0x7f04ed35a780
  .elts = 0x20b7d00
  .nelts = 1
  .size = 16
  .nalloc = 4
  .pool = 0x20b7840
  elements:
    0x20b7d00: {key = ("test.yexin."    , 11), key_hash = 0           , value = NULL}

i = 8396
array = 0x7f04ed396ff0
  .elts = 0x20b7a08
  .nelts = 1
  .size = 16
  .nalloc = 4
  .pool = 0x20b7840
  elements:
    0x20b7a08: {key = ("www.qq."        , 7 ), key_hash = 0           , value = NULL}

--------------------------------
          the pool:
--------------------------------
pool = 0x20b7020
  .d
    .last = 0x20b741d
    .end = 0x20b7420
    .next = 0x20b7430
    .failed = 0
  .max = 944
  .current = 0x20b7020
  .chain = (nil)
  .large = (nil)
  .cleanup = (nil)
  .log = 0x608700
available pool memory = 3

pool = 0x20b7430
  .d
    .last = 0x20b754b
    .end = 0x20b7830
    .next = (nil)
    .failed = 0
  .max = 774912049
  .current = 0x3730312e33
  .chain = 0x332e37392e303831
  .large = 0x3730312e33
  .cleanup = 0x32342e37312e3431
  .log = 0x30342e
available pool memory = 741

--------------------------------
  array.dns_wc_head after sort:
--------------------------------
array = 0x7ffce5196478
  .elts = 0x7f04ed93b010
  .nelts = 3
  .size = 32
  .nalloc = 16384
  .pool = 0x20b7840
  elements:
    0x7f04ed93b010: {key = ("cn.com.baidu."  , 13), key_hash = 0           , value = "180.97.33.107"  }
    0x7f04ed93b030: {key = ("com.baidu."     , 10), key_hash = 0           , value = "180.97.33.107"  }
    0x7f04ed93b050: {key = ("com.google."    , 11), key_hash = 0           , value = "93.46.8.89"     }

--------------------------------
  array.dns_wc_tail after sort:
--------------------------------
array = 0x7ffce51964a8
  .elts = 0x7f04ed8ba010
  .nelts = 2
  .size = 32
  .nalloc = 16384
  .pool = 0x20b7840
  elements:
    0x7f04ed8ba010: {key = ("test.yexin"     , 10), key_hash = 0           , value = "255.255.255.255"}
    0x7f04ed8ba030: {key = ("www.qq"         , 6 ), key_hash = 0           , value = "14.17.42.40"    }

--------------------------------
the hash:
--------------------------------
hash = 0x7ffce5196410: **buckets = 0x20b7550, size = 9
  0x20b7550: buckets[0] = 0x20b75a0
  0x20b7558: buckets[1] = 0x20b75c0
  0x20b7560: buckets[2] = 0x20b75e0
  0x20b7568: buckets[3] = 0x20b7600
  0x20b7570: buckets[4] = 0x20b7620
  0x20b7578: buckets[5] = 0x20b7660
  0x20b7580: buckets[6] = (nil)
  0x20b7588: buckets[7] = 0x20b76a0
  0x20b7590: buckets[8] = (nil)

  key 203430122 : buckets 4: 0x20b7620: {value = "14.17.42.40"    , len = 10, name = "www.qq.com"     }
  key 270263191 : buckets 2: 0x20b75e0: {value = "180.97.33.107"  , len = 13, name = "www.baidu.com"  }
  key 123724152 : buckets 0: 0x20b75a0: {value = "116.207.117.62" , len = 14, name = "www.taobao.com" }
  key -1626056701: buckets 7: 0x20b76a0: {value = "119.84.14.199"  , len = 10, name = "www.360.cn"     }
  key -702889725: buckets 1: 0x20b75c0: {value = "93.46.8.89"     , len = 14, name = "www.google.com" }
  key 1528635686: buckets 5: 0x20b7660: {value = "121.14.1.189"   , len = 15, name = "www.sina.com.cn"}
  key -640386838: buckets 7: 0x20b76b8: {value = "61.136.167.72"  , len = 11, name = "www.163.com"    }
  key 1313636595: buckets 3: 0x20b7600: {value = "119.97.155.2"   , len = 12, name = "www.sohu.com"   }
  key -650258232: buckets 4: 0x20b7638: {value = "255.255.255.1"  , len = 14, name = "test.yexin.com" }
value = NULL
hash = 0x20b77d8: **buckets = 0x20b77f0, size = 1
0x20b77f0: buckets[0] = 0x20bc0e0
buckets 0: 0x20bc0e0: {value = "0x20b7758       ", len = 2, name = "cn"             }
    value = NULL
    hash = 0x20b7758: **buckets = 0x20b7770, size = 1
        0x20b7770: buckets[0] = 0x20b7780
        buckets 0: 0x20b7780: {value = "0x20b76f8       ", len = 3, name = "com"            }
        value = NULL
        hash = 0x20b76f8: **buckets = 0x20b7710, size = 1
                0x20b7710: buckets[0] = 0x20b7720
                buckets 0: 0x20b7720 {value = "180.97.33.107"  , len = 5, name = "baidu"          }
buckets 0: 0x20bc0f0: {value = "0x20b77b8       ", len = 3, name = "com"            }
    value = NULL
    hash = 0x20b77b8: **buckets = 0x20b77d0, size = 1
        0x20b77d0: buckets[0] = 0x20bc080
        buckets 0: 0x20bc080 {value = "180.97.33.107"  , len = 5, name = "baidu"          }
        buckets 0: 0x20bc090 {value = "93.46.8.89"     , len = 6, name = "google"         }
value = NULL
hash = 0x20bc1e0: **buckets = 0x20bc1f8, size = 1
0x20bc1f8: buckets[0] = 0x20bc200
buckets 0: 0x20bc200: {value = "0x20b77f8       ", len = 4, name = "test"           }
    value = NULL
    hash = 0x20b77f8: **buckets = 0x20b7810, size = 1
        0x20b7810: buckets[0] = 0x20bc140
        buckets 0: 0x20bc140 {value = "255.255.255.255", len = 5, name = "yexin"          }
buckets 0: 0x20bc210: {value = "0x20bc180       ", len = 3, name = "www"            }
    value = NULL
    hash = 0x20bc180: **buckets = 0x20bc198, size = 1
        0x20bc198: buckets[0] = 0x20bc1a0
        buckets 0: 0x20bc1a0 {value = "14.17.42.40"    , len = 2, name = "qq"             }

--------------------------------
the pool:
--------------------------------
pool = 0x20b7020
  .d
    .last = 0x20b741d
    .end = 0x20b7420
    .next = 0x20b7430
    .failed = 1
  .max = 944
  .current = 0x20b7020
  .chain = (nil)
  .large = (nil)
  .cleanup = (nil)
  .log = 0x608700
available pool memory = 3

pool = 0x20b7430
  .d
    .last = 0x20b7818
    .end = 0x20b7830
    .next = 0x20bc060
    .failed = 0
  .max = 774912049
  .current = 0x3730312e33
  .chain = 0x332e37392e303831
  .large = 0x3730312e33
  .cleanup = 0x32342e37312e3431
  .log = 0x30342e
available pool memory = 24

pool = 0x20bc060
  .d
    .last = 0x20bc260
    .end = 0x20bc460
    .next = (nil)
    .failed = 0
  .max = 34304513
  .current = 0x75646961620005
  .chain = 0x20b7231
  .large = 0x656c676f6f670006
  .cleanup = (nil)
  .log = (nil)
available pool memory = 512

--------------------------------
find test:
--------------------------------
(url = "*..com."        , key = -17068745  ) not found!
(url = "*.baidu.com.cn" , key = 234385007  ) found, (ip = "180.97.33.107  ")
(url = "*.baidu.com"    , key = -724157846 ) found, (ip = "180.97.33.107  ")
(url = ".baidu.com"     , key = 1733355200 ) found, (ip = "180.97.33.107  ")
(url = "www.qq.*"       , key = -1367384621) found, (ip = "14.17.42.40    ")
(url = "*.google.com"   , key = -1465170800) found, (ip = "93.46.8.89     ")
(url = "www.qq.com"     , key = 203430122  ) found, (ip = "14.17.42.40    ")
(url = "www.baidu.com"  , key = 270263191  ) found, (ip = "180.97.33.107  ")
(url = "www.taobao.com" , key = 123724152  ) found, (ip = "116.207.117.62 ")
(url = "www.360.cn"     , key = -1626056701) found, (ip = "119.84.14.199  ")
(url = "www.google.com" , key = -702889725 ) found, (ip = "93.46.8.89     ")
(url = "www.sina.com.cn", key = 1528635686 ) found, (ip = "121.14.1.189   ")
(url = "www.163.com"    , key = -640386838 ) found, (ip = "61.136.167.72  ")
(url = "www.sohu.com"   , key = 1313636595 ) found, (ip = "119.97.155.2   ")
(url = "test.yexin.com" , key = -650258232 ) found, (ip = "255.255.255.1  ")
(url = ".baidu.com"     , key = 1733355200 ) found, (ip = "180.97.33.107  ")
(url = "test.yexin.*"   , key = -2061009615) found, (ip = "255.255.255.255")
(url = "www.qq.*"       , key = -1367384621) found, (ip = "14.17.42.40    ")

(url = "*.xx.xx"        , key = 51835370   ) not found!
(url = "www.baidu.com"  , key = 270263191  ) found, (ip = "180.97.33.107  ")
(url = "www.baidu."     , key = -239024726 ) not found!

参考:
《深入理解nginx》
http://www.cnblogs.com/chengxuyuancc/p/3782808.html
http://www.uml.org.cn/zjjs/201504131.asp



你可能感兴趣的:(nginx学习——建立hash表的前提条件)