菜鸟nginx源码剖析数据结构篇(七) 哈希表 ngx_hash_t(下)

  • Author:Echo Chen(陈斌)

  • Email:chenb19870707@gmail.com

  • Blog:Blog.csdn.net/chen19870707

  • Date:Nov 3rd, 2014

    在前面一篇文章《菜鸟nginx源码剖析数据结构篇(六) 哈希表 ngx_hash_t(上)》 里介绍了 普通哈希表 和 带有通配符的哈希表 的基本结构和初始化方法,由于篇幅的原因未能解析完结,这篇继续解源码中剩余的部分。

  • 1.普通哈希表ngx_hash_t查找 ngx_hash_find

    普通哈希表的查找比较简单,思想就是先根据hash值找到对应桶,然后遍历这个桶的每一个元素,逐字匹配是否关键字完全相同,完全相同则找到,否则继续,直至找到这个桶的结尾(value = NULL)。

    1. 1: /* @hash 表示哈希表的结构体
    1. 2: * @key  表示根据哈希方法计算出来的hash
    1. 3: * @name 表示实际关键字地址
    1. 4: * @len  表示实际关键字长度
    1. 5: */
    1. 6: void *ngx_hash_find(ngx_hash_t *hash, ngx_uint_t key, u_char *name, size_t len)
    1. 7: {
    1. 8:     ngx_uint_t       i;
    1. 9:     ngx_hash_elt_t  *elt;
    1. 10: 
    1. 11:     //根据hash值找到桶索引
    1. 12:     elt = hash->buckets[key % hash->size];
    1. 13: 
    1. 14:     if (elt == NULL) {
    1. 15:         return NULL;
    1. 16:     }
    1. 17: 
    1. 18:     //桶结束的标志为 value 为 NULL
    1. 19:     while (elt->value) {
    1. 20:         //关键字长度不匹配,下一个
    1. 21:         if (len != (size_t) elt->len) {
    1. 22:             goto next;
    1. 23:         }
    1. 24:        
    1. 25:         //遍历关键字每一个字符,若全部对得上,则找到,否则有一个不同下一个
    1. 26:         for (i = 0; i < len; i++) {
    1. 27:             if (name[i] != elt->name[i]) {
    1. 28:                 goto next;
    1. 29:             }
    1. 30:         }
    1. 31: 
    1. 32:         return elt->value;
    1. 33: 
    1. 34:     next:
    1. 35:         //从elt关键字开始向后移动关键字长度个,并行对齐,即为下一个ngx_hash_elt_t
    1. 36:         elt = (ngx_hash_elt_t *) ngx_align_ptr(&elt->name[0] + elt->len,
    1. 37:                                                sizeof(void *));
    1. 38:         continue;
    1. 39:     }
    1. 40: 
    1. 41:     return NULL;
    1. 42: }

2.支持通配符哈希表的前置通配符查找 ngx_hash_find_wc_head

还记得上节在ngx_hash_wildcard_init中,用value指针低2位来携带信息吗?其是有特殊意义的,如下图所示

  • 00 - value 是 "example.com" 和 "*.example.com"的数据指针
  • 01 - value 仅仅是 "*.example.com"的数据指针
  • 10 - value 是 支持通配符哈希表是 "example.com" 和 "*.example.com" 指针
  • 11 - value 仅仅是 "*.example.com"的指针 

