diff --git a/tutorial01/leptjson.c b/tutorial01/leptjson.c index 5299fe1d..e83ecc5a 100644 --- a/tutorial01/leptjson.c +++ b/tutorial01/leptjson.c @@ -3,7 +3,6 @@ #include /* NULL */ #define EXPECT(c, ch) do { assert(*c->json == (ch)); c->json++; } while(0) - typedef struct { const char* json; }lept_context; @@ -15,23 +14,70 @@ static void lept_parse_whitespace(lept_context* c) { c->json = p; } -static int lept_parse_null(lept_context* c, lept_value* v) { - EXPECT(c, 'n'); - if (c->json[0] != 'u' || c->json[1] != 'l' || c->json[2] != 'l') - return LEPT_PARSE_INVALID_VALUE; +//static int lept_parse_null(lept_context* c, lept_value* v) { +// EXPECT(c, 'n'); +// if (c->json[0] != 'u' || c->json[1] != 'l' || c->json[2] != 'l') +// return LEPT_PARSE_INVALID_VALUE; +// c->json += 3; +// lept_parse_whitespace(c); //跳过空格,然后看是否不为结束位置 +// if(c->json[0] != '\0'){ +// return LEPT_PARSE_ROOT_NOT_SINGULAR; +// } +// v->type = LEPT_NULL; +// return LEPT_PARSE_OK; +//} +//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; +//} +// +//static int lept_parse_false(lept_context * c, lept_value * v){ +// EXPECT(c, 'f'); +// if(c->json[0] != 'a' || c->json[1] != 'l' || c->json[2] != 's' || c->json[3] != 'e') +// return LEPT_PARSE_INVALID_VALUE; +// c->json += 4; +// v->type = LEPT_FALSE; +// return LEPT_PARSE_OK; +//} + +static int lept_parse_literal(lept_context * c, lept_value * v, char prefix){ + c->json++; + switch(prefix){ + case 'f': if (c->json[0] != 'a' || c->json[1] != 'l' || c->json[2] != 's' || c->json[3] != 'e') + return LEPT_PARSE_INVALID_VALUE; break; + case 't': if (c->json[0] != 'r' || c->json[1] != 'u' || c->json[2] != 'e') + return LEPT_PARSE_INVALID_VALUE; break; + case 'n': if (c->json[0] != 'u' || c->json[1] != 'l' || c->json[2] != 'l') + return LEPT_PARSE_INVALID_VALUE; + break; + } c->json += 3; - v->type = LEPT_NULL; + if(prefix == 'f') c->json++; + switch (prefix) { + case 'f': v->type = LEPT_FALSE; break; + case 't': v->type = LEPT_TRUE; break; + case 'n': v->type = LEPT_NULL; lept_parse_whitespace(c); + if(*c->json != '\0') return LEPT_PARSE_ROOT_NOT_SINGULAR;break; + } return LEPT_PARSE_OK; } static int lept_parse_value(lept_context* c, lept_value* v) { switch (*c->json) { - case 'n': return lept_parse_null(c, v); + case 'n': return lept_parse_literal(c, v, 'n'); case '\0': return LEPT_PARSE_EXPECT_VALUE; + case 't': return lept_parse_literal(c, v, 't'); + case 'f': return lept_parse_literal(c, v, 'f'); default: return LEPT_PARSE_INVALID_VALUE; } } + + int lept_parse(lept_value* v, const char* json) { lept_context c; assert(v != NULL); diff --git a/tutorial01/test.c b/tutorial01/test.c index e7672181..4f043ffd 100644 --- a/tutorial01/test.c +++ b/tutorial01/test.c @@ -7,6 +7,7 @@ static int main_ret = 0; static int test_count = 0; static int test_pass = 0; +//测试框架 #define EXPECT_EQ_BASE(equality, expect, actual, format) \ do {\ test_count++;\ @@ -17,7 +18,7 @@ static int test_pass = 0; main_ret = 1;\ }\ } while(0) - +//测试函数宏 #define EXPECT_EQ_INT(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, "%d") static void test_parse_null() { @@ -57,11 +58,27 @@ static void test_parse_root_not_singular() { EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v)); } +static void test_parse_false(){ + lept_value v; + v.type = LEPT_TRUE; + EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, "false")); + EXPECT_EQ_INT(LEPT_FALSE, lept_get_type(&v)); +} + +static void test_parse_true(){ + lept_value v; + v.type = LEPT_FALSE; + EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, "true")); + EXPECT_EQ_INT(LEPT_TRUE, lept_get_type(&v)); +} + static void test_parse() { test_parse_null(); test_parse_expect_value(); test_parse_invalid_value(); test_parse_root_not_singular(); + test_parse_true(); + test_parse_false(); } int main() { diff --git a/tutorial01_answer/leptjson.c b/tutorial01_answer/leptjson.c index f4f2b17b..f5bc1a6b 100644 --- a/tutorial01_answer/leptjson.c +++ b/tutorial01_answer/leptjson.c @@ -15,40 +15,41 @@ static void lept_parse_whitespace(lept_context* c) { c->json = p; } -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; -} -static int lept_parse_false(lept_context* c, lept_value* v) { - EXPECT(c, 'f'); - if (c->json[0] != 'a' || c->json[1] != 'l' || c->json[2] != 's' || c->json[3] != 'e') - return LEPT_PARSE_INVALID_VALUE; - c->json += 4; - v->type = LEPT_FALSE; +static int lept_parse_literal(lept_context * c, lept_value * v, char prefix){ + c->json++; + switch(prefix){ + case 'f': if (c->json[0] != 'a' || c->json[1] != 'l' || c->json[2] != 's' || c->json[3] != 'e') + return LEPT_PARSE_INVALID_VALUE; + case 't': if (c->json[0] != 'r' || c->json[1] != 'u' || c->json[2] != 'e') + return LEPT_PARSE_INVALID_VALUE; + case 'n': if (c->json[0] != 'u' || c->json[1] != 'l' || c->json[2] != 'l') + return LEPT_PARSE_INVALID_VALUE; + } + c->json += 3; + if(prefix == 'f') c->json++; + switch (prefix) { + case 'f': v->type = LEPT_FALSE; + case 't': v->type = LEPT_TRUE; + case 'n': v->type = LEPT_NULL; + } return LEPT_PARSE_OK; } -static int lept_parse_null(lept_context* c, lept_value* v) { - EXPECT(c, 'n'); - if (c->json[0] != 'u' || c->json[1] != 'l' || c->json[2] != 'l') +static int lept_parse_number(lept_context * c, lept_value * v){ + if(*c->json == '+') { return LEPT_PARSE_INVALID_VALUE; - c->json += 3; - v->type = LEPT_NULL; + } return LEPT_PARSE_OK; } 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); + case 't': return lept_parse_literal(c, v, 't'); + case 'f': return lept_parse_literal(c, v, 'f'); + case 'n': return lept_parse_literal(c, v, 'n'); case '\0': return LEPT_PARSE_EXPECT_VALUE; - default: return LEPT_PARSE_INVALID_VALUE; + default: return lept_parse_number(c, v); //for numbers } } diff --git a/tutorial02/leptjson.c b/tutorial02/leptjson.c index 7693e43b..d374757b 100644 --- a/tutorial02/leptjson.c +++ b/tutorial02/leptjson.c @@ -1,8 +1,13 @@ #include "leptjson.h" #include /* assert() */ #include /* NULL, strtod() */ +#include +#include +#include #define EXPECT(c, ch) do { assert(*c->json == (ch)); c->json++; } while(0) +#define ISDIGIT0(ch) ((ch) >= '0' && (ch) <= '9') +#define ISDIGIT1(ch) ((ch) >= '1' && (ch) <= '9') typedef struct { const char* json; @@ -15,39 +20,51 @@ static void lept_parse_whitespace(lept_context* c) { c->json = p; } -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; -} - -static int lept_parse_false(lept_context* c, lept_value* v) { - EXPECT(c, 'f'); - if (c->json[0] != 'a' || c->json[1] != 'l' || c->json[2] != 's' || c->json[3] != 'e') - return LEPT_PARSE_INVALID_VALUE; - c->json += 4; - v->type = LEPT_FALSE; - return LEPT_PARSE_OK; -} - -static int lept_parse_null(lept_context* c, lept_value* v) { - EXPECT(c, 'n'); - if (c->json[0] != 'u' || c->json[1] != 'l' || c->json[2] != 'l') - return LEPT_PARSE_INVALID_VALUE; +static int lept_parse_literal(lept_context * c, lept_value * v, char prefix){ + c->json++; + switch(prefix){ + case 'f': if (c->json[0] != 'a' || c->json[1] != 'l' || c->json[2] != 's' || c->json[3] != 'e') + return LEPT_PARSE_INVALID_VALUE; break; + case 't': if (c->json[0] != 'r' || c->json[1] != 'u' || c->json[2] != 'e') + return LEPT_PARSE_INVALID_VALUE; break; + case 'n': if (c->json[0] != 'u' || c->json[1] != 'l' || c->json[2] != 'l') + return LEPT_PARSE_INVALID_VALUE; break; + } c->json += 3; - v->type = LEPT_NULL; + if(prefix == 'f') c->json++; + switch (prefix) { + case 'f': v->type = LEPT_FALSE; break; + case 't': v->type = LEPT_TRUE; break; + case 'n': v->type = LEPT_NULL; lept_parse_whitespace(c); + if(*c->json != '\0') return LEPT_PARSE_ROOT_NOT_SINGULAR;break; + } return LEPT_PARSE_OK; } static int lept_parse_number(lept_context* c, lept_value* v) { char* end; /* \TODO validate number */ + //errno是记录系统中存在错误的变量,只有当库函数出错的时候才会出现,设置为其他值代表不同的错误 + errno = 0; v->n = strtod(c->json, &end); + //范围过大并且INF, inf这些宏在C语言里头是合法的,但是在JSON里头是INVALID的,所以解析结果会不一样 + if(errno == ERANGE && (v->n == HUGE_VAL || v->n == -HUGE_VAL)) return LEPT_PARSE_NUMBER_TOO_BIG; if (c->json == end) return LEPT_PARSE_INVALID_VALUE; + //开头的非法字符 + //the grammer is number = [ "-" ] int [ frac ] [ exp ] + if(*c->json == '-') c->json++; + if(!ISDIGIT0(*c->json)) return LEPT_PARSE_INVALID_VALUE; + //0 end with other numbers + if(*c->json == '0' && (c->json[1] != '\0' && c->json[1] != 'e' && c->json[1] + != 'E' && c->json[1] != '.')) return LEPT_PARSE_ROOT_NOT_SINGULAR; + while(ISDIGIT1(*c->json)) c->json++; + //1.234E+10 case + if(*c->json == '.') { + c->json++; + if(*c->json == '\0') return LEPT_PARSE_INVALID_VALUE; + } + while(ISDIGIT0(*c->json)) c->json++; c->json = end; v->type = LEPT_NUMBER; return LEPT_PARSE_OK; @@ -55,11 +72,12 @@ static int lept_parse_number(lept_context* c, lept_value* v) { 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 't': return lept_parse_literal(c, v, 't'); + case 'f': return lept_parse_literal(c, v, 'f'); + case 'n': return lept_parse_literal(c, v, 'n'); case '\0': return LEPT_PARSE_EXPECT_VALUE; + default: return lept_parse_number(c, v); + } } diff --git a/tutorial02/test.c b/tutorial02/test.c index eaa5db69..9583f86d 100644 --- a/tutorial02/test.c +++ b/tutorial02/test.c @@ -7,6 +7,8 @@ static int main_ret = 0; static int test_count = 0; static int test_pass = 0; + + #define EXPECT_EQ_BASE(equality, expect, actual, format) \ do {\ test_count++;\ @@ -70,6 +72,15 @@ static void test_parse_number() { TEST_NUMBER(1.234E+10, "1.234E+10"); TEST_NUMBER(1.234E-10, "1.234E-10"); TEST_NUMBER(0.0, "1e-10000"); /* must underflow */ + TEST_NUMBER(1.0000000000000002, "1.0000000000000002"); /* the smallest number > 1 */ + TEST_NUMBER( 4.9406564584124654e-324, "4.9406564584124654e-324"); /* minimum denormal */ + TEST_NUMBER(-4.9406564584124654e-324, "-4.9406564584124654e-324"); + TEST_NUMBER( 2.2250738585072009e-308, "2.2250738585072009e-308"); /* Max subnormal double */ + TEST_NUMBER(-2.2250738585072009e-308, "-2.2250738585072009e-308"); + TEST_NUMBER( 2.2250738585072014e-308, "2.2250738585072014e-308"); /* Min normal positive double */ + TEST_NUMBER(-2.2250738585072014e-308, "-2.2250738585072014e-308"); + TEST_NUMBER( 1.7976931348623157e+308, "1.7976931348623157e+308"); /* Max double */ + TEST_NUMBER(-1.7976931348623157e+308, "-1.7976931348623157e+308"); } #define TEST_ERROR(error, json)\ @@ -89,7 +100,7 @@ static void test_parse_invalid_value() { TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "nul"); TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "?"); -#if 0 +#if 1 /* invalid number */ TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "+0"); TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "+1"); @@ -105,7 +116,7 @@ static void test_parse_invalid_value() { static void test_parse_root_not_singular() { TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, "null x"); -#if 0 +#if 1 /* invalid number */ TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, "0123"); /* after zero should be '.' , 'E' , 'e' or nothing */ TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, "0x0"); @@ -114,7 +125,7 @@ static void test_parse_root_not_singular() { } static void test_parse_number_too_big() { -#if 0 +#if 1 TEST_ERROR(LEPT_PARSE_NUMBER_TOO_BIG, "1e309"); TEST_ERROR(LEPT_PARSE_NUMBER_TOO_BIG, "-1e309"); #endif diff --git a/tutorial03/leptjson.c b/tutorial03/leptjson.c index 07f7e2c7..c065aad9 100644 --- a/tutorial03/leptjson.c +++ b/tutorial03/leptjson.c @@ -99,10 +99,26 @@ static int lept_parse_string(lept_context* c, lept_value* v) { lept_set_string(v, (const char*)lept_context_pop(c, len), len); c->json = p; return LEPT_PARSE_OK; + case '\\': + ch = *p++; + switch(ch){ + case 'b': PUTC(c, '\b'); break; + case 't': PUTC(c, '\t'); break; + case 'f': PUTC(c, '\f'); break; + case 'n': PUTC(c, '\n'); break; + case 'r': PUTC(c, '\r'); break; + case '\"': PUTC(c, '\"'); break; + case '/': PUTC(c, '/'); break; + case '\\': PUTC(c, '\\'); break; + default: return LEPT_PARSE_INVALID_STRING_ESCAPE; + } + break; case '\0': c->top = head; return LEPT_PARSE_MISS_QUOTATION_MARK; default: + //处理控制字符 + if(ch >= '\x01' && ch <= '\x1F') return LEPT_PARSE_INVALID_STRING_CHAR; PUTC(c, ch); } } @@ -154,11 +170,14 @@ lept_type lept_get_type(const lept_value* v) { int lept_get_boolean(const lept_value* v) { /* \TODO */ - return 0; + assert(v != NULL); + return v->type; } void lept_set_boolean(lept_value* v, int b) { /* \TODO */ + if(b == 0) v->type = LEPT_FALSE; + else v->type = LEPT_TRUE; } double lept_get_number(const lept_value* v) { @@ -168,6 +187,8 @@ double lept_get_number(const lept_value* v) { void lept_set_number(lept_value* v, double n) { /* \TODO */ + v->type = LEPT_NUMBER; + v->u.n = n; } const char* lept_get_string(const lept_value* v) { diff --git a/tutorial03/test.c b/tutorial03/test.c index ac788aca..6a590a2e 100644 --- a/tutorial03/test.c +++ b/tutorial03/test.c @@ -107,7 +107,7 @@ static void test_parse_number() { static void test_parse_string() { TEST_STRING("", "\"\""); TEST_STRING("Hello", "\"Hello\""); -#if 0 +#if 1 TEST_STRING("Hello\nWorld", "\"Hello\\nWorld\""); TEST_STRING("\" \\ / \b \f \n \r \t", "\"\\\" \\\\ \\/ \\b \\f \\n \\r \\t\""); #endif @@ -163,7 +163,7 @@ static void test_parse_missing_quotation_mark() { } static void test_parse_invalid_string_escape() { -#if 0 +#if 1 TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, "\"\\v\""); TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, "\"\\'\""); TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, "\"\\0\""); @@ -172,7 +172,7 @@ static void test_parse_invalid_string_escape() { } static void test_parse_invalid_string_char() { -#if 0 +#if 1 TEST_ERROR(LEPT_PARSE_INVALID_STRING_CHAR, "\"\x01\""); TEST_ERROR(LEPT_PARSE_INVALID_STRING_CHAR, "\"\x1F\""); #endif @@ -190,10 +190,25 @@ static void test_access_null() { static void test_access_boolean() { /* \TODO */ /* Use EXPECT_TRUE() and EXPECT_FALSE() */ + lept_value v; + lept_init(&v); + //由于C没有bool型,所以使用int + lept_set_boolean(&v, 1); + EXPECT_TRUE(LEPT_TRUE, lept_get_boolean(&v)); + lept_set_boolean(&v, 0); + EXPECT_FALSE(LEPT_FALSE, lept_get_boolean(&v)); + lept_set_null(&v); + EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v)); } static void test_access_number() { /* \TODO */ + lept_value v; + lept_init(&v); + lept_set_number(&v, 114514); + EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(&v)); + EXPECT_EQ_DOUBLE(114514, lept_get_number(&v)); + } static void test_access_string() { diff --git a/tutorial04/leptjson.c b/tutorial04/leptjson.c index 0a123bf2..b92b58f4 100644 --- a/tutorial04/leptjson.c +++ b/tutorial04/leptjson.c @@ -8,6 +8,7 @@ #include /* HUGE_VAL */ #include /* NULL, malloc(), realloc(), free(), strtod() */ #include /* memcpy() */ +#include #ifndef LEPT_PARSE_STACK_INIT_SIZE #define LEPT_PARSE_STACK_INIT_SIZE 256 @@ -89,21 +90,49 @@ static int lept_parse_number(lept_context* c, lept_value* v) { c->json = p; return LEPT_PARSE_OK; } - +//自己的代码相比作者存在较大性能差异,而且也不美观,于是在理解了作者思路之后重新按其思路试着写一下 static const char* lept_parse_hex4(const char* p, unsigned* u) { /* \TODO */ + for(int i = 0; i < 4; i++){ + *u <<= 4; + if(*p >= '0' && *p <= '9') *u |= (*p - '0'); + else if(*p >= 'a' && *p <= 'f') *u |= (*p - 'a') + 10; + else if(*p >= 'A' && *p <= 'F') *u |= (*p - 'A') + 10; + else return NULL; + p++; + } return p; } +//自制函数 + static void lept_encode_utf8(lept_context* c, unsigned u) { /* \TODO */ + assert(u <= 0x1FFFF); + if(u >= 0x0000 && u <= 0x007F) PUTC(c, u); + else if(u >= 0x080 && u <= 0x07FF){ + PUTC(c, 0xC0 | ((u >> 6) & 0x1F)); + PUTC(c, 0x80 | (u & 0x3F)); + } + else if(u >= 0x0800 && u <= 0xFFFF){ + PUTC(c, 0xE0 | ((u >> 12) & 0xFF)); + PUTC(c, 0x80 | ((u >> 6) & 0x3F)); + PUTC(c, 0x80 | (u & 0x3F)); + } + else if(u >= 0x10000 && u <= 0x10FFFF){ + + PUTC(c, 0xF0 | ((u >> 18) & 0x07)); + PUTC(c, 0x80 | ((u >> 12) & 0x3F)); + PUTC(c, 0x80 | ((u >> 6) & 0x3F)); + PUTC(c, 0x80 | (u & 0x3F)); + } } #define STRING_ERROR(ret) do { c->top = head; return ret; } while(0) static int lept_parse_string(lept_context* c, lept_value* v) { size_t head = c->top, len; - unsigned u; + unsigned u = 0, u2 = 0; const char* p; EXPECT(c, '\"'); p = c->json; @@ -113,7 +142,7 @@ static int lept_parse_string(lept_context* c, lept_value* v) { case '\"': len = c->top - head; lept_set_string(v, (const char*)lept_context_pop(c, len), len); - c->json = p; + c->json = p; //回收缓冲区空间 return LEPT_PARSE_OK; case '\\': switch (*p++) { @@ -129,6 +158,14 @@ static int lept_parse_string(lept_context* c, lept_value* v) { if (!(p = lept_parse_hex4(p, &u))) STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX); /* \TODO surrogate handling */ + if(u >= 0xD800 && u <= 0xDBFF){ + if(*p++ != '\\') STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE); + if(*p++ != 'u') STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE); + if (!(p = lept_parse_hex4(p, &u2))) + STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX); + if(u2 < 0xDC00 || u2 > 0xDFFF) STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE); + u = 0x10000 + ((u - 0xD800) << 10 | (u2 - 0xDC00)); + } lept_encode_utf8(c, u); break; default: @@ -229,3 +266,5 @@ void lept_set_string(lept_value* v, const char* s, size_t len) { v->u.s.len = len; v->type = LEPT_STRING; } + +//codepoint = 0x10000 + (H - 0xD800) x 0x400 + (L - 0xDC00) diff --git "a/tutorial04_answer/tutorial4\346\200\273\347\273\223.md" "b/tutorial04_answer/tutorial4\346\200\273\347\273\223.md" new file mode 100644 index 00000000..200ea15c --- /dev/null +++ "b/tutorial04_answer/tutorial4\346\200\273\347\273\223.md" @@ -0,0 +1,6 @@ +由于字符串存在不定长并且需要处理转义的情况,因此需要一个缓冲区来缓存解析后的结果。 +这章的trick还是蛮多的,而且不习惯宏定义以及随着码量越来越大,需要注意的问题越来越多。 + +作者在解析16进制数的时候使用了位运算进行加速,蛮有启发性的,以至于lept_encode_utf8几乎是我唯一一个实现与之一般的函数 + +lept_parse_hex4分为两个部分,主要是为了处理好代理对的情况,然后再注意到使用STRING_ERROR来实现还原堆栈初始状态和返回状态信息。整个解析就是如此了 \ No newline at end of file diff --git a/tutorial05/leptjson.c b/tutorial05/leptjson.c index d2c83f34..33f12e20 100644 --- a/tutorial05/leptjson.c +++ b/tutorial05/leptjson.c @@ -9,6 +9,7 @@ #include /* NULL, malloc(), realloc(), free(), strtod() */ #include /* memcpy() */ + #ifndef LEPT_PARSE_STACK_INIT_SIZE #define LEPT_PARSE_STACK_INIT_SIZE 256 #endif @@ -138,6 +139,7 @@ static int lept_parse_string(lept_context* c, lept_value* v) { switch (ch) { case '\"': len = c->top - head; + //将栈上的所有字符弹出,分配内存,生成字符串值 lept_set_string(v, (const char*)lept_context_pop(c, len), len); c->json = p; return LEPT_PARSE_OK; @@ -187,6 +189,7 @@ 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++; v->type = LEPT_ARRAY; @@ -195,25 +198,41 @@ static int lept_parse_array(lept_context* c, lept_value* v) { return LEPT_PARSE_OK; } for (;;) { - lept_value e; + lept_value e; //生成一个临时的元素值 lept_init(&e); - if ((ret = lept_parse_value(c, &e)) != LEPT_PARSE_OK) - return ret; + + //使用了递归函数来解决无限嵌套的JSON的问题 + //array只是指定了'[',']'里面的元素的内容还是属于字面值或者数字,字符串等的 + if ((ret = lept_parse_value(c, &e)) != LEPT_PARSE_OK) { + break; + } + //将产生的对象压栈,这是个元素大小不确定的堆栈 memcpy(lept_context_push(c, sizeof(lept_value)), &e, sizeof(lept_value)); size++; - if (*c->json == ',') + lept_parse_whitespace(c); + if (*c->json == ',') { c->json++; + lept_parse_whitespace(c); + } else if (*c->json == ']') { c->json++; v->type = LEPT_ARRAY; v->u.a.size = size; size *= sizeof(lept_value); + //已经解析结束了,我们将堆栈中的临时值取出来,分配内存 memcpy(v->u.a.e = (lept_value*)malloc(size), lept_context_pop(c, size), size); + //malloc从堆中获取内存,当使用完成之后记得free return LEPT_PARSE_OK; } - else - return LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET; + else { + ret = LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET; + break; + } } + //只有存在错误的时候才需要free + for(int i = 0; i < size; i++) + lept_free((lept_value*)lept_context_pop(c, sizeof(lept_value))); + return ret; } static int lept_parse_value(lept_context* c, lept_value* v) { @@ -253,9 +272,17 @@ void lept_free(lept_value* v) { assert(v != NULL); if (v->type == LEPT_STRING) free(v->u.s.s); + //数组内的元素需要递归释放内存 + else if(v->type == LEPT_ARRAY){ + for(int i = 0; i < v->u.a.size; i++){ + lept_free(&v->u.a.e[i]); + } + free(v->u.a.e); + } v->type = LEPT_NULL; } + lept_type lept_get_type(const lept_value* v) { assert(v != NULL); return v->type; diff --git a/tutorial05/leptjson.h b/tutorial05/leptjson.h index e18f14f7..70915413 100644 --- a/tutorial05/leptjson.h +++ b/tutorial05/leptjson.h @@ -2,6 +2,7 @@ #define LEPTJSON_H__ #include /* size_t */ +#include typedef enum { LEPT_NULL, LEPT_FALSE, LEPT_TRUE, LEPT_NUMBER, LEPT_STRING, LEPT_ARRAY, LEPT_OBJECT } lept_type; diff --git a/tutorial05/test.c b/tutorial05/test.c index 62389f0a..541eb6a3 100644 --- a/tutorial05/test.c +++ b/tutorial05/test.c @@ -2,7 +2,6 @@ #define _CRTDBG_MAP_ALLOC #include #endif -#include #include #include #include "leptjson.h" @@ -134,6 +133,21 @@ static void test_parse_array() { EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, "[ ]")); EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(&v)); EXPECT_EQ_SIZE_T(0, lept_get_array_size(&v)); + //用lept_get_array_element这个API访问数组元素 + EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, "[ null, false, true, 123, \"abc\"]")); + EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(&v)); + EXPECT_EQ_SIZE_T(5, lept_get_array_size(&v)); + EXPECT_EQ_INT(LEPT_NULL, lept_get_type(lept_get_array_element(&v, 0))); + EXPECT_EQ_INT(LEPT_FALSE, lept_get_type(lept_get_array_element(&v, 1))); + EXPECT_EQ_INT(LEPT_TRUE, lept_get_type(lept_get_array_element(&v, 2))); + EXPECT_EQ_DOUBLE(123,lept_get_number(lept_get_array_element(&v, 3))); + EXPECT_EQ_INT(LEPT_STRING, lept_get_type(lept_get_array_element(&v, 4))); + //不能直接使用TEST_STRING=>会直接创建一个新的变量名为v,并且类型为NULL + EXPECT_EQ_STRING("abc", lept_get_string(lept_get_array_element(&v, 4)), + lept_get_string_length((lept_get_array_element(&v, 4)))); + lept_free(&v); + lept_init(&v); + EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, "[ [ ] , [ 0 ] , [ 0 , 1 ] , [[ 0 , 1 , 2 ]] ]")); lept_free(&v); } @@ -167,7 +181,7 @@ static void test_parse_invalid_value() { TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "nan"); /* invalid value in array */ -#if 0 +#if 1 TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "[1,]"); TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "[\"a\", nul]"); #endif @@ -229,7 +243,7 @@ static void test_parse_invalid_unicode_surrogate() { } static void test_parse_miss_comma_or_square_bracket() { -#if 0 +#if 1 TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, "[1"); TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, "[1}"); TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, "[1 2"); diff --git "a/tutorial05/tutorial05\346\200\273\347\273\223.md" "b/tutorial05/tutorial05\346\200\273\347\273\223.md" new file mode 100644 index 00000000..4a4507aa --- /dev/null +++ "b/tutorial05/tutorial05\346\200\273\347\273\223.md" @@ -0,0 +1,52 @@ +1. 编写测试检验结果,由于给出的测试数据都是合法的,所以,可以放心检测。与前面的不同的地方在于,数组是复合类型,因此,对于每个数组元素,需要单独进行解析。已有的API `lept_get_array_element`可以实现剥离每个元素,然后进行检查。 + +2.在合理的位置加上`lept_parse_whitespace`即可 + +3.使用检测工具发现 +```c++ +# valgrind --tool=memcheck --leak-check=full ./test +==207== Memcheck, a memory error detector +==207== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al. +==207== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info +==207== Command: ./test +==207== +227/227 (100.00%) passed +==207== +==207== HEAP SUMMARY: +==207== in use at exit: 364 bytes in 6 blocks +==207== total heap usage: 34 allocs, 28 frees, 4,539 bytes allocated +==207== +==207== 124 (120 direct, 4 indirect) bytes in 1 blocks are definitely lost in loss record 2 of 4 +==207== at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) +==207== by 0x111936: lept_parse_array (in /json-tutorial/tutorial05/test) +==207== by 0x111A66: lept_parse_value (in /json-tutorial/tutorial05/test) +==207== by 0x111AFE: lept_parse (in /json-tutorial/tutorial05/test) +==207== by 0x10CEE0: test_parse_array (in /json-tutorial/tutorial05/test) +==207== by 0x1106F3: test_parse (in /json-tutorial/tutorial05/test) +==207== by 0x110CA3: main (in /json-tutorial/tutorial05/test) +==207== +==207== 240 (96 direct, 144 indirect) bytes in 1 blocks are definitely lost in loss record 4 of 4 +==207== at 0x4C31B0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) +==207== by 0x111936: lept_parse_array (in /json-tutorial/tutorial05/test) +==207== by 0x111A66: lept_parse_value (in /json-tutorial/tutorial05/test) +==207== by 0x111AFE: lept_parse (in /json-tutorial/tutorial05/test) +==207== by 0x10D436: test_parse_array (in /json-tutorial/tutorial05/test) +==207== by 0x1106F3: test_parse (in /json-tutorial/tutorial05/test) +==207== by 0x110CA3: main (in /json-tutorial/tutorial05/test) +==207== +==207== LEAK SUMMARY: +==207== definitely lost: 216 bytes in 2 blocks +==207== indirectly lost: 148 bytes in 4 blocks +==207== possibly lost: 0 bytes in 0 blocks +==207== still reachable: 0 bytes in 0 blocks +==207== suppressed: 0 bytes in 0 blocks +==207== +==207== For counts of detected and suppressed errors, rerun with: -v +==207== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0) +``` +就是说,`lept_parse_array`的某个位置malloc没有释放,实际上测试用例在`test.c`生成。我们在`lept_free`中合理安排,将所有申请的堆内存释放即可 + +4. 异常处理:因为得到的`json`字符串可能是不合法的,然而已有的代码框架不会将已经存储的值弹出,因此我们需要特判出异常情况,先弹出再返回 + +5.空悬指针问题: +这里返回指针会出现一个生命周期问题,当realloc分配的空间不足以在原位置连续时,则会拷贝到另一处,而之前的指针e就会变为空悬指针。 \ No newline at end of file diff --git a/tutorial06/leptjson.c b/tutorial06/leptjson.c index 64e3bd0d..a984648c 100644 --- a/tutorial06/leptjson.c +++ b/tutorial06/leptjson.c @@ -8,6 +8,7 @@ #include /* HUGE_VAL */ #include /* NULL, malloc(), realloc(), free(), strtod() */ #include /* memcpy() */ +#include #ifndef LEPT_PARSE_STACK_INIT_SIZE #define LEPT_PARSE_STACK_INIT_SIZE 256 @@ -24,6 +25,7 @@ typedef struct { size_t size, top; }lept_context; +//将解析结果存到缓冲区 static void* lept_context_push(lept_context* c, size_t size) { void* ret; assert(size > 0); @@ -39,6 +41,7 @@ static void* lept_context_push(lept_context* c, size_t size) { return ret; } +//弹出堆栈中的数据 static void* lept_context_pop(lept_context* c, size_t size) { assert(c->top >= size); return c->stack + (c->top -= size); @@ -127,8 +130,10 @@ static void lept_encode_utf8(lept_context* c, unsigned u) { #define STRING_ERROR(ret) do { c->top = head; return ret; } while(0) -static int lept_parse_string(lept_context* c, lept_value* v) { - size_t head = c->top, len; + +static int lept_parse_string_raw(lept_context * c, char ** str, size_t * len){ + //str指向c->stack + size_t head = c->top; unsigned u, u2; const char* p; EXPECT(c, '\"'); @@ -137,9 +142,9 @@ static int lept_parse_string(lept_context* c, lept_value* v) { char ch = *p++; switch (ch) { case '\"': - len = c->top - head; - lept_set_string(v, (const char*)lept_context_pop(c, len), len); + *len = c->top - head; c->json = p; + *str = lept_context_pop(c, *len); return LEPT_PARSE_OK; case '\\': switch (*p++) { @@ -181,6 +186,20 @@ static int lept_parse_string(lept_context* c, lept_value* v) { } } + +static int lept_parse_string(lept_context* c, lept_value* v) { + //解析结果写入value + int ret; + char * s; + size_t len; + if((ret = lept_parse_string_raw(c, &s, &len)) == LEPT_PARSE_OK) { + lept_set_string(v, s, len); + } + return ret; +} + + + static int lept_parse_value(lept_context* c, lept_value* v); static int lept_parse_array(lept_context* c, lept_value* v) { @@ -221,13 +240,14 @@ static int lept_parse_array(lept_context* c, lept_value* v) { } } /* Pop and free values on the stack */ + //Invalid parse for (i = 0; i < size; i++) lept_free((lept_value*)lept_context_pop(c, sizeof(lept_value))); return ret; } static int lept_parse_object(lept_context* c, lept_value* v) { - size_t size; + size_t i, size; lept_member m; int ret; EXPECT(c, '{'); @@ -235,7 +255,7 @@ static int lept_parse_object(lept_context* c, lept_value* v) { if (*c->json == '}') { c->json++; v->type = LEPT_OBJECT; - v->u.o.m = 0; + v->u.o.m = NULL; v->u.o.size = 0; return LEPT_PARSE_OK; } @@ -246,14 +266,61 @@ static int lept_parse_object(lept_context* c, lept_value* v) { /* \todo parse key to m.k, m.klen */ /* \todo parse ws colon ws */ /* parse value */ + //解析m.k + char * str; + if(*c->json == '"'){ + if(lept_parse_string_raw(c, &str, &m.klen) != LEPT_PARSE_OK){ + ret = LEPT_PARSE_MISS_KEY; + break; + } + } + lept_parse_whitespace(c); + m.k = (char*)malloc(m.klen + 1); + memcpy(m.k, str, m.klen); + m.k[m.klen] = '\0'; + lept_parse_whitespace(c); + //跳过冒号 + if(*c->json != ':'){ + ret = LEPT_PARSE_MISS_COLON; + break; + } + c->json++; + lept_parse_whitespace(c); if ((ret = lept_parse_value(c, &m.v)) != LEPT_PARSE_OK) break; + lept_parse_whitespace(c); + memcpy(lept_context_push(c, sizeof(lept_member)), &m, sizeof(lept_member)); size++; m.k = NULL; /* ownership is transferred to member on stack */ /* \todo parse ws [comma | right-curly-brace] ws */ + lept_parse_whitespace(c); + if(*c->json == ','){ + c->json++; + lept_parse_whitespace(c); + } + else if(*c->json == '}'){ + c->json++; + size_t s = sizeof(lept_member) * size; + v->type = LEPT_OBJECT; + v->u.o.m = (lept_member * )malloc(s); + memcpy(v->u.o.m, lept_context_pop(c, s), s); + v->u.o.size = size; + return LEPT_PARSE_OK; + } + else{ + ret = LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET; + break; + } } /* \todo Pop and free members on the stack */ + free(m.k); + for(i = 0; i < size; i++){ + lept_member * m = (lept_member * )lept_context_pop(c, sizeof(lept_member)); + free(m->k); + lept_free(&m->v); + } + v->type = LEPT_NULL; return ret; } @@ -303,6 +370,13 @@ void lept_free(lept_value* v) { lept_free(&v->u.a.e[i]); free(v->u.a.e); break; + case LEPT_OBJECT: + for(i = 0; i < v->u.o.size; i++) { + free(v->u.o.m[i].k); + lept_free(&v->u.o.m->v); + } + free(v->u.o.m); + break; default: break; } v->type = LEPT_NULL; diff --git a/tutorial06/test.c b/tutorial06/test.c index 544eaeb3..6848ce67 100644 --- a/tutorial06/test.c +++ b/tutorial06/test.c @@ -352,7 +352,7 @@ static void test_parse() { test_parse_number(); test_parse_string(); test_parse_array(); -#if 0 +#if 1 test_parse_object(); #endif diff --git "a/tutorial06/tutorial06\346\200\273\347\273\223.md" "b/tutorial06/tutorial06\346\200\273\347\273\223.md" new file mode 100644 index 00000000..d4d96126 --- /dev/null +++ "b/tutorial06/tutorial06\346\200\273\347\273\223.md" @@ -0,0 +1,25 @@ +### 解析JSON对象 +json对象的完整语法: +```c++ +member = string ws : ws value +object = { ws [ member *(ws %x2C ws member) ] ws } +``` + +json对象示例: +```c++ +{age: 10, name: xxx} +``` +于是我们可以使用以下结构存储JSON对象的key和value,使用 `char *`作为key是为了节约空间 +```c++ +lept_member{ + char * k; size_t len; + lept_value v; +}; +``` +重构`lept_parse_string`为`lept_parse_string_raw`以及`lept_parse_string`分别处理字符串分离以及解析出来,这里原作者写了一个不容易察觉的bug, +为了性能使用了指针,但是在内部如果分配空间不足的时候会重新拷贝并生成新的对象,而旧指针仍指向将被销毁的对象。 + +`lept_object`的结构比`lept_value`更复杂些,是嵌套式的,`lept_object`又可以嵌套在`lept_value`中。 + +鉴于`lept_object`这种嵌套的性质, 与`lept_array`很像,所以解析的思路可以很类似。 + diff --git a/tutorial07/leptjson.c b/tutorial07/leptjson.c index 5307b892..c3f86fb9 100644 --- a/tutorial07/leptjson.c +++ b/tutorial07/leptjson.c @@ -348,9 +348,36 @@ int lept_parse(lept_value* v, const char* json) { static void lept_stringify_string(lept_context* c, const char* s, size_t len) { /* ... */ + //from json object to context + size_t i = 0; + assert(s != NULL); + PUTC(c, '\"'); + char buffer[7]; + for(; i < len; i++){ + switch(s[i]){ + case '\t': PUTS(c, "\\t", 2); break; + case '\b': PUTS(c, "\\b", 2); break; + case '/': PUTC(c, '/'); break; + case '\n': PUTS(c, "\\n", 2); break; + case '\r': PUTS(c, "\\r", 2); break; + case '\f': PUTS(c, "\\f", 2); break; + case '\\': PUTS(c, "\\\\", 2); break; + case '\"': PUTS(c, "\\\"", 2); break; + default: { + if(s[i] < 0x20){ + sprintf(buffer, "\\u%04x", s[i]); + PUTS(c, buffer, 6); + break; + } + PUTC(c, s[i]); break; + } + } + } + PUTC(c, '\"'); } static void lept_stringify_value(lept_context* c, const lept_value* v) { + size_t i = 0; switch (v->type) { case LEPT_NULL: PUTS(c, "null", 4); break; case LEPT_FALSE: PUTS(c, "false", 5); break; @@ -359,9 +386,25 @@ static void lept_stringify_value(lept_context* c, const lept_value* v) { case LEPT_STRING: lept_stringify_string(c, v->u.s.s, v->u.s.len); break; case LEPT_ARRAY: /* ... */ + //递归处理聚合类型 + //all tests passed! + PUTC(c, '['); + for(; i < v->u.a.size; i++){ + if(i > 0) PUTC(c, ','); + lept_stringify_value(c, &v->u.a.e[i]); + } + PUTC(c, ']'); break; case LEPT_OBJECT: /* ... */ + PUTC(c, '{'); + for(; i < v->u.o.size; i++){ + if(i > 0) PUTC(c, ','); + lept_stringify_string(c, v->u.o.m[i].k, v->u.o.m[i].klen); + PUTC(c, ':'); + lept_stringify_value(c, &v->u.o.m[i].v); + } + PUTC(c, '}'); break; default: assert(0 && "invalid type"); } diff --git "a/tutorial07/tutorial07\346\200\273\347\273\223.md" "b/tutorial07/tutorial07\346\200\273\347\273\223.md" new file mode 100644 index 00000000..496d11b2 --- /dev/null +++ "b/tutorial07/tutorial07\346\200\273\347\273\223.md" @@ -0,0 +1,4 @@ +###文本化 +实现的功能是将`lept_object`转化为文本。 + +对于聚合类型比如数组和对象,通过递归下处理来生成json文本。与解析的过程相比算是逆过程。 diff --git a/tutorial08/leptjson.c b/tutorial08/leptjson.c index 59dae292..342c3dc3 100644 --- a/tutorial08/leptjson.c +++ b/tutorial08/leptjson.c @@ -111,7 +111,7 @@ static const char* lept_parse_hex4(const char* p, unsigned* u) { } static void lept_encode_utf8(lept_context* c, unsigned u) { - if (u <= 0x7F) + if (u <= 0x7F) PUTC(c, u & 0xFF); else if (u <= 0x7FF) { PUTC(c, 0xC0 | ((u >> 6) & 0xFF)); @@ -417,15 +417,32 @@ char* lept_stringify(const lept_value* v, size_t* length) { void lept_copy(lept_value* dst, const lept_value* src) { assert(src != NULL && dst != NULL && src != dst); + size_t i; switch (src->type) { case LEPT_STRING: lept_set_string(dst, src->u.s.s, src->u.s.len); break; case LEPT_ARRAY: /* \todo */ + //数组 + lept_set_array(dst, src->u.a.size); + for(i = 0; i < src->u.a.size; i++){ + lept_copy(&dst->u.a.e[i], &src->u.a.e[i]); + } + dst->u.a.size = src->u.a.size; break; case LEPT_OBJECT: /* \todo */ + //对象 + lept_set_object(dst, src->u.o.size); + + for(i = 0; i < src->u.o.size; i++){ + //k + lept_value * val = lept_set_object_value(dst, src->u.o.m[i].k, src->u.o.m[i].klen); + //v + lept_copy(val, &src->u.o.m[i].v); + } + dst->u.o.size = src->u.o.size; break; default: lept_free(dst); @@ -481,13 +498,13 @@ lept_type lept_get_type(const lept_value* v) { } int lept_is_equal(const lept_value* lhs, const lept_value* rhs) { - size_t i; + size_t i, index; assert(lhs != NULL && rhs != NULL); if (lhs->type != rhs->type) return 0; switch (lhs->type) { case LEPT_STRING: - return lhs->u.s.len == rhs->u.s.len && + return lhs->u.s.len == rhs->u.s.len && memcmp(lhs->u.s.s, rhs->u.s.s, lhs->u.s.len) == 0; case LEPT_NUMBER: return lhs->u.n == rhs->u.n; @@ -500,6 +517,15 @@ int lept_is_equal(const lept_value* lhs, const lept_value* rhs) { return 1; case LEPT_OBJECT: /* \todo */ + /* size compare */ + if(lhs->u.o.size != rhs->u.o.size) + return 0; + /* key-value comp */ + for(i = 0; i < lhs->u.o.size; i++){ + index = lept_find_object_index(rhs, lhs->u.o.m[i].k, lhs->u.o.m[i].klen); + if(index == LEPT_KEY_NOT_EXIST) return 0; //key not exist + if(!lept_is_equal(&lhs->u.o.m[i].v, &rhs->u.o.m[index].v)) return 0; //value not match + } return 1; default: return 1; @@ -609,12 +635,25 @@ void lept_popback_array_element(lept_value* v) { lept_value* lept_insert_array_element(lept_value* v, size_t index) { assert(v != NULL && v->type == LEPT_ARRAY && index <= v->u.a.size); /* \todo */ - return NULL; + if(v->u.a.size == v->u.a.capacity) lept_reserve_array(v, v->u.a.capacity == 0? 1: (v->u.a.size << 1)); //扩容为原来一倍 + memcpy(&v->u.a.e[index + 1], &v->u.a.e[index], (v->u.a.size - index) * sizeof(lept_value)); + lept_init(&v->u.a.e[index]); + v->u.a.size++; + return &v->u.a.e[index]; } void lept_erase_array_element(lept_value* v, size_t index, size_t count) { assert(v != NULL && v->type == LEPT_ARRAY && index + count <= v->u.a.size); /* \todo */ + //回收完空间,然后将index 后面count个元素移到index处,最后将空闲的count个元素重新初始化 + size_t i; + for(i = index; i < index + count; i++){ + lept_free(&v->u.a.e[i]); + } + memcpy(v->u.a.e + index, v->u.a.e + index + count, (v->u.a.size - index - count) * sizeof(lept_value)); + for(i = v->u.a.size - count; i < v->u.a.size; i++) + lept_init(&v->u.a.e[i]); + v->u.a.size -= count; } void lept_set_object(lept_value* v, size_t capacity) { @@ -634,22 +673,39 @@ size_t lept_get_object_size(const lept_value* v) { size_t lept_get_object_capacity(const lept_value* v) { assert(v != NULL && v->type == LEPT_OBJECT); /* \todo */ - return 0; + return v->u.o.capacity; } void lept_reserve_object(lept_value* v, size_t capacity) { assert(v != NULL && v->type == LEPT_OBJECT); /* \todo */ + if(v->u.o.capacity < capacity){ + v->u.o.capacity = capacity; + v->u.o.m = (lept_member *) realloc(v->u.o.m, capacity * sizeof(lept_member)); + } } void lept_shrink_object(lept_value* v) { assert(v != NULL && v->type == LEPT_OBJECT); /* \todo */ + if(v->u.o.capacity > v->u.o.size) { + v->u.o.capacity = v->u.o.size; + v->u.o.m = (lept_member *)realloc(v->u.o.m, v->u.o.capacity * sizeof(lept_value)); + } } void lept_clear_object(lept_value* v) { assert(v != NULL && v->type == LEPT_OBJECT); /* \todo */ + size_t i; + for(i = 0; i < v->u.o.size; i++){ + //回收k和v空间 + free(v->u.o.m[i].k); + v->u.o.m[i].k = NULL; + v->u.o.m[i].klen = 0; + lept_free(&v->u.o.m[i].v); + } + v->u.o.size = 0; } const char* lept_get_object_key(const lept_value* v, size_t index) { @@ -674,6 +730,7 @@ size_t lept_find_object_index(const lept_value* v, const char* key, size_t klen) size_t i; assert(v != NULL && v->type == LEPT_OBJECT && key != NULL); for (i = 0; i < v->u.o.size; i++) + //memcpy是比较两个缓冲区的字符字典序情况: 0相同 >0大于 <0小于 if (v->u.o.m[i].klen == klen && memcmp(v->u.o.m[i].k, key, klen) == 0) return i; return LEPT_KEY_NOT_EXIST; @@ -687,10 +744,32 @@ lept_value* lept_find_object_value(lept_value* v, const char* key, size_t klen) lept_value* lept_set_object_value(lept_value* v, const char* key, size_t klen) { assert(v != NULL && v->type == LEPT_OBJECT && key != NULL); /* \todo */ - return NULL; + size_t i, index; + index = lept_find_object_index(v, key, klen); + if(index != LEPT_KEY_NOT_EXIST) + return &v->u.o.m[index].v; + //key not exist, then we make room and init + if(v->u.o.size == v->u.o.capacity){ + lept_reserve_object(v, v->u.o.capacity == 0? 1: (v->u.o.capacity << 1)); + } + i = v->u.o.size; + v->u.o.m[i].k = (char *)malloc((klen + 1)); + memcpy(v->u.o.m[i].k, key, klen); + v->u.o.m[i].k[klen] = '\0'; + v->u.o.m[i].klen = klen; + lept_init(&v->u.o.m[i].v); + v->u.o.size++; + return &v->u.o.m[i].v; } void lept_remove_object_value(lept_value* v, size_t index) { assert(v != NULL && v->type == LEPT_OBJECT && index < v->u.o.size); /* \todo */ + free(v->u.o.m[index].k); + lept_free(&v->u.o.m[index].v); + //think like a list + memcpy(v->u.o.m + index, v->u.o.m + index + 1, (v->u.o.size - index - 1) * sizeof(lept_member)); + v->u.o.m[--v->u.o.size].k = NULL; + v->u.o.m[v->u.o.size].klen = 0; + lept_init(&v->u.o.m[v->u.o.size].v); } diff --git a/tutorial08/test.c b/tutorial08/test.c index d82c01a3..f5e287e5 100644 --- a/tutorial08/test.c +++ b/tutorial08/test.c @@ -591,7 +591,7 @@ static void test_access_array() { for (i = 0; i < 6; i++) EXPECT_EQ_DOUBLE((double)i + 2, lept_get_number(lept_get_array_element(&a, i))); -#if 0 +#if 1 for (i = 0; i < 2; i++) { lept_init(&e); lept_set_number(&e, i); @@ -626,7 +626,7 @@ static void test_access_array() { } static void test_access_object() { -#if 0 +#if 1 lept_value o, v, *pv; size_t i, j, index; diff --git "a/tutorial08/tutorial08\346\200\273\347\273\223.md" "b/tutorial08/tutorial08\346\200\273\347\273\223.md" new file mode 100644 index 00000000..e1f0f6e8 --- /dev/null +++ "b/tutorial08/tutorial08\346\200\273\347\273\223.md" @@ -0,0 +1,193 @@ +### tutorial08总结 + +终于到了最后的篇章qwq + +这一章要实现的就是动态数组,动态对象,以及`lept_is_euqal`等函数,与之前的篇章基本一致。 + +这一章多了一些东西,值语义,引用语义等,需要好好理解,可以配合Milo的一篇回答再 +更多了解一下 +[如何理解 C++ 中的深拷贝和浅拷贝? - Milo Yip的回答 - 知乎]( +https://www.zhihu.com/question/36370072/answer/68086634) + +本单元练习内容: + +1. 完成 lept_is_equal() 里的对象比较部分。不需要考虑对象内有重复键的情况。 +2. 打开 test_access_array() 里的 #if 0,实现 lept_insert_array_element()、lept_erase_array_element() 和 lept_clear_array()。 +3. 打开 test_access_object() 里的 #if 0,参考动态数组,实现第 5 部分列出的所有函数。 +4. 完成 lept_copy() 里的数组和对象的复制部分。 + +### lept_is_equal() +对于`object`类型的每个对象,需要比较`klen`字段以及`k`字段,于是使用 `lept_find_object_index`来查找是否存在; +接下来的`v`字段可以采用`lept_value`递归解析。 +```c++ + case LEPT_OBJECT: + /* \todo */ + /* size compare */ + if(lhs->u.o.size != rhs->u.o.size) + return 0; + /* key-value comp */ + for(i = 0; i < lhs->u.o.size; i++){ + index = lept_find_object_index(rhs, lhs->u.o.m[i].k, lhs->u.o.m[i].klen); + if(index == LEPT_KEY_NOT_EXIST) return 0; //key not exist + if(!lept_is_equal(&lhs->u.o.m[i].v, &rhs->u.o.m[index].v)) return 0; //value not match + } + return 1; + +``` + +### lept_insert_array_element +需要先考虑是否需要扩容,扩容系数为2。将`[index, end)`的内容右移,然后将`index`初始化,最后调整长度,返回`[index]`指针 +```c++ +lept_value* lept_insert_array_element(lept_value* v, size_t index) { + assert(v != NULL && v->type == LEPT_ARRAY && index <= v->u.a.size); + /* \todo */ + if(v->u.a.size == v->u.a.capacity) lept_reserve_array(v, v->u.a.capacity == 0? 1: (v->u.a.size << 1)); //扩容为原来一倍 + memcpy(&v->u.a.e[index + 1], &v->u.a.e[index], (v->u.a.size - index) * sizeof(lept_value)); + lept_init(&v->u.a.e[index]); + v->u.a.size++; + return &v->u.a.e[index]; +} +``` + +### lept_erase_array_element +回收完空间,然后将index 后面count个元素移到index处,最后将空闲的count个元素重新初始化 +```c++ +void lept_erase_array_element(lept_value* v, size_t index, size_t count) { + assert(v != NULL && v->type == LEPT_ARRAY && index + count <= v->u.a.size); + /* \todo */ + size_t i; + for(i = index; i < index + count; i++){ + lept_free(&v->u.a.e[i]); + } + memcpy(v->u.a.e + index, v->u.a.e + index + count, (v->u.a.size - index - count) * sizeof(lept_value)); + for(i = v->u.a.size - count; i < v->u.a.size; i++) + lept_init(&v->u.a.e[i]); + v->u.a.size -= count; +} +``` + +### lept_object部分函数 + +#### lept_reserve_object +与`lept_reserve_array`差不多 +```c++ +void lept_reserve_object(lept_value* v, size_t capacity) { + assert(v != NULL && v->type == LEPT_OBJECT); + /* \todo */ + if(v->u.o.capacity < capacity){ + v->u.o.capacity = capacity; + v->u.o.m = (lept_member *) realloc(v->u.o.m, capacity * sizeof(lept_member)); + } +} +``` + +#### lept_shrink_object +与`lept_shrink_array`差不多 +```c++ +void lept_shrink_object(lept_value* v) { + assert(v != NULL && v->type == LEPT_OBJECT); + /* \todo */ + if(v->u.o.capacity > v->u.o.size) { + v->u.o.capacity = v->u.o.size; + v->u.o.m = (lept_member *)realloc(v->u.o.m, v->u.o.capacity * sizeof(lept_value)); + } +} +``` + +#### lept_clear_object +`object`类型包含`k`, `k`是普通字符串可以`free`释放,要避免空悬指针问题;`v`使用 `lept_free`释放 +```c++ +void lept_clear_object(lept_value* v) { + assert(v != NULL && v->type == LEPT_OBJECT); + /* \todo */ + size_t i; + for(i = 0; i < v->u.o.size; i++){ + //回收k和v空间 + free(v->u.o.m[i].k); + v->u.o.m[i].k = NULL; + v->u.o.m[i].klen = 0; + lept_free(&v->u.o.m[i].v); + } + v->u.o.size = 0; +} +``` + +#### lept_set_object_value +设置`k`字段为`key`的对象的值,如果在查找过程中找到了已经存在`key`,则返回;否则新申请一块空间并初始化,然后返回 +```c++ +lept_value* lept_set_object_value(lept_value* v, const char* key, size_t klen) { + assert(v != NULL && v->type == LEPT_OBJECT && key != NULL); + /* \todo */ + size_t i, index; + index = lept_find_object_index(v, key, klen); + if(index != LEPT_KEY_NOT_EXIST) + return &v->u.o.m[index].v; + //key not exist, then we make room and init + if(v->u.o.size == v->u.o.capacity){ + lept_reserve_object(v, v->u.o.capacity == 0? 1: (v->u.o.capacity << 1)); + } + i = v->u.o.size; + v->u.o.m[i].k = (char *)malloc((klen + 1)); + memcpy(v->u.o.m[i].k, key, klen); + v->u.o.m[i].k[klen] = '\0'; + v->u.o.m[i].klen = klen; + lept_init(&v->u.o.m[i].v); + v->u.o.size++; + return &v->u.o.m[i].v; +} +``` + +#### lept_remove_object_value +类似于链表去除节点,该节点后面的节点接上 +```c++ +void lept_remove_object_value(lept_value* v, size_t index) { + assert(v != NULL && v->type == LEPT_OBJECT && index < v->u.o.size); + /* \todo */ + free(v->u.o.m[index].k); + lept_free(&v->u.o.m[index].v); + //think like a list + memcpy(v->u.o.m + index, v->u.o.m + index + 1, (v->u.o.size - index - 1) * sizeof(lept_member)); + v->u.o.m[--v->u.o.size].k = NULL; + v->u.o.m[v->u.o.size].klen = 0; + lept_init(&v->u.o.m[v->u.o.size].v); +} +``` + +#### lept_copy +```c++ +void lept_copy(lept_value* dst, const lept_value* src) { + assert(src != NULL && dst != NULL && src != dst); + size_t i; + switch (src->type) { + case LEPT_STRING: + lept_set_string(dst, src->u.s.s, src->u.s.len); + break; + case LEPT_ARRAY: + /* \todo */ + //数组 + lept_set_array(dst, src->u.a.size); + for(i = 0; i < src->u.a.size; i++){ + lept_copy(&dst->u.a.e[i], &src->u.a.e[i]); + } + dst->u.a.size = src->u.a.size; + break; + case LEPT_OBJECT: + /* \todo */ + //对象 + lept_set_object(dst, src->u.o.size); + + for(i = 0; i < src->u.o.size; i++){ + //k + lept_value * val = lept_set_object_value(dst, src->u.o.m[i].k, src->u.o.m[i].klen); + //v + lept_copy(val, &src->u.o.m[i].v); + } + dst->u.o.size = src->u.o.size; + break; + default: + lept_free(dst); + memcpy(dst, src, sizeof(lept_value)); + break; + } +} +```