nginx 0.7.8
[nginx-catap.git] / src / core / ngx_hash.c
blob2954266ae3b5e9c52de56fb701c5f18b6fc02cb9
2 /*
3 * Copyright (C) Igor Sysoev
4 */
7 #include <ngx_config.h>
8 #include <ngx_core.h>
11 void *
12 ngx_hash_find(ngx_hash_t *hash, ngx_uint_t key, u_char *name, size_t len)
14 ngx_uint_t i;
15 ngx_hash_elt_t *elt;
17 #if 0
18 ngx_str_t line;
20 line.len = len;
21 line.data = name;
22 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "hf:\"%V\"", &line);
23 #endif
25 elt = hash->buckets[key % hash->size];
27 if (elt == NULL) {
28 return NULL;
31 while (elt->value) {
32 if (len != (size_t) elt->len) {
33 goto next;
36 for (i = 0; i < len; i++) {
37 if (name[i] != elt->name[i]) {
38 goto next;
42 return elt->value;
44 next:
46 elt = (ngx_hash_elt_t *) ngx_align_ptr(&elt->name[0] + elt->len,
47 sizeof(void *));
48 continue;
51 return NULL;
55 void *
56 ngx_hash_find_wc_head(ngx_hash_wildcard_t *hwc, u_char *name, size_t len)
58 void *value;
59 ngx_uint_t i, n, key;
61 #if 0
62 ngx_str_t line;
64 line.len = len;
65 line.data = name;
66 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "wch:\"%V\"", &line);
67 #endif
69 n = len;
71 while (n) {
72 if (name[n - 1] == '.') {
73 break;
76 n--;
79 key = 0;
81 for (i = n; i < len; i++) {
82 key = ngx_hash(key, name[i]);
85 #if 0
86 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "key:\"%ui\"", key);
87 #endif
89 value = ngx_hash_find(&hwc->hash, key, &name[n], len - n);
91 if (value) {
94 * the 2 low bits of value have the special meaning:
95 * 00 - value is data pointer,
96 * 01 - value is pointer to wildcard hash allowing
97 * "*.example.com" only,
98 * 11 - value is pointer to wildcard hash allowing
99 * both "example.com" and "*.example.com".
102 if ((uintptr_t) value & 1) {
104 hwc = (ngx_hash_wildcard_t *) ((uintptr_t) value & (uintptr_t) ~3);
106 if (n == 0) {
107 if ((uintptr_t) value & 2) {
108 return hwc->value;
110 } else {
111 return NULL;
115 value = ngx_hash_find_wc_head(hwc, name, n - 1);
117 if (value) {
118 return value;
121 return hwc->value;
124 return value;
127 return hwc->value;
131 void *
132 ngx_hash_find_wc_tail(ngx_hash_wildcard_t *hwc, u_char *name, size_t len)
134 void *value;
135 ngx_uint_t i, key;
137 #if 0
138 ngx_str_t line;
140 line.len = len;
141 line.data = name;
142 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "wct:\"%V\"", &line);
143 #endif
145 key = 0;
147 for (i = 0; i < len; i++) {
148 if (name[i] == '.') {
149 break;
152 key = ngx_hash(key, name[i]);
155 if (i == len) {
156 return NULL;
159 #if 0
160 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "key:\"%ui\"", key);
161 #endif
163 value = ngx_hash_find(&hwc->hash, key, name, i);
165 if (value) {
168 * the 2 low bits of value have the special meaning:
169 * 00 - value is data pointer,
170 * 01 - value is pointer to wildcard hash allowing "example.*".
173 if ((uintptr_t) value & 1) {
175 i++;
177 hwc = (ngx_hash_wildcard_t *) ((uintptr_t) value & (uintptr_t) ~3);
179 value = ngx_hash_find_wc_tail(hwc, &name[i], len - i);
181 if (value) {
182 return value;
185 return hwc->value;
188 return value;
191 return hwc->value;
195 void *
196 ngx_hash_find_combined(ngx_hash_combined_t *hash, ngx_uint_t key, u_char *name,
197 size_t len)
199 void *value;
201 if (hash->hash.buckets) {
202 value = ngx_hash_find(&hash->hash, key, name, len);
204 if (value) {
205 return value;
209 if (hash->wc_head && hash->wc_head->hash.buckets) {
210 value = ngx_hash_find_wc_head(hash->wc_head, name, len);
212 if (value) {
213 return value;
217 if (hash->wc_tail && hash->wc_tail->hash.buckets) {
218 value = ngx_hash_find_wc_tail(hash->wc_tail, name, len);
220 if (value) {
221 return value;
225 return NULL;
229 #define NGX_HASH_ELT_SIZE(name) \
230 (sizeof(void *) + ngx_align((name)->key.len + 1, sizeof(void *)))
232 ngx_int_t
233 ngx_hash_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names, ngx_uint_t nelts)
235 u_char *elts;
236 size_t len;
237 u_short *test;
238 ngx_uint_t i, n, key, size, start, bucket_size;
239 ngx_hash_elt_t *elt, **buckets;
241 for (n = 0; n < nelts; n++) {
242 if (names[n].key.len >= 255) {
243 ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0,
244 "the \"%V\" value to hash is to long: %uz bytes, "
245 "the maximum length can be 255 bytes only",
246 &names[n].key, names[n].key.len);
247 return NGX_ERROR;
250 if (hinit->bucket_size < NGX_HASH_ELT_SIZE(&names[n]) + sizeof(void *))
252 ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0,
253 "could not build the %s, you should "
254 "increase %s_bucket_size: %i",
255 hinit->name, hinit->name, hinit->bucket_size);
256 return NGX_ERROR;
260 test = ngx_alloc(hinit->max_size * sizeof(u_short), hinit->pool->log);
261 if (test == NULL) {
262 return NGX_ERROR;
265 bucket_size = hinit->bucket_size - sizeof(void *);
267 start = nelts / (bucket_size / (2 * sizeof(void *)));
268 start = start ? start : 1;
270 if (hinit->max_size > 10000 && hinit->max_size / nelts < 100) {
271 start = hinit->max_size - 1000;
274 for (size = start; size < hinit->max_size; size++) {
276 ngx_memzero(test, size * sizeof(u_short));
278 for (n = 0; n < nelts; n++) {
279 if (names[n].key.data == NULL) {
280 continue;
283 key = names[n].key_hash % size;
284 test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n]));
286 #if 0
287 ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,
288 "%ui: %ui %ui \"%V\"",
289 size, key, test[key], &names[n].key);
290 #endif
292 if (test[key] > (u_short) bucket_size) {
293 goto next;
297 goto found;
299 next:
301 continue;
304 ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0,
305 "could not build the %s, you should increase "
306 "either %s_max_size: %i or %s_bucket_size: %i",
307 hinit->name, hinit->name, hinit->max_size,
308 hinit->name, hinit->bucket_size);
310 ngx_free(test);
312 return NGX_ERROR;
314 found:
316 for (i = 0; i < size; i++) {
317 test[i] = sizeof(void *);
320 for (n = 0; n < nelts; n++) {
321 if (names[n].key.data == NULL) {
322 continue;
325 key = names[n].key_hash % size;
326 test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n]));
329 len = 0;
331 for (i = 0; i < size; i++) {
332 if (test[i] == sizeof(void *)) {
333 continue;
336 test[i] = (u_short) (ngx_align(test[i], ngx_cacheline_size));
338 len += test[i];
341 if (hinit->hash == NULL) {
342 hinit->hash = ngx_pcalloc(hinit->pool, sizeof(ngx_hash_wildcard_t)
343 + size * sizeof(ngx_hash_elt_t *));
344 if (hinit->hash == NULL) {
345 ngx_free(test);
346 return NGX_ERROR;
349 buckets = (ngx_hash_elt_t **)
350 ((u_char *) hinit->hash + sizeof(ngx_hash_wildcard_t));
352 } else {
353 buckets = ngx_pcalloc(hinit->pool, size * sizeof(ngx_hash_elt_t *));
354 if (buckets == NULL) {
355 ngx_free(test);
356 return NGX_ERROR;
360 elts = ngx_palloc(hinit->pool, len + ngx_cacheline_size);
361 if (elts == NULL) {
362 ngx_free(test);
363 return NGX_ERROR;
366 elts = ngx_align_ptr(elts, ngx_cacheline_size);
368 for (i = 0; i < size; i++) {
369 if (test[i] == sizeof(void *)) {
370 continue;
373 buckets[i] = (ngx_hash_elt_t *) elts;
374 elts += test[i];
378 for (i = 0; i < size; i++) {
379 test[i] = 0;
382 for (n = 0; n < nelts; n++) {
383 if (names[n].key.data == NULL) {
384 continue;
387 key = names[n].key_hash % size;
388 elt = (ngx_hash_elt_t *) ((u_char *) buckets[key] + test[key]);
390 elt->value = names[n].value;
391 elt->len = (u_char) names[n].key.len;
393 ngx_strlow(elt->name, names[n].key.data, names[n].key.len);
395 test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n]));
398 for (i = 0; i < size; i++) {
399 if (buckets[i] == NULL) {
400 continue;
403 elt = (ngx_hash_elt_t *) ((u_char *) buckets[i] + test[i]);
405 elt->value = NULL;
408 ngx_free(test);
410 hinit->hash->buckets = buckets;
411 hinit->hash->size = size;
413 #if 0
415 for (i = 0; i < size; i++) {
416 ngx_str_t val;
417 ngx_uint_t key;
419 elt = buckets[i];
421 if (elt == NULL) {
422 ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,
423 "%ui: NULL", i);
424 continue;
427 while (elt->value) {
428 val.len = elt->len;
429 val.data = &elt->name[0];
431 key = hinit->key(val.data, val.len);
433 ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,
434 "%ui: %p \"%V\" %ui", i, elt, &val, key);
436 elt = (ngx_hash_elt_t *) ngx_align_ptr(&elt->name[0] + elt->len,
437 sizeof(void *));
441 #endif
443 return NGX_OK;
447 ngx_int_t
448 ngx_hash_wildcard_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names,
449 ngx_uint_t nelts)
451 size_t len, dot_len;
452 ngx_uint_t i, n, dot;
453 ngx_array_t curr_names, next_names;
454 ngx_hash_key_t *name, *next_name;
455 ngx_hash_init_t h;
456 ngx_hash_wildcard_t *wdc;
458 if (ngx_array_init(&curr_names, hinit->temp_pool, nelts,
459 sizeof(ngx_hash_key_t))
460 != NGX_OK)
462 return NGX_ERROR;
465 if (ngx_array_init(&next_names, hinit->temp_pool, nelts,
466 sizeof(ngx_hash_key_t))
467 != NGX_OK)
469 return NGX_ERROR;
472 for (n = 0; n < nelts; n = i) {
474 #if 0
475 ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,
476 "wc0: \"%V\"", &names[n].key);
477 #endif
479 dot = 0;
481 for (len = 0; len < names[n].key.len; len++) {
482 if (names[n].key.data[len] == '.') {
483 dot = 1;
484 break;
488 name = ngx_array_push(&curr_names);
489 if (name == NULL) {
490 return NGX_ERROR;
493 name->key.len = len;
494 name->key.data = names[n].key.data;
495 name->key_hash = hinit->key(name->key.data, name->key.len);
496 name->value = names[n].value;
498 #if 0
499 ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,
500 "wc1: \"%V\" %ui", &name->key, dot);
501 #endif
503 dot_len = len + 1;
505 if (dot) {
506 len++;
509 next_names.nelts = 0;
511 if (names[n].key.len != len) {
512 next_name = ngx_array_push(&next_names);
513 if (next_name == NULL) {
514 return NGX_ERROR;
517 next_name->key.len = names[n].key.len - len;
518 next_name->key.data = names[n].key.data + len;
519 next_name->key_hash= 0;
520 next_name->value = names[n].value;
522 #if 0
523 ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,
524 "wc2: \"%V\"", &next_name->key);
525 #endif
528 for (i = n + 1; i < nelts; i++) {
529 if (ngx_strncmp(names[n].key.data, names[i].key.data, len) != 0) {
530 break;
533 if (!dot
534 && names[i].key.len > len
535 && names[i].key.data[len] != '.')
537 break;
540 next_name = ngx_array_push(&next_names);
541 if (next_name == NULL) {
542 return NGX_ERROR;
545 next_name->key.len = names[i].key.len - dot_len;
546 next_name->key.data = names[i].key.data + dot_len;
547 next_name->key_hash= 0;
548 next_name->value = names[i].value;
550 #if 0
551 ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,
552 "wc3: \"%V\"", &next_name->key);
553 #endif
556 if (next_names.nelts) {
558 h = *hinit;
559 h.hash = NULL;
561 if (ngx_hash_wildcard_init(&h, (ngx_hash_key_t *) next_names.elts,
562 next_names.nelts)
563 != NGX_OK)
565 return NGX_ERROR;
568 wdc = (ngx_hash_wildcard_t *) h.hash;
570 if (names[n].key.len == len) {
571 wdc->value = names[n].value;
572 #if 0
573 ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,
574 "wdc: \"%V\"", wdc->value);
575 #endif
578 name->value = (void *) ((uintptr_t) wdc | (dot ? 1 : 3));
582 if (ngx_hash_init(hinit, (ngx_hash_key_t *) curr_names.elts,
583 curr_names.nelts)
584 != NGX_OK)
586 return NGX_ERROR;
589 return NGX_OK;
593 ngx_uint_t
594 ngx_hash_key(u_char *data, size_t len)
596 ngx_uint_t i, key;
598 key = 0;
600 for (i = 0; i < len; i++) {
601 key = ngx_hash(key, data[i]);
604 return key;
608 ngx_uint_t
609 ngx_hash_key_lc(u_char *data, size_t len)
611 ngx_uint_t i, key;
613 key = 0;
615 for (i = 0; i < len; i++) {
616 key = ngx_hash(key, ngx_tolower(data[i]));
619 return key;
623 ngx_uint_t
624 ngx_hash_strlow(u_char *dst, u_char *src, size_t n)
626 ngx_uint_t key;
628 key = 0;
630 while (n--) {
631 *dst = ngx_tolower(*src);
632 key = ngx_hash(key, *dst);
633 dst++;
634 src++;
637 return key;
641 ngx_int_t
642 ngx_hash_keys_array_init(ngx_hash_keys_arrays_t *ha, ngx_uint_t type)
644 ngx_uint_t asize;
646 if (type == NGX_HASH_SMALL) {
647 asize = 4;
648 ha->hsize = 107;
650 } else {
651 asize = NGX_HASH_LARGE_ASIZE;
652 ha->hsize = NGX_HASH_LARGE_HSIZE;
655 if (ngx_array_init(&ha->keys, ha->temp_pool, asize, sizeof(ngx_hash_key_t))
656 != NGX_OK)
658 return NGX_ERROR;
661 if (ngx_array_init(&ha->dns_wc_head, ha->temp_pool, asize,
662 sizeof(ngx_hash_key_t))
663 != NGX_OK)
665 return NGX_ERROR;
668 if (ngx_array_init(&ha->dns_wc_tail, ha->temp_pool, asize,
669 sizeof(ngx_hash_key_t))
670 != NGX_OK)
672 return NGX_ERROR;
675 ha->keys_hash = ngx_pcalloc(ha->temp_pool, sizeof(ngx_array_t) * ha->hsize);
676 if (ha->keys_hash == NULL) {
677 return NGX_ERROR;
680 ha->dns_wc_head_hash = ngx_pcalloc(ha->temp_pool,
681 sizeof(ngx_array_t) * ha->hsize);
682 if (ha->dns_wc_head_hash == NULL) {
683 return NGX_ERROR;
686 ha->dns_wc_tail_hash = ngx_pcalloc(ha->temp_pool,
687 sizeof(ngx_array_t) * ha->hsize);
688 if (ha->dns_wc_tail_hash == NULL) {
689 return NGX_ERROR;
692 return NGX_OK;
696 ngx_int_t
697 ngx_hash_add_key(ngx_hash_keys_arrays_t *ha, ngx_str_t *key, void *value,
698 ngx_uint_t flags)
700 size_t len;
701 u_char *p;
702 ngx_str_t *name;
703 ngx_uint_t i, k, n, skip, last;
704 ngx_array_t *keys, *hwc;
705 ngx_hash_key_t *hk;
707 last = key->len;
709 if (flags & NGX_HASH_WILDCARD_KEY) {
712 * supported wildcards:
713 * "*.example.com", ".example.com", and "www.example.*"
716 n = 0;
718 for (i = 0; i < key->len; i++) {
720 if (key->data[i] == '*') {
721 if (++n > 1) {
722 return NGX_DECLINED;
726 if (key->data[i] == '.' && key->data[i + 1] == '.') {
727 return NGX_DECLINED;
731 if (key->len > 1 && key->data[0] == '.') {
732 skip = 1;
733 goto wildcard;
736 if (key->len > 2) {
738 if (key->data[0] == '*' && key->data[1] == '.') {
739 skip = 2;
740 goto wildcard;
743 if (key->data[i - 2] == '.' && key->data[i - 1] == '*') {
744 skip = 0;
745 last -= 2;
746 goto wildcard;
750 if (n) {
751 return NGX_DECLINED;
755 /* exact hash */
757 k = 0;
759 for (i = 0; i < last; i++) {
760 if (!(flags & NGX_HASH_READONLY_KEY)) {
761 key->data[i] = ngx_tolower(key->data[i]);
763 k = ngx_hash(k, key->data[i]);
766 k %= ha->hsize;
768 /* check conflicts in exact hash */
770 name = ha->keys_hash[k].elts;
772 if (name) {
773 for (i = 0; i < ha->keys_hash[k].nelts; i++) {
774 if (last != name[i].len) {
775 continue;
778 if (ngx_strncmp(key->data, name[i].data, last) == 0) {
779 return NGX_BUSY;
783 } else {
784 if (ngx_array_init(&ha->keys_hash[k], ha->temp_pool, 4,
785 sizeof(ngx_str_t))
786 != NGX_OK)
788 return NGX_ERROR;
792 name = ngx_array_push(&ha->keys_hash[k]);
793 if (name == NULL) {
794 return NGX_ERROR;
797 *name = *key;
799 hk = ngx_array_push(&ha->keys);
800 if (hk == NULL) {
801 return NGX_ERROR;
804 hk->key = *key;
805 hk->key_hash = ngx_hash_key(key->data, last);
806 hk->value = value;
808 return NGX_OK;
811 wildcard:
813 /* wildcard hash */
815 k = ngx_hash_strlow(&key->data[skip], &key->data[skip], last - skip);
817 k %= ha->hsize;
819 if (skip == 1) {
821 /* check conflicts in exact hash for ".example.com" */
823 name = ha->keys_hash[k].elts;
825 if (name) {
826 len = last - skip;
828 for (i = 0; i < ha->keys_hash[k].nelts; i++) {
829 if (len != name[i].len) {
830 continue;
833 if (ngx_strncmp(&key->data[1], name[i].data, len) == 0) {
834 return NGX_BUSY;
838 } else {
839 if (ngx_array_init(&ha->keys_hash[k], ha->temp_pool, 4,
840 sizeof(ngx_str_t))
841 != NGX_OK)
843 return NGX_ERROR;
847 name = ngx_array_push(&ha->keys_hash[k]);
848 if (name == NULL) {
849 return NGX_ERROR;
852 name->len = last - 1;
853 name->data = ngx_pnalloc(ha->temp_pool, name->len);
854 if (name->data == NULL) {
855 return NGX_ERROR;
858 ngx_memcpy(name->data, &key->data[1], name->len);
862 if (skip) {
865 * convert "*.example.com" to "com.example.\0"
866 * and ".example.com" to "com.example\0"
869 p = ngx_pnalloc(ha->temp_pool, last);
870 if (p == NULL) {
871 return NGX_ERROR;
874 len = 0;
875 n = 0;
877 for (i = last - 1; i; i--) {
878 if (key->data[i] == '.') {
879 ngx_memcpy(&p[n], &key->data[i + 1], len);
880 n += len;
881 p[n++] = '.';
882 len = 0;
883 continue;
886 len++;
889 if (len) {
890 ngx_memcpy(&p[n], &key->data[1], len);
891 n += len;
894 p[n] = '\0';
896 hwc = &ha->dns_wc_head;
897 keys = &ha->dns_wc_head_hash[k];
899 } else {
901 /* convert "www.example.*" to "www.example\0" */
903 last++;
905 p = ngx_pnalloc(ha->temp_pool, last);
906 if (p == NULL) {
907 return NGX_ERROR;
910 ngx_cpystrn(p, key->data, last);
912 hwc = &ha->dns_wc_tail;
913 keys = &ha->dns_wc_tail_hash[k];
917 hk = ngx_array_push(hwc);
918 if (hk == NULL) {
919 return NGX_ERROR;
922 hk->key.len = last - 1;
923 hk->key.data = p;
924 hk->key_hash = 0;
925 hk->value = value;
928 /* check conflicts in wildcard hash */
930 name = keys->elts;
932 if (name) {
933 len = last - skip;
935 for (i = 0; i < keys->nelts; i++) {
936 if (len != name[i].len) {
937 continue;
940 if (ngx_strncmp(key->data + skip, name[i].data, len) == 0) {
941 return NGX_BUSY;
945 } else {
946 if (ngx_array_init(keys, ha->temp_pool, 4, sizeof(ngx_str_t)) != NGX_OK)
948 return NGX_ERROR;
952 name = ngx_array_push(keys);
953 if (name == NULL) {
954 return NGX_ERROR;
957 name->len = last - skip;
958 name->data = ngx_pnalloc(ha->temp_pool, name->len);
959 if (name->data == NULL) {
960 return NGX_ERROR;
963 ngx_memcpy(name->data, key->data + skip, name->len);
965 return NGX_OK;