查找的思路就是根据value的指针信息来搜索,源代码如下:

  1. 1: /* @hwc  表示支持通配符的哈希表的结构体
  1. 2: * @name 表示实际关键字地址
  1. 3: * @len  表示实际关键字长度
  1. 4: */
  1. 5: void *ngx_hash_find_wc_head(ngx_hash_wildcard_t *hwc, u_char *name, size_t len)
  1. 6: {
  1. 7:     void        *value;
  1. 8:     ngx_uint_t   i, n, key;
  1. 9: 
  1. 10:     n = len;
  1. 11:    
  1. 12:     //从后往前搜索第一个dot,则n 到 len-1 即为关键字中最后一个 子关键字
  1. 13:     while (n) {
  1. 14:         if (name[n - 1] == '.') {
  1. 15:             break;
  1. 16:         }
  1. 17: 
  1. 18:         n--;
  1. 19:     }
  1. 20: 
  1. 21:     key = 0;
  1. 22:    
  1. 23:     //n 到 len-1 即为关键字中最后一个 子关键字,计算其hash值
  1. 24:     for (i = n; i < len; i++) {
  1. 25:         key = ngx_hash(key, name[i]);
  1. 26:     }
  1. 27:    
  1. 28:     //调用普通查找找到关键字的value(用户自定义数据指针)
  1. 29:     value = ngx_hash_find(&hwc->hash, key, &name[n], len - n);
  1. 30: 
  1. 31:     /**还记得上节在ngx_hash_wildcard_init中,用value指针低2位来携带信息吗?其是有特殊意义的,如下:
  1. 32:      * 00 - value "example.com" "*.example.com"的数据指针
  1. 33:      * 01 - value 仅仅是 "*.example.com"的数据指针
  1. 34:      * 10 - value 支持通配符哈希表是 "example.com" "*.example.com" 指针
  1. 35:      * 11 - value 仅仅是 "*.example.com"的指针
  1. 36:      */
  1. 37:     if (value)
  1. 38:     {
  1. 39: 
  1. 40:         if ((uintptr_t) value & 2) {
  1. 41:            
  1. 42:             //搜索到了最后一个子关键字且没有通配符,如"example.com"的example
  1. 43:             if (n == 0) {
  1. 44:                 //value低两位为11,仅为"*.example.com"的指针,这里没有通配符,没招到,返回NULL
  1. 45:                 if ((uintptr_t) value & 1) {
  1. 46:                     return NULL;
  1. 47:                 }
  1. 48:                
  1. 49:                 //value低两位为10,为"example.com"的指针,value就在下一级的ngx_hash_wildcard_t 的value中,去掉携带的低2位11
  1. 50:                 hwc = (ngx_hash_wildcard_t *)
  1. 51:                                           ((uintptr_t) value & (uintptr_t) ~3);
  1. 52:                 return hwc->value;
  1. 53:             }
  1. 54:            
  1. 55:             //还未搜索完,低两位为11或10,继续去下级ngx_hash_wildcard_t中搜索
  1. 56:             hwc = (ngx_hash_wildcard_t *) ((uintptr_t) value & (uintptr_t) ~3);
  1. 57:            
  1. 58:             //继续搜索 关键字中剩余部分,如"example.com",搜索 0 到 n -1 即为 example
  1. 59:             value = ngx_hash_find_wc_head(hwc, name, n - 1);
  1. 60: 
  1. 61:             //若找到,则返回
  1. 62:             if (value)
  1. 63:             {
  1. 64:                 return value;
  1. 65:             }
  1. 66:            
  1. 67:             //低两位为00 找到,即为wc->value
  1. 68:             return hwc->value;
  1. 69:         }
  1. 70: 
  1. 71:         //低两位为01
  1. 72:         if ((uintptr_t) value & 1)
  1. 73:         {
  1. 74:             //关键字没有通配符,错误返回空
  1. 75:             if (n == 0)
  1. 76:             {
  1. 77:                 return NULL;
  1. 78:             }
  1. 79:            
  1. 80:             //有通配符,直接返回
  1. 81:             return (void *) ((uintptr_t) value & (uintptr_t) ~3);
  1. 82:         }
  1. 83: 
  1. 84:         //低两位为00,直接返回
  1. 85:         return value;
  1. 86:     }
  1. 87: 
  1. 88:     return hwc->value;
  1. 89: }

3.支持通配符哈希表的后置通配符查找 ngx_hash_find_wc_tail

