diff --git a/.gitignore b/.gitignore index e3412016..36a4b8db 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ */build/ +*/tags +*/cscope.* diff --git a/tutorial01/leptjson.c b/tutorial01/leptjson.c index 5299fe1d..0e40378b 100644 --- a/tutorial01/leptjson.c +++ b/tutorial01/leptjson.c @@ -2,8 +2,10 @@ #include /* assert() */ #include /* NULL */ +/* 这个EXPECT有副作用啊,而不是单纯地assert,意味着EXPECT不能注释掉。*/ #define EXPECT(c, ch) do { assert(*c->json == (ch)); c->json++; } while(0) +/* 因为此字符串多次使用,为了减少传递多个参数,故放到一个struct中 */ typedef struct { const char* json; }lept_context; @@ -20,13 +22,37 @@ static int lept_parse_null(lept_context* c, lept_value* v) { if (c->json[0] != 'u' || c->json[1] != 'l' || c->json[2] != 'l') return LEPT_PARSE_INVALID_VALUE; c->json += 3; + v->type = LEPT_NULL; 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_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_value(lept_context* c, lept_value* v) { switch (*c->json) { case 'n': return lept_parse_null(c, v); + case 'f': return lept_parse_false(c, v); + case 't': return lept_parse_true(c, v); case '\0': return LEPT_PARSE_EXPECT_VALUE; default: return LEPT_PARSE_INVALID_VALUE; } @@ -34,11 +60,17 @@ static int lept_parse_value(lept_context* c, lept_value* v) { int lept_parse(lept_value* v, const char* json) { lept_context c; + int ret; assert(v != NULL); c.json = json; v->type = LEPT_NULL; lept_parse_whitespace(&c); - return lept_parse_value(&c, v); + if ((ret = lept_parse_value(&c, v)) == LEPT_PARSE_OK) { + lept_parse_whitespace(&c); + if(*c.json != '\0') + ret = LEPT_PARSE_ROOT_NOT_SINGULAR; + } + return ret; } lept_type lept_get_type(const lept_value* v) { diff --git a/tutorial01/my_note01.md b/tutorial01/my_note01.md new file mode 100644 index 00000000..bdcc4f83 --- /dev/null +++ b/tutorial01/my_note01.md @@ -0,0 +1,76 @@ +## tutorial01学习笔记 + +要点: + +* CMake +* API设计(.h)与实现(.c) +* 测试驱动TDD + +### 关于测试驱动的开发流程 + +1. 加入一个测试 +2. 运行所有测试,新测试将会失败 +3. 编写实现代码 +4. 运行所有测试,若测试失败,回到3 +5. 重构代码 +6. 回到1 + +TDD是先写测试后实现,优点:需求明确,刚好满足测试,不会写不需要的代码,可靠。 + +不论TDD还是先实现后测试,都应该保证足够的覆盖率的单元测试。 + +我认为可以写一个test.h,设计一些基础的宏,然后每个文件写对应的测试用例。本代码为了简便只有test.c + +### 学到的知识 +milo yip的代码写得极好!思路非常清晰且层次分明,我试着还原一下思路, + +#### API的设计思路 + +(1)先考虑最终的API效果 + +首先是宏观的总体目标,是对外的pulic API + +`int lept_parse(lept_value* v, const char* json);` + +parse取词的话,返回值用于表示结果对错,参数有两个,out为一个struct lept_value,用于保存结果,in为字符串。 + +那么这个数据结构里面的内容呢?这个后续交代, + +想合理地表达null,boolean,string,array,object,float这六种JSON类型,可能的struct应该会是: + +``` c +typedef enum { LEPT_NULL, LEPT_TRUE, LEPT_FALSE, LEPT_ARRAY, LEPT_OBJECT, LEPT_FLOAT } lept_type; +typedef struct lept_value_t { + lept_type t; //保存数据类型 +} lept_value; +``` + +注意这里的lept_value数据结构只包含一个枚举值,这是null和boolean特有的简化版, + +如果是array或者其他需要修改此结构,对此我还没有比较好的思路,因为如果子结构包含信息过多,对于null和boolean而言又略显多余。 + +如果是一个较少信息量的类型,估计应该是加入一个`void*`类型指针,用于指向特定结构,例如string指向字符串,array指向`lept_value_t*`数值. + +是否需要size等信息,拭目以待,我觉得如果不放在lept_value_t里面也会放在子类型里面。可能放在子类型里面更好,毕竟很多类型不需要。 + +(2)余下内容太多,自行体会吧,不写了 + + +#### TDD的设计思路 +首先写一个test的宏来做测试,为什么要用宏呢?因为c没有泛型编程嘛。 + +**Milo的解答中说,原因是为了出错后显示的__LINE__宏找到对应位置,而不是每次都在该函数一个位置** + +Milo Yip用了 EXPECT_EQ_BASE这个宏作为EXPECT_EQ_INT的辅助宏非常巧妙, +具体参见`test.c` + +EXPECT_EQ_BASE中包含了与类型无关的内容,用format还获得实际不同类型的情况, +以及equality表达式更像是一个谓词Predicate,这样不仅可用于相等表达式。 + +我个人认为这种通用性强的代码应该在重构中写,实现的时候暂时别想这么复杂。 + +#### 解答篇收获 +有个测试空白字符末尾是否有其他字符的过程,用于每个parse的结束,因此,可以思考下是否有非常适合放置这个过程的位置。 + +对于内部函数,最好加上static,一方面便于查阅时了解其用途,一方面,编译器检查,如果此函数未被使用,也会提示警告。 + diff --git a/tutorial01/test.c b/tutorial01/test.c index e7672181..ddba115d 100644 --- a/tutorial01/test.c +++ b/tutorial01/test.c @@ -20,34 +20,45 @@ static int test_pass = 0; #define EXPECT_EQ_INT(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, "%d") -static void test_parse_null() { +static void test_parse_true() { lept_value v; v.type = LEPT_FALSE; - EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, "null")); - EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v)); + EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, "true")); + EXPECT_EQ_INT(LEPT_TRUE, lept_get_type(&v)); } -static void test_parse_expect_value() { +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_null() { + lept_value v; v.type = LEPT_FALSE; - EXPECT_EQ_INT(LEPT_PARSE_EXPECT_VALUE, lept_parse(&v, "")); + EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, "null")); EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v)); +} - v.type = LEPT_FALSE; - EXPECT_EQ_INT(LEPT_PARSE_EXPECT_VALUE, lept_parse(&v, " ")); - EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v)); +#define TEST_ERROR(error, json) \ + do { \ + lept_value v; \ + v.type = LEPT_FALSE; \ + EXPECT_EQ_INT(LEPT_PARSE_INVALID_VALUE, lept_parse(&v, "nul")); \ + EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v)); \ + } while(0) + +static void test_parse_expect_value() { + TEST_ERROR(LEPT_PARSE_EXPECT_VALUE, ""); + TEST_ERROR(LEPT_PARSE_EXPECT_VALUE, " "); } static void test_parse_invalid_value() { - lept_value v; - v.type = LEPT_FALSE; - EXPECT_EQ_INT(LEPT_PARSE_INVALID_VALUE, lept_parse(&v, "nul")); - EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v)); - - v.type = LEPT_FALSE; - EXPECT_EQ_INT(LEPT_PARSE_INVALID_VALUE, lept_parse(&v, "?")); - EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v)); + TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "nul"); + TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "?"); + TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "tru"); + TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "fals"); } static void test_parse_root_not_singular() { @@ -59,6 +70,8 @@ static void test_parse_root_not_singular() { static void test_parse() { test_parse_null(); + test_parse_true(); + test_parse_false(); test_parse_expect_value(); test_parse_invalid_value(); test_parse_root_not_singular(); diff --git a/tutorial02/leptjson.c b/tutorial02/leptjson.c index 7693e43b..1e449ca3 100644 --- a/tutorial02/leptjson.c +++ b/tutorial02/leptjson.c @@ -1,8 +1,12 @@ #include "leptjson.h" #include /* assert() */ #include /* NULL, strtod() */ +#include /* errno, ERANGE */ +#include /* HUGE_VAL */ -#define EXPECT(c, ch) do { assert(*c->json == (ch)); c->json++; } while(0) +#define EXPECT(c, ch) do { assert(*c->json == (ch)); c->json++; } while(0) +#define ISDIGIT(ch) ((ch) >= '0' && (ch) <= '9') +#define ISDIGIT_1TO9(ch) ((ch) >= '1' && (ch) <= '9') typedef struct { const char* json; @@ -15,49 +19,53 @@ 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; +static int lept_parse_literal(lept_context* c, lept_value* v, const char* literal, lept_type type) { + size_t i; + EXPECT(c, *literal); + for(i = 0; literal[i + 1]; i++) { + if(c->json[i] != literal[i + 1]) + return LEPT_PARSE_INVALID_VALUE; + } + c->json += i; + v->type = type; 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_number(lept_context* c, lept_value* v) { + const char* p = c->json; + if(*p == '-') p++; + if(*p == '0') p++; + else { + if(!ISDIGIT_1TO9(*p)) return LEPT_PARSE_INVALID_VALUE; + for (p++; ISDIGIT(*p); p++); + } + if(*p == '.') { + p++; + if(!ISDIGIT(*p)) return LEPT_PARSE_INVALID_VALUE; + for (p++; ISDIGIT(*p); p++); + } + if(*p == 'e' || *p == 'E') { + p++; + if (*p == '+' || *p == '-') p++; + if (!ISDIGIT(*p)) return LEPT_PARSE_INVALID_VALUE; + for (p++; ISDIGIT(*p); 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; - c->json += 3; - v->type = LEPT_NULL; - return LEPT_PARSE_OK; -} + errno = 0; + v->n = strtod(c->json, NULL); + if(errno == ERANGE && (v->n == HUGE_VAL || v->n == -HUGE_VAL)) + return LEPT_PARSE_NUMBER_TOO_BIG; -static int lept_parse_number(lept_context* c, lept_value* v) { - char* end; - /* \TODO validate number */ - v->n = strtod(c->json, &end); - if (c->json == end) - return LEPT_PARSE_INVALID_VALUE; - c->json = end; v->type = LEPT_NUMBER; + c->json = p; 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, "true", LEPT_TRUE); + case 'f': return lept_parse_literal(c, v, "false", LEPT_FALSE); + case 'n': return lept_parse_literal(c, v, "null", LEPT_NULL); default: return lept_parse_number(c, v); case '\0': return LEPT_PARSE_EXPECT_VALUE; } diff --git a/tutorial02/my_note02.md b/tutorial02/my_note02.md new file mode 100644 index 00000000..9e392f7e --- /dev/null +++ b/tutorial02/my_note02.md @@ -0,0 +1,9 @@ +## 学习笔记02 + +### 为什么用#if 0宏 +因为其支持嵌套,而用`/**/`不支持嵌套。 + +### code review +注意不变字符串作为参数用`const char*` + +注意参数顺序,最好输入放前面,输出放后面 diff --git a/tutorial02/test.c b/tutorial02/test.c index 6e3ebed2..1dba1821 100644 --- a/tutorial02/test.c +++ b/tutorial02/test.c @@ -70,6 +70,21 @@ 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 */ + + /* the smallest number > 1 */ + TEST_NUMBER(1.0000000000000002,"1.0000000000000002"); + /* mininum denormal */ + TEST_NUMBER( 4.9406564584124654e-324," 4.9406564584124654e-324"); + TEST_NUMBER(-4.9406564584124654e-324,"-4.9406564584124654e-324"); + /* Max subnormal double */ + TEST_NUMBER( 2.2250738585072009e-308," 2.2250738585072009e-308"); + TEST_NUMBER(-2.2250738585072009e-308,"-2.2250738585072009e-308"); + /* Min normal positive double */ + TEST_NUMBER( 2.2250738585072014e-308, "2.2250738585072014e-308"); + TEST_NUMBER(-2.2250738585072014e-308, "-2.2250738585072014e-308"); + /* Max double */ + TEST_NUMBER( 1.7976931348623157e+308, "1.7976931348623157e+308"); + TEST_NUMBER(-1.7976931348623157e+308, "-1.7976931348623157e+308"); } #define TEST_ERROR(error, json)\ @@ -89,7 +104,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 +120,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 '.' or nothing */ TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, "0x0"); @@ -114,7 +129,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..09dfc746 100644 --- a/tutorial03/leptjson.c +++ b/tutorial03/leptjson.c @@ -2,9 +2,16 @@ #include /* assert() */ #include /* errno, ERANGE */ #include /* HUGE_VAL */ -#include /* NULL, malloc(), realloc(), free(), strtod() */ #include /* memcpy() */ +#ifdef _WINDOWS +#define _CRTDBG_MAP_ALLOC +#endif +#include /* NULL, malloc(), realloc(), free(), strtod() */ +#ifdef _WINDOWS +#include +#endif + #ifndef LEPT_PARSE_STACK_INIT_SIZE #define LEPT_PARSE_STACK_INIT_SIZE 256 #endif @@ -86,6 +93,7 @@ static int lept_parse_number(lept_context* c, lept_value* v) { return LEPT_PARSE_OK; } + static int lept_parse_string(lept_context* c, lept_value* v) { size_t head = c->top, len; const char* p; @@ -99,10 +107,29 @@ 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 '\\': + switch (*p++) { + case '\"' : PUTC(c, '\"'); break; + case '\\' : PUTC(c, '\\'); break; + case '/' : PUTC(c, '/'); break; + case 'b' : PUTC(c, '\b'); break; + case 'f' : PUTC(c, '\f'); break; + case 'n' : PUTC(c, '\n'); break; + case 'r' : PUTC(c, '\r'); break; + case 't' : PUTC(c, '\t'); break; + default: + c->top = head; + return LEPT_PARSE_INVALID_STRING_ESCAPE; + } + break; case '\0': c->top = head; return LEPT_PARSE_MISS_QUOTATION_MARK; default: + if ((unsigned char)ch < 0x20) { + c->top = head; + return LEPT_PARSE_INVALID_STRING_CHAR; + } PUTC(c, ch); } } @@ -153,12 +180,14 @@ lept_type lept_get_type(const lept_value* v) { } int lept_get_boolean(const lept_value* v) { - /* \TODO */ - return 0; + assert(v != NULL && (v->type == LEPT_TRUE || v->type == LEPT_FALSE)); /*注意这里还需判断v.type*/ + return v->type == LEPT_TRUE; } void lept_set_boolean(lept_value* v, int b) { - /* \TODO */ + lept_free(v); /*注意这里如果之前是字符串还得先释放内存,lept_free中包含assert,不需此处断言,下同*/ + v->type = b ? LEPT_TRUE : LEPT_FALSE; + return; } double lept_get_number(const lept_value* v) { @@ -167,7 +196,10 @@ double lept_get_number(const lept_value* v) { } void lept_set_number(lept_value* v, double n) { - /* \TODO */ + lept_free(v); /*注意这里如果之前是字符串还得先释放内存,lept_free中包含assert,不需此处断言,下同*/ + v->type = LEPT_NUMBER; + v->u.n = n; + return; } const char* lept_get_string(const lept_value* v) { diff --git a/tutorial03/my_note03.md b/tutorial03/my_note03.md new file mode 100644 index 00000000..b5b1fdc4 --- /dev/null +++ b/tutorial03/my_note03.md @@ -0,0 +1,39 @@ +## tutorial03笔记 + +escape和unescape是非常容易出bug的地方,很多时候没注意就会忘了加斜杠之类。 + +程序执行前后细抠细节,比如如下程序,在字符串退出前`c->top=head`,保证后置条件成立,这个常容易忘掉。 + +``` c +static int lept_parse_string(lept_context* c, lept_value* v) { +//some code... + 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; + case '\\': + switch (*p++) { + case '\"' : PUTC(c, '\"'); break; + case '\\' : PUTC(c, '\\'); break; + case '/' : PUTC(c, '/'); break; + case 'b' : PUTC(c, '\b'); break; + case 'f' : PUTC(c, '\f'); break; + case 'n' : PUTC(c, '\n'); break; + case 'r' : PUTC(c, '\r'); break; + case 't' : PUTC(c, '\t'); break; + default: + c->top = head; + return LEPT_PARSE_INVALID_STRING_ESCAPE; + } + break; + case '\0': + c->top = head; + return LEPT_PARSE_MISS_QUOTATION_MARK; + default: + PUTC(c, ch); + } + } +} +``` diff --git a/tutorial03/test.c b/tutorial03/test.c index ac788aca..7a74c5a2 100644 --- a/tutorial03/test.c +++ b/tutorial03/test.c @@ -1,8 +1,15 @@ #include -#include #include #include "leptjson.h" +#ifdef _WINDOWS +#define _CRTDBG_MAP_ALLOC +#endif +#include /* NULL, malloc(), realloc(), free(), strtod() */ +#ifdef _WINDOWS +#include +#endif + static int main_ret = 0; static int test_count = 0; static int test_pass = 0; @@ -107,7 +114,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 @@ -188,12 +195,24 @@ static void test_access_null() { } static void test_access_boolean() { - /* \TODO */ /* Use EXPECT_TRUE() and EXPECT_FALSE() */ + lept_value v; + lept_init(&v); + lept_set_string(&v, "a", 1); /*故意设置一个string类型来测试最后的lept_free有没有异常*/ + lept_set_boolean(&v, 1); + EXPECT_TRUE(lept_get_boolean(&v)); + lept_set_boolean(&v, 0); + EXPECT_FALSE(lept_get_boolean(&v)); + lept_free(&v); } static void test_access_number() { - /* \TODO */ + lept_value v; + lept_init(&v); + lept_set_string(&v, "a", 1); /*故意设置一个string类型来测试最后的lept_free有没有异常*/ + lept_set_number(&v, 255); + EXPECT_EQ_INT(255, lept_get_number(&v)); + lept_free(&v); } static void test_access_string() { @@ -227,6 +246,9 @@ static void test_parse() { } int main() { +#ifdef _WINDOWS + _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); +#endif test_parse(); printf("%d/%d (%3.2f%%) passed\n", test_pass, test_count, test_pass * 100.0 / test_count); return main_ret; diff --git a/tutorial03_answer/leptjson.c b/tutorial03_answer/leptjson.c index 89117e15..7cc0cdfb 100644 --- a/tutorial03_answer/leptjson.c +++ b/tutorial03_answer/leptjson.c @@ -122,7 +122,7 @@ static int lept_parse_string(lept_context* c, lept_value* v) { c->top = head; return LEPT_PARSE_MISS_QUOTATION_MARK; default: - if ((unsigned char)ch < 0x20) { + if ((unsigned char)ch < 0x20) { c->top = head; return LEPT_PARSE_INVALID_STRING_CHAR; } diff --git a/tutorial04/leptjson.c b/tutorial04/leptjson.c index 0a123bf2..6079ad86 100644 --- a/tutorial04/leptjson.c +++ b/tutorial04/leptjson.c @@ -91,12 +91,39 @@ static int lept_parse_number(lept_context* c, lept_value* v) { } static const char* lept_parse_hex4(const char* p, unsigned* u) { - /* \TODO */ + int i; + *u = 0; + for (i = 0; i < 4; i++) { + char ch = *p++; + *u <<= 4; + if (ISDIGIT(ch)) *u |= (ch - '0'); + else if ( ch >= 'A' && ch <= 'F') *u |= ch - ('A' - 10); + else if ( ch >= 'a' && ch <= 'f') *u |= ch - ('a' - 10); + else return NULL; + } return p; } static void lept_encode_utf8(lept_context* c, unsigned u) { - /* \TODO */ + assert(u <= 0x10FFFF); + if (u <= 0x7F) { + PUTC(c, u & 0xFF); /* 之所有 & 0xFF是因为编译器会将unsigned转char时提示最高位的截断误差*/ + } + else if (u <= 0x07FF) { + PUTC(c, (0xC0 | ((u >> 6) & 0xFF))); + PUTC(c, (0x80 | ( u & 0x3F))); + } + else if (u <= 0xFFFF) { + PUTC(c, (0xE0 | ((u >> 12) & 0xFF))); + PUTC(c, (0x80 | ((u >> 6) & 0x3F))); + PUTC(c, (0x80 | ( u & 0x3F))); + } + else if (u <= 0x10FFFF) { + PUTC(c, (0xF0 | ((u >> 18) & 0xFF))); + 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) @@ -104,6 +131,7 @@ static void lept_encode_utf8(lept_context* c, unsigned u) { static int lept_parse_string(lept_context* c, lept_value* v) { size_t head = c->top, len; unsigned u; + unsigned l; const char* p; EXPECT(c, '\"'); p = c->json; @@ -128,7 +156,16 @@ static int lept_parse_string(lept_context* c, lept_value* v) { case 'u': if (!(p = lept_parse_hex4(p, &u))) STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX); - /* \TODO surrogate handling */ + if(u >= 0xD800 && u <= 0xDBFF) { + if (p[0] != '\\' || p[1] != 'u') + STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE); + p += 2; + if(!(p = lept_parse_hex4(p, &l))) + STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX); + if(!(l >= 0xDC00 && l <= 0xDFFF)) + STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE); + u = ((u - 0xD800) << 10 ) | (l - 0xDC00) | 0x10000; + } lept_encode_utf8(c, u); break; default: diff --git a/tutorial04/my_note04.md b/tutorial04/my_note04.md new file mode 100644 index 00000000..9a9bf5a4 --- /dev/null +++ b/tutorial04/my_note04.md @@ -0,0 +1,29 @@ + +Unicode 返回为 0 至 0x10FFFF,收录了当今几乎所有文字,每个文字映射为Unicode的一个码点。 + +注意U+FFFF也只能表示`16*16*16*16=65536`个字,所以不能容纳所有文字(2016年收录共约128237个), +所以需要扩展: + +``` +同学可能会发现,4 位的 16 进制数字只能表示 0 至 0xFFFF,但之前我们说 UCS 的码点是从 0 至 0x10FFFF,那怎么能表示多出来的码点? + +其实,U+0000 至 U+FFFF 这组 Unicode 字符称为基本多文种平面(basic multilingual plane, BMP),还有另外 16 个平面。那么 BMP 以外的字符,JSON 会使用代理对(surrogate pair)表示 \uXXXX\uYYYY。在 BMP 中,保留了 2048 个代理码点。 +如果第一个码点是 U+D800 至 U+DBFF,我们便知道它的代码对的高代理项(high surrogate),之后应该伴随一个 U+DC00 至 U+DFFF 的低代理项(low surrogate)。然后,我们用下列公式把代理对 (H, L) 变换成真实的码点 +``` + + +然而Unicode使用不太方便,为了将Unicode转为比较方便的字节,于是采用(Uniform Transformation Format)UTF, +来存储各个码点,比方说UTF-8,UTF-16和UTF-32,其中UTF-8最流行,为可变长编码。 + +对于 JSON字符串中的 \uXXXX 是以 16 进制表示码点 U+0000 至 U+FFFF,我们需要: + +1. 解析 4 位十六进制整数为码点; +2. 由于字符串是以 UTF-8 存储,我们要把这个码点编码成 UTF-8。 + +也就是说U+XXXX是unicode格式,也是我们可见格式,转化为UTF-8的字节序列(char*)便于程序处理的格式, + +UTF-8 成为现时互联网上最流行的格式,有几个原因: + +1. 它采用字节为编码单元,不会有字节序(endianness)的问题。 +2. 每个 ASCII 字符只需一个字节去储存。 +3. 如果程序原来是以字节方式储存字符,理论上不需要特别改动就能处理 UTF-8 的数据。