From 5b0dee7d9f05cdb4798e865d7b0b5db31c2aac76 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Sun, 2 Oct 2016 01:26:21 +0800 Subject: [PATCH 1/5] Add tutorial04 --- readme.md | 2 +- tutorial04/CMakeLists.txt | 10 + tutorial04/images/Utf8webgrowth.png | Bin 0 -> 37658 bytes tutorial04/leptjson.c | 231 +++++++++++++++++++++++ tutorial04/leptjson.h | 49 +++++ tutorial04/test.c | 279 ++++++++++++++++++++++++++++ tutorial04/tutorial04.md | 149 +++++++++++++++ 7 files changed, 719 insertions(+), 1 deletion(-) create mode 100644 tutorial04/CMakeLists.txt create mode 100644 tutorial04/images/Utf8webgrowth.png create mode 100644 tutorial04/leptjson.c create mode 100644 tutorial04/leptjson.h create mode 100644 tutorial04/test.c create mode 100644 tutorial04/tutorial04.md diff --git a/readme.md b/readme.md index 4a018c94..0fae74a5 100644 --- a/readme.md +++ b/readme.md @@ -41,7 +41,7 @@ 1. [启程](tutorial01/tutorial01.md)(2016/9/15 完成):编译环境、JSON 简介、测试驱动、解析器主要函数及各数据结构。练习 JSON 布尔类型的解析。[启程解答篇](tutorial01_answer/tutorial01_answer.md)(2016/9/17 完成)。 2. [解析数字](tutorial02/tutorial02.md)(2016/9/18 完成):JSON number 的语法。练习 JSON number 类型的校验。[解析数字解答篇](tutorial02_answer/tutorial02_answer.md)(2016/9/20 完成)。 3. [解析字符串](tutorial03/tutorial03.md)(2016/9/22 完成):使用 union 存储 variant、自动扩展的堆栈、JSON string 的语法、valgrind。练习最基本的 JSON string 类型的解析、内存释放。[解析字符串解答篇](tutorial03_answer/tutorial03_answer.md)(2016/9/27 完成)。 -4. Unicode:Unicode 和 UTF-8 的基本知识、JSON string 的 unicode 处理。练习完成 JSON string 类型的解析。 +4. [Unicode](tutorial04/tutorial04.md)(2016/10/2 完成):Unicode 和 UTF-8 的基本知识、JSON string 的 unicode 处理。练习完成 JSON string 类型的解析。 5. 解析数组:JSON array 的语法。练习完成 JSON array 类型的解析、相关内存释放。 6. 解析对象:JSON object 的语法、重构 string 解析函数。练习完成 JSON object 的解析、相关内存释放。 7. 生成器:JSON 生成过程、注意事项。练习完成 JSON 生成器。 diff --git a/tutorial04/CMakeLists.txt b/tutorial04/CMakeLists.txt new file mode 100644 index 00000000..49ba19de --- /dev/null +++ b/tutorial04/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required (VERSION 2.6) +project (leptjson_test C) + +if (CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ansi -pedantic -Wall") +endif() + +add_library(leptjson leptjson.c) +add_executable(leptjson_test test.c) +target_link_libraries(leptjson_test leptjson) diff --git a/tutorial04/images/Utf8webgrowth.png b/tutorial04/images/Utf8webgrowth.png new file mode 100644 index 0000000000000000000000000000000000000000..45c5e086e4ca0e2ccc4d692bb709ce6476c7df72 GIT binary patch literal 37658 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�!LyJ9~Ou6u|c40BwF%T-vgi5a>iR@Wx3j6HxJW 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 QZ7U26p00i_>zopr0JA~C+W-In literal 0 HcmV?d00001 diff --git a/tutorial04/leptjson.c b/tutorial04/leptjson.c new file mode 100644 index 00000000..0a123bf2 --- /dev/null +++ b/tutorial04/leptjson.c @@ -0,0 +1,231 @@ +#ifdef _WINDOWS +#define _CRTDBG_MAP_ALLOC +#include +#endif +#include "leptjson.h" +#include /* assert() */ +#include /* errno, ERANGE */ +#include /* HUGE_VAL */ +#include /* NULL, malloc(), realloc(), free(), strtod() */ +#include /* memcpy() */ + +#ifndef LEPT_PARSE_STACK_INIT_SIZE +#define LEPT_PARSE_STACK_INIT_SIZE 256 +#endif + +#define EXPECT(c, ch) do { assert(*c->json == (ch)); c->json++; } while(0) +#define ISDIGIT(ch) ((ch) >= '0' && (ch) <= '9') +#define ISDIGIT1TO9(ch) ((ch) >= '1' && (ch) <= '9') +#define PUTC(c, ch) do { *(char*)lept_context_push(c, sizeof(char)) = (ch); } while(0) + +typedef struct { + const char* json; + char* stack; + size_t size, top; +}lept_context; + +static void* lept_context_push(lept_context* c, size_t size) { + void* ret; + assert(size > 0); + if (c->top + size >= c->size) { + if (c->size == 0) + c->size = LEPT_PARSE_STACK_INIT_SIZE; + while (c->top + size >= c->size) + c->size += c->size >> 1; /* c->size * 1.5 */ + c->stack = (char*)realloc(c->stack, c->size); + } + ret = c->stack + c->top; + c->top += size; + return ret; +} + +static void* lept_context_pop(lept_context* c, size_t size) { + assert(c->top >= size); + return c->stack + (c->top -= size); +} + +static void lept_parse_whitespace(lept_context* c) { + const char *p = c->json; + while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') + p++; + c->json = p; +} + +static int lept_parse_literal(lept_context* c, lept_value* v, const char* literal, lept_type type) { + size_t i; + EXPECT(c, literal[0]); + 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_number(lept_context* c, lept_value* v) { + const char* p = c->json; + if (*p == '-') p++; + if (*p == '0') p++; + else { + if (!ISDIGIT1TO9(*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++); + } + errno = 0; + v->u.n = strtod(c->json, NULL); + if (errno == ERANGE && (v->u.n == HUGE_VAL || v->u.n == -HUGE_VAL)) + return LEPT_PARSE_NUMBER_TOO_BIG; + v->type = LEPT_NUMBER; + c->json = p; + return LEPT_PARSE_OK; +} + +static const char* lept_parse_hex4(const char* p, unsigned* u) { + /* \TODO */ + return p; +} + +static void lept_encode_utf8(lept_context* c, unsigned u) { + /* \TODO */ +} + +#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; + const char* p; + EXPECT(c, '\"'); + p = c->json; + for (;;) { + char ch = *p++; + 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; + case 'u': + if (!(p = lept_parse_hex4(p, &u))) + STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX); + /* \TODO surrogate handling */ + lept_encode_utf8(c, u); + break; + default: + STRING_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE); + } + break; + case '\0': + STRING_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK); + default: + if ((unsigned char)ch < 0x20) + STRING_ERROR(LEPT_PARSE_INVALID_STRING_CHAR); + PUTC(c, ch); + } + } +} + +static int lept_parse_value(lept_context* c, lept_value* v) { + switch (*c->json) { + 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 '"': return lept_parse_string(c, v); + case '\0': return LEPT_PARSE_EXPECT_VALUE; + } +} + +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) { + lept_parse_whitespace(&c); + if (*c.json != '\0') { + v->type = LEPT_NULL; + ret = LEPT_PARSE_ROOT_NOT_SINGULAR; + } + } + assert(c.top == 0); + free(c.stack); + return ret; +} + +void lept_free(lept_value* v) { + assert(v != NULL); + if (v->type == LEPT_STRING) + free(v->u.s.s); + v->type = LEPT_NULL; +} + +lept_type lept_get_type(const lept_value* v) { + assert(v != NULL); + return v->type; +} + +int lept_get_boolean(const lept_value* v) { + assert(v != NULL && (v->type == LEPT_TRUE || v->type == LEPT_FALSE)); + return v->type == LEPT_TRUE; +} + +void lept_set_boolean(lept_value* v, int b) { + lept_free(v); + v->type = b ? LEPT_TRUE : LEPT_FALSE; +} + +double lept_get_number(const lept_value* v) { + assert(v != NULL && v->type == LEPT_NUMBER); + return v->u.n; +} + +void lept_set_number(lept_value* v, double n) { + lept_free(v); + v->u.n = n; + v->type = LEPT_NUMBER; +} + +const char* lept_get_string(const lept_value* v) { + assert(v != NULL && v->type == LEPT_STRING); + return v->u.s.s; +} + +size_t lept_get_string_length(const lept_value* v) { + assert(v != NULL && v->type == LEPT_STRING); + return v->u.s.len; +} + +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; +} diff --git a/tutorial04/leptjson.h b/tutorial04/leptjson.h new file mode 100644 index 00000000..1a5aa367 --- /dev/null +++ b/tutorial04/leptjson.h @@ -0,0 +1,49 @@ +#ifndef LEPTJSON_H__ +#define LEPTJSON_H__ + +#include /* size_t */ + +typedef enum { LEPT_NULL, LEPT_FALSE, LEPT_TRUE, LEPT_NUMBER, LEPT_STRING, LEPT_ARRAY, LEPT_OBJECT } lept_type; + +typedef struct { + union { + struct { char* s; size_t len; }s; /* string: null-terminated string, string length */ + double n; /* number */ + }u; + lept_type type; +}lept_value; + +enum { + LEPT_PARSE_OK = 0, + LEPT_PARSE_EXPECT_VALUE, + LEPT_PARSE_INVALID_VALUE, + LEPT_PARSE_ROOT_NOT_SINGULAR, + LEPT_PARSE_NUMBER_TOO_BIG, + LEPT_PARSE_MISS_QUOTATION_MARK, + LEPT_PARSE_INVALID_STRING_ESCAPE, + LEPT_PARSE_INVALID_STRING_CHAR, + LEPT_PARSE_INVALID_UNICODE_HEX, + LEPT_PARSE_INVALID_UNICODE_SURROGATE +}; + +#define lept_init(v) do { (v)->type = LEPT_NULL; } while(0) + +int lept_parse(lept_value* v, const char* json); + +void lept_free(lept_value* v); + +lept_type lept_get_type(const lept_value* v); + +#define lept_set_null(v) lept_free(v) + +int lept_get_boolean(const lept_value* v); +void lept_set_boolean(lept_value* v, int b); + +double lept_get_number(const lept_value* v); +void lept_set_number(lept_value* v, double n); + +const char* lept_get_string(const lept_value* v); +size_t lept_get_string_length(const lept_value* v); +void lept_set_string(lept_value* v, const char* s, size_t len); + +#endif /* LEPTJSON_H__ */ diff --git a/tutorial04/test.c b/tutorial04/test.c new file mode 100644 index 00000000..9c8bb600 --- /dev/null +++ b/tutorial04/test.c @@ -0,0 +1,279 @@ +#ifdef _WINDOWS +#define _CRTDBG_MAP_ALLOC +#include +#endif +#include +#include +#include +#include "leptjson.h" + +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++;\ + if (equality)\ + test_pass++;\ + else {\ + fprintf(stderr, "%s:%d: expect: " format " actual: " format "\n", __FILE__, __LINE__, expect, actual);\ + main_ret = 1;\ + }\ + } while(0) + +#define EXPECT_EQ_INT(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, "%d") +#define EXPECT_EQ_DOUBLE(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, "%.17g") +#define EXPECT_EQ_STRING(expect, actual, alength) \ + EXPECT_EQ_BASE(sizeof(expect) - 1 == alength && memcmp(expect, actual, alength) == 0, expect, actual, "%s") +#define EXPECT_TRUE(actual) EXPECT_EQ_BASE((actual) != 0, "true", "false", "%s") +#define EXPECT_FALSE(actual) EXPECT_EQ_BASE((actual) == 0, "false", "true", "%s") + +static void test_parse_null() { + lept_value v; + lept_init(&v); + lept_set_boolean(&v, 0); + EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, "null")); + EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v)); + lept_free(&v); +} + +static void test_parse_true() { + lept_value v; + lept_init(&v); + lept_set_boolean(&v, 0); + EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, "true")); + EXPECT_EQ_INT(LEPT_TRUE, lept_get_type(&v)); + lept_free(&v); +} + +static void test_parse_false() { + lept_value v; + lept_init(&v); + lept_set_boolean(&v, 1); + EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, "false")); + EXPECT_EQ_INT(LEPT_FALSE, lept_get_type(&v)); + lept_free(&v); +} + +#define TEST_NUMBER(expect, json)\ + do {\ + lept_value v;\ + lept_init(&v);\ + EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));\ + EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(&v));\ + EXPECT_EQ_DOUBLE(expect, lept_get_number(&v));\ + lept_free(&v);\ + } while(0) + +static void test_parse_number() { + TEST_NUMBER(0.0, "0"); + TEST_NUMBER(0.0, "-0"); + TEST_NUMBER(0.0, "-0.0"); + TEST_NUMBER(1.0, "1"); + TEST_NUMBER(-1.0, "-1"); + TEST_NUMBER(1.5, "1.5"); + TEST_NUMBER(-1.5, "-1.5"); + TEST_NUMBER(3.1416, "3.1416"); + TEST_NUMBER(1E10, "1E10"); + TEST_NUMBER(1e10, "1e10"); + TEST_NUMBER(1E+10, "1E+10"); + TEST_NUMBER(1E-10, "1E-10"); + TEST_NUMBER(-1E10, "-1E10"); + TEST_NUMBER(-1e10, "-1e10"); + TEST_NUMBER(-1E+10, "-1E+10"); + TEST_NUMBER(-1E-10, "-1E-10"); + 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_STRING(expect, json)\ + do {\ + lept_value v;\ + lept_init(&v);\ + EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));\ + EXPECT_EQ_INT(LEPT_STRING, lept_get_type(&v));\ + EXPECT_EQ_STRING(expect, lept_get_string(&v), lept_get_string_length(&v));\ + lept_free(&v);\ + } while(0) + +static void test_parse_string() { + TEST_STRING("", "\"\""); + TEST_STRING("Hello", "\"Hello\""); + TEST_STRING("Hello\nWorld", "\"Hello\\nWorld\""); + TEST_STRING("\" \\ / \b \f \n \r \t", "\"\\\" \\\\ \\/ \\b \\f \\n \\r \\t\""); + TEST_STRING("Hello\0World", "\"Hello\\u0000World\""); + TEST_STRING("\x24", "\"\\u0024\""); /* Dollar sign U+0024 */ + TEST_STRING("\xC2\xA2", "\"\\u00A2\""); /* Cents sign U+00A2 */ + TEST_STRING("\xE2\x82\xAC", "\"\\u20AC\""); /* Euro sign U+20AC */ + TEST_STRING("\xF0\x9D\x84\x9E", "\"\\uD834\\uDD1E\""); /* G clef sign U+1D11E */ +} + +#define TEST_ERROR(error, json)\ + do {\ + lept_value v;\ + lept_init(&v);\ + v.type = LEPT_FALSE;\ + EXPECT_EQ_INT(error, lept_parse(&v, json));\ + EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\ + lept_free(&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() { + TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "nul"); + TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "?"); + + /* invalid number */ + TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "+0"); + TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "+1"); + TEST_ERROR(LEPT_PARSE_INVALID_VALUE, ".123"); /* at least one digit before '.' */ + TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "1."); /* at least one digit after '.' */ + TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "INF"); + TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "inf"); + TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "NAN"); + TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "nan"); +} + +static void test_parse_root_not_singular() { + TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, "null x"); + + /* invalid number */ + TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, "0123"); /* after zero should be '.' or nothing */ + TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, "0x0"); + TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, "0x123"); +} + +static void test_parse_number_too_big() { + TEST_ERROR(LEPT_PARSE_NUMBER_TOO_BIG, "1e309"); + TEST_ERROR(LEPT_PARSE_NUMBER_TOO_BIG, "-1e309"); +} + +static void test_parse_missing_quotation_mark() { + TEST_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK, "\""); + TEST_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK, "\"abc"); +} + +static void test_parse_invalid_string_escape() { + TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, "\"\\v\""); + TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, "\"\\'\""); + TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, "\"\\0\""); + TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, "\"\\x12\""); +} + +static void test_parse_invalid_string_char() { + TEST_ERROR(LEPT_PARSE_INVALID_STRING_CHAR, "\"\x01\""); + TEST_ERROR(LEPT_PARSE_INVALID_STRING_CHAR, "\"\x1F\""); +} + +static void test_parse_invalid_unicode_hex() { + TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, "\"\\u\""); + TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, "\"\\u0\""); + TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, "\"\\u01\""); + TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, "\"\\u012\""); + TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, "\"\\u01234\""); + TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, "\"\\u/000\""); + TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, "\"\\uG000\""); + TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, "\"\\u0/00\""); + TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, "\"\\u0G00\""); + TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, "\"\\u0/00\""); + TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, "\"\\u00G0\""); + TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, "\"\\u000/\""); + TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, "\"\\u000G\""); +} + +static void test_parse_invalid_unicode_surrogate() { + TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, "\"\\uD800\""); + TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, "\"\\uDBFF\""); + TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, "\"\\uD800\\\\\""); + TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, "\"\\uD800\\uDBFF\""); + TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, "\"\\uD800\\uE000\""); +} + +static void test_parse() { + test_parse_null(); + test_parse_true(); + test_parse_false(); + test_parse_number(); + test_parse_string(); + test_parse_expect_value(); + test_parse_invalid_value(); + test_parse_root_not_singular(); + test_parse_number_too_big(); + test_parse_missing_quotation_mark(); + test_parse_invalid_string_escape(); + test_parse_invalid_string_char(); + test_parse_invalid_unicode_hex(); + test_parse_invalid_unicode_surrogate(); +} + +static void test_access_null() { + lept_value v; + lept_init(&v); + lept_set_string(&v, "a", 1); + lept_set_null(&v); + EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v)); + lept_free(&v); +} + +static void test_access_boolean() { + lept_value v; + lept_init(&v); + lept_set_string(&v, "a", 1); + 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() { + lept_value v; + lept_init(&v); + lept_set_string(&v, "a", 1); + lept_set_number(&v, 1234.5); + EXPECT_EQ_DOUBLE(1234.5, lept_get_number(&v)); + lept_free(&v); +} + +static void test_access_string() { + lept_value v; + lept_init(&v); + lept_set_string(&v, "", 0); + EXPECT_EQ_STRING("", lept_get_string(&v), lept_get_string_length(&v)); + lept_set_string(&v, "Hello", 5); + EXPECT_EQ_STRING("Hello", lept_get_string(&v), lept_get_string_length(&v)); + lept_free(&v); +} + +static void test_access() { + test_access_null(); + test_access_boolean(); + test_access_number(); + test_access_string(); +} + +int main() { +#ifdef _WINDOWS + _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); +#endif + test_parse(); + test_access(); + printf("%d/%d (%3.2f%%) passed\n", test_pass, test_count, test_pass * 100.0 / test_count); + return main_ret; +} diff --git a/tutorial04/tutorial04.md b/tutorial04/tutorial04.md new file mode 100644 index 00000000..02d6984d --- /dev/null +++ b/tutorial04/tutorial04.md @@ -0,0 +1,149 @@ +# 从零开始的 JSON 库教程(四):Unicode + +* Milo Yip +* 2016/10/2 + +本文是[《从零开始的 JSON 库教程》](https://zhuanlan.zhihu.com/json-tutorial)的第四个单元。代码位于 [json-tutorial/tutorial04](https://github.com/miloyip/json-tutorial/blob/master/tutorial04_answer)。 + +## 1. Unicode + +在上一个单元,我们已经能解析「一般」的 JSON 字符串,仅仅没有处理 `\uXXXX` 这种转义序列。为了解析这种序列,我们必须了解有关 Unicode 的基本概念。 + +读者应该知道 ASCII,它是一种字符编码,把 128 个字符映射至整数 0 ~ 127。例如,`1` → 49,`A` → 65,`B` → 66 等等。这种 7-bit 字符编码系统非常简单,在计算机中以一个字节存储一个字符。然而,它仅适合美国英语,甚至一些英语中常用的标点符号、重音符号都不能表示,无法表示各国语言,特别是中日韩语等表意文字。 + +在 Unicode 出现之前,各地区制定了不同的编码系统,如中文主要用 GB 2312 和大五码、日文主要用 JIS 等。这样会造成很多不便,例如一个文本信息很难混合各种语言的文字。 + +因此,在上世纪80年代末,Xerox、Apple 等公司开始研究,是否能制定一套多语言的统一编码系统。后来,多个机构成立了 Unicode 联盟,在 1991 年释出 Unicode 1.0,收录了 24 种语言共 7161 个字符。在四分之一个世纪后的 2016年,Unicode 已释出 9.0 版本,收录 135 种语言共 128237 个字符。 + +这些字符被收录为统一字符集(Universal Coded Character Set, UCS),每个字符映射至一个整数码点(code point),码点的范围是 0 至 0x10FFFF,码点又通常记作 U+XXXX,当中 XXXX 为 16 进位数字。例如 `劲` → U+52B2、`峰` → U+5CF0。很明显,UCS 中的字符无法像 ASCII 般以一个字节存储。 + +因此,Unicode 还制定了各种储存码点的方式,这些方式称为 Unicode 转换格式(Uniform Transformation Format, UTF)。现时流行的 UTF 为 UTF-8、UTF-16 和 UTF-32。每种 UTF 会把一个码点储存为一至多个编码单元(code unit)。例如 UTF-8 的编码单元是 8 位的字节、UTF-16 为 16 位、UTF-32 为 32 位。除 UTF-32 外,UTF-8 和 UTF-16 都是可变长度编码。 + +UTF-8 成为现时互联网上最流行的格式,有几个原因: + +1. 它采用字节为编码单元,不会有字节序(endianness)的问题。 +2. 每个 ASCII 字符只需一个字节去储存。 +3. 如果程序原来是以字节方式储存字符,理论上不需要特别改动就能处理 UTF-8 的数据。 + +## 2. 需求 + +由于 UTF-8 的普及性,大部分的 JSON 也通常会以 UTF-8 存储。我们的 JSON 库也会只支持 UTF-8。(RapidJSON 同时支持 UTF-8、UTF-16LE/BE、UTF-32LE/BE、ASCII。) + +C 标准库没有关于 Unicode 的处理功能(C++11 有),我们会实现 JSON 库所需的字符编码处理功能。 + +对于非转义(unescaped)的字符,只要它们不少于 32(0 ~ 31 是不合法的编码单元),我们可以直接复制至结果,这一点我们稍后再说明。我们假设输入是以合法 UTF-8 编码。 + +而对于 JSON字符串中的 `\uXXXX` 是以 16 进制表示码点 U+0000 至 U+FFFF,我们需要: + +1. 解析 4 位十六进制整数为码点; +2. 由于字符串是以 UTF-8 存储,我们要把这个码点编码成 UTF-8。 + +同学可能会发现,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) 变换成真实的码点: + +~~~ +codepoint = 0x10000 + (H − 0xD800) × 0x400 + (L − 0xDC00) +~~~ + +举个例子,高音谱号字符 `𝄞` → U+1D11E 不是 BMP 之内的字符。在 JSON 中可写成转义序列 `\uD834\uDD1E`,我们解析第一个 `\uD834` 得到码点 U+D834,我们发现它是 U+D800 至 U+DBFF 内的码点,所以它是高代理项。然后我们解析下一个转义序列 `\uDD1E` 得到码点 U+DD1E,它在 U+DC00 至 U+DFFF 之内,是合法的低代理项。我们计算其码点: + +~~~ +H = 0xD834, L = 0xDD1E +codepoint = 0x10000 + (H − 0xD800) × 0x400 + (L − 0xDC00) + = 0x10000 + (0xD834 - 0xD800) × 0x400 + (0xDD1E − 0xDC00) + = 0x10000 + 0x34 × 0x400 + 0x11E + = 0x10000 + 0xD000 + 0x11E + = 0x1D11E +~~~ + +这样就得出这转义序列的码点,然后我们再把它编码成 UTF-8。如果只有高代理项而欠缺低代理项,或是低代理项不在合法码点范围,我们都返回 `LEPT_PARSE_INVALID_UNICODE_SURROGATE` 错误。如果 `\u` 后不是 4 位十六进位数字,则返回 `LEPT_PARSE_INVALID_UNICODE_HEX` 错误。 + +## 3. UTF-8 编码 + +UTF-8 在网页上的使用率势无可挡: + +![ ](images/Utf8webgroth.png) + +(图片来自 [Wikipedia Common](https://commons.wikimedia.org/wiki/File:Utf8webgrowth.svg),数据来自 Google 对网页字符编码的统计。) + +由于我们的 JSON 库也只支持 UTF-8,我们需要把码点编码成 UTF-8。这里简单介绍一下 UTF-8 的编码方式。 + +UTF-8 的编码单元是 8 位字节,每个码点编码成 1 至 4 个字节。它的编码方式很简单,按照码点的范围,把码点的二进位分拆成 1 至最多 4 个字节: + +| 码点范围 | 码点位数 | 字节1 | 字节2 | 字节3 | 字节4 | +|:------------------:|:--------:|:--------:|:--------:|:--------:|:--------:| +| U+0000 ~ U+007F | 7 | 0xxxxxxx | +| U+0080 ~ U+07FF | 11 | 110xxxxx | 10xxxxxx | +| U+0800 ~ U+FFFF | 16 | 1110xxxx | 10xxxxxx | 10xxxxxx | +| U+10000 ~ U+10FFFF | 21 | 11110xxx | 10xxxxxx | 10xxxxxx | 10xxxxxx | + +这个编码方法的好处之一是,码点范围 U+0000 ~ U+007F 编码为一字节,与 ASCII 编码兼容。这范围的 Unicode 码点也是和 ASCII 的字符相同。因此,一个 ASCII 文本也是一个 UTF-8 文本。 + +我们举一个例子解析多字节的情况,欧元符号 `€` → U+20AC: + +1. U+20AC 在 U+0800 ~ U+FFFF 的范围内,应编码成 3 个字节。 +2. U+20AC 的二进位为 10000010101100 +3. 3 个字节的情况我们要 16 位的码点,所以在前面补两个 0,成为 0010000010101100 +4. 按上表把二进位分成 3 组:0010, 000010, 101100 +5. 加上每个字节的前缀:11100010, 10000010, 10101100 +6. 用十六进位表示即:0xE2, 0x82, 0xAC + +对于这例子的范围,对应的 C 代码是这样的: + +~~~c +if (u >= 0x0800 && u <= 0xFFFF) { + OutputByte(0xE0 | ((u >> 12) & 0xFF)); /* 0xE0 = 11000000 */ + OutputByte(0x80 | ((u >> 6) & 0x3F)); /* 0x80 = 10000000 */ + OutputByte(0x80 | ( u & 0x3F)); /* 0x3F = 00111111 */ +} +~~~ + +UTF-8 的解码稍复杂一点,但我们的 JSON 库不会校验 JSON 文本是否符合 UTF-8,所以这里也不展开了。 + +## 4. 实现 `\uXXXX` 解析 + +我们只需要在其它转义符的处理中加入对 `\uXXXX` 的处理: + +~~~c +static int lept_parse_string(lept_context* c, lept_value* v) { + unsigned u; + /* ... */ + for (;;) { + char ch = *p++; + switch (ch) { + /* ... */ + case '\\': + switch (*p++) { + /* ... */ + case 'u': + if (!(p = lept_parse_hex4(p, &u))) + STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX); + /* \TODO surrogate handling */ + lept_encode_utf8(c, u); + break; + /* ... */ + } + /* ... */ + } + } +} +~~~ + +上面代码的过程很简单,遇到 `\u` 转义时,调用 `lept_parse_hex4()` 解析 4 位十六进数字,存储为码点 `u`。这个函数在成功时返回解析后的文本指针,失败返回 `NULL`。如果失败,就返回 `LEPT_PARSE_INVALID_UNICODE_HEX` 错误。最后,把码点编码成 UTF-8,写进缓冲区。这里没有处理代理对,留作练习。 + +顺带一提,我为 `lept_parse_string()` 做了个简单的重构,把返回错误码的处理抽取为宏: + +~~~c +#define STRING_ERROR(ret) do { c->top = head; return ret; } while(0) +~~~ + +## 5. 总结与练习 + +本单元介绍了 Unicode 的基本知识,同学应该了解到一些常用的 Unicode 术语,如码点、编码单元、UTF-8、代理对等。这次的练习代码只有个空壳,要由同学填充。完成后应该能通过所有单元测试,届时我们的 JSON 字符串解析就完全符合标准了。 + +1. 实现 `lept_parse_hex4()`,不合法的十六进位数返回 `LEPT_PARSE_INVALID_UNICODE_HEX`。 +2. 按第 3 节谈到的 UTF-8 编码原理,实现 `lept_encode_utf8()`。这函数假设码点在正确范围 U+0000 ~ U+10FFFF(用断言检测)。 +3. 加入对代理对的处理,不正确的代理对范围要返回 `LEPT_PARSE_INVALID_UNICODE_SURROGATE` 错误。 + +如果你遇到问题,有不理解的地方,或是有建议,都欢迎在评论或 [issue](https://github.com/miloyip/json-tutorial/issues) 中提出,让所有人一起讨论。 From 1f8339b118bc4f537be10268b803e705b187ed2c Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Sun, 2 Oct 2016 01:27:56 +0800 Subject: [PATCH 2/5] Fix image link --- tutorial04/tutorial04.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorial04/tutorial04.md b/tutorial04/tutorial04.md index 02d6984d..8e8b1753 100644 --- a/tutorial04/tutorial04.md +++ b/tutorial04/tutorial04.md @@ -63,7 +63,7 @@ codepoint = 0x10000 + (H − 0xD800) × 0x400 + (L − 0xDC00) UTF-8 在网页上的使用率势无可挡: -![ ](images/Utf8webgroth.png) +![ ](images/Utf8webgrowth.png) (图片来自 [Wikipedia Common](https://commons.wikimedia.org/wiki/File:Utf8webgrowth.svg),数据来自 Google 对网页字符编码的统计。) From b05b70ff9db704a370f80865414a5c46dff5736e Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Sun, 2 Oct 2016 01:39:06 +0800 Subject: [PATCH 3/5] Add TOC --- tutorial04/tutorial04.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tutorial04/tutorial04.md b/tutorial04/tutorial04.md index 8e8b1753..91483bad 100644 --- a/tutorial04/tutorial04.md +++ b/tutorial04/tutorial04.md @@ -5,6 +5,14 @@ 本文是[《从零开始的 JSON 库教程》](https://zhuanlan.zhihu.com/json-tutorial)的第四个单元。代码位于 [json-tutorial/tutorial04](https://github.com/miloyip/json-tutorial/blob/master/tutorial04_answer)。 +本单元内容: + +1. [Unicode](#1-unicode) +2. [2.需求](#2-需求) +3. [3. UTF-8 编码](#3-utf-8-编码) +4. [4. 实现 `\uXXXX` 解析](#4-实现-uxxxx-解析) +5. [5. 总结与练习](#5-总结与练习) + ## 1. Unicode 在上一个单元,我们已经能解析「一般」的 JSON 字符串,仅仅没有处理 `\uXXXX` 这种转义序列。为了解析这种序列,我们必须了解有关 Unicode 的基本概念。 @@ -78,7 +86,7 @@ UTF-8 的编码单元是 8 位字节,每个码点编码成 1 至 4 个字节 | U+0800 ~ U+FFFF | 16 | 1110xxxx | 10xxxxxx | 10xxxxxx | | U+10000 ~ U+10FFFF | 21 | 11110xxx | 10xxxxxx | 10xxxxxx | 10xxxxxx | -这个编码方法的好处之一是,码点范围 U+0000 ~ U+007F 编码为一字节,与 ASCII 编码兼容。这范围的 Unicode 码点也是和 ASCII 的字符相同。因此,一个 ASCII 文本也是一个 UTF-8 文本。 +这个编码方法的好处之一是,码点范围 U+0000 ~ U+007F 编码为一个字节,与 ASCII 编码兼容。这范围的 Unicode 码点也是和 ASCII 字符相同的。因此,一个 ASCII 文本也是一个 UTF-8 文本。 我们举一个例子解析多字节的情况,欧元符号 `€` → U+20AC: From 19993ddebf7edfcbc7c4a26d679133fe33e99090 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Sun, 2 Oct 2016 01:42:37 +0800 Subject: [PATCH 4/5] Fix TOC --- tutorial04/tutorial04.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tutorial04/tutorial04.md b/tutorial04/tutorial04.md index 91483bad..54b1e0ee 100644 --- a/tutorial04/tutorial04.md +++ b/tutorial04/tutorial04.md @@ -8,10 +8,10 @@ 本单元内容: 1. [Unicode](#1-unicode) -2. [2.需求](#2-需求) -3. [3. UTF-8 编码](#3-utf-8-编码) -4. [4. 实现 `\uXXXX` 解析](#4-实现-uxxxx-解析) -5. [5. 总结与练习](#5-总结与练习) +2. [需求](#2-需求) +3. [UTF-8 编码](#3-utf-8-编码) +4. [实现 `\uXXXX` 解析](#4-实现-uxxxx-解析) +5. [总结与练习](#5-总结与练习) ## 1. Unicode From 3b12a5a9605ef78dbc4af2ab36adf5d9f3545034 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Sun, 2 Oct 2016 02:03:55 +0800 Subject: [PATCH 5/5] Add and fix test --- tutorial04/test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tutorial04/test.c b/tutorial04/test.c index 9c8bb600..beaa8724 100644 --- a/tutorial04/test.c +++ b/tutorial04/test.c @@ -118,6 +118,7 @@ static void test_parse_string() { TEST_STRING("\xC2\xA2", "\"\\u00A2\""); /* Cents sign U+00A2 */ TEST_STRING("\xE2\x82\xAC", "\"\\u20AC\""); /* Euro sign U+20AC */ TEST_STRING("\xF0\x9D\x84\x9E", "\"\\uD834\\uDD1E\""); /* G clef sign U+1D11E */ + TEST_STRING("\xF0\x9D\x84\x9E", "\"\\ud834\\udd1e\""); /* G clef sign U+1D11E */ } #define TEST_ERROR(error, json)\ @@ -186,7 +187,6 @@ static void test_parse_invalid_unicode_hex() { TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, "\"\\u0\""); TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, "\"\\u01\""); TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, "\"\\u012\""); - TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, "\"\\u01234\""); TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, "\"\\u/000\""); TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, "\"\\uG000\""); TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, "\"\\u0/00\"");