3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Nginx, Inc.
8 #include <ngx_config.h>
13 ngx_hash_find(ngx_hash_t *hash, ngx_uint_t key, u_char *name, size_t len)
19 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "hf:\"%*s\"", len, name);
22 elt = hash->buckets[key % hash->size];
29 if (len != (size_t) elt->len) {
33 for (i = 0; i < len; i++) {
34 if (name[i] != elt->name[i]) {
43 elt = (ngx_hash_elt_t *) ngx_align_ptr(&elt->name[0] + elt->len,
53 ngx_hash_find_wc_head(ngx_hash_wildcard_t *hwc, u_char *name, size_t len)
59 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "wch:\"%*s\"", len, name);
65 if (name[n - 1] == '.') {
74 for (i = n; i < len; i++) {
75 key = ngx_hash(key, name[i]);
79 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "key:\"%ui\"", key);
82 value = ngx_hash_find(&hwc->hash, key, &name[n], len - n);
85 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "value:\"%p\"", value);
91 * the 2 low bits of value have the special meaning:
92 * 00 - value is data pointer for both "example.com"
93 * and "*.example.com";
94 * 01 - value is data pointer for "*.example.com" only;
95 * 10 - value is pointer to wildcard hash allowing
96 * both "example.com" and "*.example.com";
97 * 11 - value is pointer to wildcard hash allowing
98 * "*.example.com" only.
101 if ((uintptr_t) value & 2) {
107 if ((uintptr_t) value & 1) {
111 hwc = (ngx_hash_wildcard_t *)
112 ((uintptr_t) value & (uintptr_t) ~3);
116 hwc = (ngx_hash_wildcard_t *) ((uintptr_t) value & (uintptr_t) ~3);
118 value = ngx_hash_find_wc_head(hwc, name, n - 1);
127 if ((uintptr_t) value & 1) {
136 return (void *) ((uintptr_t) value & (uintptr_t) ~3);
147 ngx_hash_find_wc_tail(ngx_hash_wildcard_t *hwc, u_char *name, size_t len)
153 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "wct:\"%*s\"", len, name);
158 for (i = 0; i < len; i++) {
159 if (name[i] == '.') {
163 key = ngx_hash(key, name[i]);
171 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "key:\"%ui\"", key);
174 value = ngx_hash_find(&hwc->hash, key, name, i);
177 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "value:\"%p\"", value);
183 * the 2 low bits of value have the special meaning:
184 * 00 - value is data pointer;
185 * 11 - value is pointer to wildcard hash allowing "example.*".
188 if ((uintptr_t) value & 2) {
192 hwc = (ngx_hash_wildcard_t *) ((uintptr_t) value & (uintptr_t) ~3);
194 value = ngx_hash_find_wc_tail(hwc, &name[i], len - i);
211 ngx_hash_find_combined(ngx_hash_combined_t *hash, ngx_uint_t key, u_char *name,
216 if (hash->hash.buckets) {
217 value = ngx_hash_find(&hash->hash, key, name, len);
228 if (hash->wc_head && hash->wc_head->hash.buckets) {
229 value = ngx_hash_find_wc_head(hash->wc_head, name, len);
236 if (hash->wc_tail && hash->wc_tail->hash.buckets) {
237 value = ngx_hash_find_wc_tail(hash->wc_tail, name, len);
248 #define NGX_HASH_ELT_SIZE(name) \
249 (sizeof(void *) + ngx_align((name)->key.len + 2, sizeof(void *)))
252 ngx_hash_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names, ngx_uint_t nelts)
257 ngx_uint_t i, n, key, size, start, bucket_size;
258 ngx_hash_elt_t *elt, **buckets;
260 if (hinit->max_size == 0) {
261 ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0,
262 "could not build %s, you should "
263 "increase %s_max_size: %i",
264 hinit->name, hinit->name, hinit->max_size);
268 for (n = 0; n < nelts; n++) {
269 if (hinit->bucket_size < NGX_HASH_ELT_SIZE(&names[n]) + sizeof(void *))
271 ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0,
272 "could not build %s, you should "
273 "increase %s_bucket_size: %i",
274 hinit->name, hinit->name, hinit->bucket_size);
279 test = ngx_alloc(hinit->max_size * sizeof(u_short), hinit->pool->log);
284 bucket_size = hinit->bucket_size - sizeof(void *);
286 start = nelts / (bucket_size / (2 * sizeof(void *)));
287 start = start ? start : 1;
289 if (hinit->max_size > 10000 && nelts && hinit->max_size / nelts < 100) {
290 start = hinit->max_size - 1000;
293 for (size = start; size <= hinit->max_size; size++) {
295 ngx_memzero(test, size * sizeof(u_short));
297 for (n = 0; n < nelts; n++) {
298 if (names[n].key.data == NULL) {
302 key = names[n].key_hash % size;
303 test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n]));
306 ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,
307 "%ui: %ui %ui \"%V\"",
308 size, key, test[key], &names[n].key);
311 if (test[key] > (u_short) bucket_size) {
323 size = hinit->max_size;
325 ngx_log_error(NGX_LOG_WARN, hinit->pool->log, 0,
326 "could not build optimal %s, you should increase "
327 "either %s_max_size: %i or %s_bucket_size: %i; "
328 "ignoring %s_bucket_size",
329 hinit->name, hinit->name, hinit->max_size,
330 hinit->name, hinit->bucket_size, hinit->name);
334 for (i = 0; i < size; i++) {
335 test[i] = sizeof(void *);
338 for (n = 0; n < nelts; n++) {
339 if (names[n].key.data == NULL) {
343 key = names[n].key_hash % size;
344 test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n]));
349 for (i = 0; i < size; i++) {
350 if (test[i] == sizeof(void *)) {
354 test[i] = (u_short) (ngx_align(test[i], ngx_cacheline_size));
359 if (hinit->hash == NULL) {
360 hinit->hash = ngx_pcalloc(hinit->pool, sizeof(ngx_hash_wildcard_t)
361 + size * sizeof(ngx_hash_elt_t *));
362 if (hinit->hash == NULL) {
367 buckets = (ngx_hash_elt_t **)
368 ((u_char *) hinit->hash + sizeof(ngx_hash_wildcard_t));
371 buckets = ngx_pcalloc(hinit->pool, size * sizeof(ngx_hash_elt_t *));
372 if (buckets == NULL) {
378 elts = ngx_palloc(hinit->pool, len + ngx_cacheline_size);
384 elts = ngx_align_ptr(elts, ngx_cacheline_size);
386 for (i = 0; i < size; i++) {
387 if (test[i] == sizeof(void *)) {
391 buckets[i] = (ngx_hash_elt_t *) elts;
395 for (i = 0; i < size; i++) {
399 for (n = 0; n < nelts; n++) {
400 if (names[n].key.data == NULL) {
404 key = names[n].key_hash % size;
405 elt = (ngx_hash_elt_t *) ((u_char *) buckets[key] + test[key]);
407 elt->value = names[n].value;
408 elt->len = (u_short) names[n].key.len;
410 ngx_strlow(elt->name, names[n].key.data, names[n].key.len);
412 test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n]));
415 for (i = 0; i < size; i++) {
416 if (buckets[i] == NULL) {
420 elt = (ngx_hash_elt_t *) ((u_char *) buckets[i] + test[i]);
427 hinit->hash->buckets = buckets;
428 hinit->hash->size = size;
432 for (i = 0; i < size; i++) {
439 ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,
446 val.data = &elt->name[0];
448 key = hinit->key(val.data, val.len);
450 ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,
451 "%ui: %p \"%V\" %ui", i, elt, &val, key);
453 elt = (ngx_hash_elt_t *) ngx_align_ptr(&elt->name[0] + elt->len,
465 ngx_hash_wildcard_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names,
469 ngx_uint_t i, n, dot;
470 ngx_array_t curr_names, next_names;
471 ngx_hash_key_t *name, *next_name;
473 ngx_hash_wildcard_t *wdc;
475 if (ngx_array_init(&curr_names, hinit->temp_pool, nelts,
476 sizeof(ngx_hash_key_t))
482 if (ngx_array_init(&next_names, hinit->temp_pool, nelts,
483 sizeof(ngx_hash_key_t))
489 for (n = 0; n < nelts; n = i) {
492 ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,
493 "wc0: \"%V\"", &names[n].key);
498 for (len = 0; len < names[n].key.len; len++) {
499 if (names[n].key.data[len] == '.') {
505 name = ngx_array_push(&curr_names);
511 name->key.data = names[n].key.data;
512 name->key_hash = hinit->key(name->key.data, name->key.len);
513 name->value = names[n].value;
516 ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,
517 "wc1: \"%V\" %ui", &name->key, dot);
526 next_names.nelts = 0;
528 if (names[n].key.len != len) {
529 next_name = ngx_array_push(&next_names);
530 if (next_name == NULL) {
534 next_name->key.len = names[n].key.len - len;
535 next_name->key.data = names[n].key.data + len;
536 next_name->key_hash = 0;
537 next_name->value = names[n].value;
540 ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,
541 "wc2: \"%V\"", &next_name->key);
545 for (i = n + 1; i < nelts; i++) {
546 if (ngx_strncmp(names[n].key.data, names[i].key.data, len) != 0) {
551 && names[i].key.len > len
552 && names[i].key.data[len] != '.')
557 next_name = ngx_array_push(&next_names);
558 if (next_name == NULL) {
562 next_name->key.len = names[i].key.len - dot_len;
563 next_name->key.data = names[i].key.data + dot_len;
564 next_name->key_hash = 0;
565 next_name->value = names[i].value;
568 ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,
569 "wc3: \"%V\"", &next_name->key);
573 if (next_names.nelts) {
578 if (ngx_hash_wildcard_init(&h, (ngx_hash_key_t *) next_names.elts,
585 wdc = (ngx_hash_wildcard_t *) h.hash;
587 if (names[n].key.len == len) {
588 wdc->value = names[n].value;
591 name->value = (void *) ((uintptr_t) wdc | (dot ? 3 : 2));
594 name->value = (void *) ((uintptr_t) name->value | 1);
598 if (ngx_hash_init(hinit, (ngx_hash_key_t *) curr_names.elts,
610 ngx_hash_key(u_char *data, size_t len)
616 for (i = 0; i < len; i++) {
617 key = ngx_hash(key, data[i]);
625 ngx_hash_key_lc(u_char *data, size_t len)
631 for (i = 0; i < len; i++) {
632 key = ngx_hash(key, ngx_tolower(data[i]));
640 ngx_hash_strlow(u_char *dst, u_char *src, size_t n)
647 *dst = ngx_tolower(*src);
648 key = ngx_hash(key, *dst);
658 ngx_hash_keys_array_init(ngx_hash_keys_arrays_t *ha, ngx_uint_t type)
662 if (type == NGX_HASH_SMALL) {
667 asize = NGX_HASH_LARGE_ASIZE;
668 ha->hsize = NGX_HASH_LARGE_HSIZE;
671 if (ngx_array_init(&ha->keys, ha->temp_pool, asize, sizeof(ngx_hash_key_t))
677 if (ngx_array_init(&ha->dns_wc_head, ha->temp_pool, asize,
678 sizeof(ngx_hash_key_t))
684 if (ngx_array_init(&ha->dns_wc_tail, ha->temp_pool, asize,
685 sizeof(ngx_hash_key_t))
691 ha->keys_hash = ngx_pcalloc(ha->temp_pool, sizeof(ngx_array_t) * ha->hsize);
692 if (ha->keys_hash == NULL) {
696 ha->dns_wc_head_hash = ngx_pcalloc(ha->temp_pool,
697 sizeof(ngx_array_t) * ha->hsize);
698 if (ha->dns_wc_head_hash == NULL) {
702 ha->dns_wc_tail_hash = ngx_pcalloc(ha->temp_pool,
703 sizeof(ngx_array_t) * ha->hsize);
704 if (ha->dns_wc_tail_hash == NULL) {
713 ngx_hash_add_key(ngx_hash_keys_arrays_t *ha, ngx_str_t *key, void *value,
719 ngx_uint_t i, k, n, skip, last;
720 ngx_array_t *keys, *hwc;
725 if (flags & NGX_HASH_WILDCARD_KEY) {
728 * supported wildcards:
729 * "*.example.com", ".example.com", and "www.example.*"
734 for (i = 0; i < key->len; i++) {
736 if (key->data[i] == '*') {
742 if (key->data[i] == '.' && key->data[i + 1] == '.') {
746 if (key->data[i] == '\0') {
751 if (key->len > 1 && key->data[0] == '.') {
758 if (key->data[0] == '*' && key->data[1] == '.') {
763 if (key->data[i - 2] == '.' && key->data[i - 1] == '*') {
779 for (i = 0; i < last; i++) {
780 if (!(flags & NGX_HASH_READONLY_KEY)) {
781 key->data[i] = ngx_tolower(key->data[i]);
783 k = ngx_hash(k, key->data[i]);
788 /* check conflicts in exact hash */
790 name = ha->keys_hash[k].elts;
793 for (i = 0; i < ha->keys_hash[k].nelts; i++) {
794 if (last != name[i].len) {
798 if (ngx_strncmp(key->data, name[i].data, last) == 0) {
804 if (ngx_array_init(&ha->keys_hash[k], ha->temp_pool, 4,
812 name = ngx_array_push(&ha->keys_hash[k]);
819 hk = ngx_array_push(&ha->keys);
825 hk->key_hash = ngx_hash_key(key->data, last);
835 k = ngx_hash_strlow(&key->data[skip], &key->data[skip], last - skip);
841 /* check conflicts in exact hash for ".example.com" */
843 name = ha->keys_hash[k].elts;
848 for (i = 0; i < ha->keys_hash[k].nelts; i++) {
849 if (len != name[i].len) {
853 if (ngx_strncmp(&key->data[1], name[i].data, len) == 0) {
859 if (ngx_array_init(&ha->keys_hash[k], ha->temp_pool, 4,
867 name = ngx_array_push(&ha->keys_hash[k]);
872 name->len = last - 1;
873 name->data = ngx_pnalloc(ha->temp_pool, name->len);
874 if (name->data == NULL) {
878 ngx_memcpy(name->data, &key->data[1], name->len);
885 * convert "*.example.com" to "com.example.\0"
886 * and ".example.com" to "com.example\0"
889 p = ngx_pnalloc(ha->temp_pool, last);
897 for (i = last - 1; i; i--) {
898 if (key->data[i] == '.') {
899 ngx_memcpy(&p[n], &key->data[i + 1], len);
910 ngx_memcpy(&p[n], &key->data[1], len);
916 hwc = &ha->dns_wc_head;
917 keys = &ha->dns_wc_head_hash[k];
921 /* convert "www.example.*" to "www.example\0" */
925 p = ngx_pnalloc(ha->temp_pool, last);
930 ngx_cpystrn(p, key->data, last);
932 hwc = &ha->dns_wc_tail;
933 keys = &ha->dns_wc_tail_hash[k];
937 /* check conflicts in wildcard hash */
944 for (i = 0; i < keys->nelts; i++) {
945 if (len != name[i].len) {
949 if (ngx_strncmp(key->data + skip, name[i].data, len) == 0) {
955 if (ngx_array_init(keys, ha->temp_pool, 4, sizeof(ngx_str_t)) != NGX_OK)
961 name = ngx_array_push(keys);
966 name->len = last - skip;
967 name->data = ngx_pnalloc(ha->temp_pool, name->len);
968 if (name->data == NULL) {
972 ngx_memcpy(name->data, key->data + skip, name->len);
975 /* add to wildcard hash */
977 hk = ngx_array_push(hwc);
982 hk->key.len = last - 1;