ngx_hash_find_wc_tail与前置通配符查找差不多,这里value低两位仅有两种标志,更加简单:

  • 00 - value 是指向 用户自定义数据
  • 11 - value的指向下一个哈希表 
    源代码如下:
  1. 1: void *ngx_hash_find_wc_tail(ngx_hash_wildcard_t *hwc, u_char *name, size_t len)
  1. 2: {
  1. 3:     void        *value;
  1. 4:     ngx_uint_t   i, key;
  1. 5: 
  1. 6: 
  1. 7:     key = 0;
  1. 8: 
  1. 9:     //从前往前搜索第一个dot,则0 到 i 即为关键字中第一个 子关键字
  1. 10:     for (i = 0; i < len; i++)
  1. 11:     {
  1. 12:         if (name[i] == '.')
  1. 13:         {
  1. 14:             break;
  1. 15:         }
  1. 16:         //计算哈希值
  1. 17:         key = ngx_hash(key, name[i]);
  1. 18:     }
  1. 19: 
  1. 20:     //没有通配符,返回NULL
  1. 21:     if (i == len)
  1. 22:     {
  1. 23:         return NULL;
  1. 24:     }
  1. 25: 
  1. 26:     //调用普通查找找到关键字的value(用户自定义数据指针)
  1. 27:     value = ngx_hash_find(&hwc->hash, key, name, i);
  1. 28: 
  1. 29: 
  1. 30:     /**还记得上节在ngx_hash_wildcard_init中,用value指针低2位来携带信息吗?其是有特殊意义的,如下:
  1. 31:      * 00 - value 是数据指针
  1. 32:      * 11 - value的指向下一个哈希表
  1. 33:      */
  1. 34:     if (value)
  1. 35:     {
  1. 36:         //低2位为11,value的指向下一个哈希表,递归搜索
  1. 37:         if ((uintptr_t) value & 2)
  1. 38:         {
  1. 39: 
  1. 40:             i++;
  1. 41: 
  1. 42:             hwc = (ngx_hash_wildcard_t *) ((uintptr_t) value & (uintptr_t) ~3);
  1. 43: 
  1. 44:             value = ngx_hash_find_wc_tail(hwc, &name[i], len - i);
  1. 45: 
  1. 46:             //找到低两位00,返回
  1. 47:             if (value)
  1. 48:             {
  1. 49:                 return value;
  1. 50:             }
  1. 51:            
  1. 52:             //找打低两位11,返回hwc->value
  1. 53:             return hwc->value;
  1. 54:         }
  1. 55: 
  1. 56:         return value;
  1. 57:     }
  1. 58: 
  1. 59:     //低2位为00,直接返回数据
  1. 60:     return hwc->value;
  1. 61: }

4.组合哈希表查找 ngx_hash_find_combined

组合哈希表的查找思路非常简单,先在普通哈希表中查找,没找到再去前置通配符哈希表中查找,最后去后置通配符哈希表中查找,源代码如下:

  1. 1: void *ngx_hash_find_combined(ngx_hash_combined_t *hash, ngx_uint_t key, u_char *name,size_t len)
  1. 2: {
  1. 3:     void  *value;
  1. 4:    
  1. 5:     //在普通hash表中查找
  1. 6:     if (hash->hash.buckets) {
  1. 7:         value = ngx_hash_find(&hash->hash, key, name, len);
  1. 8: 
  1. 9:         if (value) {
  1. 10:             return value;
  1. 11:         }
  1. 12:     }
  1. 13: 
  1. 14:     if (len == 0) {
  1. 15:         return NULL;
  1. 16:     }
  1. 17:    
  1. 18:     //在前置通配符哈希表中查找
  1. 19:     if (hash->wc_head && hash->wc_head->hash.buckets) {
  1. 20:         value = ngx_hash_find_wc_head(hash->wc_head, name, len);
  1. 21: 
  1. 22:         if (value) {
  1. 23:             return value;
  1. 24:         }
  1. 25:     }
  1. 26:    
  1. 27:     //在后置通配符哈希表中查找
  1. 28:     if (hash->wc_tail && hash->wc_tail->hash.buckets) {
  1. 29:         value = ngx_hash_find_wc_tail(hash->wc_tail, name, len);
  1. 30: 
  1. 31:         if (value) {
  1. 32:             return value;
  1. 33:         }
  1. 34:     }
  1. 35: 
  1. 36:     return NULL;
  1. 37: }

