咱们来继续看found后的处理:
found:
//小样,找到你了~~
//当前的size,可就是需要的桶大小了。
//老毛子这里将test[0]~test[size-1]的值,重新计算一遍。
//感觉是因为他太高兴了,觉得”终于成功计算出size了“,忘记了,其实test里边的除了那些被初始化为0后,一致没有分配k-v对给它的桶之 //外,不都变成了需要的内存的大小了么。其实这里是为了更加逻辑清晰,因为每个桶还要进行ngx_cacheline_size粒度的对齐呢。
//你又算一遍是为了清晰?有木有,有木有!
for (i = 0; i < size; i++) { test[i] = sizeof(void *); } for (n = 0; n < nelts; n++) {
//name说:我key是NULL你就别算我了 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])); }
//有些桶里啊,压根没有k-v对,所以啊,len这个里边就不算他们了。也就是这些都不需要内存的。
//当前的size[i]可是在k-v对进行了sizeof(void *)为准的对齐后的值啊。
len = 0; for (i = 0; i < size; i++) { if (test[i] == sizeof(void *)) { continue; }
//但是仅仅k-v对齐是不够的,我整个bucket要以ngx_cacheline_size对齐。
//你再算一下,ngx_cacheline_size对齐了吧 test[i] = (u_short) (ngx_align(test[i], ngx_cacheline_size)); len += test[i]; }
//我了个去。终于当前的len就是真正需要的内存大小了。
if (hinit->hash == NULL) {
//恩,这种情况下,也就是hinit->hash==NULL的情况,我们得分配一个ngx_hash_wildcard_t在,这个是在pool里分配的。
//然后还需要把hinit->hash指向 ngx_hash_wildcard_t元素中的ngx_hash_elt_t *。
hinit->hash = ngx_pcalloc(hinit->pool, sizeof(ngx_hash_wildcard_t) + size * sizeof(ngx_hash_elt_t *));
//你要是分配不成功,你就返回ERROR吧。
if (hinit->hash == NULL) { ngx_free(test); return NGX_ERROR; }
//经过下边这招,你发现,ngx_hash_wildcard_t中的ngx_hash_t中的ngx_hash_elt_t *的地址被放置到了buckets。
//反过来说,buckets指向ngx_hash_wildcard_t中的ngx_hash_t中的ngx_hash_elt_t *。
//看来下边就是要给这个buckets这个指针指向的ngx_hash_elt_t *指向的ngx_hash_elt_t赋值了。
buckets = (ngx_hash_elt_t **) ((u_char *) hinit->hash + sizeof(ngx_hash_wildcard_t)); } else {
//如果hinit->hash不是NULL呢,咱们就不分配ngx_hash_wildcard_t,直接整个ngx_hash_t就OK了。
//buckets直接指向ngx_hash_elt_t *
buckets = ngx_pcalloc(hinit->pool, size * sizeof(ngx_hash_elt_t *)); if (buckets == NULL) { ngx_free(test); return NGX_ERROR; } }
//不仅仅是将上述ngx_hash_wildcard_t或者ngx_hash_t分配到hinit->pool,刚计算的所有bucket需要的len大小的内存和
//ngx_cacheline_size大小内存也在内存池中分配了。小样,别以为我不知道你为啥要加上ngx_cacheline_size。因为多余的这个
//ngx_cacheline_size可以保证你调整elts后,使用新的elts指向的内存池,依然是大于len的,这样不会产生非法内存访问。因为原来的
//elts不会移动超过ngx_cacheline_size就可以调整到ngx_cacheline_size对齐的地方。
//在数学上就是 [a,a+b]之间必有一个数,是b的整数倍。
elts = ngx_palloc(hinit->pool, len + ngx_cacheline_size);
//要是分配不成功,就返回ERROR
if (elts == NULL) { ngx_free(test); return NGX_ERROR; }
//在这里做对齐调整。嘿嘿,之所以知道上边你加上ngx_cacheline_size就是因为这里,你暴露了。
elts = ngx_align_ptr(elts, ngx_cacheline_size);
//好了,内存初地址也有了,桶多少也知道了,每个桶的k-v占用内存的计数(包括所需数据结构占用的内存)也在test[i]了。
//那么,我们就开始分配桶,把每个桶的指针上,都赋值正确的内存地址吧。
for (i = 0; i < size; i++) { if (test[i] == sizeof(void *)) { continue; } buckets[i] = (ngx_hash_elt_t *) elts; elts += test[i]; }
//用完了,清下0。
for (i = 0; i < size; i++) { test[i] = 0; }
//最后,我们把桶k-v对的值填入就可以了。也就是根据key_hash值计算key-value应该在哪个桶,并且放进去。
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])); }
//分配完了k-v,我们把每个bucket的k-v结尾标志给赋值NULL。
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;
//Log级别的问题,别关心了#if 0 for (i = 0; i < size; i++) { ngx_str_t val; ngx_uint_t key; elt = buckets[i]; if (elt == NULL) { ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0, "%ui: NULL", i); continue; } while (elt->value) { val.len = elt->len; val.data = &elt->name[0]; key = hinit->key(val.data, val.len); ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0, "%ui: %p /"%V/" %ui", i, elt, &val, key); elt = (ngx_hash_elt_t *) ngx_align_ptr(&elt->name[0] + elt->len, sizeof(void *)); } }#endif return NGX_OK;}