| |
| /* |
| * Copyright (C) Igor Sysoev |
| * Copyright (C) Nginx, Inc. |
| */ |
| |
| |
| #include <ngx_config.h> |
| #include <ngx_core.h> |
| |
| |
| static void *ngx_radix_alloc(ngx_radix_tree_t *tree); |
| |
| |
| ngx_radix_tree_t * |
| ngx_radix_tree_create(ngx_pool_t *pool, ngx_int_t preallocate) |
| { |
| uint32_t key, mask, inc; |
| ngx_radix_tree_t *tree; |
| |
| tree = ngx_palloc(pool, sizeof(ngx_radix_tree_t)); |
| if (tree == NULL) { |
| return NULL; |
| } |
| |
| tree->pool = pool; |
| tree->free = NULL; |
| tree->start = NULL; |
| tree->size = 0; |
| |
| tree->root = ngx_radix_alloc(tree); |
| if (tree->root == NULL) { |
| return NULL; |
| } |
| |
| tree->root->right = NULL; |
| tree->root->left = NULL; |
| tree->root->parent = NULL; |
| tree->root->value = NGX_RADIX_NO_VALUE; |
| |
| if (preallocate == 0) { |
| return tree; |
| } |
| |
| /* |
| * Preallocation of first nodes : 0, 1, 00, 01, 10, 11, 000, 001, etc. |
| * increases TLB hits even if for first lookup iterations. |
| * On 32-bit platforms the 7 preallocated bits takes continuous 4K, |
| * 8 - 8K, 9 - 16K, etc. On 64-bit platforms the 6 preallocated bits |
| * takes continuous 4K, 7 - 8K, 8 - 16K, etc. There is no sense to |
| * to preallocate more than one page, because further preallocation |
| * distributes the only bit per page. Instead, a random insertion |
| * may distribute several bits per page. |
| * |
| * Thus, by default we preallocate maximum |
| * 6 bits on amd64 (64-bit platform and 4K pages) |
| * 7 bits on i386 (32-bit platform and 4K pages) |
| * 7 bits on sparc64 in 64-bit mode (8K pages) |
| * 8 bits on sparc64 in 32-bit mode (8K pages) |
| */ |
| |
| if (preallocate == -1) { |
| switch (ngx_pagesize / sizeof(ngx_radix_tree_t)) { |
| |
| /* amd64 */ |
| case 128: |
| preallocate = 6; |
| break; |
| |
| /* i386, sparc64 */ |
| case 256: |
| preallocate = 7; |
| break; |
| |
| /* sparc64 in 32-bit mode */ |
| default: |
| preallocate = 8; |
| } |
| } |
| |
| mask = 0; |
| inc = 0x80000000; |
| |
| while (preallocate--) { |
| |
| key = 0; |
| mask >>= 1; |
| mask |= 0x80000000; |
| |
| do { |
| if (ngx_radix32tree_insert(tree, key, mask, NGX_RADIX_NO_VALUE) |
| != NGX_OK) |
| { |
| return NULL; |
| } |
| |
| key += inc; |
| |
| } while (key); |
| |
| inc >>= 1; |
| } |
| |
| return tree; |
| } |
| |
| |
| ngx_int_t |
| ngx_radix32tree_insert(ngx_radix_tree_t *tree, uint32_t key, uint32_t mask, |
| uintptr_t value) |
| { |
| uint32_t bit; |
| ngx_radix_node_t *node, *next; |
| |
| bit = 0x80000000; |
| |
| node = tree->root; |
| next = tree->root; |
| |
| while (bit & mask) { |
| if (key & bit) { |
| next = node->right; |
| |
| } else { |
| next = node->left; |
| } |
| |
| if (next == NULL) { |
| break; |
| } |
| |
| bit >>= 1; |
| node = next; |
| } |
| |
| if (next) { |
| if (node->value != NGX_RADIX_NO_VALUE) { |
| return NGX_BUSY; |
| } |
| |
| node->value = value; |
| return NGX_OK; |
| } |
| |
| while (bit & mask) { |
| next = ngx_radix_alloc(tree); |
| if (next == NULL) { |
| return NGX_ERROR; |
| } |
| |
| next->right = NULL; |
| next->left = NULL; |
| next->parent = node; |
| next->value = NGX_RADIX_NO_VALUE; |
| |
| if (key & bit) { |
| node->right = next; |
| |
| } else { |
| node->left = next; |
| } |
| |
| bit >>= 1; |
| node = next; |
| } |
| |
| node->value = value; |
| |
| return NGX_OK; |
| } |
| |
| |
| ngx_int_t |
| ngx_radix32tree_delete(ngx_radix_tree_t *tree, uint32_t key, uint32_t mask) |
| { |
| uint32_t bit; |
| ngx_radix_node_t *node; |
| |
| bit = 0x80000000; |
| node = tree->root; |
| |
| while (node && (bit & mask)) { |
| if (key & bit) { |
| node = node->right; |
| |
| } else { |
| node = node->left; |
| } |
| |
| bit >>= 1; |
| } |
| |
| if (node == NULL) { |
| return NGX_ERROR; |
| } |
| |
| if (node->right || node->left) { |
| if (node->value != NGX_RADIX_NO_VALUE) { |
| node->value = NGX_RADIX_NO_VALUE; |
| return NGX_OK; |
| } |
| |
| return NGX_ERROR; |
| } |
| |
| for ( ;; ) { |
| if (node->parent->right == node) { |
| node->parent->right = NULL; |
| |
| } else { |
| node->parent->left = NULL; |
| } |
| |
| node->right = tree->free; |
| tree->free = node; |
| |
| node = node->parent; |
| |
| if (node->right || node->left) { |
| break; |
| } |
| |
| if (node->value != NGX_RADIX_NO_VALUE) { |
| break; |
| } |
| |
| if (node->parent == NULL) { |
| break; |
| } |
| } |
| |
| return NGX_OK; |
| } |
| |
| |
| uintptr_t |
| ngx_radix32tree_find(ngx_radix_tree_t *tree, uint32_t key) |
| { |
| uint32_t bit; |
| uintptr_t value; |
| ngx_radix_node_t *node; |
| |
| bit = 0x80000000; |
| value = NGX_RADIX_NO_VALUE; |
| node = tree->root; |
| |
| while (node) { |
| if (node->value != NGX_RADIX_NO_VALUE) { |
| value = node->value; |
| } |
| |
| if (key & bit) { |
| node = node->right; |
| |
| } else { |
| node = node->left; |
| } |
| |
| bit >>= 1; |
| } |
| |
| return value; |
| } |
| |
| |
| static void * |
| ngx_radix_alloc(ngx_radix_tree_t *tree) |
| { |
| char *p; |
| |
| if (tree->free) { |
| p = (char *) tree->free; |
| tree->free = tree->free->right; |
| return p; |
| } |
| |
| if (tree->size < sizeof(ngx_radix_node_t)) { |
| tree->start = ngx_pmemalign(tree->pool, ngx_pagesize, ngx_pagesize); |
| if (tree->start == NULL) { |
| return NULL; |
| } |
| |
| tree->size = ngx_pagesize; |
| } |
| |
| p = tree->start; |
| tree->start += sizeof(ngx_radix_node_t); |
| tree->size -= sizeof(ngx_radix_node_t); |
| |
| return p; |
| } |