Nginx源代码分析--基本数据结构--hash

来源:互联网 发布:网络给手机发短信 编辑:程序博客网 时间:2024/05/19 23:25

咱们来看这段代码:

 

 

//这段让人欢喜让人忧的宏定义

 

//先说说有什么需求:

 

         //桶的大小是有限制的,在ngx_hash_init_t 数据结构中有bucket_size这个值。

         //因此,针对桶的每个k-v对的分配内存的大小和附属的数据机构中元素占用的内存的加和值,必须小于这个值。

         //否则如果加和值,大于bucket_size,则该k-v对无法分配到桶,因为没有这样大小的桶容纳这个k-v对所需要的数据结构。

 

//这段宏的目的是,计算每个需要存储到bucket的k-v对的分配内存字节数,以便之后进行上述加和值的计算,并与bucket_size比较,以判断//是否合法。

 

//由于name这个k-v对中,会有key的值存储到bucket,而key的长度,即key.size_t不占空间,另外key_hash也不占空间,这都是分配过

//程中需要用的,void* value是占内存的。所以整个k-v对数据占用的内存大小是key.len大小的key的数值经过粒度为sizeof(void*)对齐

//后,加上void *value的占用内存大小。

 

 

//当然,只要分配到bucket键值对,还必须有一个该bucket键值对结束表示,即void *value=NULL。这个在与bucket_size比较之前要加

//上,参见后述代码

 

#define NGX_HASH_ELT_SIZE(name)                                               /
    (sizeof(void *) + ngx_align((name)->key.len + 2, sizeof(void *)))

 

 

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;

    //对每个k-v键值对,都要进行检测

 

    for (n = 0; n < nelts; n++) {

 

        //要加上sizeof(void *),因为bucket最后需要k-v对结束标志,是void * value来做的。


        if (hinit->bucket_size < NGX_HASH_ELT_SIZE(&names[n]) + sizeof(void *))
        {
            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;
        }
    }

 

   //估计给这个内存池,取名test的意思是下边要“看看大概需要多少个桶"。这个老毛子英语看来也不咋地,顶多6级,因为想法跟我一样。

 

   //分配最大大小即,hinit->max_size指定大小的内存池。


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

 

   //这里为什么要-sizeof(void *)呢?还是那个原因,我得预留一个当标识啊。其他的键值对可不能占用我这个标识,嘿嘿~~

  //这个计算是为了之后做决定。给一个bucket分配的多个k-v对占用的空间是不能大于这个值的,因为大于了则加上末尾表示void  

  //*==NULL占用的空间后,就大于bucket_size了。这种情况下,需要把这个k-v值放到其他桶。其实也说明,当前的桶数目不足。参见后边 

  //代码


    bucket_size = hinit->bucket_size - sizeof(void *);

 

  //这个老毛子,又开始耍小聪明了:预测一下。这个家伙觉得,需要的桶数目小于start数目的可能性太小了,所以应该从start开始试。star的 

  //值是如下计算的:

  //是k-v对的个数,除以bucket_size/(2*sizeof(void *))的个数。

  //当然如果是小数(start=0)则应该变1,桶个数不能是0吧,嘿嘿。

 


    start = nelts / (bucket_size / (2 * sizeof(void *)));

 

 

    start = start ? start : 1;

 

   //老毛子还觉得。。。。。。


    if (hinit->max_size > 10000 && hinit->max_size / nelts < 100) {
        start = hinit->max_size - 1000;
    }

 

 

   //最后终于得出个,他认为的小于这个值可能性很小的值,即start,并开始从这个值试验,到底需要多少个桶。

   //这个就是试验过程了。

 

    for (size = start; size < hinit->max_size; size++) {

 

         //老毛子每次试验桶多少合适,和每个桶里有多少k-v对,总是用test的前几个存储这些值。不用这么小气吧,整个局部变量又不浪费内

        //存。

        //这里是每次都把存储每个桶k-v值的单元清0
        ngx_memzero(test, size * sizeof(u_short));

        for (n = 0; n < nelts; n++) {
            if (names[n].key.data == NULL) {
                continue;
            }
            //这个就是计算分配到哪个桶。size是预测的应该有多少个桶。


            key = names[n].key_hash % size;

 

            //分配的这个桶的内存数目应该加上对齐后内存数目。

 

            test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n]));

 

 

//和Log级别有关,如果0 则打印log,否则不打印
#if 0
            ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,
                          "%ui: %ui %ui /"%V/"",
                          size, key, test[key], &names[n].key);
#endif

 

            //这里终于用到了bucket_size,大于这个值,则说明这个size不合适啊goto next,调整下桶的数目


            if (test[key] > (u_short) bucket_size) {
                goto next;
            }
        }

 

        //如果都满足了,哈哈,这个数目就足够了。跳转到found去处理吧。起个名字叫finish也好啊。英语水平果然和我差不多。


        goto found;

    next:

        continue;
    }

原创粉丝点击