5.支持通配符哈希表初始化 ngx_hash_wildcard_init

首先看一下ngx_hash_wildcard_init 的内存结构,当构造此类型的hash表的时候,实际上是构造了一个hash表的一个“链表”,是通过hash表中的key“链接”起来的。比如:对于“*.abc.com”将会构造出2个hash表,第一个hash表中有一个key为com的表项,该表项的value包含有指向第二个hash表的指针,而第二个hash表中有一个表项abc,该表项的value包含有指向*.abc.com对应的value的指针。那么查询的时候,比如查询www.abc.com的时候,先查com,通过查com可以找到第二级的hash表,在第二级hash表中,再查找abc,依次类推,直到在某一级的hash表中查到的表项对应的value对应一个真正的值而非一个指向下一级hash表的指针的时候,查询过程结束。

理解了这个,我们就可以看源代码了,ngx_hash_wildcard是一个递归函数,递归创建如上图的hash链表,如下为注释版源代码。

精彩的读点有:

  • 由于指针都字节对齐了,低4位肯定为0,这种操作(name->value = (void *) ((uintptr_t) wdc | (dot ? 3 : 2)) ) 巧妙的使用了指针的低位携带额外信息,节省了内存,让人不得不佩服ngx设计者的想象力。
  1. 1: /*hinit为初始化结构体指针,names为预加入哈希表数组,elts为预加入数组大小
  1. 2: 特别要注意的是这里的key已经都是被预处理过的。例如:“*.abc.com”或者“.abc.com”被预处理完成以后,
  1. 3: 变成了“com.abc.”。而“mail.xxx.*”则被预处理为“mail.xxx.”*/
  1. 4: ngx_int_t ngx_hash_wildcard_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names,ngx_uint_t nelts)
  1. 5: {
  1. 6:     size_t                len, dot_len;
  1. 7:     ngx_uint_t            i, n, dot;
  1. 8:     ngx_array_t           curr_names, next_names;
  1. 9:     ngx_hash_key_t       *name, *next_name;
  1. 10:     ngx_hash_init_t       h;
  1. 11:     ngx_hash_wildcard_t  *wdc;
  1. 12: 
  1. 13:     //初始化临时动态数组curr_names,curr_names是存放当前关键字的数组
  1. 14:     if (ngx_array_init(&curr_names, hinit->temp_pool, nelts,
  1. 15:                        sizeof(ngx_hash_key_t))
  1. 16:         != NGX_OK)
  1. 17:     {
  1. 18:         return NGX_ERROR;
  1. 19:     }
  1. 20: 
  1. 21:     //初始化临时动态数组next_names,next_names是存放关键字去掉后剩余关键字
  1. 22:     if (ngx_array_init(&next_names, hinit->temp_pool, nelts, sizeof(ngx_hash_key_t)) != NGX_OK)
  1. 23:     {
  1. 24:         return NGX_ERROR;
  1. 25:     }
  1. 26: 
  1. 27:     //遍历 names 数组
  1. 28:     for (n = 0; n < nelts; n = i)
  1. 29:     {
  1. 30:         dot = 0;
  1. 31: 
  1. 32:         //查找 dot
  1. 33:         for (len = 0; len < names[n].key.len; len++)
  1. 34:         {
  1. 35:             if (names[n].key.data[len] == '.')
  1. 36:             {
  1. 37:                 dot = 1;
  1. 38:                 break;
  1. 39:             }
  1. 40:         }
  1. 41: 
  1. 42:         //将关键字dot以前的关键字放入curr_names
  1. 43:         name = ngx_array_push(&curr_names);
  1. 44:         if (name == NULL) {
  1. 45:             return NGX_ERROR;
  1. 46:         }
  1. 47: 
  1. 48:         name->key.len = len;
  1. 49:         name->key.data = names[n].key.data;
  1. 50:         name->key_hash = hinit->key(name->key.data, name->key.len);
  1. 51:         name->value = names[n].value;
  1. 52: 
  1. 53:         dot_len = len + 1;
  1. 54: 
  1. 55:         //len指向dot后剩余关键字
  1. 56:         if (dot)
  1. 57:         {
  1. 58:             len++;
  1. 59:         }
  1. 60: 
  1. 61:         next_names.nelts = 0;
  1. 62: 
  1. 63:         //如果names[n] dot后还有剩余关键字,将剩余关键字放入next_names中
  1. 64:         if (names[n].key.len != len)
  1. 65:         {
  1. 66:             next_name = ngx_array_push(&next_names);
  1. 67:             if (next_name == NULL) {
  1. 68:                 return NGX_ERROR;
  1. 69:             }
  1. 70: 
  1. 71:             next_name->key.len = names[n].key.len - len;
  1. 72:             next_name->key.data = names[n].key.data + len;
  1. 73:             next_name->key_hash = 0;
  1. 74:             next_name->value = names[n].value;
  1. 75: 
  1. 76:         }
  1. 77: 
  1. 78:         //如果上面搜索到的关键字没有dot,从n+1遍历names,将关键字比它长的全部放入next_name
  1. 79:         for (i = n + 1; i < nelts; i++)
  1. 80:         {
  1. 81:             //前len个关键字相同
  1. 82:             if (ngx_strncmp(names[n].key.data, names[i].key.data, len) != 0) {
  1. 83:                 break;
  1. 84:             }
  1. 85: 
  1. 86: 
  1. 87:             if (!dot
  1. 88:                 && names[i].key.len > len
  1. 89:                 && names[i].key.data[len] != '.')
  1. 90:             {
  1. 91:                 break;
  1. 92:             }
  1. 93: 
  1. 94:             next_name = ngx_array_push(&next_names);
  1. 95:             if (next_name == NULL) {
  1. 96:                 return NGX_ERROR;
  1. 97:             }
  1. 98: 
  1. 99:             next_name->key.len = names[i].key.len - dot_len;
  1. 100:             next_name->key.data = names[i].key.data + dot_len;
  1. 101:             next_name->key_hash = 0;
  1. 102:             next_name->value = names[i].value;
  1. 103: 
  1. 104:         }
  1. 105: 
  1. 106:         //如果next_name非空
  1. 107:         if (next_names.nelts)
  1. 108:         {
  1. 109:             h = *hinit;
  1. 110:             h.hash = NULL;
  1. 111: 
  1. 112:             //递归,创建一个新的哈西表
  1. 113:             if (ngx_hash_wildcard_init(&h, (ngx_hash_key_t *) next_names.elts,next_names.nelts) != NGX_OK)
  1. 114:             {
  1. 115:                 return NGX_ERROR;
  1. 116:             }
  1. 117: 
  1. 118:             wdc = (ngx_hash_wildcard_t *) h.hash;
  1. 119:            
  1. 120:             //如上图,将用户value值放入新的hash表
  1. 121:             if (names[n].key.len == len)
  1. 122:             {
  1. 123:                 wdc->value = names[n].value;
  1. 124:             }
  1. 125:            
  1. 126:             //并将当前value值指向新的hash表
  1. 127:             name->value = (void *) ((uintptr_t) wdc | (dot ? 3 : 2));
  1. 128: 
  1. 129:         } else if (dot)
  1. 130:         {
  1. 131:             name->value = (void *) ((uintptr_t) name->value | 1);
  1. 132:         }
  1. 133:     }
  1. 134: 
  1. 135:     //将最外层hash初始化
  1. 136:     if (ngx_hash_init(hinit, (ngx_hash_key_t *) curr_names.elts,curr_names.nelts) != NGX_OK)
  1. 137:     {
  1. 138:         return NGX_ERROR;
  1. 139:     }
  1. 140: 
  1. 141:     return NGX_OK;
  1. 142: }

