最近在看github上一个JSON库教程.记一下临时的一些点
tutorial01
- 头文件用宏加入include guard,防止重复声明
#ifndef LEPTJSON_H__
#define LEPTJSON_H__
/* ... */
#endif /* LEPTJSON_H__ */
- 没有硬编码,全部枚举化,用了两次数据结构抽象来提高代码的复用性,健壮性
typedef enum { LEPT_NULL, LEPT_FALSE, LEPT_TRUE, LEPT_NUMBER, LEPT_STRING, LEPT_ARRAY, LEPT_OBJECT } lept_type;
typedef struct {
lept_type type;
}lept_value;
- 多行宏编写用了do{}while(0)的方式,防止宏预处理后的代码问题
#define M() do { a(); b(); } while(0)
- 入参的合法性判断统一采用assert()
tutorial2
- 统一的解析入口函数lept_parse_value(),划分类型进入对应的子解析函数,子解析函数的EXPECT()感觉多余了,重复判断首字符
static int lept_parse_value(lept_context* c, lept_value* v) {
switch (*c->json) {
case 't': return lept_parse_true(c, v);
case 'f': return lept_parse_false(c, v);
case 'n': return lept_parse_null(c, v);
default: return lept_parse_number(c, v);
case '\0': return LEPT_PARSE_EXPECT_VALUE;
}
}
static int lept_parse_true(lept_context* c, lept_value* v) {
EXPECT(c, 't');
if (c->json[0] != 'r' || c->json[1] != 'u' || c->json[2] != 'e')
return LEPT_PARSE_INVALID_VALUE;
c->json += 3;
v->type = LEPT_TRUE;
return LEPT_PARSE_OK;
}
tutorial03
- 字符串的解析涉及到转义字符 / 。一个解析出来的值不可能同时为数字或者字符串,数据结构这块采用了union来节省内存.左边是struct的内存图,右边是union的
typedef struct {
union {
struct { char* s; size_t len; }s; /* string */
double n; /* number */
}u;
lept_type type;
}lept_value;
- 字符串的动态malloc和free函数也写的很简洁
void lept_set_string(lept_value* v, const char* s, size_t len) {
assert(v != NULL && (s != NULL || len == 0));
lept_free(v);
v->u.s.s = (char*)malloc(len + 1);
memcpy(v->u.s.s, s, len);
v->u.s.s[len] = '\0';
v->u.s.len = len;
v->type = LEPT_STRING;
}
void lept_free(lept_value* v) {
assert(v != NULL);
if (v->type == LEPT_STRING)
free(v->u.s.s);
v->type = LEPT_NULL;
}
- 缓存这块用动态数组,JSON用stack来做缓存,就可以满足解析数据对象的存取
//加入stack对应数据结构
typedef struct {
const char* json;
char* stack;
size_t size, top;
}lept_context;
//parse函数添加stack的初始化和最后的free操作
int lept_parse(lept_value* v, const char* json) {
lept_context c;
int ret;
assert(v != NULL);
c.json = json;
c.stack = NULL; /* <- */
c.size = c.top = 0; /* <- */
lept_init(v);
lept_parse_whitespace(&c);
if ((ret = lept_parse_value(&c, v)) == LEPT_PARSE_OK) {
/* ... */
}
assert(c.top == 0); /* <- 保证所有数据正常解析后都会被pop,stack最后为空*/
free(c.stack); /* <- */
return ret;
}
//push和pop函数
//宏设置的好处是可以在编译选项中动态修改
#ifndef LEPT_PARSE_STACK_INIT_SIZE
#define LEPT_PARSE_STACK_INIT_SIZE 256
#endif
static void* lept_context_push(lept_context* c, size_t size) {
void* ret;
assert(size > 0);
//当前stack容量不足,需动态增加
if (c->top + size >= c->size) {
if (c->size == 0)
c->size = LEPT_PARSE_STACK_INIT_SIZE;
while (c->top + size >= c->size)
//位操作扩大为原来的1.5倍
c->size += c->size >> 1; /* c->size * 1.5 */
//初始化的时候realloc(NULL,size)等价于malloc(size)
c->stack = (char*)realloc(c->stack, c->size);
}
ret = c->stack + c->top;
c->top += size;
//返回push后的top的地址
return ret;
}
static void* lept_context_pop(lept_context* c, size_t size) {
assert(c->top >= size);
return c->stack + (c->top -= size);
}