overview
- ngx_list_t (100%)
 - ngx_queue_t (100%)
 - ngx_array_t (100%)
 - ngx_rbtree_t (100%)
 - ngx_radix_t (100%)
 - 支持通配符的散列表 (0%)
 
ngx_list_t 链表容器
- 存储数组的链表
 - 每个链表元素 ngx_list_part_t 又是一个数组,拥有连续的内存
 
1  | typedef struct ngx_list_part_s ngx_list_part_t;  | 
ngx_queue_t 双向链表
- 实现了排序功能
 - 非常轻量级,是一个纯粹的双向链表
 - 支持两个链表间的合并
 
1  | typedef struct ngx_queue_s ngx_queue_t;  | 
双向链表的容器和节点本身全用 ngx_queue_t 表示,很难区分某个 ngx_queue_t 是节点还是容器。所以Nginx将所有容器和节点的方法全部列出来,在不同的意义上用不同的方法。
链表容器方法:1
2
3
4
5
6
7
8
9
10
11
12ngx_queue_init()
ngx_queue_empty()
ngx_queue_insert_head()
ngx_queue_insert_tail()
ngx_queue_head()
ngx_queue_last()
ngx_queue_sentinel()
ngx_queue_remove()
ngx_queue_split()
ngx_queue_add()
ngx_queue_middle()
ngx_queue_sort()                    // 插入排序,并不适合大量数据排序
链表节点方法:1
2
3
4ngx_queue_next()
ngx_queue_prev()
ngx_queue_data()
ngx_queue_insert_after()
除 ngx_queue_middle 和 ngx_queue_sort ,其他全部宏实现
例子:1
2
3
4
5
6
7
8
9
10
11
12
13
14typedef struct {
    u_char*     str;
    ngx_queue_t qElel;
    int num;
}TestNode;
ngx_init_t compTestNode(const ngx_queue_t* a, const ngx_queue_t* b)
{
    TestNode* aNode = ngx_queue_data(a, TestNode, qElel);
    TestNode* bNode = ngx_queue_data(b, TestNode, qElel);
    return aNode->num > bNode->num;
}
双向链表能通用的关键在这:1
2
3
4
  (type*) ((u_char*)q - offsetof(type, link))
通过内部成员 ngx_queue_t 可以找到所属的数据结构 TestNode, 进而找到数据结构中的其他成员
ngx_array_t 动态数组
- 顺序容器
 - 数组的形式存储元素
 - 数组容量达到上限时动态改变数组的大小
 
与 C++ STL vector 容器扩容的区别
STL vector 扩容方式
1  | // n 为当前新申请空间,若n大于剩余空间,则重新分配空间  | 
ngx_array_t 扩容方式
1  | ngx_array_push(...); // 扩容到 2×size  | 
1  | typedef struct ngx_array_s ngx_array_t;  | 
ngx_rbtree_t 红黑树
- 关联容器
 
Nginx应用场景:支持快速检索,插入,删除操作,也支持范围查询,遍历。
- 定时器管理
 - 文件缓存
 
红黑树:1
2
3
4
5
6
7
8
9
10
11
12
13
14typedef struct ngx_rbtree_s ngx_rbtree_t;
typedef void (*ngx_rbtree_insert_pt) (ngx_rbtree_node_t *root,
ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
struct ngx_rbtree_s {
  ngx_rbtree_node_t   *root;
  ngx_rbtree_node_t   *sentinel;  // 指向NIL哨兵节点
  ngx_rbtree_insert_pt insert;    // 自定义比较函数,当两个节点key相同时,通过这个函数进行区分
}
ngx_rbtree_init(...);
ngx_rbtree_insert(...);
ngx_rbtree_delete(...);
红黑树的一个节点:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
typedef ngx_uint_t ngx_rbtree_key_t;
typedef struct ngx_rbtree_node_s ngx_rbtree_node_t;
struct ngx_rbtree_node_s {
  ngx_rbtree_key_t    key;        // 无符号整型关键字
  ngx_rbtree_node_t   *left;
  ngx_rbtree_node_t   *right;
  ngx_rbtree_node_t   *parent;
  u_char              color;      // 0 黑色,1红色
  u_char              data;       // 空间太小,很少使用
  //TODO 看看size,为什么此处加一个,data,为了空间充分利用吗?
}
ngx_rbt_red(...);
ngx_rbt_black(...);
ngx_rbt_is_red(...);
ngx_rbt_is_black(...);
ngx_rbt_copy_color(...);
ngx_rbtree_min(...);                // 找到当前节点及其子树中的最小节点
ngx_rbtree_sentinel_init(...);
1  | typedef struct {  | 
ngx_radix_tree_t 基数树
与ngx_rbtree 区别:
- 存储的每个节点都必须以32位整型作为区别任意两个节点的唯一标识。
 - 负责分配每个节点占用的内存
 - 没有红黑树所谓的子平衡
 
1  | typedef struct ngx_radix_node_s ngx_radix_node_t;  | 
1  | typedef struct {  | 
支持通配符的散列表
解决碰撞方法:
- 分离链表法:
同义词的所有元素,放在散列表外的链表中。当找到散列表的位置后,需遍历链表再去找到元素 - 开放寻址法:所有元素都存在散列表中,依次检查其后连续的槽(此为nginx中用到的方法)
 
1  | typedef struct {  | 
1  | typedef struct {  | 
支持通配符的散列表1
2
3
4
5
6
7typedef struct {
  ngx_hash_t hash;                  // 基本散列表
  void *value;                      // 指向用户数据
} ngx_hash_wildcard_t;
ngx_hash_find_wc_head(...);
ngx_hash_find_wc_tail(...);
1  | typedef struct {  |