6. 哈希键数组初始化 ngx_hash_keys_array_init

初始化ngx_hash_keys_arrays_t 结构体,type的取值范围只有两个,NGX_HASH_SMALL表示初始化元素较少,NGX_HASH_LARGE表示初始化元素较多,在向ha中加入时必须调用此方法

  1. 1: ngx_int_t ngx_hash_keys_array_init(ngx_hash_keys_arrays_t *ha, ngx_uint_t type)
  1. 2: {
  1. 3:     ngx_uint_t  asize;
  1. 4: 
  1. 5:     if (type == NGX_HASH_SMALL)
  1. 6:     {
  1. 7:         asize = 4;
  1. 8:         ha->hsize = 107;
  1. 9:     }
  1. 10:     else
  1. 11:     {
  1. 12:         asize = NGX_HASH_LARGE_ASIZE;
  1. 13:         ha->hsize = NGX_HASH_LARGE_HSIZE;
  1. 14:     }
  1. 15: 
  1. 16:     //初始化 存放非通配符关键字的数组
  1. 17:     if (ngx_array_init(&ha->keys, ha->temp_pool, asize, sizeof(ngx_hash_key_t)) != NGX_OK)
  1. 18:     {
  1. 19:         return NGX_ERROR;
  1. 20:     }
  1. 21: 
  1. 22:     //初始化 存放前置通配符处理好的关键字 数组
  1. 23:     if (ngx_array_init(&ha->dns_wc_head, ha->temp_pool, asize, sizeof(ngx_hash_key_t)) != NGX_OK)
  1. 24:     {
  1. 25:         return NGX_ERROR;
  1. 26:     }
  1. 27: 
  1. 28:     //初始化 存放后置通配符处理好的关键字 数组
  1. 29:     if (ngx_array_init(&ha->dns_wc_tail, ha->temp_pool, asize, sizeof(ngx_hash_key_t))!= NGX_OK)
  1. 30:     {
  1. 31:         return NGX_ERROR;
  1. 32:     }
  1. 33: 
  1. 34:     /*初始化 二位数组 ,这个数组存放的第一个维度代表的是bucket的编号,
  1. 35:       那么keys_hash[i]中存放的是所有的key算出来的hash值对hsize取模以后的值为ikey
  1. 36:       假设有3key,分别是key1,key2key3假设hash值算出来以后对hsize取模的值都是i
  1. 37:       那么这三个key的值就顺序存放在keys_hash[i][0],keys_hash[i][1], keys_hash[i][2]。
  1. 38:       该值在调用的过程中用来保存和检测是否有冲突的key值,也就是是否有重复。*/
  1. 39:     ha->keys_hash = ngx_pcalloc(ha->temp_pool, sizeof(ngx_array_t) * ha->hsize);
  1. 40:     if (ha->keys_hash == NULL)
  1. 41:     {
  1. 42:         return NGX_ERROR;
  1. 43:     }
  1. 44: 
  1. 45:     // 该数组在调用的过程中用来保存和检测是否有冲突的前向通配符的key值,也就是是否有重复。
  1. 46:     ha->dns_wc_head_hash = ngx_pcalloc(ha->temp_pool,sizeof(ngx_array_t) * ha->hsize);
  1. 47: 
  1. 48:     if (ha->dns_wc_head_hash == NULL)
  1. 49:     {
  1. 50:         return NGX_ERROR;
  1. 51:     }
  1. 52: 
  1. 53:     // 该数组在调用的过程中用来保存和检测是否有冲突的后向通配符的key值,也就是是否有重复。
  1. 54:     ha->dns_wc_tail_hash = ngx_pcalloc(ha->temp_pool, sizeof(ngx_array_t) * ha->hsize);
  1. 55:     if (ha->dns_wc_tail_hash == NULL)
  1. 56:     {
  1. 57:         return NGX_ERROR;
  1. 58:     }
  1. 59: 
  1. 60:     return NGX_OK;
  1. 61: }

