ngx_radix_tree.h

// 未被使用的节点
#define NGX_RADIX_NO_VALUE (uintptr_t) -1 typedef struct ngx_radix_node_s ngx_radix_node_t; struct ngx_radix_node_s {
ngx_radix_node_t *right; // 右子树的根节点
ngx_radix_node_t *left; // 左子树的根节点
ngx_radix_node_t *parent; // 父节点
uintptr_t value; // 值域
}; typedef struct {
ngx_radix_node_t *root; // 树根
ngx_pool_t *pool; // 该树所用的内存池
ngx_radix_node_t *free; // 空闲的节点由free开始连成一个链表,节点间通过right指针连接
char *start;
size_t size;
} ngx_radix_tree_t;

ngx_radix_tree.c

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; // 为根节点分配内存(实际上不一定有重新的内存分配操作,具体详见ngx_radix_alloc部分)
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; // 如果指定的预分配节点数为 0,则直接返回这个树就好了
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)
*/ // 下面这部分就很有意思了,你可以看上面的英文注释。简单说,一个 x bits 的值,对应其 Radix 树
// 有 x + 1 层,那么节点的个数就是 2^(x+1) -1 个(数据结构常识,你也可以很容易证明这个结论)。
if (preallocate == -1) { // 根据 pagesize 大小,确定可以分配多少个 radix 树结构
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; // preallocate 为几,最终 mask 就有几个最高位为1,其他为0。整个循环过程中 mask 不断右移并在
// 最高位添置新 1。
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;
} // mask 为掩码,用于截取 key 中的部分比特位,将其插入到 tree 数中,对应的值为 value
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;
} // next 不为 NULL,是因 bit & mask 为 0 退出上面的 while 的
if (next) {
if (node->value != NGX_RADIX_NO_VALUE) {
return NGX_BUSY;
} node->value = value;
return NGX_OK;
} // next 为 NULL,从 tree 新分配一个节点
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;
} // 节点从 Radix 树中删除后,会放入到 free 链表中
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)) {
// key 该位为 1,表示接下来找右子树
if (key & bit) {
node = node->right;
// key 该位为 0,表示接下来找左子树
} 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;
} // node 为根节点
if (node->parent == NULL) {
break;
}
} return NGX_OK;
} // 在 tree 树中查找 key 值,key 是一个无符号的32位整数,每一位对应从树根开始
// 查找时选择左子树(0)还是右子树(1)
uintptr_t
ngx_radix32tree_find(ngx_radix_tree_t *tree, uint32_t key)
{
uint32_t bit;
uintptr_t value;
ngx_radix_node_t *node; // 初始状态下最高位为1,用于后面的“与”操作,确定左右子树
bit = 0x80000000;
value = NGX_RADIX_NO_VALUE;
node = tree->root; // 从树根开始 // 理论上最多循环32次(key为32位),实际上查找到node为NULL,则表明上一轮循环中已经是叶子节点
while (node) {
if (node->value != NGX_RADIX_NO_VALUE) {
value = node->value;
} // 该位为 1 则右子树
if (key & bit) {
node = node->right; // 该位为 0 则左子树
} else {
node = node->left;
} bit >>= 1;
} // 返回找到的节点的值
return value;
} static void *
ngx_radix_alloc(ngx_radix_tree_t *tree)
{
char *p; // 创建Radix树时会调用,此时free为NULL,不会进入该if分支
// 插入时调用到这里,free 值非零,则返回 free
if (tree->free) {
p = (char *) tree->free;
tree->free = tree->free->right;
return p;
} // 创建Radix树时会调用,此时tree->size为0,会进入该if分支
if (tree->size < sizeof(ngx_radix_node_t)) {
// 以ngx_pagesize大小内存对齐的方式,从内存池tree->pool中分配ngx_pagesize大小的内存给start
// ngx_pagesize 是在 src/os/unix/ngx_posix_init.c 和 src/os/win32/ngx_win32_init.c
// 的 ngx_os_init() 函数中初始化的。pagesize 的值与处理器架构有关。
tree->start = ngx_pmemalign(tree->pool, ngx_pagesize, ngx_pagesize);
if (tree->start == NULL) {
return NULL;
} // tree->size 为刚才分配的内存大小
tree->size = ngx_pagesize;
} // tree->start 加上 ngx_radix_node_t 将要占用的大小
// tree->size 减去 ngx_radix_node_t 将要占用的大小
p = tree->start;
tree->start += sizeof(ngx_radix_node_t);
tree->size -= sizeof(ngx_radix_node_t); // 虽然返回值类型是 void*,但是调用处都会转为 ngx_radix_node_t
return p;
}

