概述:
本节我们介绍Twemproxy的两个重要的自定义数据结构:array和string。
array:
故名思议,array就是数组,array的代码在nc_array.c和nc_array.h中,我们先来看看array的结构:
数据结构:
| struct array { uint32_t nelem; /* # element */ void *elem; /* element */ size_t size; /* element size */ uint32_t nalloc; /* # allocated element */ }; |
array的结构包含:
- 当前数组中元素的数量
- 指向元素的void指针
- 元素的大小
- 预申请的空间的元素数
由此可见,array应该具有以下特点:
- 需要预申请空间,空间不足则需要重新申请
- 数组每个元素大小必须相同
后面我们将通过数组的操作验证。
操作:
对数组的操作包含下面这些,这些都是在nc_array.h中声明和定义的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
/** 将数组置为null **/
static
inline
void
array_null(struct
array
*a)
{
a->nelem
=
0;
a->elem
=
NULL;
a->size
=
0;
a->nalloc
=
0;
}
/** 设置一个数组struct的值 **/
static
inline
void
array_set(struct
array
*a,
void
*elem,
size_t
size,
uint32_t
nalloc)
{
a->nelem
=
0;
a->elem
=
elem;
a->size
=
size;
a->nalloc
=
nalloc;
}
/** 返回数组的元素数 **/
static
inline
uint32_t
array_n(const
struct
array
*a)
{
return
a->nelem;
}
struct
array
*array_create(uint32_t
n,
size_t
size); //创建包含n个大小为size的elements的数组
void
array_destroy(struct
array
*a); //销毁一个数组
rstatus_t
array_init(struct
array
*a,
uint32_t
n,
size_t
size); //初始化一个数组
void
array_deinit(struct
array
*a); //deinit
uint32_t
array_idx(struct
array
*a,
void
*elem); //返回元素的数组下标
void
*array_push(struct
array
*a); //push一个元素,返回该元素的指针
void
*array_pop(struct
array
*a); //pop一个元素,返回这个元素的指针
void
*array_get(struct
array
*a,
uint32_t
idx); //返回idx位置上的元素
void
*array_top(struct
array
*a);
//返回队尾的元素
void
array_swap(struct
array
*a,
struct
array
*b); //交换两个数组
void
array_sort(struct
array
*a,
array_compare_t
compare); //数组排序
rstatus_t
array_each(struct
array
*a,
array_each_t
func,
void
*data); //数组迭代
|
接下来我们通过create和push验证一下我们之前的猜想:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | struct array * array_create(uint32_t n, size_t size) { struct array *a; ASSERT(n != 0 && size != 0); a = nc_alloc(sizeof(*a)); //申请array的空间 if (a == NULL) { return NULL; } a->elem = nc_alloc(n * size); //预申请elements空间 if (a->elem == NULL) { nc_free(a); return NULL; } a->nelem = 0; a->size = size; a->nalloc = n; return a; } void * array_push(struct array *a) { void *elem, *new; size_t size; if (a->nelem == a->nalloc) { /* array已满,重新申请空间,重新申请的大小为原来的2倍 */ size = a->size * a->nalloc; new = nc_realloc(a->elem, 2 * size); if (new == NULL) { return NULL; } a->elem = new; a->nalloc *= 2; } elem = (uint8_t *)a->elem + a->size * a->nelem; a->nelem++; //增加element数 return elem; } |
从代码可以看出是如何realloc空间的,这里实现的奇怪的是,push操作是将elements数加1,然后返回末尾的element指针。也就是说,调用者需要先调用array_push得到elements指针,然后为其设置字段值,来完成数组元素的push。
另外值得一提的是,array提供了create/destroy和init/deinit这两组创建和销毁函数,这两组函数的区别是:create/destroy同时创建/销毁array和elements空间,而init和deinit只创建/销毁elements空间,看一下array_destroy代码就明白了:
|
|
void
array_destroy(struct
array
*a)
{
array_deinit(a);
nc_free(a);
}
|
array的代码很少,只有200多行,就不详细介绍其他的操作了。
string:
string的代码在nc_string.c和nc_string.h中,封装了一些对字符串的操作。
数据结构:
| struct string { uint32_t len; /* string length */ uint8_t *data; /* string data */ }; |
string的结构包含两个字段:字符串的长度、指向字符串的指针,就这么简单。
操作:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
/** set字符串 **/
#define string_set_text(_str, _text) do { \
(_str)->len
=
(uint32_t)(sizeof(_text)
-
1);\
(_str)->data
=
(uint8_t
*)(_text); \
}
while
(0);
/** set字符串 **/
#define string_set_raw(_str, _raw) do { \
(_str)->len
=
(uint32_t)(nc_strlen(_raw)); \
(_str)->data
=
(uint8_t
*)(_raw);
\
}
while
(0);
void
string_init(struct
string
*str); //初始化string
void
string_deinit(struct
string
*str); //dinit
bool
string_empty(const
struct
string
*str); //判断字符串是否是空
rstatus_t
string_duplicate(struct
string
*dst,
const
struct
string
*src); //duplicate一个字符串
rstatus_t
string_copy(struct
string
*dst,
const
uint8_t *src,
uint32_t
srclen); //copy一个字符串
int
string_compare(const
struct
string
*s1,
const
struct
string
*s2); //字符串比较
|
底下还有用宏定义的一些操作,不过都是操作char*的,这里就不列举了。总的来说string的结构很简单,代码也只有100多行。
小结:
本节我们看了一下array和string这两个数据结构,都很简单,后面我们马上会用到。