7. 向ngx_hash_keys_array中添加关键字

  1. 1: ngx_int_t ngx_hash_keys_array_init(ngx_hash_keys_arrays_t *ha, ngx_uint_t type)
  1. 2: {
  1. 3:     ngx_uint_t  asize;
  1. 4: 
  1. 5:     if (type == NGX_HASH_SMALL)
  1. 6:     {
  1. 7:         asize = 4;
  1. 8:         ha->hsize = 107;
  1. 9:     }
  1. 10:     else
  1. 11:     {
  1. 12:         asize = NGX_HASH_LARGE_ASIZE;
  1. 13:         ha->hsize = NGX_HASH_LARGE_HSIZE;
  1. 14:     }
  1. 15: 
  1. 16:     //初始化 存放非通配符关键字的数组
  1. 17:     if (ngx_array_init(&ha->keys, ha->temp_pool, asize, sizeof(ngx_hash_key_t)) != NGX_OK)
  1. 18:     {
  1. 19:         return NGX_ERROR;
  1. 20:     }
  1. 21: 
  1. 22:     //初始化 存放前置通配符处理好的关键字 数组
  1. 23:     if (ngx_array_init(&ha->dns_wc_head, ha->temp_pool, asize, sizeof(ngx_hash_key_t)) != NGX_OK)
  1. 24:     {
  1. 25:         return NGX_ERROR;
  1. 26:     }
  1. 27: 
  1. 28:     //初始化 存放后置通配符处理好的关键字 数组
  1. 29:     if (ngx_array_init(&ha->dns_wc_tail, ha->temp_pool, asize, sizeof(ngx_hash_key_t))!= NGX_OK)
  1. 30:     {
  1. 31:         return NGX_ERROR;
  1. 32:     }
  1. 33: 
  1. 34:     /*初始化 二位数组 ,这个数组存放的第一个维度代表的是bucket的编号,
  1. 35:       那么keys_hash[i]中存放的是所有的key算出来的hash值对hsize取模以后的值为ikey
  1. 36:       假设有3key,分别是key1,key2key3假设hash值算出来以后对hsize取模的值都是i
  1. 37:       那么这三个key的值就顺序存放在keys_hash[i][0],keys_hash[i][1], keys_hash[i][2]。
  1. 38:       该值在调用的过程中用来保存和检测是否有冲突的key值,也就是是否有重复。*/
  1. 39:     ha->keys_hash = ngx_pcalloc(ha->temp_pool, sizeof(ngx_array_t) * ha->hsize);
  1. 40:     if (ha->keys_hash == NULL)
  1. 41:     {
  1. 42:         return NGX_ERROR;
  1. 43:     }
  1. 44: 
  1. 45:     // 该数组在调用的过程中用来保存和检测是否有冲突的前向通配符的key值,也就是是否有重复。
  1. 46:     ha->dns_wc_head_hash = ngx_pcalloc(ha->temp_pool,sizeof(ngx_array_t) * ha->hsize);
  1. 47: 
  1. 48:     if (ha->dns_wc_head_hash == NULL)
  1. 49:     {
  1. 50:         return NGX_ERROR;
  1. 51:     }
  1. 52: 
  1. 53:     // 该数组在调用的过程中用来保存和检测是否有冲突的后向通配符的key值,也就是是否有重复。
  1. 54:     ha->dns_wc_tail_hash = ngx_pcalloc(ha->temp_pool, sizeof(ngx_array_t) * ha->hsize);
  1. 55:     if (ha->dns_wc_tail_hash == NULL)
  1. 56:     {
  1. 57:         return NGX_ERROR;
  1. 58:     }
  1. 59: 
  1. 60:     return NGX_OK;
  1. 61: }

