码迷,mamicode.com
首页 > 其他好文 > 详细

hash

时间:2015-03-31 09:08:29      阅读:117      评论:0      收藏:0      [点我收藏+]

标签:

   Nginx的hash结构,创建后不可修改,唯一的操作就是查询;


hash结构创建后的示意图

技术分享





ngx_hash_key_t

ngx_hash_key_t的提供将是ngx_hash_init的参数,由ngx_hash_keys_arrays_t提供;

typedef struct {
    ngx_str_t         key;    //元素关键字,字符串
    ngx_uint_t        key_hash; //散列计算出的关键码
    void             *value;  //指向用户自定义的数据
} ngx_hash_key_t;

typedef struct {
    ngx_uint_t        hsize;		//简易散列表的大小

    ngx_pool_t       *pool;
    ngx_pool_t       *temp_pool;

    ngx_array_t       keys;			//使用动态数组保存着不含有统配符关键字的元素
    ngx_array_t      *keys_hash;	//简易散列表

    ngx_array_t       dns_wc_head;	//保存着含有前置通配符关键字的元素的中间关键字
    ngx_array_t      *dns_wc_head_hash; 

    ngx_array_t       dns_wc_tail;	//保存着含有后置通配符关键字的元素的中间关键字
    ngx_array_t      *dns_wc_tail_hash;
} ngx_hash_keys_arrays_t;

初始化ngx_hash_keys_arrays_t

ngx_int_t
ngx_hash_keys_array_init(ngx_hash_keys_arrays_t *ha, ngx_uint_t type)


添加ngx_hash_key_t

ngx_int_t
ngx_hash_add_key(ngx_hash_keys_arrays_t *ha, ngx_str_t *key, void *value,
    ngx_uint_t flags)


hash初始化

//hash初始化
ngx_int_t
ngx_hash_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names, ngx_uint_t nelts)
{
    u_char          *elts;
    size_t           len;
    u_short         *test;
    ngx_uint_t       i, n, key, size, start, bucket_size;
    ngx_hash_elt_t  *elt, **buckets;

    for (n = 0; n < nelts; n++) {
        if (hinit->bucket_size < NGX_HASH_ELT_SIZE(&names[n]) + sizeof(void *))	
		  //确保一个bucket至少能存放一个实际元素以及结束哨兵
        {
            ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0,
                          "could not build the %s, you should "
                          "increase %s_bucket_size: %i",
                          hinit->name, hinit->name, hinit->bucket_size);
            return NGX_ERROR;
        }
    }

//ngx_hash_t

    test = ngx_alloc(hinit->max_size * sizeof(u_short), hinit->pool->log);
    if (test == NULL) {
        return NGX_ERROR;
    }

	//bucket_size为一个槽的空间最大大小
    bucket_size = hinit->bucket_size - sizeof(void *);  	//计算一个bucket除去哨兵所占空间后的实际可用空间大小

	//bucket_size / (2 * sizeof(void*))为最大可以存储的实际元素个数
    start = nelts / (bucket_size / (2 * sizeof(void *)));	//计算所需bucket的最小个数
    start = start ? start : 1;

    if (hinit->max_size > 10000 && nelts && hinit->max_size / nelts < 100) {
		//说明要存的实际个数非常多,那就有必要将start起始值抬高,经验值
        start = hinit->max_size - 1000;
    }

	//根据初始化散列表预先加入的所有元素确定size
    for (size = start; size < hinit->max_size; size++) {		//获得hash结构最终节点数的逻辑

        ngx_memzero(test, size * sizeof(u_short));

        for (n = 0; n < nelts; n++) {		
            if (names[n].key.data == NULL) {
                continue;
            }

            key = names[n].key_hash % size;	//大小
            test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n]));	//这个bucket的总大小

#if 0
            ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,
                          "%ui: %ui %ui \"%V\"",
                          size, key, test[key], &names[n].key);
