lept_json库的学习之array
这一节讲一下parse_array
JSON数组语法
Array是一个复合数据类型,Array自身不存储实际值,而只存储其元素Element,再由它的元素存储实际值。
Array语法如下:
[](空数组) [element1,element2,…](多个元素).
数据结构
array的数据结构这里讲两种
顺序表,也就是C/C++中的数组,
线性表最大的好处是能以 O ( 1 ) O(1) O(1) 用索引访问任意元素,次要好处是内存布局紧凑,省内存之余还有高缓存一致性(cache coherence),
因为它内存都紧密的排放再一块了。
但顺序表的缺点是不能快速插入元素,而且我们在解析 JSON 数组的时候,还不知道应该分配多大的一整块内存才合适。
链表
它的最大优点是可快速地插入元素(开端、末端或中间),但需要以 O ( n ) O(n) O(n) 时间去经索引取得内容。如果我们只需顺序遍历,那么是没有问题的。还有一个小缺点,就是相对数组而言,链表在存储每个元素时有额外内存开销(存储下一节点的指针),而且遍历时元素所在的内存可能不连续,令缓存不命中(cache miss)的机会上升。(这里是项目原话,我并不清楚缓存不命中什么意思)
这里其实就涉及到数据结构知识里的顺序表和链表的区别了,
顺序表在插入删除元素时,需要对目标元素位置后面的所有元素进行重排,所以插入删除操作消耗时间。
而链表则是在寻找的时候只能一个一个往下找,在索引的时候会花费很多时间,而增加和删除只需要把前后节点的link修改一下就可以,而且链表不会需要固定一个上限,可以无限的往后link新的节点。但也因此,其内存不连续。
就此二者而言,各有优劣。
在这个json中,使用的是顺序表,叶老师通过之前string用到的堆栈来解决解析array时会遇到的数组大小未知问题。
一下是数据结构代码:
typedef struct lept_value lept_value;//前向声明
struct lept_value{
//我们可使用 C 语言的 union 来节省内存:
union{
struct {
lept_value* e; size_t size,capacity; }m_arr;/* array */
struct {
char* s; size_t len; }m_str; /* string */
double m_num; /* number */
}u;
lept_type type;
};
valuee是数组,size是大小(元素个数元素大小),capacity是容量(分配的内存大小)
在确定完数据结构之后,其实就可以写接口api了,不过我打算把所有接口统一到最后讲(虽然也就是贴一下代码),现在先讲解析。
Parse_Array
因为数组中包含了value,很容易想到我们将会使用到递归结构。
遇到左中括号([)开始,解析第一个元素,第一个元素解析完如果是逗号(,),则解析下一个元素,直到遇到右中括号为止(])。
每解析一个元素时,我们用一个临时元素value来存储这个解析得到的值,然后把临时值压栈,直到整个数组元素全部解析完,再把栈内元素全部弹出,分配内存,生成数组。
上代码:
static int lept_parse_value(lept_context& c, lept_value& v); /* 前向声明 */
static int lept_parse_array(lept_context& c, lept_value& v) {
size_t size = 0;
int ret;
EXPECT(c, '[');
lept_parse_whitespace(c);//解析数组中存在的空白
if (*c.json == ']') {
c.json++;
lept_set_array(v, 0);//set_array接口
return LEPT_PARSE_OK;
}
for (;;) {
lept_value e;//临时value
lept_init