-

菜鸟nginx源码剖析数据结构篇(七) 哈希表 ngx_hash_t(下)[转]的更多相关文章

  1. 菜鸟nginx源码剖析数据结构篇(十一) 共享内存ngx_shm_t[转]

    菜鸟nginx源码剖析数据结构篇(十一) 共享内存ngx_shm_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csdn ...

  2. 菜鸟nginx源码剖析数据结构篇(十) 自旋锁ngx_spinlock[转]

    菜鸟nginx源码剖析数据结构篇(十) 自旋锁ngx_spinlock Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csd ...

  3. 菜鸟nginx源码剖析数据结构篇(九) 内存池ngx_pool_t[转]

    菜鸟nginx源码剖析数据结构篇(九) 内存池ngx_pool_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csdn. ...

  4. 菜鸟nginx源码剖析数据结构篇(八) 缓冲区链表ngx_chain_t[转]

    菜鸟nginx源码剖析数据结构篇(八) 缓冲区链表 ngx_chain_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.c ...

  5. 菜鸟nginx源码剖析数据结构篇(六) 哈希表 ngx_hash_t(上)[转]

    菜鸟nginx源码剖析数据结构篇(六) 哈希表 ngx_hash_t(上) Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.c ...

  6. 菜鸟nginx源码剖析数据结构篇(五) 基数树 ngx_radix_tree_t[转]

    菜鸟nginx源码剖析数据结构篇(五) 基数树 ngx_radix_tree_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blo ...

  7. 菜鸟nginx源码剖析数据结构篇(四)红黑树ngx_rbtree_t[转]

    菜鸟nginx源码剖析数据结构篇(四)红黑树ngx_rbtree_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csdn ...

  8. 菜鸟nginx源码剖析数据结构篇(三) 单向链表 ngx_list_t[转]

    菜鸟nginx源码剖析数据结构篇(三) 单向链表 ngx_list_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csd ...

  9. 菜鸟nginx源码剖析数据结构篇(一)动态数组ngx_array_t[转]

    菜鸟nginx源码剖析数据结构篇(一)动态数组ngx_array_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csdn ...