#endif

            if (test[key] > (u_short) bucket_size) {	//如果任何一个buckets满溢了,就到下一个
                goto next;
            }
        }

        goto found;

    next:

        continue;
    }

    ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0,
                  "could not build the %s, you should increase "
                  "either %s_max_size: %i or %s_bucket_size: %i",
                  hinit->name, hinit->name, hinit->max_size,
                  hinit->name, hinit->bucket_size);

    ngx_free(test);

    return NGX_ERROR;

found:

    for (i = 0; i < size; i++) {
        test[i] = sizeof(void *);
    }

    for (n = 0; n < nelts; n++) {	
        if (names[n].key.data == NULL) {
            continue;
        }

        key = names[n].key_hash % size;
        test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n]));
    }

    len = 0;

    for (i = 0; i < size; i++) {
        if (test[i] == sizeof(void *)) {
            continue;
        }

        test[i] = (u_short) (ngx_align(test[i], ngx_cacheline_size));

        len += test[i];
    }

    if (hinit->hash == NULL) {
        hinit->hash = ngx_pcalloc(hinit->pool, sizeof(ngx_hash_wildcard_t)
                                             + size * sizeof(ngx_hash_elt_t *));
        if (hinit->hash == NULL) {
            ngx_free(test);
            return NGX_ERROR;
        }

        buckets = (ngx_hash_elt_t **)
                      ((u_char *) hinit->hash + sizeof(ngx_hash_wildcard_t));

    } else {
        buckets = ngx_pcalloc(hinit->pool, size * sizeof(ngx_hash_elt_t *));
        if (buckets == NULL) {
            ngx_free(test);
            return NGX_ERROR;
        }
    }

    elts = ngx_palloc(hinit->pool, len + ngx_cacheline_size);
    if (elts == NULL) {
        ngx_free(test);
        return NGX_ERROR;
    }

    elts = ngx_align_ptr(elts, ngx_cacheline_size);

    for (i = 0; i < size; i++) {
        if (test[i] == sizeof(void *)) {
            continue;
        }

        buckets[i] = (ngx_hash_elt_t *) elts;
        elts += test[i];

    }

    for (i = 0; i < size; i++) {
        test[i] = 0;
    }

    for (n = 0; n < nelts; n++) {
        if (names[n].key.data == NULL) {
            continue;
        }

        key = names[n].key_hash % size;
        elt = (ngx_hash_elt_t *) ((u_char *) buckets[key] + test[key]);

        elt->value = names[n].value;
        elt->len = (u_short) names[n].key.len;

        ngx_strlow(elt->name, names[n].key.data, names[n].key.len);

        test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n]));   //存放
    }

    for (i = 0; i < size; i++) {
        if (buckets[i] == NULL) {
            continue;
        }

        elt = (ngx_hash_elt_t *) ((u_char *) buckets[i] + test[i]);

        elt->value = NULL;
    }

    ngx_free(test);

    hinit->hash->buckets = buckets;
    hinit->hash->size = size;   //大小


    return NGX_OK;
}


查询元素

//查询元素
void *
ngx_hash_find(ngx_hash_t *hash, ngx_uint_t key, u_char *name, size_t len)
{
    ngx_uint_t       i;
    ngx_hash_elt_t  *elt;

#if 0
    ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "hf:\"%*s\"", len, name);
#endif

    elt = hash->buckets[key % hash->size];	//找到对应的槽

    if (elt == NULL) {
        return NULL;
    }

    while (elt->value) {	//用户自定义的数据
        if (len != (size_t) elt->len) {		//首先比较的是元素关键字的长度,长度不相等直接跳出
            goto next;
        }

        for (i = 0; i < len; i++) {
            if (name[i] != elt->name[i]) {	//然后比较每一个字符的大小
                goto next;
            }
        }

        return elt->value;		//找到了

    next:

        elt = (ngx_hash_elt_t *) ngx_align_ptr(&elt->name[0] + elt->len,	//到下一个偏移的elt位置
                                               sizeof(void *));
        continue;
    }

    return NULL;
}


hash

标签:

原文地址:http://blog.csdn.net/skyuppour/article/details/44759337

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!