ngx_radix_tree.h

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

ngx_radix_tree.c

  1. static void *ngx_radix_alloc(ngx_radix_tree_t *tree);
  2. ngx_radix_tree_t *
  3. ngx_radix_tree_create(ngx_pool_t *pool, ngx_int_t preallocate)
  4. {
  5. uint32_t key, mask, inc;
  6. ngx_radix_tree_t *tree;
  7. // 为该树的结构体分配内存
  8. tree = ngx_palloc(pool, sizeof(ngx_radix_tree_t));
  9. if (tree == NULL) {
  10. return NULL;
  11. }
  12. // 初始化各成员
  13. tree->pool = pool;
  14. tree->free = NULL;
  15. tree->start = NULL;
  16. tree->size = 0;
  17. // 为根节点分配内存(实际上不一定有重新的内存分配操作,具体详见ngx_radix_alloc部分)
  18. tree->root = ngx_radix_alloc(tree);
  19. if (tree->root == NULL) {
  20. return NULL;
  21. }
  22. // 根节点的初始化
  23. tree->root->right = NULL;
  24. tree->root->left = NULL;
  25. tree->root->parent = NULL;
  26. tree->root->value = NGX_RADIX_NO_VALUE;
  27. // 如果指定的预分配节点数为 0,则直接返回这个树就好了
  28. if (preallocate == 0) {
  29. return tree;
  30. }
  31. /*
  32. * Preallocation of first nodes : 0, 1, 00, 01, 10, 11, 000, 001, etc.
  33. * increases TLB hits even if for first lookup iterations.
  34. * On 32-bit platforms the 7 preallocated bits takes continuous 4K,
  35. * 8 - 8K, 9 - 16K, etc. On 64-bit platforms the 6 preallocated bits
  36. * takes continuous 4K, 7 - 8K, 8 - 16K, etc. There is no sense to
  37. * to preallocate more than one page, because further preallocation
  38. * distributes the only bit per page. Instead, a random insertion
  39. * may distribute several bits per page.
  40. *
  41. * Thus, by default we preallocate maximum
  42. * 6 bits on amd64 (64-bit platform and 4K pages)
  43. * 7 bits on i386 (32-bit platform and 4K pages)
  44. * 7 bits on sparc64 in 64-bit mode (8K pages)
  45. * 8 bits on sparc64 in 32-bit mode (8K pages)
  46. */
  47. // 下面这部分就很有意思了,你可以看上面的英文注释。简单说,一个 x bits 的值,对应其 Radix 树
  48. // 有 x + 1 层,那么节点的个数就是 2^(x+1) -1 个(数据结构常识,你也可以很容易证明这个结论)。
  49. if (preallocate == -1) {
  50. // 根据 pagesize 大小,确定可以分配多少个 radix 树结构
  51. switch (ngx_pagesize / sizeof(ngx_radix_tree_t)) {
  52. /* amd64 */
  53. case 128:
  54. preallocate = 6;
  55. break;
  56. /* i386, sparc64 */
  57. case 256:
  58. preallocate = 7;
  59. break;
  60. /* sparc64 in 32-bit mode */
  61. default:
  62. preallocate = 8;
  63. }
  64. }
  65. mask = 0;
  66. inc = 0x80000000;
  67. // preallocate 为几,最终 mask 就有几个最高位为1,其他为0。整个循环过程中 mask 不断右移并在
  68. // 最高位添置新 1。
  69. while (preallocate--) {
  70. key = 0;
  71. mask >>= 1;
  72. mask |= 0x80000000;
  73. do {
  74. if (ngx_radix32tree_insert(tree, key, mask, NGX_RADIX_NO_VALUE)
  75. != NGX_OK)
  76. {
  77. return NULL;
  78. }
  79. key += inc;
  80. } while (key);
  81. inc >>= 1;
  82. }
  83. return tree;
  84. }
  85. // mask 为掩码,用于截取 key 中的部分比特位,将其插入到 tree 数中,对应的值为 value
  86. ngx_int_t
  87. ngx_radix32tree_insert(ngx_radix_tree_t *tree, uint32_t key, uint32_t mask,
  88. uintptr_t value)
  89. {
  90. uint32_t bit;
  91. ngx_radix_node_t *node, *next;
  92. bit = 0x80000000;
  93. node = tree->root;
  94. next = tree->root;
  95. while (bit & mask) {
  96. if (key & bit) {
  97. next = node->right;
  98. } else {
  99. next = node->left;
  100. }
  101. // 当前节点为叶子节点,停止循环查找
  102. if (next == NULL) {
  103. break;
  104. }
  105. bit >>= 1;
  106. node = next;
  107. }
  108. // next 不为 NULL,是因 bit & mask 为 0 退出上面的 while 的
  109. if (next) {
  110. if (node->value != NGX_RADIX_NO_VALUE) {
  111. return NGX_BUSY;
  112. }
  113. node->value = value;
  114. return NGX_OK;
  115. }
  116. // next 为 NULL,从 tree 新分配一个节点
  117. while (bit & mask) {
  118. next = ngx_radix_alloc(tree);
  119. if (next == NULL) {
  120. return NGX_ERROR;
  121. }
  122. next->right = NULL;
  123. next->left = NULL;
  124. next->parent = node;
  125. next->value = NGX_RADIX_NO_VALUE;
  126. if (key & bit) {
  127. node->right = next;
  128. } else {
  129. node->left = next;
  130. }
  131. bit >>= 1;
  132. node = next;
  133. }
  134. node->value = value;
  135. return NGX_OK;
  136. }
  137. // 节点从 Radix 树中删除后,会放入到 free 链表中
  138. ngx_int_t
  139. ngx_radix32tree_delete(ngx_radix_tree_t *tree, uint32_t key, uint32_t mask)
  140. {
  141. uint32_t bit;
  142. ngx_radix_node_t *node;
  143. bit = 0x80000000;
  144. node = tree->root;
  145. while (node && (bit & mask)) {
  146. // key 该位为 1,表示接下来找右子树
  147. if (key & bit) {
  148. node = node->right;
  149. // key 该位为 0,表示接下来找左子树
  150. } else {
  151. node = node->left;
  152. }
  153. bit >>= 1;
  154. }
  155. // 要删除的节点不存在
  156. if (node == NULL) {
  157. return NGX_ERROR;
  158. }
  159. // 要删除的节点还有子节点
  160. if (node->right || node->left) {
  161. if (node->value != NGX_RADIX_NO_VALUE) {
  162. node->value = NGX_RADIX_NO_VALUE;
  163. return NGX_OK;
  164. }
  165. // 要删除的节点有子树,但是该节点的值为无效值,则视为错误
  166. return NGX_ERROR;
  167. }
  168. for ( ;; ) {
  169. // 如果该节点是右节点
  170. if (node->parent->right == node) {
  171. node->parent->right = NULL;
  172. // 如果该节点是左节点
  173. } else {
  174. node->parent->left = NULL;
  175. }
  176. node->right = tree->free;
  177. tree->free = node;
  178. node = node->parent;
  179. if (node->right || node->left) {
  180. break;
  181. }
  182. if (node->value != NGX_RADIX_NO_VALUE) {
  183. break;
  184. }
  185. // node 为根节点
  186. if (node->parent == NULL) {
  187. break;
  188. }
  189. }
  190. return NGX_OK;
  191. }
  192. // 在 tree 树中查找 key 值,key 是一个无符号的32位整数,每一位对应从树根开始
  193. // 查找时选择左子树(0)还是右子树(1)
  194. uintptr_t
  195. ngx_radix32tree_find(ngx_radix_tree_t *tree, uint32_t key)
  196. {
  197. uint32_t bit;
  198. uintptr_t value;
  199. ngx_radix_node_t *node;
  200. // 初始状态下最高位为1,用于后面的“与”操作,确定左右子树
  201. bit = 0x80000000;
  202. value = NGX_RADIX_NO_VALUE;
  203. node = tree->root; // 从树根开始
  204. // 理论上最多循环32次(key为32位),实际上查找到node为NULL,则表明上一轮循环中已经是叶子节点
  205. while (node) {
  206. if (node->value != NGX_RADIX_NO_VALUE) {
  207. value = node->value;
  208. }
  209. // 该位为 1 则右子树
  210. if (key & bit) {
  211. node = node->right;
  212. // 该位为 0 则左子树
  213. } else {
  214. node = node->left;
  215. }
  216. bit >>= 1;
  217. }
  218. // 返回找到的节点的值
  219. return value;
  220. }
  221. static void *
  222. ngx_radix_alloc(ngx_radix_tree_t *tree)
  223. {
  224. char *p;
  225. // 创建Radix树时会调用,此时free为NULL,不会进入该if分支
  226. // 插入时调用到这里,free 值非零,则返回 free
  227. if (tree->free) {
  228. p = (char *) tree->free;
  229. tree->free = tree->free->right;
  230. return p;
  231. }
  232. // 创建Radix树时会调用,此时tree->size为0,会进入该if分支
  233. if (tree->size < sizeof(ngx_radix_node_t)) {
  234. // 以ngx_pagesize大小内存对齐的方式,从内存池tree->pool中分配ngx_pagesize大小的内存给start
  235. // ngx_pagesize 是在 src/os/unix/ngx_posix_init.c 和 src/os/win32/ngx_win32_init.c
  236. // 的 ngx_os_init() 函数中初始化的。pagesize 的值与处理器架构有关。
  237. tree->start = ngx_pmemalign(tree->pool, ngx_pagesize, ngx_pagesize);
  238. if (tree->start == NULL) {
  239. return NULL;
  240. }
  241. // tree->size 为刚才分配的内存大小
  242. tree->size = ngx_pagesize;
  243. }
  244. // tree->start 加上 ngx_radix_node_t 将要占用的大小
  245. // tree->size 减去 ngx_radix_node_t 将要占用的大小
  246. p = tree->start;
  247. tree->start += sizeof(ngx_radix_node_t);
  248. tree->size -= sizeof(ngx_radix_node_t);
  249. // 虽然返回值类型是 void*,但是调用处都会转为 ngx_radix_node_t
  250. return p;
  251. }

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. systemtap 安装试用

    1. 安装 yum install -y systemtap systemtap-runtime 2. 环境准备    a. 自动安装依赖 stap-prep b. 手动安装依赖 kernel-deb ...

  2. NumPy-快速处理数据--矩阵运算

    本文摘自<用Python做科学计算>,版权归原作者所有. 1. NumPy-快速处理数据--ndarray对象--数组的创建和存取 2. NumPy-快速处理数据--ndarray对象-- ...

  3. Centos6.8 安装MySql

        启动Centos6.8   输入命令: yum install mysql mysql-server -y 等待安装完成. 启动MySQL,输入命令: /etc/init.d/mysqld s ...

  4. 解决div嵌套时IE8和FF无法自适应高度

    解决div嵌套时IE8和FF无法自适应高度 还是做类似新浪评论回复的时候,将回复的DIV嵌套在一个DIV中,然后点击回复的时候显示子DIV,这是父DIV的高度是会变化的,于是我将父DIV的高度设置为h ...

  5. dos命令行连接操作ORACLE数据库

    C:\Adminstrator> sqlplus "/as sysdba" 查看是否连接到数据库 SQL> select status from v$instance; ...

  6. 打包python文件,让文件程序化

    通过对源文件打包,Python程序可以在没有安装 Python的环境中运行,也可以作为一个独立文件方便传递和管理. 现在网上主流的打包方式有两种py2exe或者pyinstaller两款多平台的Pyt ...

  7. Cousera 无法播放视频 解决办法 widows 和 linux

    查资料得知,Cousera无法播放课程视频原因在于DNS污染. 尽管通过FQ软件把视频看完了,在最后一课找到了这个解决办法,现在拿出来分享给大家: Windows: 请参照以下链接: http://j ...

  8. GOF23设计模式之策略模式(strategy)

    一.策略模式概述 策略模式对应于解决某一个问题的一个算法族,允许用户从该算法族中任选一种算法解决一个问题,同时可以方便的更换算法或者增加新的算法.并且由客户端决定调用哪个算法. 策略模式的本质: 分离 ...

  9. Bootstrap组件系列之福利篇几款好用的组件(推荐)

    引用 :http://www.jb51.net/article/87189.htm 一.时间组件 bootstrap风格的时间组件非常多,你可以在github上面随便搜索“datepicker”关键字 ...

  10. 数据结构与算法JavaScript描述——使用队列

    1.使用队列:方块舞的舞伴分配问题 前面我们提到过,经常用队列模拟排队的人.下面我们使用队列来模拟跳方块舞的人.当 男男女女来到舞池,他们按照自己的性别排成两队.当舞池中有地方空出来时,选两个队 列中 ...