随机推荐

  1. 今天真的很SB

    在公司Review系统网页上,写了一篇几百字的作文, 然后突然手一抖,竟然没有保存就切换页面了, 赶快退回来...没了,啥都没了... 怎么办... 还好洒家N久之前看了一本什么什么杂七杂八的书, 里 ...

  2. Activiti学习笔记4 — 流程实例化

    1.创建流程引擎对象 private ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine(); 2.启动流程 流程 ...

  3. Java面试(1)

    一.Java基础 什么是字符串常量池? Java中的字符串常量池(String Pool)是存储在Java堆内存中的字符串池: String是java中比较特殊的类,我们可以使用new运算符创建Str ...

  4. jupyter中使用graphviz

    参考: https://www.cnblogs.com/zhanjiahui/p/11335038.html https://blog.csdn.net/linxid/article/details/ ...

  5. 线性dp——cf1067A

    考虑三种情况,刷表dp+前缀和预处理即可 #include<bits/stdc++.h> using namespace std; ; ],f[][][],ans,s; int main( ...

  6. bzoj 1196: [HNOI2006]公路修建问题(二分+贪心)

    传送门 解题思路 看到最大,肯定要先想二分答案.二分之后首先从小到大枚举\(k\)个小于\(lim\)的所有一级公路,然后用并查集连到一起,然后就在剩下的里面从小到大找n-1-k个二级公路,模仿最小生 ...

  7. 2018湖南NOIP集训报告7.15~7.26

    Day1 主打内容:dfs/bfs及其优化剪枝,以及贪心算法的应用. 老师:\(Gromah\) 不得不说这个老师真的是个有趣的强大怪... 今天讲的挺水的,其实就是搜索吧,也没啥好听的,追会儿小说\ ...

  8. MongoDB后台运行

    文章目录 命令方式(推荐) 命令行和配置文件方式 命令行: 配置文件: 命令方式(推荐) 如果想在后台运行,启动时只需添加 --fork函数即可. fork: 以守护进程的方式运行MongoDB. 指 ...

  9. 不能scp到本地mac,mac打开ssh服务

    设置->共享->远程登录->所有用户

  10. HTML <body>的常用属性

    bgColor : 网页背景色  如 <body bgColor="red"> Background : 网页背景图片的地址 如 <body background ...