Nginx 源码完全注释(10)ngx_radix_tree的更多相关文章

  1. Nginx 源码完全注释(11)ngx_spinlock

    Nginx 是多进程模式的,一个 master 与多个 workers,一般工作在多核 CPU 上,所以自旋锁就是必须用到的.Nginx 中的自旋锁的定义,位于 ngx_spinlock.c 中,如下 ...

  2. Nginx源码完全注释(6)core/murmurhash

    下面是摘自 Google Code 的 Murmurhash 开源项目主页上的 Murmurhash2,Nginx 就是采用的这个. uint32_t MurmurHash2 ( const void ...

  3. Nginx源码完全注释(8)ngx_errno.c

    errno.h中的strerror(int errno)可以确定指定的errno的错误的提示信息.在 Nginx 中,将所有错误提示信息预先存储在一个数组里,而预先确定这个数组的大小,是在自动化脚本中 ...

  4. Nginx源码完全注释(9)nginx.c: ngx_get_options

    本文分析 ngxin.c 中的 ngx_get_options 函数,其影响: nginx.c 中的: static ngx_uint_t ngx_show_help; static ngx_uint ...

  5. Nginx源码完全注释(5)core/ngx_cpuinfo.c

    /* * Copyright (C) Igor Sysoev * Copyright (C) Nginx, Inc. */ #include <ngx_config.h> #include ...

  6. Nginx源码完全注释(2)ngx_array.h / ngx_array.c

    数组头文件 ngx_array.h #include <ngx_config.h> #include <ngx_core.h> struct ngx_array_s { voi ...

  7. nginx源码完全注释(1)ngx_alloc.h / ngx_alloc.c

    首先看 ngx_alloc.h 文件,主要声明或宏定义了 ngx_alloc,ngx_calloc,ngx_memalign,ngx_free. /* * Copyright (C) Igor Sys ...

  8. Nginx源码完全注释(7)ngx_palloc.h/ngx_palloc.c

    ngx_palloc.h /* * NGX_MAX_ALLOC_FROM_POOL should be (ngx_pagesize - 1), i.e. 4095 on x86. * On Windo ...

  9. Nginx源码完全注释(4)ngx_queue.h / ngx_queue.c

    队列头文件ngx_queue.h #include <ngx_config.h> #include <ngx_core.h> #ifndef _NGX_QUEUE_H_INCL ...

随机推荐

  1. spring 自动扫描组件

    在Spring2.5中,有4种类型的组件自动扫描注释类型 @Component – 指示自动扫描组件. @Repository – 表示在持久层DAO组件. @Service – 表示在业务层服务组件 ...

  2. HTML 按钮换肤

    .button2{ background-image: url(images/input-bg.jpg); width: 83px; height: 31px; border: none 0px; f ...

  3. 【转】简明 Python 教程

    原文网址:http://woodpecker.org.cn/abyteofpython_cn/chinese/ 简明 Python 教程Swaroop, C. H. 著沈洁元  译www.byteof ...

  4. dts、dtb编译

    设备树(Device Tree)包括DTC(device tree compiler),DTS(device tree source和DTB(device tree blob). dtc编译器能够把 ...

  5. Android插件化初识

    含义:可以简单理解为将一个app分为多个小的app,其中有一个为宿主app. 解决的主要问题:代码加载.资源加载. 插件的方式:apk安装,apk不安装,dex包 插件化的优点: 1) 模块解耦,应用 ...

  6. cookie跨域问题汇总

    一.通过nginx反向代理 通过nginx反向代理后,使得浏览器认为访问的资源都是属于相同协议,域名和端口的. 详细见:<nginx实现跨域访问> 二.jsonp方式请求 v jquery ...

  7. 阻塞队列之四:ArrayBlockingQueue

    一.ArrayBlockingQueue简介 一个由循环数组支持的有界阻塞队列.它的本质是一个基于数组的BlockingQueue的实现. 它的容纳大小是固定的.此队列按 FIFO(先进先出)原则对元 ...

  8. (转)Docker volume plugin - enabled create local volume on docker host

    原文地址:https://hub.docker.com/r/cwspear/docker-local-persist-volume-plugin/ Short Description Create n ...

  9. WARN deprecation:&L - HHH90000012: Recognized obsolete hibernate namespace http://hibernate.sourceforge.net/hibernate-configuration. Use namespace http://www.hibernate.org/dtd/hibernate-configuration

    WARN deprecation:&L - HHH90000012: Recognized obsolete hibernate namespace http://hibernate.sour ...

  10. 5月9日上课笔记-网页定位、网页动画【HTML5】

    一.网页定位 position: static (默认值) relative 相对定位(相对原来的位置) right left botton top absolute 绝对定位 fixed: 固定定位 ...