咱们来继续看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;
}