在core目录下,可以发现Nginx其实自己实现了很多基本的数据结构,本篇会对数组Array进行学习。
/* ngx_core.h */
typedef struct ngx_array_s ngx_array_t;
/* ngx_palloc.h */
// 将alloc赋值给p, 如果p等于NULL, 那么返回rc
#define ngx_test_null(p, alloc, rc) if ((p = alloc) == NULL) { return rc; }
/* ngx_array.h */
struct ngx_array_s { // 数组结构体
void *elts; // 数组首地址
ngx_uint_t nelts; // 数组的已有元素个数
size_t size; // 数组每个元素的字节大小
ngx_uint_t nalloc; // 数组容量
ngx_pool_t *pool; // 存放数组的内存池
};
/* 数组初始化
param array: 待初始化的数组指针
pool: 存放数组的内存池指针
n: 数组容量
size: 数组元素的字节大小
*/
ngx_inline static ngx_int_t ngx_array_init(ngx_array_t *array, ngx_pool_t *pool,
ngx_uint_t n, size_t size)
{
// 在内存池中为数组分配内存空间, 大小为 数组容量 * 数组元素字节大小
if (!(array->elts = ngx_palloc(pool, n * size))) {
return NGX_ERROR;
}
array->nelts = 0; // 数组的已有元素个数为0
array->size = size; // 数组元素的字节大小为size
array->nalloc = n; // 数组容量为n
array->pool = pool; // 存放数组的内存池为pool
return NGX_OK; // 返回NGX_OK, 表示初始化成功
}
/* ngx_array.c */
/* 创建数组
param p: 存放数组结构体和数组的内存池指针
n: 数组容量
size: 数组元素的字节大小
*/
ngx_array_t *ngx_create_array(ngx_pool_t *p, ngx_uint_t n, size_t size)
{
ngx_array_t *a;
// 在内存池中分配一个数组结构体大小的内存空间, 如果分配失败, 那么返回NULL
ngx_test_null(a, ngx_palloc(p, sizeof(ngx_array_t)), NULL);
// 在内存池中为数组分配内存空间, 大小为 数组容量 * 数组元素字节大小, 如果分配失败, 那么返回NULL
ngx_test_null(a->elts, ngx_palloc(p, n * size), NULL);
// 初始化数组结构体的其他成员变量
a->pool = p;
a->nelts = 0;
a->nalloc = n;
a->size = size;
return a;
}
/* 添加数组元素
param a: 待添加元素的数组结构体指针
*/
void *ngx_push_array(ngx_array_t *a)
{
void *elt, *new;
ngx_pool_t *p;
if (a->nelts == a->nalloc) {
// 数组a的已有元素个数等于其容量, 说明数组已经满了
p = a->pool;
// 如果存放数组的内存池的当前可用空间的首地址就是数组的结束地址并且内存池的可用空间足以存放一个数组元素,
// 那么可以直接在此内存池中为数组扩容
if ((char *) a->elts + a->size * a->nelts == p->last
&& (unsigned) (p->end - p->last) >= a->size)
{
p->last += a->size;
a->nalloc++;
} else {
// 为数组进行扩容, 容量为以前的2倍
ngx_test_null(new, ngx_palloc(p, 2 * a->nalloc * a->size), NULL);
// 将原来的数组元素拷贝至新的存放空间
ngx_memcpy(new, a->elts, a->nalloc * a->size);
// 数组结构体的首地址成员指向新的存放空间
a->elts = new;
// 更新数组结构体的容量成员为之前的2倍
a->nalloc *= 2;
}
}
// elt指向数组中存放下一个元素的地址
elt = (char *) a->elts + a->size * a->nelts;
a->nelts++;
return elt;
}
/* 销毁数组和数组结构体
param a: 待销毁的数组结构体指针
*/
void ngx_destroy_array(ngx_array_t *a)
{
ngx_pool_t *p;
p = a->pool;
/* note: 注意在之前创建数组的时候, 我们在内存池中首先申请的是存放数组结构体的空间,
然后才申请的存放数组的空间, 因此在释放这些空间时, 需要先尝试释放存放数组的空间,
接着才是释放存放数组结构体的空间 */
// 如果数组的结束地址等于内存池的可用空间的首地址, 说明在分配数组空间后, 该内存池没有分配其他空间(大块内存除外),
// 这时, 我们只需要对内存池的last指针进行前移即可释放数组空间
if ((char *) a->elts + a->size * a->nalloc == p->last) {
p->last -= a->size * a->nalloc;
}
// 如果数组结构体的结束地址等于内存池的可用空间的首地址, 说明在分配数组结构体空间后, 该内存池没有分配其他空间(大块内存除外),
// 这时, 我们只需要对内存池的last指针进行前移即可释放数组结构体空间
if ((char *) a + sizeof(ngx_array_t) == p->last) {
p->last = (char *) a;
}
// 通过以上的分析, 我们可以发现用于存放数组元素和数组信息的空间并不一定能释放, 假设内存池在分配这些空间后, 又为其他对象分配了
// 空间(大块内存除外), 那么
}