[
  {
    "path": ".gitignore",
    "content": "*/build/\n"
  },
  {
    "path": "readme.md",
    "content": "# 从零开始的 JSON 库教程\n\n* Milo Yip\n* 2016/9/15\n\n也许有很多同学上过 C/C++ 的课后，可以完成一些简单的编程练习，又能在一些网站刷题，但对于如何开发有实际用途的程序可能感到束手无策。本教程希望能以一个简单的项目开发形式，让同学能逐步理解如何从无到有去开发软件。\n\n为什么选择 JSON？因为它足够简单，除基本编程外不需大量技术背景知识。JSON 有标准，可按照标准逐步实现。JSON 也是实际在许多应用上会使用的格式，所以才会有大量的开源库。\n\n这是一个免费、开源的教程，如果你喜欢，也可以打赏鼓励。因为工作及家庭因素，不能保证每篇文章的首发时间，请各位见谅。\n\n## 对象与目标\n\n教程对象：学习过基本 C/C++ 编程的同学。\n\n通过这个教程，同学可以了解如何从零开始写一个 JSON 库，其特性如下：\n\n* 符合标准的 JSON 解析器和生成器\n* 手写的递归下降解析器（recursive descent parser）\n* 使用标准 C 语言（C89）\n* 跨平台／编译器（如 Windows／Linux／OS X，vc／gcc／clang）\n* 仅支持 UTF-8 JSON 文本\n* 仅支持以 `double` 存储 JSON number 类型\n* 解析器和生成器的代码合共少于 500 行\n\n除了围绕 JSON 作为例子，希望能在教程中讲述一些课题：\n\n* 测试驱动开发（test driven development, TDD）\n* C 语言编程风格\n* 数据结构\n* API 设计\n* 断言\n* Unicode\n* 浮点数\n* Github、CMake、valgrind、Doxygen 等工具\n\n## 教程大纲\n\n本教程预计分为 9 个单元，第 1-8 个单元附带练习和解答。\n\n1. [启程](tutorial01/tutorial01.md)（2016/9/15 完成）：编译环境、JSON 简介、测试驱动、解析器主要函数及各数据结构。练习 JSON 布尔类型的解析。[启程解答篇](tutorial01_answer/tutorial01_answer.md)（2016/9/17 完成）。\n2. [解析数字](tutorial02/tutorial02.md)（2016/9/18 完成）：JSON number 的语法。练习 JSON number 类型的校验。[解析数字解答篇](tutorial02_answer/tutorial02_answer.md)（2016/9/20 完成）。\n3. [解析字符串](tutorial03/tutorial03.md)（2016/9/22 完成）：使用 union 存储 variant、自动扩展的堆栈、JSON string 的语法、valgrind。练习最基本的 JSON string 类型的解析、内存释放。[解析字符串解答篇](tutorial03_answer/tutorial03_answer.md)（2016/9/27 完成）。\n4. [Unicode](tutorial04/tutorial04.md)（2016/10/2 完成）：Unicode 和 UTF-8 的基本知识、JSON string 的 unicode 处理。练习完成 JSON string 类型的解析。[Unicode 解答篇](tutorial04_answer/tutorial04_answer.md)（2016/10/6 完成）。\n5. [解析数组](tutorial05/tutorial05.md)（2016/10/7 完成）：JSON array 的语法。练习完成 JSON array 类型的解析、相关内存释放。[解析数组解答篇](tutorial05_answer/tutorial05_answer.md)（2016/10/13 完成）。\n6. [解析对象](tutorial06/tutorial06.md)（2016/10/29 完成）：JSON object 的语法、重构 string 解析函数。练习完成 JSON object 的解析、相关内存释放。[解析对象解答篇](tutorial06_answer/tutorial06_answer.md)（2016/11/15 完成）。\n7. [生成器](tutorial07/tutorial07.md)（2016/12/20 完成）：JSON 生成过程、注意事项。练习完成 JSON 生成器。[生成器解答篇](tutorial07_answer/tutorial07_answer.md)（2017/1/5 完成）。\n8. [访问与其他功能](tutorial08/tutorial08.md)（2018/6/2 完成）：JSON array／object 的访问及修改。练习完成相关功能。\n9. 终点及新开始：加入 nativejson-benchmark 测试，与 RapidJSON 对比及展望。\n\n## 关于作者\n\n叶劲峰（Milo Yip）现任腾讯 T4 专家、互动娱乐事业群魔方工作室群游戏客户端技术总监。他获得香港大学认知科学学士（BCogSc）、香港中文大学系统工程及工程管理哲学硕士（MPhil）。他是《游戏引擎架构》译者、《C++ Primer 中文版（第五版）》审校。他曾参与《天涯明月刀》、《斗战神》、《爱丽丝：疯狂回归》、《美食从天降》、《王子传奇》等游戏项目，以及多个游戏引擎及中间件的研发。他是开源项目 [RapidJSON](https://github.com/miloyip/rapidjson) 的作者，开发 [nativejson-benchmark](https://github.com/miloyip/nativejson-benchmark) 比较 41 个开源原生 JSON 库的标准符合程度及性能。他在 1990 年学习 C 语言，1995 年开始使用 C++ 于各种项目。\n"
  },
  {
    "path": "tutorial01/CMakeLists.txt",
    "content": "cmake_minimum_required (VERSION 2.6)\nproject (leptjson_test C)\n\nif (CMAKE_C_COMPILER_ID MATCHES \"GNU|Clang\")\n    set(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -ansi -pedantic -Wall\")\nendif()\n\nadd_library(leptjson leptjson.c)\nadd_executable(leptjson_test test.c)\ntarget_link_libraries(leptjson_test leptjson)\n"
  },
  {
    "path": "tutorial01/images/makefile",
    "content": "%.png: %.dot\n\tdot $< -Tpng -o $@\n\nDOTFILES = $(basename $(wildcard *.dot))\nall: $(addsuffix .png, $(DOTFILES))\n"
  },
  {
    "path": "tutorial01/images/requirement.dot",
    "content": "digraph {\n    rankdir=LR\n    compound=true\n    fontname=\"Inconsolata, Consolas\"\n    fontsize=10\n    margin=\"0,0\"\n    ranksep=0.5\n    nodesep=1\n    penwidth=0.5\n    \n    node [shape=box, fontname=\"Inconsolata, Consolas\", fontsize=10, penwidth=0.5, style=filled, colorscheme=spectral7]\n    edge [fontname=\"Inconsolata, Consolas\", fontsize=10, penwidth=0.5]\n\n    JSON [fillcolor=6]\n    tree [fillcolor=5]\n    i [style=invis]\n\n    JSON -> tree [label=\"1. parse\"]\n    tree -> JSON [label=\"3. stringify\"]\n    tree -> i [label=\"2. access\", dir=back, arrowtail=normal]\n}"
  },
  {
    "path": "tutorial01/leptjson.c",
    "content": "#include \"leptjson.h\"\n#include <assert.h>  /* assert() */\n#include <stdlib.h>  /* NULL */\n\n#define EXPECT(c, ch)       do { assert(*c->json == (ch)); c->json++; } while(0)\n\ntypedef struct {\n    const char* json;\n}lept_context;\n\nstatic void lept_parse_whitespace(lept_context* c) {\n    const char *p = c->json;\n    while (*p == ' ' || *p == '\\t' || *p == '\\n' || *p == '\\r')\n        p++;\n    c->json = p;\n}\n\nstatic int lept_parse_null(lept_context* c, lept_value* v) {\n    EXPECT(c, 'n');\n    if (c->json[0] != 'u' || c->json[1] != 'l' || c->json[2] != 'l')\n        return LEPT_PARSE_INVALID_VALUE;\n    c->json += 3;\n    v->type = LEPT_NULL;\n    return LEPT_PARSE_OK;\n}\n\nstatic int lept_parse_value(lept_context* c, lept_value* v) {\n    switch (*c->json) {\n        case 'n':  return lept_parse_null(c, v);\n        case '\\0': return LEPT_PARSE_EXPECT_VALUE;\n        default:   return LEPT_PARSE_INVALID_VALUE;\n    }\n}\n\nint lept_parse(lept_value* v, const char* json) {\n    lept_context c;\n    assert(v != NULL);\n    c.json = json;\n    v->type = LEPT_NULL;\n    lept_parse_whitespace(&c);\n    return lept_parse_value(&c, v);\n}\n\nlept_type lept_get_type(const lept_value* v) {\n    assert(v != NULL);\n    return v->type;\n}\n"
  },
  {
    "path": "tutorial01/leptjson.h",
    "content": "#ifndef LEPTJSON_H__\n#define LEPTJSON_H__\n\ntypedef enum { LEPT_NULL, LEPT_FALSE, LEPT_TRUE, LEPT_NUMBER, LEPT_STRING, LEPT_ARRAY, LEPT_OBJECT } lept_type;\n\ntypedef struct {\n    lept_type type;\n}lept_value;\n\nenum {\n    LEPT_PARSE_OK = 0,\n    LEPT_PARSE_EXPECT_VALUE,\n    LEPT_PARSE_INVALID_VALUE,\n    LEPT_PARSE_ROOT_NOT_SINGULAR\n};\n\nint lept_parse(lept_value* v, const char* json);\n\nlept_type lept_get_type(const lept_value* v);\n\n#endif /* LEPTJSON_H__ */\n"
  },
  {
    "path": "tutorial01/test.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"leptjson.h\"\n\nstatic int main_ret = 0;\nstatic int test_count = 0;\nstatic int test_pass = 0;\n\n#define EXPECT_EQ_BASE(equality, expect, actual, format) \\\n    do {\\\n        test_count++;\\\n        if (equality)\\\n            test_pass++;\\\n        else {\\\n            fprintf(stderr, \"%s:%d: expect: \" format \" actual: \" format \"\\n\", __FILE__, __LINE__, expect, actual);\\\n            main_ret = 1;\\\n        }\\\n    } while(0)\n\n#define EXPECT_EQ_INT(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, \"%d\")\n\nstatic void test_parse_null() {\n    lept_value v;\n    v.type = LEPT_FALSE;\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"null\"));\n    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\n}\n\nstatic void test_parse_expect_value() {\n    lept_value v;\n\n    v.type = LEPT_FALSE;\n    EXPECT_EQ_INT(LEPT_PARSE_EXPECT_VALUE, lept_parse(&v, \"\"));\n    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\n\n    v.type = LEPT_FALSE;\n    EXPECT_EQ_INT(LEPT_PARSE_EXPECT_VALUE, lept_parse(&v, \" \"));\n    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\n}\n\nstatic void test_parse_invalid_value() {\n    lept_value v;\n    v.type = LEPT_FALSE;\n    EXPECT_EQ_INT(LEPT_PARSE_INVALID_VALUE, lept_parse(&v, \"nul\"));\n    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\n\n    v.type = LEPT_FALSE;\n    EXPECT_EQ_INT(LEPT_PARSE_INVALID_VALUE, lept_parse(&v, \"?\"));\n    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\n}\n\nstatic void test_parse_root_not_singular() {\n    lept_value v;\n    v.type = LEPT_FALSE;\n    EXPECT_EQ_INT(LEPT_PARSE_ROOT_NOT_SINGULAR, lept_parse(&v, \"null x\"));\n    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\n}\n\nstatic void test_parse() {\n    test_parse_null();\n    test_parse_expect_value();\n    test_parse_invalid_value();\n    test_parse_root_not_singular();\n}\n\nint main() {\n    test_parse();\n    printf(\"%d/%d (%3.2f%%) passed\\n\", test_pass, test_count, test_pass * 100.0 / test_count);\n    return main_ret;\n}\n"
  },
  {
    "path": "tutorial01/tutorial01.md",
    "content": "# 从零开始的 JSON 库教程（一）：启程\n\n* Milo Yip\n* 2016/9/15\n\n本文是[《从零开始的 JSON 库教程》](https://zhuanlan.zhihu.com/json-tutorial)的第一个单元。教程练习源代码位于 [json-tutorial](https://github.com/miloyip/json-tutorial)。\n\n本单元内容：\n\n1. [JSON 是什么](#1-json-是什么)\n2. [搭建编译环境](#2-搭建编译环境)\n3. [头文件与 API 设计](#3-头文件与-api-设计)\n4. [JSON 语法子集](#4-json-语法子集)\n5. [单元测试](#5-单元测试)\n6. [宏的编写技巧](#6-宏的编写技巧)\n7. [实现解析器](#7-实现解析器)\n8. [关于断言](#8-关于断言)\n9. [总结与练习](#9-总结与练习)\n10. [常见问答](#10-常见问答)\n\n## 1. JSON 是什么\n\nJSON（JavaScript Object Notation）是一个用于数据交换的文本格式，现时的标准为[ECMA-404](https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf)。\n\n虽然 JSON 源至于 JavaScript 语言，但它只是一种数据格式，可用于任何编程语言。现时具类似功能的格式有 XML、YAML，当中以 JSON 的语法最为简单。\n\n例如，一个动态网页想从服务器获得数据时，服务器从数据库查找数据，然后把数据转换成 JSON 文本格式：\n\n~~~js\n{\n    \"title\": \"Design Patterns\",\n    \"subtitle\": \"Elements of Reusable Object-Oriented Software\",\n    \"author\": [\n        \"Erich Gamma\",\n        \"Richard Helm\",\n        \"Ralph Johnson\",\n        \"John Vlissides\"\n    ],\n    \"year\": 2009,\n    \"weight\": 1.8,\n    \"hardcover\": true,\n    \"publisher\": {\n        \"Company\": \"Pearson Education\",\n        \"Country\": \"India\"\n    },\n    \"website\": null\n}\n~~~\n\n网页的脚本代码就可以把此 JSON 文本解析为内部的数据结构去使用。\n\n从此例子可看出，JSON 是树状结构，而 JSON 只包含 6 种数据类型：\n\n* null: 表示为 null\n* boolean: 表示为 true 或 false\n* number: 一般的浮点数表示方式，在下一单元详细说明\n* string: 表示为 \"...\"\n* array: 表示为 [ ... ]\n* object: 表示为 { ... }\n\n我们要实现的 JSON 库，主要是完成 3 个需求：\n\n1. 把 JSON 文本解析为一个树状数据结构（parse）。\n2. 提供接口访问该数据结构（access）。\n3. 把数据结构转换成 JSON 文本（stringify）。\n\n![requirement](images/requirement.png)\n\n我们会逐步实现这些需求。在本单元中，我们只实现最简单的 null 和 boolean 解析。\n\n## 2. 搭建编译环境\n\n我们要做的库是跨平台、跨编译器的，同学可使用任意平台进行练习。\n\n练习源代码位于 [json-tutorial](https://github.com/miloyip/json-tutorial)，当中 tutorial01 为本单元的练习代码。建议同学登记为 GitHub 用户，把项目 fork 一个自己的版本，然后在上面进行修改。不了解版本管理的同学，也可以按右方「Clone or download」按钮，简单下载一个 zip 文件。\n\n我们的 JSON 库名为 leptjson，代码文件只有 3 个：\n\n1. `leptjson.h`：leptjson 的头文件（header file），含有对外的类型和 API 函数声明。\n2. `leptjson.c`：leptjson 的实现文件（implementation file），含有内部的类型声明和函数实现。此文件会编译成库。\n3. `test.c`：我们使用测试驱动开发（test driven development, TDD）。此文件包含测试程序，需要链接 leptjson 库。\n\n为了方便跨平台开发，我们会使用一个现时最流行的软件配置工具 [CMake](https://cmake.org/)。\n\n在 Windows 下，下载安装 CMake 后，可以使用其 cmake-gui 程序：\n\n![cmake-gui](images/cmake-gui.png)\n\n先在 \"Where is the source code\" 选择 json-tutorial/tutorial01，再在 \"Where to build the binary\" 键入上一个目录加上 /build。\n\n按 Configure，选择编译器，然后按 Generate 便会生成 Visual Studio 的 .sln 和 .vcproj 等文件。注意这个 build 目录都是生成的文件，可以随时删除，也不用上传至仓库。\n\n在 OS X 下，建议安装 [Homebrew](https://brew.sh/)，然后在命令行键入：\n\n~~~\n$ brew install cmake\n$ cd github/json-tutorial/tutorial01\n$ mkdir build\n$ cd build\n$ cmake -DCMAKE_BUILD_TYPE=Debug ..\n$ make\n~~~\n\n这样会使用 GNU make 来生成项目，把 Debug 改成 Release 就会生成 Release 配置的 makefile。\n\n若你喜欢的话，CMake 也可以生成 Xcode 项目：\n\n~~~\n$ cmake -G Xcode ..\n$ open leptjson_test.xcodeproj\n~~~\n\n而在 Ubuntu 下，可使用 `apt-get` 来安装：\n\n~~~\n$ apt-get install cmake\n~~~\n\n无论使用什么平台及编译环境，编译运行后会出现：\n\n~~~\n$ ./leptjson_test\n/Users/miloyip/github/json-tutorial/tutorial01/test.c:56: expect: 3 actual: 0\n11/12 (91.67%) passed\n~~~\n\n若看到类似以上的结果，说明已成功搭建编译环境，我们可以去看看那几个代码文件的内容了。\n\n## 3. 头文件与 API 设计\n\nC 语言有头文件的概念，需要使用 `#include`去引入头文件中的类型声明和函数声明。但由于头文件也可以 `#include` 其他头文件，为避免重复声明，通常会利用宏加入 include 防范（include guard）：\n\n~~~c\n#ifndef LEPTJSON_H__\n#define LEPTJSON_H__\n\n/* ... */\n\n#endif /* LEPTJSON_H__ */\n~~~\n\n宏的名字必须是唯一的，通常习惯以 `_H__` 作为后缀。由于 leptjson 只有一个头文件，可以简单命名为 `LEPTJSON_H__`。如果项目有多个文件或目录结构，可以用 `项目名称_目录_文件名称_H__` 这种命名方式。\n\n如前所述，JSON 中有 6 种数据类型，如果把 true 和 false 当作两个类型就是 7 种，我们为此声明一个枚举类型（enumeration type）：\n\n~~~c\ntypedef enum { LEPT_NULL, LEPT_FALSE, LEPT_TRUE, LEPT_NUMBER, LEPT_STRING, LEPT_ARRAY, LEPT_OBJECT } lept_type;\n~~~\n\n因为 C 语言没有 C++ 的命名空间（namespace）功能，一般会使用项目的简写作为标识符的前缀。通常枚举值用全大写（如 `LEPT_NULL`），而类型及函数则用小写（如 `lept_type`）。\n\n接下来，我们声明 JSON 的数据结构。JSON 是一个树形结构，我们最终需要实现一个树的数据结构，每个节点使用 `lept_value` 结构体表示，我们会称它为一个 JSON 值（JSON value）。\n在此单元中，我们只需要实现 null, true 和 false 的解析，因此该结构体只需要存储一个 lept_type。之后的单元会逐步加入其他数据。\n\n~~~c\ntypedef struct {\n    lept_type type;\n}lept_value;\n~~~\n\nC 语言的结构体是以 `struct X {}` 形式声明的，定义变量时也要写成 `struct X x;`。为方便使用，上面的代码使用了 `typedef`。\n\n然后，我们现在只需要两个 API 函数，一个是解析 JSON：\n\n~~~c\nint lept_parse(lept_value* v, const char* json);\n~~~\n\n传入的 JSON 文本是一个 C 字符串（空结尾字符串／null-terminated string），由于我们不应该改动这个输入字符串，所以使用 `const char*` 类型。\n\n另一注意点是，传入的根节点指针 v 是由使用方负责分配的，所以一般用法是：\n\n~~~c\nlept_value v;\nconst char json[] = ...;\nint ret = lept_parse(&v, json);\n~~~\n\n返回值是以下这些枚举值，无错误会返回 `LEPT_PARSE_OK`，其他值在下节解释。\n\n~~~c\nenum {\n    LEPT_PARSE_OK = 0,\n    LEPT_PARSE_EXPECT_VALUE,\n    LEPT_PARSE_INVALID_VALUE,\n    LEPT_PARSE_ROOT_NOT_SINGULAR\n};\n~~~\n\n现时我们只需要一个访问结果的函数，就是获取其类型：\n\n~~~c\nlept_type lept_get_type(const lept_value* v);\n~~~\n\n## 4. JSON 语法子集\n\n下面是此单元的 JSON 语法子集，使用 [RFC7159](https://tools.ietf.org/html/rfc7159) 中的 [ABNF](https://tools.ietf.org/html/rfc5234) 表示：\n\n~~~\nJSON-text = ws value ws\nws = *(%x20 / %x09 / %x0A / %x0D)\nvalue = null / false / true \nnull  = \"null\"\nfalse = \"false\"\ntrue  = \"true\"\n~~~\n\n当中 `%xhh` 表示以 16 进制表示的字符，`/` 是多选一，`*` 是零或多个，`()` 用于分组。\n\n那么第一行的意思是，JSON 文本由 3 部分组成，首先是空白（whitespace），接着是一个值，最后是空白。\n\n第二行告诉我们，所谓空白，是由零或多个空格符（space U+0020）、制表符（tab U+0009）、换行符（LF U+000A）、回车符（CR U+000D）所组成。\n\n第三行是说，我们现时的值只可以是 null、false 或 true，它们分别有对应的字面值（literal）。\n\n我们的解析器应能判断输入是否一个合法的 JSON。如果输入的 JSON 不合符这个语法，我们要产生对应的错误码，方便使用者追查问题。\n\n在这个 JSON 语法子集下，我们定义 3 种错误码：\n\n* 若一个 JSON 只含有空白，传回 `LEPT_PARSE_EXPECT_VALUE`。\n* 若一个值之后，在空白之后还有其他字符，传回 `LEPT_PARSE_ROOT_NOT_SINGULAR`。\n* 若值不是那三种字面值，传回 `LEPT_PARSE_INVALID_VALUE`。\n\n## 5. 单元测试\n\n许多同学在做练习题时，都是以 `printf`／`cout` 打印结果，再用肉眼对比结果是否乎合预期。但当软件项目越来越复杂，这个做法会越来越低效。一般我们会采用自动的测试方式，例如单元测试（unit testing）。单元测试也能确保其他人修改代码后，原来的功能维持正确（这称为回归测试／regression testing）。\n\n常用的单元测试框架有 xUnit 系列，如 C++ 的 [Google Test](https://github.com/google/googletest)、C# 的 [NUnit](https://www.nunit.org/)。我们为了简单起见，会编写一个极简单的单元测试方式。\n\n一般来说，软件开发是以周期进行的。例如，加入一个功能，再写关于该功能的单元测试。但也有另一种软件开发方法论，称为测试驱动开发（test-driven development, TDD），它的主要循环步骤是：\n\n1. 加入一个测试。\n2. 运行所有测试，新的测试应该会失败。\n3. 编写实现代码。\n4. 运行所有测试，若有测试失败回到3。\n5. 重构代码。\n6. 回到 1。\n\nTDD 是先写测试，再实现功能。好处是实现只会刚好满足测试，而不会写了一些不需要的代码，或是没有被测试的代码。\n\n但无论我们是采用 TDD，或是先实现后测试，都应尽量加入足够覆盖率的单元测试。\n\n回到 leptjson 项目，`test.c` 包含了一个极简的单元测试框架：\n\n~~~c\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"leptjson.h\"\n\nstatic int main_ret = 0;\nstatic int test_count = 0;\nstatic int test_pass = 0;\n\n#define EXPECT_EQ_BASE(equality, expect, actual, format) \\\n    do {\\\n        test_count++;\\\n        if (equality)\\\n            test_pass++;\\\n        else {\\\n            fprintf(stderr, \"%s:%d: expect: \" format \" actual: \" format \"\\n\", __FILE__, __LINE__, expect, actual);\\\n            main_ret = 1;\\\n        }\\\n    } while(0)\n\n#define EXPECT_EQ_INT(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, \"%d\")\n\nstatic void test_parse_null() {\n    lept_value v;\n    v.type = LEPT_TRUE;\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"null\"));\n    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\n}\n\n/* ... */\n\nstatic void test_parse() {\n    test_parse_null();\n    /* ... */\n}\n\nint main() {\n    test_parse();\n    printf(\"%d/%d (%3.2f%%) passed\\n\", test_pass, test_count, test_pass * 100.0 / test_count);\n    return main_ret;\n}\n~~~\n\n现时只提供了一个 `EXPECT_EQ_INT(expect, actual)` 的宏，每次使用这个宏时，如果 expect != actual（预期值不等于实际值），便会输出错误信息。\n若按照 TDD 的步骤，我们先写一个测试，如上面的 `test_parse_null()`，而 `lept_parse()` 只返回 `LEPT_PARSE_OK`：\n\n~~~\n/Users/miloyip/github/json-tutorial/tutorial01/test.c:27: expect: 0 actual: 1\n1/2 (50.00%) passed\n~~~\n\n第一个返回 `LEPT_PARSE_OK`，所以是通过的。第二个测试因为 `lept_parse()` 没有把 `v.type` 改成 `LEPT_NULL`，造成失败。我们再实现 `lept_parse()` 令到它能通过测试。\n\n然而，完全按照 TDD 的步骤来开发，是会减慢开发进程。所以我个人会在这两种极端的工作方式取平衡。通常会在设计 API 后，先写部分测试代码，再写满足那些测试的实现。\n\n## 6. 宏的编写技巧\n\n有些同学可能不了解 `EXPECT_EQ_BASE` 宏的编写技巧，简单说明一下。反斜线代表该行未结束，会串接下一行。而如果宏里有多过一个语句（statement），就需要用 `do { /*...*/ } while(0)` 包裹成单个语句，否则会有如下的问题：\n\n~~~c\n#define M() a(); b()\nif (cond)\n    M();\nelse\n    c();\n\n/* 预处理后 */\n\nif (cond)\n    a(); b(); /* b(); 在 if 之外     */\nelse          /* <- else 缺乏对应 if */\n    c();\n~~~\n\n只用 `{ }` 也不行：\n\n~~~c\n#define M() { a(); b(); }\n\n/* 预处理后 */\n\nif (cond)\n    { a(); b(); }; /* 最后的分号代表 if 语句结束 */\nelse               /* else 缺乏对应 if */\n    c();\n~~~\n\n用 do while 就行了：\n\n~~~c\n#define M() do { a(); b(); } while(0)\n\n/* 预处理后 */\n\nif (cond)\n    do { a(); b(); } while(0);\nelse\n    c();\n~~~\n\n## 7. 实现解析器\n\n有了 API 的设计、单元测试，终于要实现解析器了。\n\n首先为了减少解析函数之间传递多个参数，我们把这些数据都放进一个 `lept_context` 结构体：\n\n~~~c\ntypedef struct {\n    const char* json;\n}lept_context;\n\n/* ... */\n\n/* 提示：这里应该是 JSON-text = ws value ws */\n/* 以下实现没处理最后的 ws 和 LEPT_PARSE_ROOT_NOT_SINGULAR */\nint lept_parse(lept_value* v, const char* json) {\n    lept_context c;\n    assert(v != NULL);\n    c.json = json;\n    v->type = LEPT_NULL;\n    lept_parse_whitespace(&c);\n    return lept_parse_value(&c, v);\n}\n~~~\n\n暂时我们只储存 json 字符串当前位置，之后的单元我们需要加入更多内容。\n\n若 `lept_parse()` 失败，会把 `v` 设为 `null` 类型，所以这里先把它设为 `null`，让 `lept_parse_value()` 写入解析出来的根值。\n\nleptjson 是一个手写的递归下降解析器（recursive descent parser）。由于 JSON 语法特别简单，我们不需要写分词器（tokenizer），只需检测下一个字符，便可以知道它是哪种类型的值，然后调用相关的分析函数。对于完整的 JSON 语法，跳过空白后，只需检测当前字符：\n\n* n ➔ null\n* t ➔ true\n* f ➔ false\n* \" ➔ string\n* 0-9/- ➔ number\n* [ ➔ array\n* { ➔ object\n\n所以，我们可以按照 JSON 语法一节的 EBNF 简单翻译成解析函数：\n\n~~~c\n#define EXPECT(c, ch) do { assert(*c->json == (ch)); c->json++; } while(0)\n\n/* ws = *(%x20 / %x09 / %x0A / %x0D) */\nstatic void lept_parse_whitespace(lept_context* c) {\n    const char *p = c->json;\n    while (*p == ' ' || *p == '\\t' || *p == '\\n' || *p == '\\r')\n        p++;\n    c->json = p;\n}\n\n/* null  = \"null\" */\nstatic int lept_parse_null(lept_context* c, lept_value* v) {\n    EXPECT(c, 'n');\n    if (c->json[0] != 'u' || c->json[1] != 'l' || c->json[2] != 'l')\n        return LEPT_PARSE_INVALID_VALUE;\n    c->json += 3;\n    v->type = LEPT_NULL;\n    return LEPT_PARSE_OK;\n}\n\n/* value = null / false / true */\n/* 提示：下面代码没处理 false / true，将会是练习之一 */\nstatic int lept_parse_value(lept_context* c, lept_value* v) {\n    switch (*c->json) {\n        case 'n':  return lept_parse_null(c, v);\n        case '\\0': return LEPT_PARSE_EXPECT_VALUE;\n        default:   return LEPT_PARSE_INVALID_VALUE;\n    }\n}\n~~~\n\n由于 `lept_parse_whitespace()` 是不会出现错误的，返回类型为 `void`。其它的解析函数会返回错误码，传递至顶层。\n\n## 8. 关于断言\n\n断言（assertion）是 C 语言中常用的防御式编程方式，减少编程错误。最常用的是在函数开始的地方，检测所有参数。有时候也可以在调用函数后，检查上下文是否正确。\n\nC 语言的标准库含有 [`assert()`](https://en.cppreference.com/w/c/error/assert) 这个宏（需 `#include <assert.h>`），提供断言功能。当程序以 release 配置编译时（定义了 `NDEBUG` 宏），`assert()` 不会做检测；而当在 debug 配置时（没定义 `NDEBUG` 宏），则会在运行时检测 `assert(cond)` 中的条件是否为真（非 0），断言失败会直接令程序崩溃。\n\n例如上面的 `lept_parse_null()` 开始时，当前字符应该是 `'n'`，所以我们使用一个宏 `EXPECT(c, ch)` 进行断言，并跳到下一字符。\n\n初使用断言的同学，可能会错误地把含[副作用](https://en.wikipedia.org/wiki/Side_effect_(computer_science))的代码放在 `assert()` 中：\n\n~~~c\nassert(x++ == 0); /* 这是错误的! */\n~~~\n\n这样会导致 debug 和 release 版的行为不一样。\n\n另一个问题是，初学者可能会难于分辨何时使用断言，何时处理运行时错误（如返回错误值或在 C++ 中抛出异常）。简单的答案是，如果那个错误是由于程序员错误编码所造成的（例如传入不合法的参数），那么应用断言；如果那个错误是程序员无法避免，而是由运行时的环境所造成的，就要处理运行时错误（例如开启文件失败）。\n\n## 9. 总结与练习\n\n本文介绍了如何配置一个编程环境，单元测试的重要性，以至于一个 JSON 解析器的子集实现。如果你读到这里，还未动手，建议你快点试一下。以下是本单元的练习，很容易的，但我也会在稍后发出解答篇。\n\n1. 修正关于 `LEPT_PARSE_ROOT_NOT_SINGULAR` 的单元测试，若 json 在一个值之后，空白之后还有其它字符，则要返回 `LEPT_PARSE_ROOT_NOT_SINGULAR`。\n2. 参考 `test_parse_null()`，加入 `test_parse_true()`、`test_parse_false()` 单元测试。\n3. 参考 `lept_parse_null()` 的实现和调用方，解析 true 和 false 值。\n\n## 10. 常见问答\n\n1. 为什么把例子命名为 leptjson？\n\n   来自于标准模型中的轻子（lepton），意为很轻量的 JSON 库。另外，建议大家为项目命名时，先 google 一下是否够独特，有很多同名的话搜寻时难以命中。\n\n2. 为什么使用宏而不用函数或内联函数？\n\n   因为这个测试框架使用了 `__LINE__` 这个编译器提供的宏，代表编译时该行的行号。如果用函数或内联函数，每次的行号便都会相同。另外，内联函数是 C99 的新增功能，本教程使用 C89。\n\n其他常见问答将会从评论中整理。\n"
  },
  {
    "path": "tutorial01_answer/CMakeLists.txt",
    "content": "cmake_minimum_required (VERSION 2.6)\nproject (leptjson_test C)\n\nif (CMAKE_C_COMPILER_ID MATCHES \"GNU|Clang\")\n    set(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -ansi -pedantic -Wall\")\nendif()\n\nadd_library(leptjson leptjson.c)\nadd_executable(leptjson_test test.c)\ntarget_link_libraries(leptjson_test leptjson)\n"
  },
  {
    "path": "tutorial01_answer/leptjson.c",
    "content": "#include \"leptjson.h\"\n#include <assert.h>  /* assert() */\n#include <stdlib.h>  /* NULL */\n\n#define EXPECT(c, ch)       do { assert(*c->json == (ch)); c->json++; } while(0)\n\ntypedef struct {\n    const char* json;\n}lept_context;\n\nstatic void lept_parse_whitespace(lept_context* c) {\n    const char *p = c->json;\n    while (*p == ' ' || *p == '\\t' || *p == '\\n' || *p == '\\r')\n        p++;\n    c->json = p;\n}\n\nstatic int lept_parse_true(lept_context* c, lept_value* v) {\n    EXPECT(c, 't');\n    if (c->json[0] != 'r' || c->json[1] != 'u' || c->json[2] != 'e')\n        return LEPT_PARSE_INVALID_VALUE;\n    c->json += 3;\n    v->type = LEPT_TRUE;\n    return LEPT_PARSE_OK;\n}\n\nstatic int lept_parse_false(lept_context* c, lept_value* v) {\n    EXPECT(c, 'f');\n    if (c->json[0] != 'a' || c->json[1] != 'l' || c->json[2] != 's' || c->json[3] != 'e')\n        return LEPT_PARSE_INVALID_VALUE;\n    c->json += 4;\n    v->type = LEPT_FALSE;\n    return LEPT_PARSE_OK;\n}\n\nstatic int lept_parse_null(lept_context* c, lept_value* v) {\n    EXPECT(c, 'n');\n    if (c->json[0] != 'u' || c->json[1] != 'l' || c->json[2] != 'l')\n        return LEPT_PARSE_INVALID_VALUE;\n    c->json += 3;\n    v->type = LEPT_NULL;\n    return LEPT_PARSE_OK;\n}\n\nstatic int lept_parse_value(lept_context* c, lept_value* v) {\n    switch (*c->json) {\n        case 't':  return lept_parse_true(c, v);\n        case 'f':  return lept_parse_false(c, v);\n        case 'n':  return lept_parse_null(c, v);\n        case '\\0': return LEPT_PARSE_EXPECT_VALUE;\n        default:   return LEPT_PARSE_INVALID_VALUE;\n    }\n}\n\nint lept_parse(lept_value* v, const char* json) {\n    lept_context c;\n    int ret;\n    assert(v != NULL);\n    c.json = json;\n    v->type = LEPT_NULL;\n    lept_parse_whitespace(&c);\n    if ((ret = lept_parse_value(&c, v)) == LEPT_PARSE_OK) {\n        lept_parse_whitespace(&c);\n        if (*c.json != '\\0')\n            ret = LEPT_PARSE_ROOT_NOT_SINGULAR;\n    }\n    return ret;\n}\n\nlept_type lept_get_type(const lept_value* v) {\n    assert(v != NULL);\n    return v->type;\n}\n"
  },
  {
    "path": "tutorial01_answer/leptjson.h",
    "content": "#ifndef LEPTJSON_H__\n#define LEPTJSON_H__\n\ntypedef enum { LEPT_NULL, LEPT_FALSE, LEPT_TRUE, LEPT_NUMBER, LEPT_STRING, LEPT_ARRAY, LEPT_OBJECT } lept_type;\n\ntypedef struct {\n    lept_type type;\n}lept_value;\n\nenum {\n    LEPT_PARSE_OK = 0,\n    LEPT_PARSE_EXPECT_VALUE,\n    LEPT_PARSE_INVALID_VALUE,\n    LEPT_PARSE_ROOT_NOT_SINGULAR\n};\n\nint lept_parse(lept_value* v, const char* json);\n\nlept_type lept_get_type(const lept_value* v);\n\n#endif /* LEPTJSON_H__ */\n"
  },
  {
    "path": "tutorial01_answer/test.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"leptjson.h\"\n\nstatic int main_ret = 0;\nstatic int test_count = 0;\nstatic int test_pass = 0;\n\n#define EXPECT_EQ_BASE(equality, expect, actual, format) \\\n    do {\\\n        test_count++;\\\n        if (equality)\\\n            test_pass++;\\\n        else {\\\n            fprintf(stderr, \"%s:%d: expect: \" format \" actual: \" format \"\\n\", __FILE__, __LINE__, expect, actual);\\\n            main_ret = 1;\\\n        }\\\n    } while(0)\n\n#define EXPECT_EQ_INT(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, \"%d\")\n\nstatic void test_parse_null() {\n    lept_value v;\n    v.type = LEPT_FALSE;\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"null\"));\n    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\n}\n\nstatic void test_parse_true() {\n    lept_value v;\n    v.type = LEPT_FALSE;\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"true\"));\n    EXPECT_EQ_INT(LEPT_TRUE, lept_get_type(&v));\n}\n\nstatic void test_parse_false() {\n    lept_value v;\n    v.type = LEPT_TRUE;\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"false\"));\n    EXPECT_EQ_INT(LEPT_FALSE, lept_get_type(&v));\n}\n\nstatic void test_parse_expect_value() {\n    lept_value v;\n\n    v.type = LEPT_FALSE;\n    EXPECT_EQ_INT(LEPT_PARSE_EXPECT_VALUE, lept_parse(&v, \"\"));\n    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\n\n    v.type = LEPT_FALSE;\n    EXPECT_EQ_INT(LEPT_PARSE_EXPECT_VALUE, lept_parse(&v, \" \"));\n    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\n}\n\nstatic void test_parse_invalid_value() {\n    lept_value v;\n    v.type = LEPT_FALSE;\n    EXPECT_EQ_INT(LEPT_PARSE_INVALID_VALUE, lept_parse(&v, \"nul\"));\n    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\n\n    v.type = LEPT_FALSE;\n    EXPECT_EQ_INT(LEPT_PARSE_INVALID_VALUE, lept_parse(&v, \"?\"));\n    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\n}\n\nstatic void test_parse_root_not_singular() {\n    lept_value v;\n    v.type = LEPT_FALSE;\n    EXPECT_EQ_INT(LEPT_PARSE_ROOT_NOT_SINGULAR, lept_parse(&v, \"null x\"));\n    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\n}\n\nstatic void test_parse() {\n    test_parse_null();\n    test_parse_true();\n    test_parse_false();\n    test_parse_expect_value();\n    test_parse_invalid_value();\n    test_parse_root_not_singular();\n}\n\nint main() {\n    test_parse();\n    printf(\"%d/%d (%3.2f%%) passed\\n\", test_pass, test_count, test_pass * 100.0 / test_count);\n    return main_ret;\n}\n"
  },
  {
    "path": "tutorial01_answer/tutorial01_answer.md",
    "content": "# 从零开始的 JSON 库教程（一）：启程解答篇\n\n* Milo Yip\n* 2016/9/17\n\n本文是[《从零开始的 JSON 库教程》](https://zhuanlan.zhihu.com/json-tutorial)的第一个单元解答篇。解答代码位于 [json-tutorial/tutorial01_answer](https://github.com/miloyip/json-tutorial/blob/master/tutorial01_answer/)。\n\n## 1. 修正 LEPT_PARSE_ROOT_NOT_SINGULAR\n\n单元测试失败的是这一行：\n\n~~~c\nEXPECT_EQ_INT(LEPT_PARSE_ROOT_NOT_SINGULAR, lept_parse(&v, \"null x\"));\n~~~\n\n我们从 JSON 语法发现，JSON 文本应该有 3 部分：\n\n~~~\nJSON-text = ws value ws\n~~~\n\n但原来的 `lept_parse()` 只处理了前两部分。我们只需要加入第三部分，解析空白，然后检查 JSON 文本是否完结：\n\n~~~c\nint lept_parse(lept_value* v, const char* json) {\n    lept_context c;\n    int ret;\n    assert(v != NULL);\n    c.json = json;\n    v->type = LEPT_NULL;\n    lept_parse_whitespace(&c);\n    if ((ret = lept_parse_value(&c, v)) == LEPT_PARSE_OK) {\n        lept_parse_whitespace(&c);\n        if (*c.json != '\\0')\n            ret = LEPT_PARSE_ROOT_NOT_SINGULAR;\n    }\n    return ret;\n}\n~~~\n\n有一些 JSON 解析器完整解析一个值之后就会顺利返回，这是不符合标准的。但有时候也有另一种需求，文本中含多个 JSON 或其他文本串接在一起，希望当完整解析一个值之后就停下来。因此，有一些 JSON 解析器会提供这种选项，例如 RapidJSON 的 `kParseStopWhenDoneFlag`。\n\n## 2. true/false 单元测试\n\n此问题很简单，只需参考 `test_parse_null()` 加入两个测试函数：\n\n~~~c\nstatic void test_parse_true() {\n    lept_value v;\n    v.type = LEPT_FALSE;\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"true\"));\n    EXPECT_EQ_INT(LEPT_TRUE, lept_get_type(&v));\n}\n\nstatic void test_parse_false() {\n    lept_value v;\n    v.type = LEPT_TRUE;\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"false\"));\n    EXPECT_EQ_INT(LEPT_FALSE, lept_get_type(&v));\n}\n\nstatic void test_parse() {\n    test_parse_null();\n    test_parse_true();\n    test_parse_false();\n    test_parse_expect_value();\n    test_parse_invalid_value();\n    test_parse_root_not_singular();\n}\n~~~\n\n但要记得在上一级的测试函数 `test_parse()` 调用这函数，否则会不起作用。还好如果我们记得用 `static` 修饰这两个函数，编译器会发出警告：\n\n~~~\ntest.c:30:13: warning: unused function 'test_parse_true' [-Wunused-function]\nstatic void test_parse_true() {\n            ^\n~~~\n\n因为 static 函数的意思是指，该函数只作用于编译单元中，那么没有被调用时，编译器是能发现的。\n\n### 3. true/false 解析\n\n这部分很简单，只要参考 `lept_parse_null()`，再写两个函数，然后在 `lept_parse_value` 按首字符分派。\n\n~~~c\nstatic int lept_parse_true(lept_context* c, lept_value* v) {\n    EXPECT(c, 't');\n    if (c->json[0] != 'r' || c->json[1] != 'u' || c->json[2] != 'e')\n        return LEPT_PARSE_INVALID_VALUE;\n    c->json += 3;\n    v->type = LEPT_TRUE;\n    return LEPT_PARSE_OK;\n}\n\nstatic int lept_parse_false(lept_context* c, lept_value* v) {\n    EXPECT(c, 'f');\n    if (c->json[0] != 'a' || c->json[1] != 'l' || c->json[2] != 's' || c->json[3] != 'e')\n        return LEPT_PARSE_INVALID_VALUE;\n    c->json += 4;\n    v->type = LEPT_FALSE;\n    return LEPT_PARSE_OK;\n}\n\nstatic int lept_parse_value(lept_context* c, lept_value* v) {\n    switch (*c->json) {\n        case 't':  return lept_parse_true(c, v);\n        case 'f':  return lept_parse_false(c, v);\n        case 'n':  return lept_parse_null(c, v);\n        case '\\0': return LEPT_PARSE_EXPECT_VALUE;\n        default:   return LEPT_PARSE_INVALID_VALUE;\n    }\n}\n~~~\n\n其实这 3 种类型都是解析字面量，可以使用单一个函数实现，例如用这种方式调用：\n\n~~~c\n        case 'n': return lept_parse_literal(c, v, \"null\", LEPT_NULL);\n~~~\n\n这样可以减少一些重复代码，不过可能有少许额外性能开销。\n\n## 4. 总结\n\n如果你能完成这个练习，恭喜你！我想你通过亲自动手，会对教程里所说的有更深入的理解。如果你遇到问题，有不理解的地方，或是有建议，都欢迎在评论或 [issue](https://github.com/miloyip/json-tutorial/issues) 中提出，让所有人一起讨论。\n\n下一单元是和数字类型相关，敬请期待。\n"
  },
  {
    "path": "tutorial02/CMakeLists.txt",
    "content": "cmake_minimum_required (VERSION 2.6)\nproject (leptjson_test C)\n\nif (CMAKE_C_COMPILER_ID MATCHES \"GNU|Clang\")\n    set(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -ansi -pedantic -Wall\")\nendif()\n\nadd_library(leptjson leptjson.c)\nadd_executable(leptjson_test test.c)\ntarget_link_libraries(leptjson_test leptjson)\n"
  },
  {
    "path": "tutorial02/leptjson.c",
    "content": "#include \"leptjson.h\"\n#include <assert.h>  /* assert() */\n#include <stdlib.h>  /* NULL, strtod() */\n\n#define EXPECT(c, ch)       do { assert(*c->json == (ch)); c->json++; } while(0)\n\ntypedef struct {\n    const char* json;\n}lept_context;\n\nstatic void lept_parse_whitespace(lept_context* c) {\n    const char *p = c->json;\n    while (*p == ' ' || *p == '\\t' || *p == '\\n' || *p == '\\r')\n        p++;\n    c->json = p;\n}\n\nstatic int lept_parse_true(lept_context* c, lept_value* v) {\n    EXPECT(c, 't');\n    if (c->json[0] != 'r' || c->json[1] != 'u' || c->json[2] != 'e')\n        return LEPT_PARSE_INVALID_VALUE;\n    c->json += 3;\n    v->type = LEPT_TRUE;\n    return LEPT_PARSE_OK;\n}\n\nstatic int lept_parse_false(lept_context* c, lept_value* v) {\n    EXPECT(c, 'f');\n    if (c->json[0] != 'a' || c->json[1] != 'l' || c->json[2] != 's' || c->json[3] != 'e')\n        return LEPT_PARSE_INVALID_VALUE;\n    c->json += 4;\n    v->type = LEPT_FALSE;\n    return LEPT_PARSE_OK;\n}\n\nstatic int lept_parse_null(lept_context* c, lept_value* v) {\n    EXPECT(c, 'n');\n    if (c->json[0] != 'u' || c->json[1] != 'l' || c->json[2] != 'l')\n        return LEPT_PARSE_INVALID_VALUE;\n    c->json += 3;\n    v->type = LEPT_NULL;\n    return LEPT_PARSE_OK;\n}\n\nstatic int lept_parse_number(lept_context* c, lept_value* v) {\n    char* end;\n    /* \\TODO validate number */\n    v->n = strtod(c->json, &end);\n    if (c->json == end)\n        return LEPT_PARSE_INVALID_VALUE;\n    c->json = end;\n    v->type = LEPT_NUMBER;\n    return LEPT_PARSE_OK;\n}\n\nstatic int lept_parse_value(lept_context* c, lept_value* v) {\n    switch (*c->json) {\n        case 't':  return lept_parse_true(c, v);\n        case 'f':  return lept_parse_false(c, v);\n        case 'n':  return lept_parse_null(c, v);\n        default:   return lept_parse_number(c, v);\n        case '\\0': return LEPT_PARSE_EXPECT_VALUE;\n    }\n}\n\nint lept_parse(lept_value* v, const char* json) {\n    lept_context c;\n    int ret;\n    assert(v != NULL);\n    c.json = json;\n    v->type = LEPT_NULL;\n    lept_parse_whitespace(&c);\n    if ((ret = lept_parse_value(&c, v)) == LEPT_PARSE_OK) {\n        lept_parse_whitespace(&c);\n        if (*c.json != '\\0') {\n            v->type = LEPT_NULL;\n            ret = LEPT_PARSE_ROOT_NOT_SINGULAR;\n        }\n    }\n    return ret;\n}\n\nlept_type lept_get_type(const lept_value* v) {\n    assert(v != NULL);\n    return v->type;\n}\n\ndouble lept_get_number(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_NUMBER);\n    return v->n;\n}\n"
  },
  {
    "path": "tutorial02/leptjson.h",
    "content": "#ifndef LEPTJSON_H__\n#define LEPTJSON_H__\n\ntypedef enum { LEPT_NULL, LEPT_FALSE, LEPT_TRUE, LEPT_NUMBER, LEPT_STRING, LEPT_ARRAY, LEPT_OBJECT } lept_type;\n\ntypedef struct {\n    double n;\n    lept_type type;\n}lept_value;\n\nenum {\n    LEPT_PARSE_OK = 0,\n    LEPT_PARSE_EXPECT_VALUE,\n    LEPT_PARSE_INVALID_VALUE,\n    LEPT_PARSE_ROOT_NOT_SINGULAR,\n    LEPT_PARSE_NUMBER_TOO_BIG\n};\n\nint lept_parse(lept_value* v, const char* json);\n\nlept_type lept_get_type(const lept_value* v);\n\ndouble lept_get_number(const lept_value* v);\n\n#endif /* LEPTJSON_H__ */\n"
  },
  {
    "path": "tutorial02/test.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"leptjson.h\"\n\nstatic int main_ret = 0;\nstatic int test_count = 0;\nstatic int test_pass = 0;\n\n#define EXPECT_EQ_BASE(equality, expect, actual, format) \\\n    do {\\\n        test_count++;\\\n        if (equality)\\\n            test_pass++;\\\n        else {\\\n            fprintf(stderr, \"%s:%d: expect: \" format \" actual: \" format \"\\n\", __FILE__, __LINE__, expect, actual);\\\n            main_ret = 1;\\\n        }\\\n    } while(0)\n\n#define EXPECT_EQ_INT(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, \"%d\")\n#define EXPECT_EQ_DOUBLE(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, \"%.17g\")\n\nstatic void test_parse_null() {\n    lept_value v;\n    v.type = LEPT_FALSE;\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"null\"));\n    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\n}\n\nstatic void test_parse_true() {\n    lept_value v;\n    v.type = LEPT_FALSE;\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"true\"));\n    EXPECT_EQ_INT(LEPT_TRUE, lept_get_type(&v));\n}\n\nstatic void test_parse_false() {\n    lept_value v;\n    v.type = LEPT_TRUE;\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"false\"));\n    EXPECT_EQ_INT(LEPT_FALSE, lept_get_type(&v));\n}\n\n#define TEST_NUMBER(expect, json)\\\n    do {\\\n        lept_value v;\\\n        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(&v));\\\n        EXPECT_EQ_DOUBLE(expect, lept_get_number(&v));\\\n    } while(0)\n\nstatic void test_parse_number() {\n    TEST_NUMBER(0.0, \"0\");\n    TEST_NUMBER(0.0, \"-0\");\n    TEST_NUMBER(0.0, \"-0.0\");\n    TEST_NUMBER(1.0, \"1\");\n    TEST_NUMBER(-1.0, \"-1\");\n    TEST_NUMBER(1.5, \"1.5\");\n    TEST_NUMBER(-1.5, \"-1.5\");\n    TEST_NUMBER(3.1416, \"3.1416\");\n    TEST_NUMBER(1E10, \"1E10\");\n    TEST_NUMBER(1e10, \"1e10\");\n    TEST_NUMBER(1E+10, \"1E+10\");\n    TEST_NUMBER(1E-10, \"1E-10\");\n    TEST_NUMBER(-1E10, \"-1E10\");\n    TEST_NUMBER(-1e10, \"-1e10\");\n    TEST_NUMBER(-1E+10, \"-1E+10\");\n    TEST_NUMBER(-1E-10, \"-1E-10\");\n    TEST_NUMBER(1.234E+10, \"1.234E+10\");\n    TEST_NUMBER(1.234E-10, \"1.234E-10\");\n    TEST_NUMBER(0.0, \"1e-10000\"); /* must underflow */\n}\n\n#define TEST_ERROR(error, json)\\\n    do {\\\n        lept_value v;\\\n        v.type = LEPT_FALSE;\\\n        EXPECT_EQ_INT(error, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\\\n    } while(0)\n\nstatic void test_parse_expect_value() {\n    TEST_ERROR(LEPT_PARSE_EXPECT_VALUE, \"\");\n    TEST_ERROR(LEPT_PARSE_EXPECT_VALUE, \" \");\n}\n\nstatic void test_parse_invalid_value() {\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"nul\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"?\");\n\n#if 0\n    /* invalid number */\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"+0\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"+1\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \".123\"); /* at least one digit before '.' */\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"1.\");   /* at least one digit after '.' */\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"INF\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"inf\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"NAN\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"nan\");\n#endif\n}\n\nstatic void test_parse_root_not_singular() {\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"null x\");\n\n#if 0\n    /* invalid number */\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0123\"); /* after zero should be '.' , 'E' , 'e' or nothing */\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0x0\");\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0x123\");\n#endif\n}\n\nstatic void test_parse_number_too_big() {\n#if 0\n    TEST_ERROR(LEPT_PARSE_NUMBER_TOO_BIG, \"1e309\");\n    TEST_ERROR(LEPT_PARSE_NUMBER_TOO_BIG, \"-1e309\");\n#endif\n}\n\nstatic void test_parse() {\n    test_parse_null();\n    test_parse_true();\n    test_parse_false();\n    test_parse_number();\n    test_parse_expect_value();\n    test_parse_invalid_value();\n    test_parse_root_not_singular();\n    test_parse_number_too_big();\n}\n\nint main() {\n    test_parse();\n    printf(\"%d/%d (%3.2f%%) passed\\n\", test_pass, test_count, test_pass * 100.0 / test_count);\n    return main_ret;\n}\n"
  },
  {
    "path": "tutorial02/tutorial02.md",
    "content": "# 从零开始的 JSON 库教程（二）：解析数字\n\n* Milo Yip\n* 2016/9/18\n\n本文是[《从零开始的 JSON 库教程》](https://zhuanlan.zhihu.com/json-tutorial)的第二个单元。本单元的源代码位于 [json-tutorial/tutorial02](https://github.com/miloyip/json-tutorial/blob/master/tutorial02/)。\n\n本单元内容：\n\n1. [初探重构](#1-初探重构)\n2. [JSON 数字语法](#2-json-数字语法)\n3. [数字表示方式](#3-数字表示方式)\n4. [单元测试](#4-单元测试)\n5. [十进制转换至二进制](#5-十进制转换至二进制)\n6. [总结与练习](#6-总结与练习)\n7. [参考](#7-参考)\n8. [常见问题](#8-常见问题)\n\n## 1. 初探重构\n\n在讨论解析数字之前，我们再补充 TDD 中的一个步骤──重构（refactoring）。根据[1]，重构是一个这样的过程：\n\n> 在不改变代码外在行为的情况下，对代码作出修改，以改进程序的内部结构。\n\n在 TDD 的过程中，我们的目标是编写代码去通过测试。但由于这个目标的引导性太强，我们可能会忽略正确性以外的软件品质。在通过测试之后，代码的正确性得以保证，我们就应该审视现时的代码，看看有没有地方可以改进，而同时能维持测试顺利通过。我们可以安心地做各种修改，因为我们有单元测试，可以判断代码在修改后是否影响原来的行为。\n\n那么，哪里要作出修改？Beck 和 Fowler（[1] 第 3 章）认为程序员要培养一种判断能力，找出程序中的坏味道。例如，在第一单元的练习中，可能大部分人都会复制 `lept_parse_null()` 的代码，作一些修改，成为 `lept_parse_true()` 和 `lept_parse_false()`。如果我们再审视这 3 个函数，它们非常相似。这违反编程中常说的 DRY（don't repeat yourself）原则。本单元的第一个练习题，就是尝试合并这 3 个函数。\n\n另外，我们也可能发现，单元测试代码也有很重复的代码，例如 `test_parse_invalid_value()` 中我们每次测试一个不合法的 JSON 值，都有 4 行相似的代码。我们可以把它用宏的方式把它们简化：\n\n~~~c\n#define TEST_ERROR(error, json)\\\n    do {\\\n        lept_value v;\\\n        v.type = LEPT_FALSE;\\\n        EXPECT_EQ_INT(error, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\\\n    } while(0)\n\nstatic void test_parse_expect_value() {\n    TEST_ERROR(LEPT_PARSE_EXPECT_VALUE, \"\");\n    TEST_ERROR(LEPT_PARSE_EXPECT_VALUE, \" \");\n}\n~~~\n\n最后，我希望指出，软件的架构难以用单一标准评分，重构时要考虑平衡各种软件品质。例如上述把 3 个函数合并后，优点是减少重复的代码，维护较容易，但缺点可能是带来性能的少量影响。\n\n## 2. JSON 数字语法\n\n回归正题，本单元的重点在于解析 JSON number 类型。我们先看看它的语法：\n\n~~~\nnumber = [ \"-\" ] int [ frac ] [ exp ]\nint = \"0\" / digit1-9 *digit\nfrac = \".\" 1*digit\nexp = (\"e\" / \"E\") [\"-\" / \"+\"] 1*digit\n~~~\n\nnumber 是以十进制表示，它主要由 4 部分顺序组成：负号、整数、小数、指数。只有整数是必需部分。注意和直觉可能不同的是，正号是不合法的。\n\n整数部分如果是 0 开始，只能是单个 0；而由 1-9 开始的话，可以加任意数量的数字（0-9）。也就是说，`0123` 不是一个合法的 JSON 数字。\n\n小数部分比较直观，就是小数点后是一或多个数字（0-9）。\n\nJSON 可使用科学记数法，指数部分由大写 E 或小写 e 开始，然后可有正负号，之后是一或多个数字（0-9）。\n\nJSON 标准 [ECMA-404](https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf) 采用图的形式表示语法，也可以更直观地看到解析时可能经过的路径：\n\n![number](images/number.png)\n\n上一单元的 null、false、true 在解析后，我们只需把它们存储为类型。但对于数字，我们要考虑怎么存储解析后的结果。\n\n## 3. 数字表示方式\n\n从 JSON 数字的语法，我们可能直观地会认为它应该表示为一个浮点数（floating point number），因为它带有小数和指数部分。然而，标准中并没有限制数字的范围或精度。为简单起见，leptjson 选择以双精度浮点数（C 中的 `double` 类型）来存储 JSON 数字。\n\n我们为 `lept_value` 添加成员：\n\n~~~c\ntypedef struct {\n    double n;\n    lept_type type;\n}lept_value;\n~~~\n\n仅当 `type == LEPT_NUMBER` 时，`n` 才表示 JSON 数字的数值。所以获取该值的 API 是这么实现的：\n\n~~~c\ndouble lept_get_number(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_NUMBER);\n    return v->n;\n}\n~~~\n\n使用者应确保类型正确，才调用此 API。我们继续使用断言来保证。\n\n## 4. 单元测试\n\n我们定义了 API 之后，按照 TDD，我们可以先写一些单元测试。这次我们使用多行的宏的减少重复代码：\n\n~~~c\n#define TEST_NUMBER(expect, json)\\\n    do {\\\n        lept_value v;\\\n        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(&v));\\\n        EXPECT_EQ_DOUBLE(expect, lept_get_number(&v));\\\n    } while(0)\n\nstatic void test_parse_number() {\n    TEST_NUMBER(0.0, \"0\");\n    TEST_NUMBER(0.0, \"-0\");\n    TEST_NUMBER(0.0, \"-0.0\");\n    TEST_NUMBER(1.0, \"1\");\n    TEST_NUMBER(-1.0, \"-1\");\n    TEST_NUMBER(1.5, \"1.5\");\n    TEST_NUMBER(-1.5, \"-1.5\");\n    TEST_NUMBER(3.1416, \"3.1416\");\n    TEST_NUMBER(1E10, \"1E10\");\n    TEST_NUMBER(1e10, \"1e10\");\n    TEST_NUMBER(1E+10, \"1E+10\");\n    TEST_NUMBER(1E-10, \"1E-10\");\n    TEST_NUMBER(-1E10, \"-1E10\");\n    TEST_NUMBER(-1e10, \"-1e10\");\n    TEST_NUMBER(-1E+10, \"-1E+10\");\n    TEST_NUMBER(-1E-10, \"-1E-10\");\n    TEST_NUMBER(1.234E+10, \"1.234E+10\");\n    TEST_NUMBER(1.234E-10, \"1.234E-10\");\n    TEST_NUMBER(0.0, \"1e-10000\"); /* must underflow */\n}\n~~~\n\n以上这些都是很基本的测试用例，也可供调试用。大部分情况下，测试案例不能穷举所有可能性。因此，除了加入一些典型的用例，我们也常会使用一些边界值，例如最大值等。练习中会让同学找一些边界值作为用例。\n\n除了这些合法的 JSON，我们也要写一些不合语法的用例：\n\n~~~c\nstatic void test_parse_invalid_value() {\n    /* ... */\n    /* invalid number */\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"+0\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"+1\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \".123\"); /* at least one digit before '.' */\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"1.\");   /* at least one digit after '.' */\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"INF\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"inf\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"NAN\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"nan\");\n}\n~~~\n\n## 5. 十进制转换至二进制\n\n我们需要把十进制的数字转换成二进制的 `double`。这并不是容易的事情 [2]。为了简单起见，leptjson 将使用标准库的 [`strtod()`](https://en.cppreference.com/w/c/string/byte/strtof) 来进行转换。`strtod()` 可转换 JSON 所要求的格式，但问题是，一些 JSON 不容许的格式，`strtod()` 也可转换，所以我们需要自行做格式校验。\n\n~~~c\n#include <stdlib.h>  /* NULL, strtod() */\n\nstatic int lept_parse_number(lept_context* c, lept_value* v) {\n    char* end;\n    /* \\TODO validate number */\n    v->n = strtod(c->json, &end);\n    if (c->json == end)\n        return LEPT_PARSE_INVALID_VALUE;\n    c->json = end;\n    v->type = LEPT_NUMBER;\n    return LEPT_PARSE_OK;\n}\n~~~\n\n加入了 number 后，value 的语法变成：\n\n~~~\nvalue = null / false / true / number\n~~~\n\n记得在第一单元中，我们说可以用一个字符就能得知 value 是什么类型，有 11 个字符可判断 number：\n\n* 0-9/- ➔ number\n\n但是，由于我们在 `lept_parse_number()` 内部将会校验输入是否正确的值，我们可以简单地把余下的情况都交给 `lept_parse_number()`：\n\n~~~c\nstatic int lept_parse_value(lept_context* c, lept_value* v) {\n    switch (*c->json) {\n        case 't':  return lept_parse_true(c, v);\n        case 'f':  return lept_parse_false(c, v);\n        case 'n':  return lept_parse_null(c, v);\n        default:   return lept_parse_number(c, v);\n        case '\\0': return LEPT_PARSE_EXPECT_VALUE;\n    }\n}\n~~~\n\n## 6. 总结与练习\n\n本单元讲述了 JSON 数字类型的语法，以及 leptjson 所采用的自行校验＋`strtod()`转换为 `double` 的方案。实际上一些 JSON 库会采用更复杂的方案，例如支持 64 位带符号／无符号整数，自行实现转换。以我的个人经验，解析／生成数字类型可以说是 RapidJSON 中最难实现的部分，也是 RapidJSON 高效性能的原因，有机会再另外撰文解释。\n\n此外我们谈及，重构与单元测试是互相依赖的软件开发技术，适当地运用可提升软件的品质。之后的单元还会有相关的话题。\n\n1. 重构合并 `lept_parse_null()`、`lept_parse_false()`、`lept_parse_true()` 为 `lept_parse_literal()`。\n2. 加入 [维基百科双精度浮点数](https://en.wikipedia.org/wiki/Double-precision_floating-point_format#Double-precision_examples) 的一些边界值至单元测试，如 min subnormal positive double、max double 等。\n3. 去掉 `test_parse_invalid_value()` 和 `test_parse_root_not_singular()` 中的 `#if 0 ... #endif`，执行测试，证实测试失败。按 JSON number 的语法在 lept_parse_number() 校验，不符合标准的情况返回 `LEPT_PARSE_INVALID_VALUE` 错误码。\n4. 去掉 `test_parse_number_too_big` 中的 `#if 0 ... #endif`，执行测试，证实测试失败。仔细阅读 [`strtod()`](https://en.cppreference.com/w/c/string/byte/strtof)，看看怎样从返回值得知数值是否过大，以返回 `LEPT_PARSE_NUMBER_TOO_BIG` 错误码。（提示：这里需要 `#include` 额外两个标准库头文件。）\n\n以上最重要的是第 3 条题目，就是要校验 JSON 的数字语法。建议可使用以下两个宏去简化一下代码：\n\n~~~c\n#define ISDIGIT(ch)         ((ch) >= '0' && (ch) <= '9')\n#define ISDIGIT1TO9(ch)     ((ch) >= '1' && (ch) <= '9')\n~~~\n\n另一提示，在校验成功以后，我们不再使用 `end` 指针去检测 `strtod()` 的正确性，第二个参数可传入 `NULL`。\n\n如果你遇到问题，有不理解的地方，或是有建议，都欢迎在评论或 [issue](https://github.com/miloyip/json-tutorial/issues) 中提出，让所有人一起讨论。\n\n## 7. 参考\n\n[1] Fowler, Martin. Refactoring: improving the design of existing code. Pearson Education India, 2009. 中译本：《重构：改善既有代码的设计》，熊节译，人民邮电出版社，2010年。\n\n[2] Gay, David M. \"Correctly rounded binary-decimal and decimal-binary conversions.\" Numerical Analysis Manuscript 90-10 (1990).\n\n## 8. 常见问题\n\n1. 为什么要把一些测试代码以 `#if 0 ... #endif` 禁用？\n\n   因为在做第 1 个练习题时，我希望能 100% 通过测试，方便做重构。另外，使用 `#if 0 ... #endif` 而不使用 `/* ... */`，是因为 C 的注释不支持嵌套（nested），而 `#if ... #endif` 是支持嵌套的。代码中已有注释时，用 `#if 0 ... #endif` 去禁用代码是一个常用技巧，而且可以把 `0` 改为 `1` 去恢复。\n\n2. 科学计数法的指数部分没有对前导零作限制吗？`1E012` 也是合法的吗？\n\n   是的，这是合法的。JSON 源自于 JavaScript（[ECMA-262, 3rd edition](https://www.ecma-international.org/publications/files/ECMA-ST-ARCH/ECMA-262,%203rd%20edition,%20December%201999.pdf)），数字语法取自 JavaScript 的十进位数字的语法（§7.8.3 Numeric Literals）。整数不容许前导零（leading zero），是因为更久的 JavaScript 版本容许以前导零来表示八进位数字，如 `052 == 42`，这种八进位常数表示方式来自于 [C 语言](https://en.cppreference.com/w/c/language/integer_constant)。禁止前导零避免了可能出现的歧义。但是在指数里就不会出现这个问题。多谢 @Smallay 提出及协助解答这个问题。\n\n其他常见问答将会从评论中整理。\n"
  },
  {
    "path": "tutorial02_answer/CMakeLists.txt",
    "content": "cmake_minimum_required (VERSION 2.6)\nproject (leptjson_test C)\n\nif (CMAKE_C_COMPILER_ID MATCHES \"GNU|Clang\")\n    set(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -ansi -pedantic -Wall\")\nendif()\n\nadd_library(leptjson leptjson.c)\nadd_executable(leptjson_test test.c)\ntarget_link_libraries(leptjson_test leptjson)\n"
  },
  {
    "path": "tutorial02_answer/leptjson.c",
    "content": "#include \"leptjson.h\"\n#include <assert.h>  /* assert() */\n#include <errno.h>   /* errno, ERANGE */\n#include <math.h>    /* HUGE_VAL */\n#include <stdlib.h>  /* NULL, strtod() */\n\n#define EXPECT(c, ch)       do { assert(*c->json == (ch)); c->json++; } while(0)\n#define ISDIGIT(ch)         ((ch) >= '0' && (ch) <= '9')\n#define ISDIGIT1TO9(ch)     ((ch) >= '1' && (ch) <= '9')\n\ntypedef struct {\n    const char* json;\n}lept_context;\n\nstatic void lept_parse_whitespace(lept_context* c) {\n    const char *p = c->json;\n    while (*p == ' ' || *p == '\\t' || *p == '\\n' || *p == '\\r')\n        p++;\n    c->json = p;\n}\n\nstatic int lept_parse_literal(lept_context* c, lept_value* v, const char* literal, lept_type type) {\n    size_t i;\n    EXPECT(c, literal[0]);\n    for (i = 0; literal[i + 1]; i++)\n        if (c->json[i] != literal[i + 1])\n            return LEPT_PARSE_INVALID_VALUE;\n    c->json += i;\n    v->type = type;\n    return LEPT_PARSE_OK;\n}\n\nstatic int lept_parse_number(lept_context* c, lept_value* v) {\n    const char* p = c->json;\n    if (*p == '-') p++;\n    if (*p == '0') p++;\n    else {\n        if (!ISDIGIT1TO9(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n    if (*p == '.') {\n        p++;\n        if (!ISDIGIT(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n    if (*p == 'e' || *p == 'E') {\n        p++;\n        if (*p == '+' || *p == '-') p++;\n        if (!ISDIGIT(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n    errno = 0;\n    v->n = strtod(c->json, NULL);\n    if (errno == ERANGE && (v->n == HUGE_VAL || v->n == -HUGE_VAL))\n        return LEPT_PARSE_NUMBER_TOO_BIG;\n    v->type = LEPT_NUMBER;\n    c->json = p;\n    return LEPT_PARSE_OK;\n}\n\nstatic int lept_parse_value(lept_context* c, lept_value* v) {\n    switch (*c->json) {\n        case 't':  return lept_parse_literal(c, v, \"true\", LEPT_TRUE);\n        case 'f':  return lept_parse_literal(c, v, \"false\", LEPT_FALSE);\n        case 'n':  return lept_parse_literal(c, v, \"null\", LEPT_NULL);\n        default:   return lept_parse_number(c, v);\n        case '\\0': return LEPT_PARSE_EXPECT_VALUE;\n    }\n}\n\nint lept_parse(lept_value* v, const char* json) {\n    lept_context c;\n    int ret;\n    assert(v != NULL);\n    c.json = json;\n    v->type = LEPT_NULL;\n    lept_parse_whitespace(&c);\n    if ((ret = lept_parse_value(&c, v)) == LEPT_PARSE_OK) {\n        lept_parse_whitespace(&c);\n        if (*c.json != '\\0') {\n            v->type = LEPT_NULL;\n            ret = LEPT_PARSE_ROOT_NOT_SINGULAR;\n        }\n    }\n    return ret;\n}\n\nlept_type lept_get_type(const lept_value* v) {\n    assert(v != NULL);\n    return v->type;\n}\n\ndouble lept_get_number(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_NUMBER);\n    return v->n;\n}\n"
  },
  {
    "path": "tutorial02_answer/leptjson.h",
    "content": "#ifndef LEPTJSON_H__\n#define LEPTJSON_H__\n\ntypedef enum { LEPT_NULL, LEPT_FALSE, LEPT_TRUE, LEPT_NUMBER, LEPT_STRING, LEPT_ARRAY, LEPT_OBJECT } lept_type;\n\ntypedef struct {\n\tdouble n;\n    lept_type type;\n}lept_value;\n\nenum {\n    LEPT_PARSE_OK = 0,\n    LEPT_PARSE_EXPECT_VALUE,\n    LEPT_PARSE_INVALID_VALUE,\n    LEPT_PARSE_ROOT_NOT_SINGULAR,\n    LEPT_PARSE_NUMBER_TOO_BIG\n};\n\nint lept_parse(lept_value* v, const char* json);\n\nlept_type lept_get_type(const lept_value* v);\n\ndouble lept_get_number(const lept_value* v);\n\n#endif /* LEPTJSON_H__ */\n"
  },
  {
    "path": "tutorial02_answer/test.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"leptjson.h\"\n\nstatic int main_ret = 0;\nstatic int test_count = 0;\nstatic int test_pass = 0;\n\n#define EXPECT_EQ_BASE(equality, expect, actual, format) \\\n    do {\\\n        test_count++;\\\n        if (equality)\\\n            test_pass++;\\\n        else {\\\n            fprintf(stderr, \"%s:%d: expect: \" format \" actual: \" format \"\\n\", __FILE__, __LINE__, expect, actual);\\\n            main_ret = 1;\\\n        }\\\n    } while(0)\n\n#define EXPECT_EQ_INT(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, \"%d\")\n#define EXPECT_EQ_DOUBLE(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, \"%.17g\")\n\nstatic void test_parse_null() {\n    lept_value v;\n    v.type = LEPT_FALSE;\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"null\"));\n    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\n}\n\nstatic void test_parse_true() {\n    lept_value v;\n    v.type = LEPT_FALSE;\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"true\"));\n    EXPECT_EQ_INT(LEPT_TRUE, lept_get_type(&v));\n}\n\nstatic void test_parse_false() {\n    lept_value v;\n    v.type = LEPT_TRUE;\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"false\"));\n    EXPECT_EQ_INT(LEPT_FALSE, lept_get_type(&v));\n}\n\n#define TEST_NUMBER(expect, json)\\\n    do {\\\n        lept_value v;\\\n        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(&v));\\\n        EXPECT_EQ_DOUBLE(expect, lept_get_number(&v));\\\n    } while(0)\n\nstatic void test_parse_number() {\n    TEST_NUMBER(0.0, \"0\");\n    TEST_NUMBER(0.0, \"-0\");\n    TEST_NUMBER(0.0, \"-0.0\");\n    TEST_NUMBER(1.0, \"1\");\n    TEST_NUMBER(-1.0, \"-1\");\n    TEST_NUMBER(1.5, \"1.5\");\n    TEST_NUMBER(-1.5, \"-1.5\");\n    TEST_NUMBER(3.1416, \"3.1416\");\n    TEST_NUMBER(1E10, \"1E10\");\n    TEST_NUMBER(1e10, \"1e10\");\n    TEST_NUMBER(1E+10, \"1E+10\");\n    TEST_NUMBER(1E-10, \"1E-10\");\n    TEST_NUMBER(-1E10, \"-1E10\");\n    TEST_NUMBER(-1e10, \"-1e10\");\n    TEST_NUMBER(-1E+10, \"-1E+10\");\n    TEST_NUMBER(-1E-10, \"-1E-10\");\n    TEST_NUMBER(1.234E+10, \"1.234E+10\");\n    TEST_NUMBER(1.234E-10, \"1.234E-10\");\n    TEST_NUMBER(0.0, \"1e-10000\"); /* must underflow */\n\n    TEST_NUMBER(1.0000000000000002, \"1.0000000000000002\"); /* the smallest number > 1 */\n    TEST_NUMBER( 4.9406564584124654e-324, \"4.9406564584124654e-324\"); /* minimum denormal */\n    TEST_NUMBER(-4.9406564584124654e-324, \"-4.9406564584124654e-324\");\n    TEST_NUMBER( 2.2250738585072009e-308, \"2.2250738585072009e-308\");  /* Max subnormal double */\n    TEST_NUMBER(-2.2250738585072009e-308, \"-2.2250738585072009e-308\");\n    TEST_NUMBER( 2.2250738585072014e-308, \"2.2250738585072014e-308\");  /* Min normal positive double */\n    TEST_NUMBER(-2.2250738585072014e-308, \"-2.2250738585072014e-308\");\n    TEST_NUMBER( 1.7976931348623157e+308, \"1.7976931348623157e+308\");  /* Max double */\n    TEST_NUMBER(-1.7976931348623157e+308, \"-1.7976931348623157e+308\");\n}\n\n#define TEST_ERROR(error, json)\\\n    do {\\\n        lept_value v;\\\n        v.type = LEPT_FALSE;\\\n        EXPECT_EQ_INT(error, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\\\n    } while(0)\n\nstatic void test_parse_expect_value() {\n    TEST_ERROR(LEPT_PARSE_EXPECT_VALUE, \"\");\n    TEST_ERROR(LEPT_PARSE_EXPECT_VALUE, \" \");\n}\n\nstatic void test_parse_invalid_value() {\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"nul\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"?\");\n\n    /* invalid number */\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"+0\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"+1\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \".123\"); /* at least one digit before '.' */\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"1.\");   /* at least one digit after '.' */\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"INF\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"inf\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"NAN\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"nan\");\n}\n\nstatic void test_parse_root_not_singular() {\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"null x\");\n\n    /* invalid number */\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0123\"); /* after zero should be '.' or nothing */\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0x0\");\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0x123\");\n}\n\nstatic void test_parse_number_too_big() {\n    TEST_ERROR(LEPT_PARSE_NUMBER_TOO_BIG, \"1e309\");\n    TEST_ERROR(LEPT_PARSE_NUMBER_TOO_BIG, \"-1e309\");\n}\n\nstatic void test_parse() {\n    test_parse_null();\n    test_parse_true();\n    test_parse_false();\n    test_parse_number();\n    test_parse_expect_value();\n    test_parse_invalid_value();\n    test_parse_root_not_singular();\n    test_parse_number_too_big();\n}\n\nint main() {\n    test_parse();\n    printf(\"%d/%d (%3.2f%%) passed\\n\", test_pass, test_count, test_pass * 100.0 / test_count);\n    return main_ret;\n}\n"
  },
  {
    "path": "tutorial02_answer/tutorial02_answer.md",
    "content": "# 从零开始的 JSON 库教程（二）：解析数字解答篇\n\n* Milo Yip\n* 2016/9/20\n\n本文是[《从零开始的 JSON 库教程》](https://zhuanlan.zhihu.com/json-tutorial)的第二个单元解答篇。解答代码位于 [json-tutorial/tutorial02_answer](https://github.com/miloyip/json-tutorial/blob/master/tutorial02_answer/)。\n\n## 1. 重构合并\n\n由于 true / false / null 的字符数量不一样，这个答案以 for 循环作比较，直至 `'\\0'`。\n\n~~~c\nstatic int lept_parse_literal(lept_context* c, lept_value* v, const char* literal, lept_type type) {\n    size_t i;\n    EXPECT(c, literal[0]);\n    for (i = 0; literal[i + 1]; i++)\n        if (c->json[i] != literal[i + 1])\n            return LEPT_PARSE_INVALID_VALUE;\n    c->json += i;\n    v->type = type;\n    return LEPT_PARSE_OK;\n}\n\nstatic int lept_parse_value(lept_context* c, lept_value* v) {\n    switch (*c->json) {\n        case 't':  return lept_parse_literal(c, v, \"true\", LEPT_TRUE);\n        case 'f':  return lept_parse_literal(c, v, \"false\", LEPT_FALSE);\n        case 'n':  return lept_parse_literal(c, v, \"null\", LEPT_NULL);\n        /* ... */\n    }\n}\n~~~\n\n注意在 C 语言中，数组长度、索引值最好使用 `size_t` 类型，而不是 `int` 或 `unsigned`。\n\n你也可以直接传送长度参数 4、5、4，只要能通过测试就行了。\n\n## 2. 边界值测试\n\n这问题其实涉及一些浮点数类型的细节，例如 IEEE-754 浮点数中，有所谓的 normal 和 subnormal 值，这里暂时不展开讨论了。以下是我加入的一些边界值，可能和同学的不完全一样。\n\n~~~\nTEST_NUMBER(1.0000000000000002, \"1.0000000000000002\"); /* the smallest number > 1 */\nTEST_NUMBER( 4.9406564584124654e-324, \"4.9406564584124654e-324\"); /* minimum denormal */\nTEST_NUMBER(-4.9406564584124654e-324, \"-4.9406564584124654e-324\");\nTEST_NUMBER( 2.2250738585072009e-308, \"2.2250738585072009e-308\");  /* Max subnormal double */\nTEST_NUMBER(-2.2250738585072009e-308, \"-2.2250738585072009e-308\");\nTEST_NUMBER( 2.2250738585072014e-308, \"2.2250738585072014e-308\");  /* Min normal positive double */\nTEST_NUMBER(-2.2250738585072014e-308, \"-2.2250738585072014e-308\");\nTEST_NUMBER( 1.7976931348623157e+308, \"1.7976931348623157e+308\");  /* Max double */\nTEST_NUMBER(-1.7976931348623157e+308, \"-1.7976931348623157e+308\");\n~~~\n\n另外，这些加入的测试用例，正常的 `strtod()` 都能通过。所以不能做到测试失败、修改实现、测试成功的 TDD 步骤。\n\n有一些 JSON 解析器不使用 `strtod()` 而自行转换，例如在校验的同时，记录负号、尾数（整数和小数）和指数，然后 naive 地计算：\n\n~~~c\nint negative = 0;\nint64_t mantissa = 0;\nint exp = 0;\n\n/* 解析... 并存储 negative, mantissa, exp */\nv->n = (negative ? -mantissa : mantissa) * pow(10.0, exp);\n~~~\n\n这种做法会有精度问题。实现正确的答案是很复杂的，RapidJSON 的初期版本也是 naive 的，后来 RapidJSON 就内部实现了三种算法（使用 `kParseFullPrecision` 选项开启），最后一种算法用到了大整数（高精度计算）。有兴趣的同学也可以先尝试做一个 naive 版本，不使用 `strtod()`。之后可再参考 Google 的 [double-conversion](https://github.com/google/double-conversion) 开源项目及相关论文。\n\n## 3. 校验数字\n\n这条题目是本单元的重点，考验同学是否能把语法手写为校验规则。我详细说明。\n\n首先，如同 `lept_parse_whitespace()`，我们使用一个指针 `p` 来表示当前的解析字符位置。这样做有两个好处，一是代码更简单，二是在某些编译器下性能更好（因为不能确定 `c` 会否被改变，从而每次更改 `c->json` 都要做一次间接访问）。如果校验成功，才把 `p` 赋值至 `c->json`。\n\n~~~c\nstatic int lept_parse_number(lept_context* c, lept_value* v) {\n    const char* p = c->json;\n    /* 负号 ... */\n    /* 整数 ... */\n    /* 小数 ... */\n    /* 指数 ... */\n    v->n = strtod(c->json, NULL);\n    v->type = LEPT_NUMBER;\n    c->json = p;\n    return LEPT_PARSE_OK;\n}\n~~~\n\n我们把语法再看一遍：\n\n~~~\nnumber = [ \"-\" ] int [ frac ] [ exp ]\nint = \"0\" / digit1-9 *digit\nfrac = \".\" 1*digit\nexp = (\"e\" / \"E\") [\"-\" / \"+\"] 1*digit\n~~~\n\n负号最简单，有的话跳过便行：\n\n~~~c\n    if (*p == '-') p++;\n~~~\n\n整数部分有两种合法情况，一是单个 `0`，否则是一个 1-9 再加上任意数量的 digit。对于第一种情况，我们像负号般跳过便行。对于第二种情况，第一个字符必须为 1-9，如果否定的就是不合法的，可立即返回错误码。然后，有多少个 digit 就跳过多少个。\n\n~~~c\n    if (*p == '0') p++;\n    else {\n        if (!ISDIGIT1TO9(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n~~~\n\n如果出现小数点，我们跳过该小数点，然后检查它至少应有一个 digit，不是 digit 就返回错误码。跳过首个 digit，我们再检查有没有 digit，有多少个跳过多少个。这里用了 for 循环技巧来做这件事。\n\n~~~c\n    if (*p == '.') {\n        p++;\n        if (!ISDIGIT(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n~~~\n\n最后，如果出现大小写 `e`，就表示有指数部分。跳过那个 `e` 之后，可以有一个正或负号，有的话就跳过。然后和小数的逻辑是一样的。\n\n~~~c\n    if (*p == 'e' || *p == 'E') {\n        p++;\n        if (*p == '+' || *p == '-') p++;\n        if (!ISDIGIT(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n~~~\n\n这里用了 18 行代码去做这个校验。当中把一些 if 用一行来排版，而没用采用传统两行缩进风格，我个人认为在不影响阅读时可以这样弹性处理。当然那些 for 也可分拆成三行：\n\n~~~c\n        p++;\n        while (ISDIGIT(*p))\n            p++;\n~~~\n\n## 4. 数字过大的处理\n\n最后这题纯粹是阅读理解题。\n\n~~~c\n#include <errno.h>   /* errno, ERANGE */\n#include <math.h>    /* HUGE_VAL */\n\nstatic int lept_parse_number(lept_context* c, lept_value* v) {\n    /* ... */\n    errno = 0;\n    v->n = strtod(c->json, NULL);\n    if (errno == ERANGE && v->n == HUGE_VAL) return LEPT_PARSE_NUMBER_TOO_BIG;\n    /* ... */\n}\n~~~\n\n许多时候课本／书籍也不会把每个标准库功能说得很仔细，我想藉此提醒同学要好好看参考文档，学会读文档编程就简单得多！[cppreference.com](https://cppreference.com) 是 C/C++ 程序员的宝库。\n\n## 5. 总结\n\n本单元的习题比上个单元较有挑战性一些，所以我花了多一些篇幅在解答篇。纯以语法来说，数字类型已经是 JSON 中最复杂的类型。如果同学能完成本单元的练习（特别是第 3 条），之后的字符串、数组和对象的语法一定难不到你。然而，接下来也会有一些新挑战，例如内存管理、数据结构、编码等，希望你能满载而归。\n\n如果你遇到问题，有不理解的地方，或是有建议，都欢迎在评论或 [issue](https://github.com/miloyip/json-tutorial/issues) 中提出，让所有人一起讨论。\n"
  },
  {
    "path": "tutorial03/CMakeLists.txt",
    "content": "cmake_minimum_required (VERSION 2.6)\nproject (leptjson_test C)\n\nif (CMAKE_C_COMPILER_ID MATCHES \"GNU|Clang\")\n    set(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -ansi -pedantic -Wall\")\nendif()\n\nadd_library(leptjson leptjson.c)\nadd_executable(leptjson_test test.c)\ntarget_link_libraries(leptjson_test leptjson)\n"
  },
  {
    "path": "tutorial03/images/makefile",
    "content": "%.png: %.dot\n\tdot $< -Tpng -o $@\n\nDOTFILES = $(basename $(wildcard *.dot))\nall: $(addsuffix .png, $(DOTFILES))\n"
  },
  {
    "path": "tutorial03/images/union_layout.dot",
    "content": "digraph {\n    rankdir=LR\n    compound=true\n    fontname=\"Inconsolata, Consolas\"\n    fontsize=10\n    margin=\"0,0\"\n    ranksep=0.5\n    nodesep=1\n    penwidth=0.5\n    \n    node [shape=Mrecord, fontname=\"Inconsolata, Consolas\", fontsize=10, penwidth=0.5, style=filled, colorscheme=spectral7]\n    edge [fontname=\"Inconsolata, Consolas\", fontsize=10, penwidth=0.5]\n\n    old [fillcolor=6, label=\"double n (8 bytes) |const char* s (4 bytes) |size_t len (4 bytes)|int type (4 bytes)\"]\n    new [fillcolor=5, label=\"{double n (8 bytes)|{const char* s (4 bytes)|size_t len (4 bytes)}}|int type (4 bytes)\"]\n    old -> new [style=invis]\n}"
  },
  {
    "path": "tutorial03/leptjson.c",
    "content": "#include \"leptjson.h\"\n#include <assert.h>  /* assert() */\n#include <errno.h>   /* errno, ERANGE */\n#include <math.h>    /* HUGE_VAL */\n#include <stdlib.h>  /* NULL, malloc(), realloc(), free(), strtod() */\n#include <string.h>  /* memcpy() */\n\n#ifndef LEPT_PARSE_STACK_INIT_SIZE\n#define LEPT_PARSE_STACK_INIT_SIZE 256\n#endif\n\n#define EXPECT(c, ch)       do { assert(*c->json == (ch)); c->json++; } while(0)\n#define ISDIGIT(ch)         ((ch) >= '0' && (ch) <= '9')\n#define ISDIGIT1TO9(ch)     ((ch) >= '1' && (ch) <= '9')\n#define PUTC(c, ch)         do { *(char*)lept_context_push(c, sizeof(char)) = (ch); } while(0)\n\ntypedef struct {\n    const char* json;\n    char* stack;\n    size_t size, top;\n}lept_context;\n\nstatic void* lept_context_push(lept_context* c, size_t size) {\n    void* ret;\n    assert(size > 0);\n    if (c->top + size >= c->size) {\n        if (c->size == 0)\n            c->size = LEPT_PARSE_STACK_INIT_SIZE;\n        while (c->top + size >= c->size)\n            c->size += c->size >> 1;  /* c->size * 1.5 */\n        c->stack = (char*)realloc(c->stack, c->size);\n    }\n    ret = c->stack + c->top;\n    c->top += size;\n    return ret;\n}\n\nstatic void* lept_context_pop(lept_context* c, size_t size) {\n    assert(c->top >= size);\n    return c->stack + (c->top -= size);\n}\n\nstatic void lept_parse_whitespace(lept_context* c) {\n    const char *p = c->json;\n    while (*p == ' ' || *p == '\\t' || *p == '\\n' || *p == '\\r')\n        p++;\n    c->json = p;\n}\n\nstatic int lept_parse_literal(lept_context* c, lept_value* v, const char* literal, lept_type type) {\n    size_t i;\n    EXPECT(c, literal[0]);\n    for (i = 0; literal[i + 1]; i++)\n        if (c->json[i] != literal[i + 1])\n            return LEPT_PARSE_INVALID_VALUE;\n    c->json += i;\n    v->type = type;\n    return LEPT_PARSE_OK;\n}\n\nstatic int lept_parse_number(lept_context* c, lept_value* v) {\n    const char* p = c->json;\n    if (*p == '-') p++;\n    if (*p == '0') p++;\n    else {\n        if (!ISDIGIT1TO9(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n    if (*p == '.') {\n        p++;\n        if (!ISDIGIT(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n    if (*p == 'e' || *p == 'E') {\n        p++;\n        if (*p == '+' || *p == '-') p++;\n        if (!ISDIGIT(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n    errno = 0;\n    v->u.n = strtod(c->json, NULL);\n    if (errno == ERANGE && (v->u.n == HUGE_VAL || v->u.n == -HUGE_VAL))\n        return LEPT_PARSE_NUMBER_TOO_BIG;\n    v->type = LEPT_NUMBER;\n    c->json = p;\n    return LEPT_PARSE_OK;\n}\n\nstatic int lept_parse_string(lept_context* c, lept_value* v) {\n    size_t head = c->top, len;\n    const char* p;\n    EXPECT(c, '\\\"');\n    p = c->json;\n    for (;;) {\n        char ch = *p++;\n        switch (ch) {\n            case '\\\"':\n                len = c->top - head;\n                lept_set_string(v, (const char*)lept_context_pop(c, len), len);\n                c->json = p;\n                return LEPT_PARSE_OK;\n            case '\\0':\n                c->top = head;\n                return LEPT_PARSE_MISS_QUOTATION_MARK;\n            default:\n                PUTC(c, ch);\n        }\n    }\n}\n\nstatic int lept_parse_value(lept_context* c, lept_value* v) {\n    switch (*c->json) {\n        case 't':  return lept_parse_literal(c, v, \"true\", LEPT_TRUE);\n        case 'f':  return lept_parse_literal(c, v, \"false\", LEPT_FALSE);\n        case 'n':  return lept_parse_literal(c, v, \"null\", LEPT_NULL);\n        default:   return lept_parse_number(c, v);\n        case '\"':  return lept_parse_string(c, v);\n        case '\\0': return LEPT_PARSE_EXPECT_VALUE;\n    }\n}\n\nint lept_parse(lept_value* v, const char* json) {\n    lept_context c;\n    int ret;\n    assert(v != NULL);\n    c.json = json;\n    c.stack = NULL;\n    c.size = c.top = 0;\n    lept_init(v);\n    lept_parse_whitespace(&c);\n    if ((ret = lept_parse_value(&c, v)) == LEPT_PARSE_OK) {\n        lept_parse_whitespace(&c);\n        if (*c.json != '\\0') {\n            v->type = LEPT_NULL;\n            ret = LEPT_PARSE_ROOT_NOT_SINGULAR;\n        }\n    }\n    assert(c.top == 0);\n    free(c.stack);\n    return ret;\n}\n\nvoid lept_free(lept_value* v) {\n    assert(v != NULL);\n    if (v->type == LEPT_STRING)\n        free(v->u.s.s);\n    v->type = LEPT_NULL;\n}\n\nlept_type lept_get_type(const lept_value* v) {\n    assert(v != NULL);\n    return v->type;\n}\n\nint lept_get_boolean(const lept_value* v) {\n    /* \\TODO */\n    return 0;\n}\n\nvoid lept_set_boolean(lept_value* v, int b) {\n    /* \\TODO */\n}\n\ndouble lept_get_number(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_NUMBER);\n    return v->u.n;\n}\n\nvoid lept_set_number(lept_value* v, double n) {\n    /* \\TODO */\n}\n\nconst char* lept_get_string(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_STRING);\n    return v->u.s.s;\n}\n\nsize_t lept_get_string_length(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_STRING);\n    return v->u.s.len;\n}\n\nvoid lept_set_string(lept_value* v, const char* s, size_t len) {\n    assert(v != NULL && (s != NULL || len == 0));\n    lept_free(v);\n    v->u.s.s = (char*)malloc(len + 1);\n    memcpy(v->u.s.s, s, len);\n    v->u.s.s[len] = '\\0';\n    v->u.s.len = len;\n    v->type = LEPT_STRING;\n}\n"
  },
  {
    "path": "tutorial03/leptjson.h",
    "content": "#ifndef LEPTJSON_H__\n#define LEPTJSON_H__\n\n#include <stddef.h> /* size_t */\n\ntypedef enum { LEPT_NULL, LEPT_FALSE, LEPT_TRUE, LEPT_NUMBER, LEPT_STRING, LEPT_ARRAY, LEPT_OBJECT } lept_type;\n\ntypedef struct {\n    union {\n        struct { char* s; size_t len; }s;  /* string: null-terminated string, string length */\n        double n;                          /* number */\n    }u;\n    lept_type type;\n}lept_value;\n\nenum {\n    LEPT_PARSE_OK = 0,\n    LEPT_PARSE_EXPECT_VALUE,\n    LEPT_PARSE_INVALID_VALUE,\n    LEPT_PARSE_ROOT_NOT_SINGULAR,\n    LEPT_PARSE_NUMBER_TOO_BIG,\n    LEPT_PARSE_MISS_QUOTATION_MARK,\n    LEPT_PARSE_INVALID_STRING_ESCAPE,\n    LEPT_PARSE_INVALID_STRING_CHAR\n};\n\n#define lept_init(v) do { (v)->type = LEPT_NULL; } while(0)\n\nint lept_parse(lept_value* v, const char* json);\n\nvoid lept_free(lept_value* v);\n\nlept_type lept_get_type(const lept_value* v);\n\n#define lept_set_null(v) lept_free(v)\n\nint lept_get_boolean(const lept_value* v);\nvoid lept_set_boolean(lept_value* v, int b);\n\ndouble lept_get_number(const lept_value* v);\nvoid lept_set_number(lept_value* v, double n);\n\nconst char* lept_get_string(const lept_value* v);\nsize_t lept_get_string_length(const lept_value* v);\nvoid lept_set_string(lept_value* v, const char* s, size_t len);\n\n#endif /* LEPTJSON_H__ */\n"
  },
  {
    "path": "tutorial03/test.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"leptjson.h\"\n\nstatic int main_ret = 0;\nstatic int test_count = 0;\nstatic int test_pass = 0;\n\n#define EXPECT_EQ_BASE(equality, expect, actual, format) \\\n    do {\\\n        test_count++;\\\n        if (equality)\\\n            test_pass++;\\\n        else {\\\n            fprintf(stderr, \"%s:%d: expect: \" format \" actual: \" format \"\\n\", __FILE__, __LINE__, expect, actual);\\\n            main_ret = 1;\\\n        }\\\n    } while(0)\n\n#define EXPECT_EQ_INT(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, \"%d\")\n#define EXPECT_EQ_DOUBLE(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, \"%.17g\")\n#define EXPECT_EQ_STRING(expect, actual, alength) \\\n    EXPECT_EQ_BASE(sizeof(expect) - 1 == (alength) && memcmp(expect, actual, alength) == 0, expect, actual, \"%s\")\n#define EXPECT_TRUE(actual) EXPECT_EQ_BASE((actual) != 0, \"true\", \"false\", \"%s\")\n#define EXPECT_FALSE(actual) EXPECT_EQ_BASE((actual) == 0, \"false\", \"true\", \"%s\")\n\nstatic void test_parse_null() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_boolean(&v, 0);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"null\"));\n    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\n    lept_free(&v);\n}\n\nstatic void test_parse_true() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_boolean(&v, 0);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"true\"));\n    EXPECT_EQ_INT(LEPT_TRUE, lept_get_type(&v));\n    lept_free(&v);\n}\n\nstatic void test_parse_false() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_boolean(&v, 1);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"false\"));\n    EXPECT_EQ_INT(LEPT_FALSE, lept_get_type(&v));\n    lept_free(&v);\n}\n\n#define TEST_NUMBER(expect, json)\\\n    do {\\\n        lept_value v;\\\n        lept_init(&v);\\\n        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(&v));\\\n        EXPECT_EQ_DOUBLE(expect, lept_get_number(&v));\\\n        lept_free(&v);\\\n    } while(0)\n\nstatic void test_parse_number() {\n    TEST_NUMBER(0.0, \"0\");\n    TEST_NUMBER(0.0, \"-0\");\n    TEST_NUMBER(0.0, \"-0.0\");\n    TEST_NUMBER(1.0, \"1\");\n    TEST_NUMBER(-1.0, \"-1\");\n    TEST_NUMBER(1.5, \"1.5\");\n    TEST_NUMBER(-1.5, \"-1.5\");\n    TEST_NUMBER(3.1416, \"3.1416\");\n    TEST_NUMBER(1E10, \"1E10\");\n    TEST_NUMBER(1e10, \"1e10\");\n    TEST_NUMBER(1E+10, \"1E+10\");\n    TEST_NUMBER(1E-10, \"1E-10\");\n    TEST_NUMBER(-1E10, \"-1E10\");\n    TEST_NUMBER(-1e10, \"-1e10\");\n    TEST_NUMBER(-1E+10, \"-1E+10\");\n    TEST_NUMBER(-1E-10, \"-1E-10\");\n    TEST_NUMBER(1.234E+10, \"1.234E+10\");\n    TEST_NUMBER(1.234E-10, \"1.234E-10\");\n    TEST_NUMBER(0.0, \"1e-10000\"); /* must underflow */\n\n    TEST_NUMBER(1.0000000000000002, \"1.0000000000000002\"); /* the smallest number > 1 */\n    TEST_NUMBER( 4.9406564584124654e-324, \"4.9406564584124654e-324\"); /* minimum denormal */\n    TEST_NUMBER(-4.9406564584124654e-324, \"-4.9406564584124654e-324\");\n    TEST_NUMBER( 2.2250738585072009e-308, \"2.2250738585072009e-308\");  /* Max subnormal double */\n    TEST_NUMBER(-2.2250738585072009e-308, \"-2.2250738585072009e-308\");\n    TEST_NUMBER( 2.2250738585072014e-308, \"2.2250738585072014e-308\");  /* Min normal positive double */\n    TEST_NUMBER(-2.2250738585072014e-308, \"-2.2250738585072014e-308\");\n    TEST_NUMBER( 1.7976931348623157e+308, \"1.7976931348623157e+308\");  /* Max double */\n    TEST_NUMBER(-1.7976931348623157e+308, \"-1.7976931348623157e+308\");\n}\n\n#define TEST_STRING(expect, json)\\\n    do {\\\n        lept_value v;\\\n        lept_init(&v);\\\n        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_STRING, lept_get_type(&v));\\\n        EXPECT_EQ_STRING(expect, lept_get_string(&v), lept_get_string_length(&v));\\\n        lept_free(&v);\\\n    } while(0)\n\nstatic void test_parse_string() {\n    TEST_STRING(\"\", \"\\\"\\\"\");\n    TEST_STRING(\"Hello\", \"\\\"Hello\\\"\");\n#if 0\n    TEST_STRING(\"Hello\\nWorld\", \"\\\"Hello\\\\nWorld\\\"\");\n    TEST_STRING(\"\\\" \\\\ / \\b \\f \\n \\r \\t\", \"\\\"\\\\\\\" \\\\\\\\ \\\\/ \\\\b \\\\f \\\\n \\\\r \\\\t\\\"\");\n#endif\n}\n\n#define TEST_ERROR(error, json)\\\n    do {\\\n        lept_value v;\\\n        lept_init(&v);\\\n        v.type = LEPT_FALSE;\\\n        EXPECT_EQ_INT(error, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\\\n        lept_free(&v);\\\n    } while(0)\n\nstatic void test_parse_expect_value() {\n    TEST_ERROR(LEPT_PARSE_EXPECT_VALUE, \"\");\n    TEST_ERROR(LEPT_PARSE_EXPECT_VALUE, \" \");\n}\n\nstatic void test_parse_invalid_value() {\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"nul\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"?\");\n\n    /* invalid number */\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"+0\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"+1\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \".123\"); /* at least one digit before '.' */\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"1.\");   /* at least one digit after '.' */\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"INF\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"inf\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"NAN\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"nan\");\n}\n\nstatic void test_parse_root_not_singular() {\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"null x\");\n\n    /* invalid number */\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0123\"); /* after zero should be '.' or nothing */\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0x0\");\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0x123\");\n}\n\nstatic void test_parse_number_too_big() {\n    TEST_ERROR(LEPT_PARSE_NUMBER_TOO_BIG, \"1e309\");\n    TEST_ERROR(LEPT_PARSE_NUMBER_TOO_BIG, \"-1e309\");\n}\n\nstatic void test_parse_missing_quotation_mark() {\n    TEST_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK, \"\\\"\");\n    TEST_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK, \"\\\"abc\");\n}\n\nstatic void test_parse_invalid_string_escape() {\n#if 0\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\v\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\'\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\0\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\x12\\\"\");\n#endif\n}\n\nstatic void test_parse_invalid_string_char() {\n#if 0\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_CHAR, \"\\\"\\x01\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_CHAR, \"\\\"\\x1F\\\"\");\n#endif\n}\n\nstatic void test_access_null() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"a\", 1);\n    lept_set_null(&v);\n    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\n    lept_free(&v);\n}\n\nstatic void test_access_boolean() {\n    /* \\TODO */\n    /* Use EXPECT_TRUE() and EXPECT_FALSE() */\n}\n\nstatic void test_access_number() {\n    /* \\TODO */\n}\n\nstatic void test_access_string() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"\", 0);\n    EXPECT_EQ_STRING(\"\", lept_get_string(&v), lept_get_string_length(&v));\n    lept_set_string(&v, \"Hello\", 5);\n    EXPECT_EQ_STRING(\"Hello\", lept_get_string(&v), lept_get_string_length(&v));\n    lept_free(&v);\n}\n\nstatic void test_parse() {\n    test_parse_null();\n    test_parse_true();\n    test_parse_false();\n    test_parse_number();\n    test_parse_string();\n    test_parse_expect_value();\n    test_parse_invalid_value();\n    test_parse_root_not_singular();\n    test_parse_number_too_big();\n    test_parse_missing_quotation_mark();\n    test_parse_invalid_string_escape();\n    test_parse_invalid_string_char();\n\n    test_access_null();\n    test_access_boolean();\n    test_access_number();\n    test_access_string();\n}\n\nint main() {\n    test_parse();\n    printf(\"%d/%d (%3.2f%%) passed\\n\", test_pass, test_count, test_pass * 100.0 / test_count);\n    return main_ret;\n}\n"
  },
  {
    "path": "tutorial03/tutorial03.md",
    "content": "# 从零开始的 JSON 库教程（三）：解析字符串\n\n* Milo Yip\n* 2016/9/22\n\n本文是[《从零开始的 JSON 库教程》](https://zhuanlan.zhihu.com/json-tutorial)的第三个单元。本单元的练习源代码位于 [json-tutorial/tutorial03](https://github.com/miloyip/json-tutorial/blob/master/tutorial03)。\n\n本单元内容：\n\n1. [JSON 字符串语法](#1-json-字符串语法)\n2. [字符串表示](#2-字符串表示)\n3. [内存管理](#3-内存管理)\n4. [缓冲区与堆栈](#4-缓冲区与堆栈)\n5. [解析字符串](#5-解析字符串)\n6. [总结和练习](#6-总结和练习)\n7. [参考](#7-参考)\n8. [常见问题](#8-常见问题)\n\n## 1. JSON 字符串语法\n\nJSON 的字符串语法和 C 语言很相似，都是以双引号把字符括起来，如 `\"Hello\"`。但字符串采用了双引号作分隔，那么怎样可以在字符串中插入一个双引号？ 把 `a\"b` 写成 `\"a\"b\"` 肯定不行，都不知道那里是字符串的结束了。因此，我们需要引入转义字符（escape character），C 语言和 JSON 都使用 `\\`（反斜线）作为转义字符，那么 `\"` 在字符串中就表示为 `\\\"`，`a\"b` 的 JSON 字符串则写成 `\"a\\\"b\"`。如以下的字符串语法所示，JSON 共支持 9 种转义序列：\n\n~~~\nstring = quotation-mark *char quotation-mark\nchar = unescaped /\n   escape (\n       %x22 /          ; \"    quotation mark  U+0022\n       %x5C /          ; \\    reverse solidus U+005C\n       %x2F /          ; /    solidus         U+002F\n       %x62 /          ; b    backspace       U+0008\n       %x66 /          ; f    form feed       U+000C\n       %x6E /          ; n    line feed       U+000A\n       %x72 /          ; r    carriage return U+000D\n       %x74 /          ; t    tab             U+0009\n       %x75 4HEXDIG )  ; uXXXX                U+XXXX\nescape = %x5C          ; \\\nquotation-mark = %x22  ; \"\nunescaped = %x20-21 / %x23-5B / %x5D-10FFFF\n~~~\n\n简单翻译一下，JSON 字符串是由前后两个双引号夹着零至多个字符。字符分为无转义字符或转义序列。转义序列有 9 种，都是以反斜线开始，如常见的 `\\n` 代表换行符。比较特殊的是 `\\uXXXX`，当中 XXXX 为 16 进位的 UTF-16 编码，本单元将不处理这种转义序列，留待下回分解。\n\n无转义字符就是普通的字符，语法中列出了合法的码点范围（码点还是在下单元才介绍）。要注意的是，该范围不包括 0 至 31、双引号和反斜线，这些码点都必须要使用转义方式表示。\n\n## 2. 字符串表示\n\n在 C 语言中，字符串一般表示为空结尾字符串（null-terminated string），即以空字符（`'\\0'`）代表字符串的结束。然而，JSON 字符串是允许含有空字符的，例如这个 JSON `\"Hello\\u0000World\"` 就是单个字符串，解析后为11个字符。如果纯粹使用空结尾字符串来表示 JSON 解析后的结果，就没法处理空字符。\n\n因此，我们可以分配内存来储存解析后的字符，以及记录字符的数目（即字符串长度）。由于大部分 C 程序都假设字符串是空结尾字符串，我们还是在最后加上一个空字符，那么不需处理 `\\u0000` 这种字符的应用可以简单地把它当作是空结尾字符串。\n\n了解需求后，我们考虑实现。`lept_value` 事实上是一种变体类型（variant type），我们通过 `type` 来决定它现时是哪种类型，而这也决定了哪些成员是有效的。首先我们简单地在这个结构中加入两个成员：\n\n~~~c\ntypedef struct {\n    char* s;\n    size_t len;\n    double n;\n    lept_type type;\n}lept_value;\n~~~\n\n然而我们知道，一个值不可能同时为数字和字符串，因此我们可使用 C 语言的 `union` 来节省内存：\n\n~~~c\ntypedef struct {\n    union {\n        struct { char* s; size_t len; }s;  /* string */\n        double n;                          /* number */\n    }u;\n    lept_type type;\n}lept_value;\n~~~\n\n这两种设计在 32 位平台时的内存布局如下，可看出右方使用 `union` 的能省下内存。\n\n![union_layout](images/union_layout.png)\n\n我们要把之前的 `v->n` 改成 `v->u.n`。而要访问字符串的数据，则要使用 `v->u.s.s` 和 `v->u.s.len`。这种写法比较麻烦吧，其实 C11 新增了匿名 struct/union 语法，就可以采用 `v->n`、`v->s`、`v->len` 来作访问。\n\n## 3. 内存管理\n\n由于字符串的长度不是固定的，我们要动态分配内存。为简单起见，我们使用标准库 `<stdlib.h>` 中的 `malloc()`、`realloc()` 和 `free()` 来分配／释放内存。\n\n当设置一个值为字符串时，我们需要把参数中的字符串复制一份：\n\n~~~c\nvoid lept_set_string(lept_value* v, const char* s, size_t len) {\n    assert(v != NULL && (s != NULL || len == 0));\n    lept_free(v);\n    v->u.s.s = (char*)malloc(len + 1);\n    memcpy(v->u.s.s, s, len);\n    v->u.s.s[len] = '\\0';\n    v->u.s.len = len;\n    v->type = LEPT_STRING;\n}\n~~~\n\n断言中的条件是，非空指针（有具体的字符串）或是零长度的字符串都是合法的。\n\n注意，在设置这个 `v` 之前，我们需要先调用 `lept_free(v)` 去清空 `v` 可能分配到的内存。例如原来已有一字符串，我们要先把它释放。然后就是简单地用 `malloc()` 分配及用 `memcpy()` 复制，并补上结尾空字符。`malloc(len + 1)` 中的 1 是因为结尾空字符。\n\n那么，再看看 `lept_free()`：\n\n~~~c\nvoid lept_free(lept_value* v) {\n    assert(v != NULL);\n    if (v->type == LEPT_STRING)\n        free(v->u.s.s);\n    v->type = LEPT_NULL;\n}\n~~~\n\n现时仅当值是字符串类型，我们才要处理，之后我们还要加上对数组及对象的释放。`lept_free(v)` 之后，会把它的类型变成 null。这个设计能避免重复释放。\n\n但也由于我们会检查 `v` 的类型，在调用所有访问函数之前，我们必须初始化该类型。所以我们加入 `lept_init(v)`，因非常简单我们用宏实现：\n\n~~~c\n#define lept_init(v) do { (v)->type = LEPT_NULL; } while(0)\n~~~\n\n用上 `do { ... } while(0)` 是为了把表达式转为语句，模仿无返回值的函数。\n\n其实在前两个单元中，我们只提供读取值的 API，没有写入的 API，就是因为写入时我们还要考虑释放内存。我们在本单元中把它们补全：\n\n~~~c\n#define lept_set_null(v) lept_free(v)\n\nint lept_get_boolean(const lept_value* v);\nvoid lept_set_boolean(lept_value* v, int b);\n\ndouble lept_get_number(const lept_value* v);\nvoid lept_set_number(lept_value* v, double n);\n\nconst char* lept_get_string(const lept_value* v);\nsize_t lept_get_string_length(const lept_value* v);\nvoid lept_set_string(lept_value* v, const char* s, size_t len);\n~~~\n\n由于 `lept_free()` 实际上也会把 `v` 变成 null 值，我们只用一个宏来提供 `lept_set_null()` 这个 API。\n\n应用方的代码在调用 `lept_parse()` 之后，最终也应该调用 `lept_free()` 去释放内存。我们把之前的单元测试也加入此调用。\n\n如果不使用 `lept_parse()`，我们需要初始化值，那么就像以下的单元测试，先 `lept_init()`，最后 `lept_free()`。\n\n~~~c\nstatic void test_access_string() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"\", 0);\n    EXPECT_EQ_STRING(\"\", lept_get_string(&v), lept_get_string_length(&v));\n    lept_set_string(&v, \"Hello\", 5);\n    EXPECT_EQ_STRING(\"Hello\", lept_get_string(&v), lept_get_string_length(&v));\n    lept_free(&v);\n}\n~~~\n\n## 4. 缓冲区与堆栈\n\n我们解析字符串（以及之后的数组、对象）时，需要把解析的结果先储存在一个临时的缓冲区，最后再用 `lept_set_string()` 把缓冲区的结果设进值之中。在完成解析一个字符串之前，这个缓冲区的大小是不能预知的。因此，我们可以采用动态数组（dynamic array）这种数据结构，即数组空间不足时，能自动扩展。C++ 标准库的 `std::vector` 也是一种动态数组。\n\n如果每次解析字符串时，都重新建一个动态数组，那么是比较耗时的。我们可以重用这个动态数组，每次解析 JSON 时就只需要创建一个。而且我们将会发现，无论是解析字符串、数组或对象，我们也只需要以先进后出的方式访问这个动态数组。换句话说，我们需要一个动态的堆栈（stack）数据结构。\n\n我们把一个动态堆栈的数据放进 `lept_context` 里：\n\n~~~c\ntypedef struct {\n    const char* json;\n    char* stack;\n    size_t size, top;\n}lept_context;\n~~~\n\n当中 `size` 是当前的堆栈容量，`top` 是栈顶的位置（由于我们会扩展 `stack`，所以不要把 `top` 用指针形式存储）。\n\n然后，我们在创建 `lept_context` 的时候初始化 `stack` 并最终释放内存：\n\n~~~c\nint lept_parse(lept_value* v, const char* json) {\n    lept_context c;\n    int ret;\n    assert(v != NULL);\n    c.json = json;\n    c.stack = NULL;        /* <- */\n    c.size = c.top = 0;    /* <- */\n    lept_init(v);\n    lept_parse_whitespace(&c);\n    if ((ret = lept_parse_value(&c, v)) == LEPT_PARSE_OK) {\n        /* ... */\n    }\n    assert(c.top == 0);    /* <- */\n    free(c.stack);         /* <- */\n    return ret;\n}\n~~~\n\n在释放时，加入了断言确保所有数据都被弹出。\n\n然后，我们实现堆栈的压入及弹出操作。和普通的堆栈不一样，我们这个堆栈是以字节储存的。每次可要求压入任意大小的数据，它会返回数据起始的指针（会 C++ 的同学可再参考[1]）：\n\n~~~c\n#ifndef LEPT_PARSE_STACK_INIT_SIZE\n#define LEPT_PARSE_STACK_INIT_SIZE 256\n#endif\n\nstatic void* lept_context_push(lept_context* c, size_t size) {\n    void* ret;\n    assert(size > 0);\n    if (c->top + size >= c->size) {\n        if (c->size == 0)\n            c->size = LEPT_PARSE_STACK_INIT_SIZE;\n        while (c->top + size >= c->size)\n            c->size += c->size >> 1;  /* c->size * 1.5 */\n        c->stack = (char*)realloc(c->stack, c->size);\n    }\n    ret = c->stack + c->top;\n    c->top += size;\n    return ret;\n}\n\nstatic void* lept_context_pop(lept_context* c, size_t size) {\n    assert(c->top >= size);\n    return c->stack + (c->top -= size);\n}\n~~~\n\n压入时若空间不足，便回以 1.5 倍大小扩展。为什么是 1.5 倍而不是两倍？可参考我在 [STL 的 vector 有哪些封装上的技巧？](https://www.zhihu.com/question/25079705/answer/30030883) 的答案。\n\n注意到这里使用了 [`realloc()`](https://en.cppreference.com/w/c/memory/realloc) 来重新分配内存，`c->stack` 在初始化时为 `NULL`，`realloc(NULL, size)` 的行为是等价于 `malloc(size)` 的，所以我们不需要为第一次分配内存作特别处理。\n\n另外，我们把初始大小以宏 `LEPT_PARSE_STACK_INIT_SIZE` 的形式定义，使用 `#ifndef X #define X ... #endif` 方式的好处是，使用者可在编译选项中自行设置宏，没设置的话就用缺省值。\n\n## 5. 解析字符串\n\n有了以上的工具，解析字符串的任务就变得很简单。我们只需要先备份栈顶，然后把解析到的字符压栈，最后计算出长度并一次性把所有字符弹出，再设置至值里便可以。以下是部分实现，没有处理转义和一些不合法字符的校验。\n\n~~~c\n#define PUTC(c, ch) do { *(char*)lept_context_push(c, sizeof(char)) = (ch); } while(0)\n\nstatic int lept_parse_string(lept_context* c, lept_value* v) {\n    size_t head = c->top, len;\n    const char* p;\n    EXPECT(c, '\\\"');\n    p = c->json;\n    for (;;) {\n        char ch = *p++;\n        switch (ch) {\n            case '\\\"':\n                len = c->top - head;\n                lept_set_string(v, (const char*)lept_context_pop(c, len), len);\n                c->json = p;\n                return LEPT_PARSE_OK;\n            case '\\0':\n                c->top = head;\n                return LEPT_PARSE_MISS_QUOTATION_MARK;\n            default:\n                PUTC(c, ch);\n        }\n    }\n}\n~~~\n\n## 6. 总结和练习\n\n之前的单元都是固定长度的数据类型（fixed length data type），而字符串类型是可变长度的数据类型（variable length data type），因此本单元花了较多篇幅讲述内存管理和数据结构的设计和实现。字符串的解析相对数字简单，以下的习题难度不高，同学们应该可轻松完成。\n\n1. 编写 `lept_get_boolean()` 等访问函数的单元测试，然后实现。\n2. 实现除了 `\\u` 以外的转义序列解析，令 `test_parse_string()` 中所有测试通过。\n3. 解决 `test_parse_invalid_string_escape()` 和 `test_parse_invalid_string_char()` 中的失败测试。\n4. 思考如何优化 `test_parse_string()` 的性能，那些优化方法有没有缺点。\n\n如果你遇到问题，有不理解的地方，或是有建议，都欢迎在评论或 [issue](https://github.com/miloyip/json-tutorial/issues) 中提出，让所有人一起讨论。\n\n## 7. 参考\n\n[1] [RapidJSON 代码剖析（一）：混合任意类型的堆栈](https://zhuanlan.zhihu.com/p/20029820)\n\n# 8. 常见问题\n\n其他常见问答将会从评论中整理。\n"
  },
  {
    "path": "tutorial03_answer/CMakeLists.txt",
    "content": "cmake_minimum_required (VERSION 2.6)\nproject (leptjson_test C)\n\nif (CMAKE_C_COMPILER_ID MATCHES \"GNU|Clang\")\n    set(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -ansi -pedantic -Wall\")\nendif()\n\nadd_library(leptjson leptjson.c)\nadd_executable(leptjson_test test.c)\ntarget_link_libraries(leptjson_test leptjson)\n"
  },
  {
    "path": "tutorial03_answer/leptjson.c",
    "content": "#ifdef _WINDOWS\n#define _CRTDBG_MAP_ALLOC\n#include <crtdbg.h>\n#endif\n#include \"leptjson.h\"\n#include <assert.h>  /* assert() */\n#include <errno.h>   /* errno, ERANGE */\n#include <math.h>    /* HUGE_VAL */\n#include <stdlib.h>  /* NULL, malloc(), realloc(), free(), strtod() */\n#include <string.h>  /* memcpy() */\n\n#ifndef LEPT_PARSE_STACK_INIT_SIZE\n#define LEPT_PARSE_STACK_INIT_SIZE 256\n#endif\n\n#define EXPECT(c, ch)       do { assert(*c->json == (ch)); c->json++; } while(0)\n#define ISDIGIT(ch)         ((ch) >= '0' && (ch) <= '9')\n#define ISDIGIT1TO9(ch)     ((ch) >= '1' && (ch) <= '9')\n#define PUTC(c, ch)         do { *(char*)lept_context_push(c, sizeof(char)) = (ch); } while(0)\n\ntypedef struct {\n    const char* json;\n    char* stack;\n    size_t size, top;\n}lept_context;\n\nstatic void* lept_context_push(lept_context* c, size_t size) {\n    void* ret;\n    assert(size > 0);\n    if (c->top + size >= c->size) {\n        if (c->size == 0)\n            c->size = LEPT_PARSE_STACK_INIT_SIZE;\n        while (c->top + size >= c->size)\n            c->size += c->size >> 1;  /* c->size * 1.5 */\n        c->stack = (char*)realloc(c->stack, c->size);\n    }\n    ret = c->stack + c->top;\n    c->top += size;\n    return ret;\n}\n\nstatic void* lept_context_pop(lept_context* c, size_t size) {\n    assert(c->top >= size);\n    return c->stack + (c->top -= size);\n}\n\nstatic void lept_parse_whitespace(lept_context* c) {\n    const char *p = c->json;\n    while (*p == ' ' || *p == '\\t' || *p == '\\n' || *p == '\\r')\n        p++;\n    c->json = p;\n}\n\nstatic int lept_parse_literal(lept_context* c, lept_value* v, const char* literal, lept_type type) {\n    size_t i;\n    EXPECT(c, literal[0]);\n    for (i = 0; literal[i + 1]; i++)\n        if (c->json[i] != literal[i + 1])\n            return LEPT_PARSE_INVALID_VALUE;\n    c->json += i;\n    v->type = type;\n    return LEPT_PARSE_OK;\n}\n\nstatic int lept_parse_number(lept_context* c, lept_value* v) {\n    const char* p = c->json;\n    if (*p == '-') p++;\n    if (*p == '0') p++;\n    else {\n        if (!ISDIGIT1TO9(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n    if (*p == '.') {\n        p++;\n        if (!ISDIGIT(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n    if (*p == 'e' || *p == 'E') {\n        p++;\n        if (*p == '+' || *p == '-') p++;\n        if (!ISDIGIT(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n    errno = 0;\n    v->u.n = strtod(c->json, NULL);\n    if (errno == ERANGE && (v->u.n == HUGE_VAL || v->u.n == -HUGE_VAL))\n        return LEPT_PARSE_NUMBER_TOO_BIG;\n    v->type = LEPT_NUMBER;\n    c->json = p;\n    return LEPT_PARSE_OK;\n}\n\nstatic int lept_parse_string(lept_context* c, lept_value* v) {\n    size_t head = c->top, len;\n    const char* p;\n    EXPECT(c, '\\\"');\n    p = c->json;\n    for (;;) {\n        char ch = *p++;\n        switch (ch) {\n            case '\\\"':\n                len = c->top - head;\n                lept_set_string(v, (const char*)lept_context_pop(c, len), len);\n                c->json = p;\n                return LEPT_PARSE_OK;\n            case '\\\\':\n                switch (*p++) {\n                    case '\\\"': PUTC(c, '\\\"'); break;\n                    case '\\\\': PUTC(c, '\\\\'); break;\n                    case '/':  PUTC(c, '/' ); break;\n                    case 'b':  PUTC(c, '\\b'); break;\n                    case 'f':  PUTC(c, '\\f'); break;\n                    case 'n':  PUTC(c, '\\n'); break;\n                    case 'r':  PUTC(c, '\\r'); break;\n                    case 't':  PUTC(c, '\\t'); break;\n                    default:\n                        c->top = head;\n                        return LEPT_PARSE_INVALID_STRING_ESCAPE;\n                }\n                break;\n            case '\\0':\n                c->top = head;\n                return LEPT_PARSE_MISS_QUOTATION_MARK;\n            default:\n                if ((unsigned char)ch < 0x20) { \n                    c->top = head;\n                    return LEPT_PARSE_INVALID_STRING_CHAR;\n                }\n                PUTC(c, ch);\n        }\n    }\n}\n\nstatic int lept_parse_value(lept_context* c, lept_value* v) {\n    switch (*c->json) {\n        case 't':  return lept_parse_literal(c, v, \"true\", LEPT_TRUE);\n        case 'f':  return lept_parse_literal(c, v, \"false\", LEPT_FALSE);\n        case 'n':  return lept_parse_literal(c, v, \"null\", LEPT_NULL);\n        default:   return lept_parse_number(c, v);\n        case '\"':  return lept_parse_string(c, v);\n        case '\\0': return LEPT_PARSE_EXPECT_VALUE;\n    }\n}\n\nint lept_parse(lept_value* v, const char* json) {\n    lept_context c;\n    int ret;\n    assert(v != NULL);\n    c.json = json;\n    c.stack = NULL;\n    c.size = c.top = 0;\n    lept_init(v);\n    lept_parse_whitespace(&c);\n    if ((ret = lept_parse_value(&c, v)) == LEPT_PARSE_OK) {\n        lept_parse_whitespace(&c);\n        if (*c.json != '\\0') {\n            v->type = LEPT_NULL;\n            ret = LEPT_PARSE_ROOT_NOT_SINGULAR;\n        }\n    }\n    assert(c.top == 0);\n    free(c.stack);\n    return ret;\n}\n\nvoid lept_free(lept_value* v) {\n    assert(v != NULL);\n    if (v->type == LEPT_STRING)\n        free(v->u.s.s);\n    v->type = LEPT_NULL;\n}\n\nlept_type lept_get_type(const lept_value* v) {\n    assert(v != NULL);\n    return v->type;\n}\n\nint lept_get_boolean(const lept_value* v) {\n    assert(v != NULL && (v->type == LEPT_TRUE || v->type == LEPT_FALSE));\n    return v->type == LEPT_TRUE;\n}\n\nvoid lept_set_boolean(lept_value* v, int b) {\n    lept_free(v);\n    v->type = b ? LEPT_TRUE : LEPT_FALSE;\n}\n\ndouble lept_get_number(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_NUMBER);\n    return v->u.n;\n}\n\nvoid lept_set_number(lept_value* v, double n) {\n    lept_free(v);\n    v->u.n = n;\n    v->type = LEPT_NUMBER;\n}\n\nconst char* lept_get_string(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_STRING);\n    return v->u.s.s;\n}\n\nsize_t lept_get_string_length(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_STRING);\n    return v->u.s.len;\n}\n\nvoid lept_set_string(lept_value* v, const char* s, size_t len) {\n    assert(v != NULL && (s != NULL || len == 0));\n    lept_free(v);\n    v->u.s.s = (char*)malloc(len + 1);\n    memcpy(v->u.s.s, s, len);\n    v->u.s.s[len] = '\\0';\n    v->u.s.len = len;\n    v->type = LEPT_STRING;\n}\n"
  },
  {
    "path": "tutorial03_answer/leptjson.h",
    "content": "#ifndef LEPTJSON_H__\n#define LEPTJSON_H__\n\n#include <stddef.h> /* size_t */\n\ntypedef enum { LEPT_NULL, LEPT_FALSE, LEPT_TRUE, LEPT_NUMBER, LEPT_STRING, LEPT_ARRAY, LEPT_OBJECT } lept_type;\n\ntypedef struct {\n    union {\n        struct { char* s; size_t len; }s;  /* string: null-terminated string, string length */\n        double n;                          /* number */\n    }u;\n    lept_type type;\n}lept_value;\n\nenum {\n    LEPT_PARSE_OK = 0,\n    LEPT_PARSE_EXPECT_VALUE,\n    LEPT_PARSE_INVALID_VALUE,\n    LEPT_PARSE_ROOT_NOT_SINGULAR,\n    LEPT_PARSE_NUMBER_TOO_BIG,\n    LEPT_PARSE_MISS_QUOTATION_MARK,\n    LEPT_PARSE_INVALID_STRING_ESCAPE,\n    LEPT_PARSE_INVALID_STRING_CHAR\n};\n\n#define lept_init(v) do { (v)->type = LEPT_NULL; } while(0)\n\nint lept_parse(lept_value* v, const char* json);\n\nvoid lept_free(lept_value* v);\n\nlept_type lept_get_type(const lept_value* v);\n\n#define lept_set_null(v) lept_free(v)\n\nint lept_get_boolean(const lept_value* v);\nvoid lept_set_boolean(lept_value* v, int b);\n\ndouble lept_get_number(const lept_value* v);\nvoid lept_set_number(lept_value* v, double n);\n\nconst char* lept_get_string(const lept_value* v);\nsize_t lept_get_string_length(const lept_value* v);\nvoid lept_set_string(lept_value* v, const char* s, size_t len);\n\n#endif /* LEPTJSON_H__ */\n"
  },
  {
    "path": "tutorial03_answer/test.c",
    "content": "#ifdef _WINDOWS\n#define _CRTDBG_MAP_ALLOC\n#include <crtdbg.h>\n#endif\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"leptjson.h\"\n\nstatic int main_ret = 0;\nstatic int test_count = 0;\nstatic int test_pass = 0;\n\n#define EXPECT_EQ_BASE(equality, expect, actual, format) \\\n    do {\\\n        test_count++;\\\n        if (equality)\\\n            test_pass++;\\\n        else {\\\n            fprintf(stderr, \"%s:%d: expect: \" format \" actual: \" format \"\\n\", __FILE__, __LINE__, expect, actual);\\\n            main_ret = 1;\\\n        }\\\n    } while(0)\n\n#define EXPECT_EQ_INT(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, \"%d\")\n#define EXPECT_EQ_DOUBLE(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, \"%.17g\")\n#define EXPECT_EQ_STRING(expect, actual, alength) \\\n    EXPECT_EQ_BASE(sizeof(expect) - 1 == alength && memcmp(expect, actual, alength) == 0, expect, actual, \"%s\")\n#define EXPECT_TRUE(actual) EXPECT_EQ_BASE((actual) != 0, \"true\", \"false\", \"%s\")\n#define EXPECT_FALSE(actual) EXPECT_EQ_BASE((actual) == 0, \"false\", \"true\", \"%s\")\n\nstatic void test_parse_null() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_boolean(&v, 0);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"null\"));\n    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\n    lept_free(&v);\n}\n\nstatic void test_parse_true() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_boolean(&v, 0);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"true\"));\n    EXPECT_EQ_INT(LEPT_TRUE, lept_get_type(&v));\n    lept_free(&v);\n}\n\nstatic void test_parse_false() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_boolean(&v, 1);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"false\"));\n    EXPECT_EQ_INT(LEPT_FALSE, lept_get_type(&v));\n    lept_free(&v);\n}\n\n#define TEST_NUMBER(expect, json)\\\n    do {\\\n        lept_value v;\\\n        lept_init(&v);\\\n        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(&v));\\\n        EXPECT_EQ_DOUBLE(expect, lept_get_number(&v));\\\n        lept_free(&v);\\\n    } while(0)\n\nstatic void test_parse_number() {\n    TEST_NUMBER(0.0, \"0\");\n    TEST_NUMBER(0.0, \"-0\");\n    TEST_NUMBER(0.0, \"-0.0\");\n    TEST_NUMBER(1.0, \"1\");\n    TEST_NUMBER(-1.0, \"-1\");\n    TEST_NUMBER(1.5, \"1.5\");\n    TEST_NUMBER(-1.5, \"-1.5\");\n    TEST_NUMBER(3.1416, \"3.1416\");\n    TEST_NUMBER(1E10, \"1E10\");\n    TEST_NUMBER(1e10, \"1e10\");\n    TEST_NUMBER(1E+10, \"1E+10\");\n    TEST_NUMBER(1E-10, \"1E-10\");\n    TEST_NUMBER(-1E10, \"-1E10\");\n    TEST_NUMBER(-1e10, \"-1e10\");\n    TEST_NUMBER(-1E+10, \"-1E+10\");\n    TEST_NUMBER(-1E-10, \"-1E-10\");\n    TEST_NUMBER(1.234E+10, \"1.234E+10\");\n    TEST_NUMBER(1.234E-10, \"1.234E-10\");\n    TEST_NUMBER(0.0, \"1e-10000\"); /* must underflow */\n\n    TEST_NUMBER(1.0000000000000002, \"1.0000000000000002\"); /* the smallest number > 1 */\n    TEST_NUMBER( 4.9406564584124654e-324, \"4.9406564584124654e-324\"); /* minimum denormal */\n    TEST_NUMBER(-4.9406564584124654e-324, \"-4.9406564584124654e-324\");\n    TEST_NUMBER( 2.2250738585072009e-308, \"2.2250738585072009e-308\");  /* Max subnormal double */\n    TEST_NUMBER(-2.2250738585072009e-308, \"-2.2250738585072009e-308\");\n    TEST_NUMBER( 2.2250738585072014e-308, \"2.2250738585072014e-308\");  /* Min normal positive double */\n    TEST_NUMBER(-2.2250738585072014e-308, \"-2.2250738585072014e-308\");\n    TEST_NUMBER( 1.7976931348623157e+308, \"1.7976931348623157e+308\");  /* Max double */\n    TEST_NUMBER(-1.7976931348623157e+308, \"-1.7976931348623157e+308\");\n}\n\n#define TEST_STRING(expect, json)\\\n    do {\\\n        lept_value v;\\\n        lept_init(&v);\\\n        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_STRING, lept_get_type(&v));\\\n        EXPECT_EQ_STRING(expect, lept_get_string(&v), lept_get_string_length(&v));\\\n        lept_free(&v);\\\n    } while(0)\n\nstatic void test_parse_string() {\n    TEST_STRING(\"\", \"\\\"\\\"\");\n    TEST_STRING(\"Hello\", \"\\\"Hello\\\"\");\n    TEST_STRING(\"Hello\\nWorld\", \"\\\"Hello\\\\nWorld\\\"\");\n    TEST_STRING(\"\\\" \\\\ / \\b \\f \\n \\r \\t\", \"\\\"\\\\\\\" \\\\\\\\ \\\\/ \\\\b \\\\f \\\\n \\\\r \\\\t\\\"\");\n}\n\n#define TEST_ERROR(error, json)\\\n    do {\\\n        lept_value v;\\\n        lept_init(&v);\\\n        v.type = LEPT_FALSE;\\\n        EXPECT_EQ_INT(error, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\\\n        lept_free(&v);\\\n    } while(0)\n\nstatic void test_parse_expect_value() {\n    TEST_ERROR(LEPT_PARSE_EXPECT_VALUE, \"\");\n    TEST_ERROR(LEPT_PARSE_EXPECT_VALUE, \" \");\n}\n\nstatic void test_parse_invalid_value() {\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"nul\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"?\");\n\n    /* invalid number */\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"+0\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"+1\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \".123\"); /* at least one digit before '.' */\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"1.\");   /* at least one digit after '.' */\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"INF\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"inf\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"NAN\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"nan\");\n}\n\nstatic void test_parse_root_not_singular() {\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"null x\");\n\n    /* invalid number */\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0123\"); /* after zero should be '.' or nothing */\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0x0\");\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0x123\");\n}\n\nstatic void test_parse_number_too_big() {\n    TEST_ERROR(LEPT_PARSE_NUMBER_TOO_BIG, \"1e309\");\n    TEST_ERROR(LEPT_PARSE_NUMBER_TOO_BIG, \"-1e309\");\n}\n\nstatic void test_parse_missing_quotation_mark() {\n    TEST_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK, \"\\\"\");\n    TEST_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK, \"\\\"abc\");\n}\n\nstatic void test_parse_invalid_string_escape() {\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\v\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\'\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\0\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\x12\\\"\");\n}\n\nstatic void test_parse_invalid_string_char() {\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_CHAR, \"\\\"\\x01\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_CHAR, \"\\\"\\x1F\\\"\");\n}\n\nstatic void test_access_null() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"a\", 1);\n    lept_set_null(&v);\n    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\n    lept_free(&v);\n}\n\nstatic void test_access_boolean() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"a\", 1);\n    lept_set_boolean(&v, 1);\n    EXPECT_TRUE(lept_get_boolean(&v));\n    lept_set_boolean(&v, 0);\n    EXPECT_FALSE(lept_get_boolean(&v));\n    lept_free(&v);\n}\n\nstatic void test_access_number() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"a\", 1);\n    lept_set_number(&v, 1234.5);\n    EXPECT_EQ_DOUBLE(1234.5, lept_get_number(&v));\n    lept_free(&v);\n}\n\nstatic void test_access_string() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"\", 0);\n    EXPECT_EQ_STRING(\"\", lept_get_string(&v), lept_get_string_length(&v));\n    lept_set_string(&v, \"Hello\", 5);\n    EXPECT_EQ_STRING(\"Hello\", lept_get_string(&v), lept_get_string_length(&v));\n    lept_free(&v);\n}\n\nstatic void test_parse() {\n    test_parse_null();\n    test_parse_true();\n    test_parse_false();\n    test_parse_number();\n    test_parse_string();\n    test_parse_expect_value();\n    test_parse_invalid_value();\n    test_parse_root_not_singular();\n    test_parse_number_too_big();\n    test_parse_missing_quotation_mark();\n    test_parse_invalid_string_escape();\n    test_parse_invalid_string_char();\n\n    test_access_null();\n    test_access_boolean();\n    test_access_number();\n    test_access_string();\n}\n\nint main() {\n#ifdef _WINDOWS\n    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);\n#endif\n    test_parse();\n    printf(\"%d/%d (%3.2f%%) passed\\n\", test_pass, test_count, test_pass * 100.0 / test_count);\n    return main_ret;\n}\n"
  },
  {
    "path": "tutorial03_answer/tutorial03_answer.md",
    "content": "# 从零开始的 JSON 库教程（三）：解析字符串解答篇\n\n* Milo Yip\n* 2016/9/27\n\n本文是[《从零开始的 JSON 库教程》](https://zhuanlan.zhihu.com/json-tutorial)的第三个单元解答编。解答代码位于 [json-tutorial/tutorial03_answer](https://github.com/miloyip/json-tutorial/blob/master/tutorial03_answer)。\n\n## 1. 访问的单元测试\n\n在编写单元测试时，我们故意先把值设为字符串，那么做可以测试设置其他类型时，有没有调用 `lept_free()` 去释放内存。\n\n~~~c\nstatic void test_access_boolean() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"a\", 1);\n    lept_set_boolean(&v, 1);\n    EXPECT_TRUE(lept_get_boolean(&v));\n    lept_set_boolean(&v, 0);\n    EXPECT_FALSE(lept_get_boolean(&v));\n    lept_free(&v);\n}\n\nstatic void test_access_number() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"a\", 1);\n    lept_set_number(&v, 1234.5);\n    EXPECT_EQ_DOUBLE(1234.5, lept_get_number(&v));\n    lept_free(&v);\n}\n~~~\n\n以下是访问函数的实现：\n\n~~~c\nint lept_get_boolean(const lept_value* v) {\n    assert(v != NULL && (v->type == LEPT_TRUE || v->type == LEPT_FALSE));\n    return v->type == LEPT_TRUE;\n}\n\nvoid lept_set_boolean(lept_value* v, int b) {\n    lept_free(v);\n    v->type = b ? LEPT_TRUE : LEPT_FALSE;\n}\n\ndouble lept_get_number(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_NUMBER);\n    return v->u.n;\n}\n\nvoid lept_set_number(lept_value* v, double n) {\n    lept_free(v);\n    v->u.n = n;\n    v->type = LEPT_NUMBER;\n}\n~~~\n\n那问题是，如果我们没有调用 `lept_free()`，怎样能发现这些内存泄漏？\n\n## 1A. Windows 下的内存泄漏检测方法\n\n在 Windows 下，可使用 Visual C++ 的 [C Runtime Library（CRT） 检测内存泄漏](https://msdn.microsoft.com/zh-cn/library/x98tx3cf.aspx)。\n\n首先，我们在两个 .c 文件首行插入这一段代码：\n\n~~~c\n#ifdef _WINDOWS\n#define _CRTDBG_MAP_ALLOC\n#include <crtdbg.h>\n#endif\n~~~\n\n并在 `main()` 开始位置插入：\n\n~~~c\nint main() {\n#ifdef _WINDOWS\n    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);\n#endif\n~~~\n\n在 Debug 配置下按 F5 生成、开始调试程序，没有任何异样。\n\n然后，我们删去 `lept_set_boolean()` 中的 `lept_free(v)`：\n\n~~~c\nvoid lept_set_boolean(lept_value* v, int b) {\n    /* lept_free(v); */\n    v->type = b ? LEPT_TRUE : LEPT_FALSE;\n}\n~~~\n\n再次按 F5 生成、开始调试程序，在输出会看到内存泄漏信息：\n\n~~~\nDetected memory leaks!\nDumping objects ->\nC:\\GitHub\\json-tutorial\\tutorial03_answer\\leptjson.c(212) : {79} normal block at 0x013D9868, 2 bytes long.\n Data: <a > 61 00 \nObject dump complete.\n~~~\n\n这正是我们在单元测试中，先设置字符串，然后设布尔值时没释放字符串所分配的内存。比较麻烦的是，它没有显示调用堆栈。从输出信息中 `... {79} ...` 我们知道是第 79 次分配的内存做成问题，我们可以加上 `_CrtSetBreakAlloc(79);` 来调试，那么它便会在第 79 次时中断于分配调用的位置，那时候就能从调用堆栈去找出来龙去脉。\n\n## 1B. Linux/OSX 下的内存泄漏检测方法\n\n在 Linux、OS X 下，我们可以使用 [valgrind](https://valgrind.org/) 工具（用 `apt-get install valgrind`、 `brew install valgrind`）。我们完全不用修改代码，只要在命令行执行：\n\n~~~\n$ valgrind --leak-check=full  ./leptjson_test\n==22078== Memcheck, a memory error detector\n==22078== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.\n==22078== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info\n==22078== Command: ./leptjson_test\n==22078== \n--22078-- run: /usr/bin/dsymutil \"./leptjson_test\"\n160/160 (100.00%) passed\n==22078== \n==22078== HEAP SUMMARY:\n==22078==     in use at exit: 27,728 bytes in 209 blocks\n==22078==   total heap usage: 301 allocs, 92 frees, 34,966 bytes allocated\n==22078== \n==22078== 2 bytes in 1 blocks are definitely lost in loss record 1 of 79\n==22078==    at 0x100012EBB: malloc (in /usr/local/Cellar/valgrind/3.11.0/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)\n==22078==    by 0x100008F36: lept_set_string (leptjson.c:208)\n==22078==    by 0x100008415: test_access_boolean (test.c:187)\n==22078==    by 0x100001849: test_parse (test.c:229)\n==22078==    by 0x1000017A3: main (test.c:235)\n==22078== \n...\n~~~\n\n它发现了在 `test_access_boolean()` 中，由 `lept_set_string()` 分配的 2 个字节（`\"a\"`）泄漏了。\n\nValgrind 还有很多功能，例如可以发现未初始化变量。我们若在应用程序或测试程序中，忘了调用 `lept_init(&v)`，那么 `v.type` 的值没被初始化，其值是不确定的（indeterministic），一些函数如果读取那个值就会出现问题：\n\n~~~c\nstatic void test_access_boolean() {\n    lept_value v;\n    /* lept_init(&v); */\n    lept_set_string(&v, \"a\", 1);\n    ...\n}\n~~~\n\n这种错误有时候测试时能正确运行（刚好 `v.type` 被设为 `0`），使我们误以为程序正确，而在发布后一些机器上却可能崩溃。这种误以为正确的假像是很危险的，我们可利用 valgrind 能自动测出来：\n\n~~~\n$ valgrind --leak-check=full  ./leptjson_test\n...\n==22174== Conditional jump or move depends on uninitialised value(s)\n==22174==    at 0x100008B5D: lept_free (leptjson.c:164)\n==22174==    by 0x100008F26: lept_set_string (leptjson.c:207)\n==22174==    by 0x1000083FE: test_access_boolean (test.c:187)\n==22174==    by 0x100001839: test_parse (test.c:229)\n==22174==    by 0x100001793: main (test.c:235)\n==22174== \n~~~\n\n它发现 `lept_free()` 中依靠了一个未初始化的值来跳转，就是 `v.type`，而错误是沿自 `test_access_boolean()`。\n\n编写单元测试时，应考虑哪些执行次序会有机会出错，例如内存相关的错误。然后我们可以利用 TDD 的步骤，先令测试失败（以内存工具检测），修正代码，再确认测试是否成功。\n\n## 2. 转义序列的解析\n\n转义序列的解析很直观，对其他不合法的字符返回 `LEPT_PARSE_INVALID_STRING_ESCAPE`：\n\n~~~c\nstatic int lept_parse_string(lept_context* c, lept_value* v) {\n    /* ... */\n    for (;;) {\n        char ch = *p++;\n        switch (ch) {\n            /* ... */\n            case '\\\\':\n                switch (*p++) {\n                    case '\\\"': PUTC(c, '\\\"'); break;\n                    case '\\\\': PUTC(c, '\\\\'); break;\n                    case '/':  PUTC(c, '/' ); break;\n                    case 'b':  PUTC(c, '\\b'); break;\n                    case 'f':  PUTC(c, '\\f'); break;\n                    case 'n':  PUTC(c, '\\n'); break;\n                    case 'r':  PUTC(c, '\\r'); break;\n                    case 't':  PUTC(c, '\\t'); break;\n                    default:\n                        c->top = head;\n                        return LEPT_PARSE_INVALID_STRING_ESCAPE;\n                }\n                break;\n           /* ... */\n       }\n   }\n }\n~~~\n\n## 3. 不合法的字符串\n\n上面已解决不合法转义，余下部分的唯一难度，是要从语法中知道哪些是不合法字符：\n\n~~~\nunescaped = %x20-21 / %x23-5B / %x5D-10FFFF\n~~~\n\n当中空缺的 %x22 是双引号，%x5C 是反斜线，都已经处理。所以不合法的字符是 %x00 至 %x1F。我们简单地在 default 里处理：\n\n~~~c\n        /* ... */\n            default:\n                if ((unsigned char)ch < 0x20) { \n                    c->top = head;\n                    return LEPT_PARSE_INVALID_STRING_CHAR;\n                }\n                PUTC(c, ch);\n        /* ... */\n~~~\n\n注意到 `char` 带不带符号，是实现定义的。如果编译器定义 `char` 为带符号的话，`(unsigned char)ch >= 0x80` 的字符，都会变成负数，并产生 `LEPT_PARSE_INVALID_STRING_CHAR` 错误。我们现时还没有测试 ASCII 以外的字符，所以有没有转型至不带符号都不影响，但下一单元开始处理 Unicode 的时候就要考虑了。\n\n## 4. 性能优化的思考\n\n这是本教程第一次的开放式问题，没有标准答案。以下列出一些我想到的。\n\n1. 如果整个字符串都没有转义符，我们不就是把字符复制了两次？第一次是从 `json` 到 `stack`，第二次是从 `stack` 到 `v->u.s.s`。我们可以在 `json` 扫描 `'\\0'`、`'\\\"'` 和 `'\\\\'` 3 个字符（ `ch < 0x20` 还是要检查），直至它们其中一个出现，才开始用现在的解析方法。这样做的话，前半没转义的部分可以只复制一次。缺点是，代码变得复杂一些，我们也不能使用 `lept_set_string()`。\n2. 对于扫描没转义部分，我们可考虑用 SIMD 加速，如 [RapidJSON 代码剖析（二）：使用 SSE4.2 优化字符串扫描](https://zhuanlan.zhihu.com/p/20037058) 的做法。这类底层优化的缺点是不跨平台，需要设置编译选项等。\n3. 在 gcc/clang 上使用 `__builtin_expect()` 指令来处理低概率事件，例如需要对每个字符做 `LEPT_PARSE_INVALID_STRING_CHAR` 检测，我们可以假设出现不合法字符是低概率事件，然后用这个指令告之编译器，那么编译器可能可生成较快的代码。然而，这类做法明显是不跨编译器，甚至是某个版本后的 gcc 才支持。\n\n## 5. 总结\n\n本解答篇除了给出一些建议方案，也介绍了内存泄漏的检测方法。JSON 字符串本身的语法并不复杂，但它需要相关的内存分配与数据结构的设计，还好这些设计都能用于之后的数组和对象类型。下一单元专门针对 Unicode，这部分也是许多 JSON 库没有妥善处理的地方。\n\n如果你遇到问题，有不理解的地方，或是有建议，都欢迎在评论或 [issue](https://github.com/miloyip/json-tutorial/issues) 中提出，让所有人一起讨论。\n"
  },
  {
    "path": "tutorial04/CMakeLists.txt",
    "content": "cmake_minimum_required (VERSION 2.6)\nproject (leptjson_test C)\n\nif (CMAKE_C_COMPILER_ID MATCHES \"GNU|Clang\")\n    set(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -ansi -pedantic -Wall\")\nendif()\n\nadd_library(leptjson leptjson.c)\nadd_executable(leptjson_test test.c)\ntarget_link_libraries(leptjson_test leptjson)\n"
  },
  {
    "path": "tutorial04/leptjson.c",
    "content": "#ifdef _WINDOWS\n#define _CRTDBG_MAP_ALLOC\n#include <crtdbg.h>\n#endif\n#include \"leptjson.h\"\n#include <assert.h>  /* assert() */\n#include <errno.h>   /* errno, ERANGE */\n#include <math.h>    /* HUGE_VAL */\n#include <stdlib.h>  /* NULL, malloc(), realloc(), free(), strtod() */\n#include <string.h>  /* memcpy() */\n\n#ifndef LEPT_PARSE_STACK_INIT_SIZE\n#define LEPT_PARSE_STACK_INIT_SIZE 256\n#endif\n\n#define EXPECT(c, ch)       do { assert(*c->json == (ch)); c->json++; } while(0)\n#define ISDIGIT(ch)         ((ch) >= '0' && (ch) <= '9')\n#define ISDIGIT1TO9(ch)     ((ch) >= '1' && (ch) <= '9')\n#define PUTC(c, ch)         do { *(char*)lept_context_push(c, sizeof(char)) = (ch); } while(0)\n\ntypedef struct {\n    const char* json;\n    char* stack;\n    size_t size, top;\n}lept_context;\n\nstatic void* lept_context_push(lept_context* c, size_t size) {\n    void* ret;\n    assert(size > 0);\n    if (c->top + size >= c->size) {\n        if (c->size == 0)\n            c->size = LEPT_PARSE_STACK_INIT_SIZE;\n        while (c->top + size >= c->size)\n            c->size += c->size >> 1;  /* c->size * 1.5 */\n        c->stack = (char*)realloc(c->stack, c->size);\n    }\n    ret = c->stack + c->top;\n    c->top += size;\n    return ret;\n}\n\nstatic void* lept_context_pop(lept_context* c, size_t size) {\n    assert(c->top >= size);\n    return c->stack + (c->top -= size);\n}\n\nstatic void lept_parse_whitespace(lept_context* c) {\n    const char *p = c->json;\n    while (*p == ' ' || *p == '\\t' || *p == '\\n' || *p == '\\r')\n        p++;\n    c->json = p;\n}\n\nstatic int lept_parse_literal(lept_context* c, lept_value* v, const char* literal, lept_type type) {\n    size_t i;\n    EXPECT(c, literal[0]);\n    for (i = 0; literal[i + 1]; i++)\n        if (c->json[i] != literal[i + 1])\n            return LEPT_PARSE_INVALID_VALUE;\n    c->json += i;\n    v->type = type;\n    return LEPT_PARSE_OK;\n}\n\nstatic int lept_parse_number(lept_context* c, lept_value* v) {\n    const char* p = c->json;\n    if (*p == '-') p++;\n    if (*p == '0') p++;\n    else {\n        if (!ISDIGIT1TO9(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n    if (*p == '.') {\n        p++;\n        if (!ISDIGIT(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n    if (*p == 'e' || *p == 'E') {\n        p++;\n        if (*p == '+' || *p == '-') p++;\n        if (!ISDIGIT(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n    errno = 0;\n    v->u.n = strtod(c->json, NULL);\n    if (errno == ERANGE && (v->u.n == HUGE_VAL || v->u.n == -HUGE_VAL))\n        return LEPT_PARSE_NUMBER_TOO_BIG;\n    v->type = LEPT_NUMBER;\n    c->json = p;\n    return LEPT_PARSE_OK;\n}\n\nstatic const char* lept_parse_hex4(const char* p, unsigned* u) {\n    /* \\TODO */\n    return p;\n}\n\nstatic void lept_encode_utf8(lept_context* c, unsigned u) {\n    /* \\TODO */\n}\n\n#define STRING_ERROR(ret) do { c->top = head; return ret; } while(0)\n\nstatic int lept_parse_string(lept_context* c, lept_value* v) {\n    size_t head = c->top, len;\n    unsigned u;\n    const char* p;\n    EXPECT(c, '\\\"');\n    p = c->json;\n    for (;;) {\n        char ch = *p++;\n        switch (ch) {\n            case '\\\"':\n                len = c->top - head;\n                lept_set_string(v, (const char*)lept_context_pop(c, len), len);\n                c->json = p;\n                return LEPT_PARSE_OK;\n            case '\\\\':\n                switch (*p++) {\n                    case '\\\"': PUTC(c, '\\\"'); break;\n                    case '\\\\': PUTC(c, '\\\\'); break;\n                    case '/':  PUTC(c, '/' ); break;\n                    case 'b':  PUTC(c, '\\b'); break;\n                    case 'f':  PUTC(c, '\\f'); break;\n                    case 'n':  PUTC(c, '\\n'); break;\n                    case 'r':  PUTC(c, '\\r'); break;\n                    case 't':  PUTC(c, '\\t'); break;\n                    case 'u':\n                        if (!(p = lept_parse_hex4(p, &u)))\n                            STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX);\n                        /* \\TODO surrogate handling */\n                        lept_encode_utf8(c, u);\n                        break;\n                    default:\n                        STRING_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE);\n                }\n                break;\n            case '\\0':\n                STRING_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK);\n            default:\n                if ((unsigned char)ch < 0x20)\n                    STRING_ERROR(LEPT_PARSE_INVALID_STRING_CHAR);\n                PUTC(c, ch);\n        }\n    }\n}\n\nstatic int lept_parse_value(lept_context* c, lept_value* v) {\n    switch (*c->json) {\n        case 't':  return lept_parse_literal(c, v, \"true\", LEPT_TRUE);\n        case 'f':  return lept_parse_literal(c, v, \"false\", LEPT_FALSE);\n        case 'n':  return lept_parse_literal(c, v, \"null\", LEPT_NULL);\n        default:   return lept_parse_number(c, v);\n        case '\"':  return lept_parse_string(c, v);\n        case '\\0': return LEPT_PARSE_EXPECT_VALUE;\n    }\n}\n\nint lept_parse(lept_value* v, const char* json) {\n    lept_context c;\n    int ret;\n    assert(v != NULL);\n    c.json = json;\n    c.stack = NULL;\n    c.size = c.top = 0;\n    lept_init(v);\n    lept_parse_whitespace(&c);\n    if ((ret = lept_parse_value(&c, v)) == LEPT_PARSE_OK) {\n        lept_parse_whitespace(&c);\n        if (*c.json != '\\0') {\n            v->type = LEPT_NULL;\n            ret = LEPT_PARSE_ROOT_NOT_SINGULAR;\n        }\n    }\n    assert(c.top == 0);\n    free(c.stack);\n    return ret;\n}\n\nvoid lept_free(lept_value* v) {\n    assert(v != NULL);\n    if (v->type == LEPT_STRING)\n        free(v->u.s.s);\n    v->type = LEPT_NULL;\n}\n\nlept_type lept_get_type(const lept_value* v) {\n    assert(v != NULL);\n    return v->type;\n}\n\nint lept_get_boolean(const lept_value* v) {\n    assert(v != NULL && (v->type == LEPT_TRUE || v->type == LEPT_FALSE));\n    return v->type == LEPT_TRUE;\n}\n\nvoid lept_set_boolean(lept_value* v, int b) {\n    lept_free(v);\n    v->type = b ? LEPT_TRUE : LEPT_FALSE;\n}\n\ndouble lept_get_number(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_NUMBER);\n    return v->u.n;\n}\n\nvoid lept_set_number(lept_value* v, double n) {\n    lept_free(v);\n    v->u.n = n;\n    v->type = LEPT_NUMBER;\n}\n\nconst char* lept_get_string(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_STRING);\n    return v->u.s.s;\n}\n\nsize_t lept_get_string_length(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_STRING);\n    return v->u.s.len;\n}\n\nvoid lept_set_string(lept_value* v, const char* s, size_t len) {\n    assert(v != NULL && (s != NULL || len == 0));\n    lept_free(v);\n    v->u.s.s = (char*)malloc(len + 1);\n    memcpy(v->u.s.s, s, len);\n    v->u.s.s[len] = '\\0';\n    v->u.s.len = len;\n    v->type = LEPT_STRING;\n}\n"
  },
  {
    "path": "tutorial04/leptjson.h",
    "content": "#ifndef LEPTJSON_H__\n#define LEPTJSON_H__\n\n#include <stddef.h> /* size_t */\n\ntypedef enum { LEPT_NULL, LEPT_FALSE, LEPT_TRUE, LEPT_NUMBER, LEPT_STRING, LEPT_ARRAY, LEPT_OBJECT } lept_type;\n\ntypedef struct {\n    union {\n        struct { char* s; size_t len; }s;  /* string: null-terminated string, string length */\n        double n;                          /* number */\n    }u;\n    lept_type type;\n}lept_value;\n\nenum {\n    LEPT_PARSE_OK = 0,\n    LEPT_PARSE_EXPECT_VALUE,\n    LEPT_PARSE_INVALID_VALUE,\n    LEPT_PARSE_ROOT_NOT_SINGULAR,\n    LEPT_PARSE_NUMBER_TOO_BIG,\n    LEPT_PARSE_MISS_QUOTATION_MARK,\n    LEPT_PARSE_INVALID_STRING_ESCAPE,\n    LEPT_PARSE_INVALID_STRING_CHAR,\n    LEPT_PARSE_INVALID_UNICODE_HEX,\n    LEPT_PARSE_INVALID_UNICODE_SURROGATE\n};\n\n#define lept_init(v) do { (v)->type = LEPT_NULL; } while(0)\n\nint lept_parse(lept_value* v, const char* json);\n\nvoid lept_free(lept_value* v);\n\nlept_type lept_get_type(const lept_value* v);\n\n#define lept_set_null(v) lept_free(v)\n\nint lept_get_boolean(const lept_value* v);\nvoid lept_set_boolean(lept_value* v, int b);\n\ndouble lept_get_number(const lept_value* v);\nvoid lept_set_number(lept_value* v, double n);\n\nconst char* lept_get_string(const lept_value* v);\nsize_t lept_get_string_length(const lept_value* v);\nvoid lept_set_string(lept_value* v, const char* s, size_t len);\n\n#endif /* LEPTJSON_H__ */\n"
  },
  {
    "path": "tutorial04/test.c",
    "content": "#ifdef _WINDOWS\n#define _CRTDBG_MAP_ALLOC\n#include <crtdbg.h>\n#endif\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"leptjson.h\"\n\nstatic int main_ret = 0;\nstatic int test_count = 0;\nstatic int test_pass = 0;\n\n#define EXPECT_EQ_BASE(equality, expect, actual, format) \\\n    do {\\\n        test_count++;\\\n        if (equality)\\\n            test_pass++;\\\n        else {\\\n            fprintf(stderr, \"%s:%d: expect: \" format \" actual: \" format \"\\n\", __FILE__, __LINE__, expect, actual);\\\n            main_ret = 1;\\\n        }\\\n    } while(0)\n\n#define EXPECT_EQ_INT(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, \"%d\")\n#define EXPECT_EQ_DOUBLE(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, \"%.17g\")\n#define EXPECT_EQ_STRING(expect, actual, alength) \\\n    EXPECT_EQ_BASE(sizeof(expect) - 1 == alength && memcmp(expect, actual, alength) == 0, expect, actual, \"%s\")\n#define EXPECT_TRUE(actual) EXPECT_EQ_BASE((actual) != 0, \"true\", \"false\", \"%s\")\n#define EXPECT_FALSE(actual) EXPECT_EQ_BASE((actual) == 0, \"false\", \"true\", \"%s\")\n\nstatic void test_parse_null() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_boolean(&v, 0);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"null\"));\n    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\n    lept_free(&v);\n}\n\nstatic void test_parse_true() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_boolean(&v, 0);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"true\"));\n    EXPECT_EQ_INT(LEPT_TRUE, lept_get_type(&v));\n    lept_free(&v);\n}\n\nstatic void test_parse_false() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_boolean(&v, 1);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"false\"));\n    EXPECT_EQ_INT(LEPT_FALSE, lept_get_type(&v));\n    lept_free(&v);\n}\n\n#define TEST_NUMBER(expect, json)\\\n    do {\\\n        lept_value v;\\\n        lept_init(&v);\\\n        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(&v));\\\n        EXPECT_EQ_DOUBLE(expect, lept_get_number(&v));\\\n        lept_free(&v);\\\n    } while(0)\n\nstatic void test_parse_number() {\n    TEST_NUMBER(0.0, \"0\");\n    TEST_NUMBER(0.0, \"-0\");\n    TEST_NUMBER(0.0, \"-0.0\");\n    TEST_NUMBER(1.0, \"1\");\n    TEST_NUMBER(-1.0, \"-1\");\n    TEST_NUMBER(1.5, \"1.5\");\n    TEST_NUMBER(-1.5, \"-1.5\");\n    TEST_NUMBER(3.1416, \"3.1416\");\n    TEST_NUMBER(1E10, \"1E10\");\n    TEST_NUMBER(1e10, \"1e10\");\n    TEST_NUMBER(1E+10, \"1E+10\");\n    TEST_NUMBER(1E-10, \"1E-10\");\n    TEST_NUMBER(-1E10, \"-1E10\");\n    TEST_NUMBER(-1e10, \"-1e10\");\n    TEST_NUMBER(-1E+10, \"-1E+10\");\n    TEST_NUMBER(-1E-10, \"-1E-10\");\n    TEST_NUMBER(1.234E+10, \"1.234E+10\");\n    TEST_NUMBER(1.234E-10, \"1.234E-10\");\n    TEST_NUMBER(0.0, \"1e-10000\"); /* must underflow */\n\n    TEST_NUMBER(1.0000000000000002, \"1.0000000000000002\"); /* the smallest number > 1 */\n    TEST_NUMBER( 4.9406564584124654e-324, \"4.9406564584124654e-324\"); /* minimum denormal */\n    TEST_NUMBER(-4.9406564584124654e-324, \"-4.9406564584124654e-324\");\n    TEST_NUMBER( 2.2250738585072009e-308, \"2.2250738585072009e-308\");  /* Max subnormal double */\n    TEST_NUMBER(-2.2250738585072009e-308, \"-2.2250738585072009e-308\");\n    TEST_NUMBER( 2.2250738585072014e-308, \"2.2250738585072014e-308\");  /* Min normal positive double */\n    TEST_NUMBER(-2.2250738585072014e-308, \"-2.2250738585072014e-308\");\n    TEST_NUMBER( 1.7976931348623157e+308, \"1.7976931348623157e+308\");  /* Max double */\n    TEST_NUMBER(-1.7976931348623157e+308, \"-1.7976931348623157e+308\");\n}\n\n#define TEST_STRING(expect, json)\\\n    do {\\\n        lept_value v;\\\n        lept_init(&v);\\\n        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_STRING, lept_get_type(&v));\\\n        EXPECT_EQ_STRING(expect, lept_get_string(&v), lept_get_string_length(&v));\\\n        lept_free(&v);\\\n    } while(0)\n\nstatic void test_parse_string() {\n    TEST_STRING(\"\", \"\\\"\\\"\");\n    TEST_STRING(\"Hello\", \"\\\"Hello\\\"\");\n    TEST_STRING(\"Hello\\nWorld\", \"\\\"Hello\\\\nWorld\\\"\");\n    TEST_STRING(\"\\\" \\\\ / \\b \\f \\n \\r \\t\", \"\\\"\\\\\\\" \\\\\\\\ \\\\/ \\\\b \\\\f \\\\n \\\\r \\\\t\\\"\");\n    TEST_STRING(\"Hello\\0World\", \"\\\"Hello\\\\u0000World\\\"\");\n    TEST_STRING(\"\\x24\", \"\\\"\\\\u0024\\\"\");         /* Dollar sign U+0024 */\n    TEST_STRING(\"\\xC2\\xA2\", \"\\\"\\\\u00A2\\\"\");     /* Cents sign U+00A2 */\n    TEST_STRING(\"\\xE2\\x82\\xAC\", \"\\\"\\\\u20AC\\\"\"); /* Euro sign U+20AC */\n    TEST_STRING(\"\\xF0\\x9D\\x84\\x9E\", \"\\\"\\\\uD834\\\\uDD1E\\\"\");  /* G clef sign U+1D11E */\n    TEST_STRING(\"\\xF0\\x9D\\x84\\x9E\", \"\\\"\\\\ud834\\\\udd1e\\\"\");  /* G clef sign U+1D11E */\n}\n\n#define TEST_ERROR(error, json)\\\n    do {\\\n        lept_value v;\\\n        lept_init(&v);\\\n        v.type = LEPT_FALSE;\\\n        EXPECT_EQ_INT(error, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\\\n        lept_free(&v);\\\n    } while(0)\n\nstatic void test_parse_expect_value() {\n    TEST_ERROR(LEPT_PARSE_EXPECT_VALUE, \"\");\n    TEST_ERROR(LEPT_PARSE_EXPECT_VALUE, \" \");\n}\n\nstatic void test_parse_invalid_value() {\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"nul\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"?\");\n\n    /* invalid number */\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"+0\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"+1\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \".123\"); /* at least one digit before '.' */\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"1.\");   /* at least one digit after '.' */\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"INF\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"inf\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"NAN\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"nan\");\n}\n\nstatic void test_parse_root_not_singular() {\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"null x\");\n\n    /* invalid number */\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0123\"); /* after zero should be '.' or nothing */\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0x0\");\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0x123\");\n}\n\nstatic void test_parse_number_too_big() {\n    TEST_ERROR(LEPT_PARSE_NUMBER_TOO_BIG, \"1e309\");\n    TEST_ERROR(LEPT_PARSE_NUMBER_TOO_BIG, \"-1e309\");\n}\n\nstatic void test_parse_missing_quotation_mark() {\n    TEST_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK, \"\\\"\");\n    TEST_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK, \"\\\"abc\");\n}\n\nstatic void test_parse_invalid_string_escape() {\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\v\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\'\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\0\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\x12\\\"\");\n}\n\nstatic void test_parse_invalid_string_char() {\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_CHAR, \"\\\"\\x01\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_CHAR, \"\\\"\\x1F\\\"\");\n}\n\nstatic void test_parse_invalid_unicode_hex() {\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u0\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u01\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u012\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u/000\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\uG000\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u0/00\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u0G00\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u00/0\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u00G0\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u000/\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u000G\\\"\");\n}\n\nstatic void test_parse_invalid_unicode_surrogate() {\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uD800\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uDBFF\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uD800\\\\\\\\\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uD800\\\\uDBFF\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uD800\\\\uE000\\\"\");\n}\n\nstatic void test_parse() {\n    test_parse_null();\n    test_parse_true();\n    test_parse_false();\n    test_parse_number();\n    test_parse_string();\n    test_parse_expect_value();\n    test_parse_invalid_value();\n    test_parse_root_not_singular();\n    test_parse_number_too_big();\n    test_parse_missing_quotation_mark();\n    test_parse_invalid_string_escape();\n    test_parse_invalid_string_char();\n    test_parse_invalid_unicode_hex();\n    test_parse_invalid_unicode_surrogate();\n}\n\nstatic void test_access_null() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"a\", 1);\n    lept_set_null(&v);\n    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\n    lept_free(&v);\n}\n\nstatic void test_access_boolean() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"a\", 1);\n    lept_set_boolean(&v, 1);\n    EXPECT_TRUE(lept_get_boolean(&v));\n    lept_set_boolean(&v, 0);\n    EXPECT_FALSE(lept_get_boolean(&v));\n    lept_free(&v);\n}\n\nstatic void test_access_number() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"a\", 1);\n    lept_set_number(&v, 1234.5);\n    EXPECT_EQ_DOUBLE(1234.5, lept_get_number(&v));\n    lept_free(&v);\n}\n\nstatic void test_access_string() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"\", 0);\n    EXPECT_EQ_STRING(\"\", lept_get_string(&v), lept_get_string_length(&v));\n    lept_set_string(&v, \"Hello\", 5);\n    EXPECT_EQ_STRING(\"Hello\", lept_get_string(&v), lept_get_string_length(&v));\n    lept_free(&v);\n}\n\nstatic void test_access() {\n    test_access_null();\n    test_access_boolean();\n    test_access_number();\n    test_access_string();\n}\n\nint main() {\n#ifdef _WINDOWS\n    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);\n#endif\n    test_parse();\n    test_access();\n    printf(\"%d/%d (%3.2f%%) passed\\n\", test_pass, test_count, test_pass * 100.0 / test_count);\n    return main_ret;\n}\n"
  },
  {
    "path": "tutorial04/tutorial04.md",
    "content": "# 从零开始的 JSON 库教程（四）：Unicode\n\n* Milo Yip\n* 2016/10/2\n\n本文是[《从零开始的 JSON 库教程》](https://zhuanlan.zhihu.com/json-tutorial)的第四个单元。代码位于 [json-tutorial/tutorial04](https://github.com/miloyip/json-tutorial/tree/master/tutorial04)。\n\n本单元内容：\n\n1. [Unicode](#1-unicode)\n2. [需求](#2-需求)\n3. [UTF-8 编码](#3-utf-8-编码)\n4. [实现 `\\uXXXX` 解析](#4-实现-uxxxx-解析)\n5. [总结与练习](#5-总结与练习)\n\n## 1. Unicode\n\n在上一个单元，我们已经能解析「一般」的 JSON 字符串，仅仅没有处理 `\\uXXXX` 这种转义序列。为了解析这种序列，我们必须了解有关 Unicode 的基本概念。\n\n读者应该知道 ASCII，它是一种字符编码，把 128 个字符映射至整数 0 ~ 127。例如，`1` → 49，`A` → 65，`B` → 66 等等。这种 7-bit 字符编码系统非常简单，在计算机中以一个字节存储一个字符。然而，它仅适合美国英语，甚至一些英语中常用的标点符号、重音符号都不能表示，无法表示各国语言，特别是中日韩语等表意文字。\n\n在 Unicode 出现之前，各地区制定了不同的编码系统，如中文主要用 GB 2312 和大五码、日文主要用 JIS 等。这样会造成很多不便，例如一个文本信息很难混合各种语言的文字。\n\n因此，在上世纪80年代末，Xerox、Apple 等公司开始研究，是否能制定一套多语言的统一编码系统。后来，多个机构成立了 Unicode 联盟，在 1991 年释出 Unicode 1.0，收录了 24 种语言共 7161 个字符。在四分之一个世纪后的 2016年，Unicode 已释出 9.0 版本，收录 135 种语言共 128237 个字符。\n\n这些字符被收录为统一字符集（Universal Coded Character Set, UCS），每个字符映射至一个整数码点（code point），码点的范围是 0 至 0x10FFFF，码点又通常记作 U+XXXX，当中 XXXX 为 16 进位数字。例如 `劲` → U+52B2、`峰` → U+5CF0。很明显，UCS 中的字符无法像 ASCII 般以一个字节存储。\n\n因此，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 都是可变长度编码。\n\nUTF-8 成为现时互联网上最流行的格式，有几个原因：\n\n1. 它采用字节为编码单元，不会有字节序（endianness）的问题。\n2. 每个 ASCII 字符只需一个字节去储存。\n3. 如果程序原来是以字节方式储存字符，理论上不需要特别改动就能处理 UTF-8 的数据。\n\n## 2. 需求\n\n由于 UTF-8 的普及性，大部分的 JSON 也通常会以 UTF-8 存储。我们的 JSON 库也会只支持 UTF-8。（RapidJSON 同时支持 UTF-8、UTF-16LE/BE、UTF-32LE/BE、ASCII。）\n\nC 标准库没有关于 Unicode 的处理功能（C++11 有），我们会实现 JSON 库所需的字符编码处理功能。\n\n对于非转义（unescaped）的字符，只要它们不少于 32（0 ~ 31 是不合法的编码单元），我们可以直接复制至结果，这一点我们稍后再说明。我们假设输入是以合法 UTF-8 编码。\n\n而对于 JSON字符串中的 `\\uXXXX` 是以 16 进制表示码点 U+0000 至 U+FFFF，我们需要：\n\n1. 解析 4 位十六进制整数为码点；\n2. 由于字符串是以 UTF-8 存储，我们要把这个码点编码成 UTF-8。\n\n同学可能会发现，4 位的 16 进制数字只能表示 0 至 0xFFFF，但之前我们说 UCS 的码点是从 0 至 0x10FFFF，那怎么能表示多出来的码点？\n\n其实，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) 变换成真实的码点：\n\n~~~\ncodepoint = 0x10000 + (H − 0xD800) × 0x400 + (L − 0xDC00)\n~~~\n\n举个例子，高音谱号字符 `𝄞` → U+1D11E 不是 BMP 之内的字符。在 JSON 中可写成转义序列 `\\uD834\\uDD1E`，我们解析第一个 `\\uD834` 得到码点 U+D834，我们发现它是 U+D800 至 U+DBFF 内的码点，所以它是高代理项。然后我们解析下一个转义序列 `\\uDD1E` 得到码点 U+DD1E，它在 U+DC00 至 U+DFFF 之内，是合法的低代理项。我们计算其码点：\n\n~~~\nH = 0xD834, L = 0xDD1E\ncodepoint = 0x10000 + (H − 0xD800) × 0x400 + (L − 0xDC00)\n          = 0x10000 + (0xD834 - 0xD800) × 0x400 + (0xDD1E − 0xDC00)\n          = 0x10000 + 0x34 × 0x400 + 0x11E\n          = 0x10000 + 0xD000 + 0x11E\n          = 0x1D11E\n~~~\n\n这样就得出这转义序列的码点，然后我们再把它编码成 UTF-8。如果只有高代理项而欠缺低代理项，或是低代理项不在合法码点范围，我们都返回 `LEPT_PARSE_INVALID_UNICODE_SURROGATE` 错误。如果 `\\u` 后不是 4 位十六进位数字，则返回 `LEPT_PARSE_INVALID_UNICODE_HEX` 错误。\n\n## 3. UTF-8 编码\n\nUTF-8 在网页上的使用率势无可挡：\n\n![ ](images/Utf8webgrowth.png)\n\n（图片来自 [Wikipedia Common](https://commons.wikimedia.org/wiki/File:Utf8webgrowth.svg)，数据来自 Google 对网页字符编码的统计。）\n\n由于我们的 JSON 库也只支持 UTF-8，我们需要把码点编码成 UTF-8。这里简单介绍一下 UTF-8 的编码方式。\n\nUTF-8 的编码单元为 8 位（1 字节），每个码点编码成 1 至 4 个字节。它的编码方式很简单，按照码点的范围，把码点的二进位分拆成 1 至最多 4 个字节：\n\n| 码点范围            | 码点位数  | 字节1     | 字节2    | 字节3    | 字节4     |\n|:------------------:|:--------:|:--------:|:--------:|:--------:|:--------:|\n| U+0000 ~ U+007F    | 7        | 0xxxxxxx |\n| U+0080 ~ U+07FF    | 11       | 110xxxxx | 10xxxxxx |\n| U+0800 ~ U+FFFF    | 16       | 1110xxxx | 10xxxxxx | 10xxxxxx |\n| U+10000 ~ U+10FFFF | 21       | 11110xxx | 10xxxxxx | 10xxxxxx | 10xxxxxx |\n\n这个编码方法的好处之一是，码点范围 U+0000 ~ U+007F 编码为一个字节，与 ASCII 编码兼容。这范围的 Unicode 码点也是和 ASCII 字符相同的。因此，一个 ASCII 文本也是一个 UTF-8 文本。\n\n我们举一个例子解析多字节的情况，欧元符号 `€` → U+20AC：\n\n1. U+20AC 在 U+0800 ~ U+FFFF 的范围内，应编码成 3 个字节。\n2. U+20AC 的二进位为 10000010101100\n3. 3 个字节的情况我们要 16 位的码点，所以在前面补两个 0，成为 0010000010101100\n4. 按上表把二进位分成 3 组：0010, 000010, 101100\n5. 加上每个字节的前缀：11100010, 10000010, 10101100\n6. 用十六进位表示即：0xE2, 0x82, 0xAC\n\n对于这例子的范围，对应的 C 代码是这样的：\n\n~~~c\nif (u >= 0x0800 && u <= 0xFFFF) {\n    OutputByte(0xE0 | ((u >> 12) & 0xFF)); /* 0xE0 = 11100000 */\n    OutputByte(0x80 | ((u >>  6) & 0x3F)); /* 0x80 = 10000000 */\n    OutputByte(0x80 | ( u        & 0x3F)); /* 0x3F = 00111111 */\n}\n~~~\n\nUTF-8 的解码稍复杂一点，但我们的 JSON 库不会校验 JSON 文本是否符合 UTF-8，所以这里也不展开了。\n\n## 4. 实现 `\\uXXXX` 解析\n\n我们只需要在其它转义符的处理中加入对 `\\uXXXX` 的处理：\n\n~~~c\nstatic int lept_parse_string(lept_context* c, lept_value* v) {\n    unsigned u;\n    /* ... */\n    for (;;) {\n        char ch = *p++;\n        switch (ch) {\n            /* ... */\n            case '\\\\':\n                switch (*p++) {\n                    /* ... */\n                    case 'u':\n                        if (!(p = lept_parse_hex4(p, &u)))\n                            STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX);\n                        /* \\TODO surrogate handling */\n                        lept_encode_utf8(c, u);\n                        break;\n                    /* ... */\n                }\n            /* ... */\n        }\n    }\n}\n~~~\n\n上面代码的过程很简单，遇到 `\\u` 转义时，调用 `lept_parse_hex4()` 解析 4 位十六进数字，存储为码点 `u`。这个函数在成功时返回解析后的文本指针，失败返回 `NULL`。如果失败，就返回 `LEPT_PARSE_INVALID_UNICODE_HEX` 错误。最后，把码点编码成 UTF-8，写进缓冲区。这里没有处理代理对，留作练习。\n\n顺带一提，我为 `lept_parse_string()` 做了个简单的重构，把返回错误码的处理抽取为宏：\n\n~~~c\n#define STRING_ERROR(ret) do { c->top = head; return ret; } while(0)\n~~~\n\n## 5. 总结与练习\n\n本单元介绍了 Unicode 的基本知识，同学应该了解到一些常用的 Unicode 术语，如码点、编码单元、UTF-8、代理对等。这次的练习代码只有个空壳，要由同学填充。完成后应该能通过所有单元测试，届时我们的 JSON 字符串解析就完全符合标准了。\n\n1. 实现 `lept_parse_hex4()`，不合法的十六进位数返回 `LEPT_PARSE_INVALID_UNICODE_HEX`。\n2. 按第 3 节谈到的 UTF-8 编码原理，实现 `lept_encode_utf8()`。这函数假设码点在正确范围 U+0000 ~ U+10FFFF（用断言检测）。\n3. 加入对代理对的处理，不正确的代理对范围要返回 `LEPT_PARSE_INVALID_UNICODE_SURROGATE` 错误。\n\n如果你遇到问题，有不理解的地方，或是有建议，都欢迎在评论或 [issue](https://github.com/miloyip/json-tutorial/issues) 中提出，让所有人一起讨论。\n"
  },
  {
    "path": "tutorial04_answer/CMakeLists.txt",
    "content": "cmake_minimum_required (VERSION 2.6)\nproject (leptjson_test C)\n\nif (CMAKE_C_COMPILER_ID MATCHES \"GNU|Clang\")\n    set(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -ansi -pedantic -Wall\")\nendif()\n\nadd_library(leptjson leptjson.c)\nadd_executable(leptjson_test test.c)\ntarget_link_libraries(leptjson_test leptjson)\n"
  },
  {
    "path": "tutorial04_answer/leptjson.c",
    "content": "#ifdef _WINDOWS\n#define _CRTDBG_MAP_ALLOC\n#include <crtdbg.h>\n#endif\n#include \"leptjson.h\"\n#include <assert.h>  /* assert() */\n#include <errno.h>   /* errno, ERANGE */\n#include <math.h>    /* HUGE_VAL */\n#include <stdlib.h>  /* NULL, malloc(), realloc(), free(), strtod() */\n#include <string.h>  /* memcpy() */\n\n#ifndef LEPT_PARSE_STACK_INIT_SIZE\n#define LEPT_PARSE_STACK_INIT_SIZE 256\n#endif\n\n#define EXPECT(c, ch)       do { assert(*c->json == (ch)); c->json++; } while(0)\n#define ISDIGIT(ch)         ((ch) >= '0' && (ch) <= '9')\n#define ISDIGIT1TO9(ch)     ((ch) >= '1' && (ch) <= '9')\n#define PUTC(c, ch)         do { *(char*)lept_context_push(c, sizeof(char)) = (ch); } while(0)\n\ntypedef struct {\n    const char* json;\n    char* stack;\n    size_t size, top;\n}lept_context;\n\nstatic void* lept_context_push(lept_context* c, size_t size) {\n    void* ret;\n    assert(size > 0);\n    if (c->top + size >= c->size) {\n        if (c->size == 0)\n            c->size = LEPT_PARSE_STACK_INIT_SIZE;\n        while (c->top + size >= c->size)\n            c->size += c->size >> 1;  /* c->size * 1.5 */\n        c->stack = (char*)realloc(c->stack, c->size);\n    }\n    ret = c->stack + c->top;\n    c->top += size;\n    return ret;\n}\n\nstatic void* lept_context_pop(lept_context* c, size_t size) {\n    assert(c->top >= size);\n    return c->stack + (c->top -= size);\n}\n\nstatic void lept_parse_whitespace(lept_context* c) {\n    const char *p = c->json;\n    while (*p == ' ' || *p == '\\t' || *p == '\\n' || *p == '\\r')\n        p++;\n    c->json = p;\n}\n\nstatic int lept_parse_literal(lept_context* c, lept_value* v, const char* literal, lept_type type) {\n    size_t i;\n    EXPECT(c, literal[0]);\n    for (i = 0; literal[i + 1]; i++)\n        if (c->json[i] != literal[i + 1])\n            return LEPT_PARSE_INVALID_VALUE;\n    c->json += i;\n    v->type = type;\n    return LEPT_PARSE_OK;\n}\n\nstatic int lept_parse_number(lept_context* c, lept_value* v) {\n    const char* p = c->json;\n    if (*p == '-') p++;\n    if (*p == '0') p++;\n    else {\n        if (!ISDIGIT1TO9(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n    if (*p == '.') {\n        p++;\n        if (!ISDIGIT(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n    if (*p == 'e' || *p == 'E') {\n        p++;\n        if (*p == '+' || *p == '-') p++;\n        if (!ISDIGIT(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n    errno = 0;\n    v->u.n = strtod(c->json, NULL);\n    if (errno == ERANGE && (v->u.n == HUGE_VAL || v->u.n == -HUGE_VAL))\n        return LEPT_PARSE_NUMBER_TOO_BIG;\n    v->type = LEPT_NUMBER;\n    c->json = p;\n    return LEPT_PARSE_OK;\n}\n\nstatic const char* lept_parse_hex4(const char* p, unsigned* u) {\n    int i;\n    *u = 0;\n    for (i = 0; i < 4; i++) {\n        char ch = *p++;\n        *u <<= 4;\n        if      (ch >= '0' && ch <= '9')  *u |= ch - '0';\n        else if (ch >= 'A' && ch <= 'F')  *u |= ch - ('A' - 10);\n        else if (ch >= 'a' && ch <= 'f')  *u |= ch - ('a' - 10);\n        else return NULL;\n    }\n    return p;\n}\n\nstatic void lept_encode_utf8(lept_context* c, unsigned u) {\n    if (u <= 0x7F) \n        PUTC(c, u & 0xFF);\n    else if (u <= 0x7FF) {\n        PUTC(c, 0xC0 | ((u >> 6) & 0xFF));\n        PUTC(c, 0x80 | ( u       & 0x3F));\n    }\n    else if (u <= 0xFFFF) {\n        PUTC(c, 0xE0 | ((u >> 12) & 0xFF));\n        PUTC(c, 0x80 | ((u >>  6) & 0x3F));\n        PUTC(c, 0x80 | ( u        & 0x3F));\n    }\n    else {\n        assert(u <= 0x10FFFF);\n        PUTC(c, 0xF0 | ((u >> 18) & 0xFF));\n        PUTC(c, 0x80 | ((u >> 12) & 0x3F));\n        PUTC(c, 0x80 | ((u >>  6) & 0x3F));\n        PUTC(c, 0x80 | ( u        & 0x3F));\n    }\n}\n\n#define STRING_ERROR(ret) do { c->top = head; return ret; } while(0)\n\nstatic int lept_parse_string(lept_context* c, lept_value* v) {\n    size_t head = c->top, len;\n    unsigned u, u2;\n    const char* p;\n    EXPECT(c, '\\\"');\n    p = c->json;\n    for (;;) {\n        char ch = *p++;\n        switch (ch) {\n            case '\\\"':\n                len = c->top - head;\n                lept_set_string(v, (const char*)lept_context_pop(c, len), len);\n                c->json = p;\n                return LEPT_PARSE_OK;\n            case '\\\\':\n                switch (*p++) {\n                    case '\\\"': PUTC(c, '\\\"'); break;\n                    case '\\\\': PUTC(c, '\\\\'); break;\n                    case '/':  PUTC(c, '/' ); break;\n                    case 'b':  PUTC(c, '\\b'); break;\n                    case 'f':  PUTC(c, '\\f'); break;\n                    case 'n':  PUTC(c, '\\n'); break;\n                    case 'r':  PUTC(c, '\\r'); break;\n                    case 't':  PUTC(c, '\\t'); break;\n                    case 'u':\n                        if (!(p = lept_parse_hex4(p, &u)))\n                            STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX);\n                        if (u >= 0xD800 && u <= 0xDBFF) { /* surrogate pair */\n                            if (*p++ != '\\\\')\n                                STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE);\n                            if (*p++ != 'u')\n                                STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE);\n                            if (!(p = lept_parse_hex4(p, &u2)))\n                                STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX);\n                            if (u2 < 0xDC00 || u2 > 0xDFFF)\n                                STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE);\n                            u = (((u - 0xD800) << 10) | (u2 - 0xDC00)) + 0x10000;\n                        }\n                        lept_encode_utf8(c, u);\n                        break;\n                    default:\n                        STRING_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE);\n                }\n                break;\n            case '\\0':\n                STRING_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK);\n            default:\n                if ((unsigned char)ch < 0x20)\n                    STRING_ERROR(LEPT_PARSE_INVALID_STRING_CHAR);\n                PUTC(c, ch);\n        }\n    }\n}\n\nstatic int lept_parse_value(lept_context* c, lept_value* v) {\n    switch (*c->json) {\n        case 't':  return lept_parse_literal(c, v, \"true\", LEPT_TRUE);\n        case 'f':  return lept_parse_literal(c, v, \"false\", LEPT_FALSE);\n        case 'n':  return lept_parse_literal(c, v, \"null\", LEPT_NULL);\n        default:   return lept_parse_number(c, v);\n        case '\"':  return lept_parse_string(c, v);\n        case '\\0': return LEPT_PARSE_EXPECT_VALUE;\n    }\n}\n\nint lept_parse(lept_value* v, const char* json) {\n    lept_context c;\n    int ret;\n    assert(v != NULL);\n    c.json = json;\n    c.stack = NULL;\n    c.size = c.top = 0;\n    lept_init(v);\n    lept_parse_whitespace(&c);\n    if ((ret = lept_parse_value(&c, v)) == LEPT_PARSE_OK) {\n        lept_parse_whitespace(&c);\n        if (*c.json != '\\0') {\n            v->type = LEPT_NULL;\n            ret = LEPT_PARSE_ROOT_NOT_SINGULAR;\n        }\n    }\n    assert(c.top == 0);\n    free(c.stack);\n    return ret;\n}\n\nvoid lept_free(lept_value* v) {\n    assert(v != NULL);\n    if (v->type == LEPT_STRING)\n        free(v->u.s.s);\n    v->type = LEPT_NULL;\n}\n\nlept_type lept_get_type(const lept_value* v) {\n    assert(v != NULL);\n    return v->type;\n}\n\nint lept_get_boolean(const lept_value* v) {\n    assert(v != NULL && (v->type == LEPT_TRUE || v->type == LEPT_FALSE));\n    return v->type == LEPT_TRUE;\n}\n\nvoid lept_set_boolean(lept_value* v, int b) {\n    lept_free(v);\n    v->type = b ? LEPT_TRUE : LEPT_FALSE;\n}\n\ndouble lept_get_number(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_NUMBER);\n    return v->u.n;\n}\n\nvoid lept_set_number(lept_value* v, double n) {\n    lept_free(v);\n    v->u.n = n;\n    v->type = LEPT_NUMBER;\n}\n\nconst char* lept_get_string(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_STRING);\n    return v->u.s.s;\n}\n\nsize_t lept_get_string_length(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_STRING);\n    return v->u.s.len;\n}\n\nvoid lept_set_string(lept_value* v, const char* s, size_t len) {\n    assert(v != NULL && (s != NULL || len == 0));\n    lept_free(v);\n    v->u.s.s = (char*)malloc(len + 1);\n    memcpy(v->u.s.s, s, len);\n    v->u.s.s[len] = '\\0';\n    v->u.s.len = len;\n    v->type = LEPT_STRING;\n}\n"
  },
  {
    "path": "tutorial04_answer/leptjson.h",
    "content": "#ifndef LEPTJSON_H__\n#define LEPTJSON_H__\n\n#include <stddef.h> /* size_t */\n\ntypedef enum { LEPT_NULL, LEPT_FALSE, LEPT_TRUE, LEPT_NUMBER, LEPT_STRING, LEPT_ARRAY, LEPT_OBJECT } lept_type;\n\ntypedef struct {\n    union {\n        struct { char* s; size_t len; }s;  /* string: null-terminated string, string length */\n        double n;                          /* number */\n    }u;\n    lept_type type;\n}lept_value;\n\nenum {\n    LEPT_PARSE_OK = 0,\n    LEPT_PARSE_EXPECT_VALUE,\n    LEPT_PARSE_INVALID_VALUE,\n    LEPT_PARSE_ROOT_NOT_SINGULAR,\n    LEPT_PARSE_NUMBER_TOO_BIG,\n    LEPT_PARSE_MISS_QUOTATION_MARK,\n    LEPT_PARSE_INVALID_STRING_ESCAPE,\n    LEPT_PARSE_INVALID_STRING_CHAR,\n    LEPT_PARSE_INVALID_UNICODE_HEX,\n    LEPT_PARSE_INVALID_UNICODE_SURROGATE\n};\n\n#define lept_init(v) do { (v)->type = LEPT_NULL; } while(0)\n\nint lept_parse(lept_value* v, const char* json);\n\nvoid lept_free(lept_value* v);\n\nlept_type lept_get_type(const lept_value* v);\n\n#define lept_set_null(v) lept_free(v)\n\nint lept_get_boolean(const lept_value* v);\nvoid lept_set_boolean(lept_value* v, int b);\n\ndouble lept_get_number(const lept_value* v);\nvoid lept_set_number(lept_value* v, double n);\n\nconst char* lept_get_string(const lept_value* v);\nsize_t lept_get_string_length(const lept_value* v);\nvoid lept_set_string(lept_value* v, const char* s, size_t len);\n\n#endif /* LEPTJSON_H__ */\n"
  },
  {
    "path": "tutorial04_answer/test.c",
    "content": "#ifdef _WINDOWS\n#define _CRTDBG_MAP_ALLOC\n#include <crtdbg.h>\n#endif\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"leptjson.h\"\n\nstatic int main_ret = 0;\nstatic int test_count = 0;\nstatic int test_pass = 0;\n\n#define EXPECT_EQ_BASE(equality, expect, actual, format) \\\n    do {\\\n        test_count++;\\\n        if (equality)\\\n            test_pass++;\\\n        else {\\\n            fprintf(stderr, \"%s:%d: expect: \" format \" actual: \" format \"\\n\", __FILE__, __LINE__, expect, actual);\\\n            main_ret = 1;\\\n        }\\\n    } while(0)\n\n#define EXPECT_EQ_INT(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, \"%d\")\n#define EXPECT_EQ_DOUBLE(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, \"%.17g\")\n#define EXPECT_EQ_STRING(expect, actual, alength) \\\n    EXPECT_EQ_BASE(sizeof(expect) - 1 == alength && memcmp(expect, actual, alength) == 0, expect, actual, \"%s\")\n#define EXPECT_TRUE(actual) EXPECT_EQ_BASE((actual) != 0, \"true\", \"false\", \"%s\")\n#define EXPECT_FALSE(actual) EXPECT_EQ_BASE((actual) == 0, \"false\", \"true\", \"%s\")\n\nstatic void test_parse_null() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_boolean(&v, 0);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"null\"));\n    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\n    lept_free(&v);\n}\n\nstatic void test_parse_true() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_boolean(&v, 0);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"true\"));\n    EXPECT_EQ_INT(LEPT_TRUE, lept_get_type(&v));\n    lept_free(&v);\n}\n\nstatic void test_parse_false() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_boolean(&v, 1);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"false\"));\n    EXPECT_EQ_INT(LEPT_FALSE, lept_get_type(&v));\n    lept_free(&v);\n}\n\n#define TEST_NUMBER(expect, json)\\\n    do {\\\n        lept_value v;\\\n        lept_init(&v);\\\n        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(&v));\\\n        EXPECT_EQ_DOUBLE(expect, lept_get_number(&v));\\\n        lept_free(&v);\\\n    } while(0)\n\nstatic void test_parse_number() {\n    TEST_NUMBER(0.0, \"0\");\n    TEST_NUMBER(0.0, \"-0\");\n    TEST_NUMBER(0.0, \"-0.0\");\n    TEST_NUMBER(1.0, \"1\");\n    TEST_NUMBER(-1.0, \"-1\");\n    TEST_NUMBER(1.5, \"1.5\");\n    TEST_NUMBER(-1.5, \"-1.5\");\n    TEST_NUMBER(3.1416, \"3.1416\");\n    TEST_NUMBER(1E10, \"1E10\");\n    TEST_NUMBER(1e10, \"1e10\");\n    TEST_NUMBER(1E+10, \"1E+10\");\n    TEST_NUMBER(1E-10, \"1E-10\");\n    TEST_NUMBER(-1E10, \"-1E10\");\n    TEST_NUMBER(-1e10, \"-1e10\");\n    TEST_NUMBER(-1E+10, \"-1E+10\");\n    TEST_NUMBER(-1E-10, \"-1E-10\");\n    TEST_NUMBER(1.234E+10, \"1.234E+10\");\n    TEST_NUMBER(1.234E-10, \"1.234E-10\");\n    TEST_NUMBER(0.0, \"1e-10000\"); /* must underflow */\n\n    TEST_NUMBER(1.0000000000000002, \"1.0000000000000002\"); /* the smallest number > 1 */\n    TEST_NUMBER( 4.9406564584124654e-324, \"4.9406564584124654e-324\"); /* minimum denormal */\n    TEST_NUMBER(-4.9406564584124654e-324, \"-4.9406564584124654e-324\");\n    TEST_NUMBER( 2.2250738585072009e-308, \"2.2250738585072009e-308\");  /* Max subnormal double */\n    TEST_NUMBER(-2.2250738585072009e-308, \"-2.2250738585072009e-308\");\n    TEST_NUMBER( 2.2250738585072014e-308, \"2.2250738585072014e-308\");  /* Min normal positive double */\n    TEST_NUMBER(-2.2250738585072014e-308, \"-2.2250738585072014e-308\");\n    TEST_NUMBER( 1.7976931348623157e+308, \"1.7976931348623157e+308\");  /* Max double */\n    TEST_NUMBER(-1.7976931348623157e+308, \"-1.7976931348623157e+308\");\n}\n\n#define TEST_STRING(expect, json)\\\n    do {\\\n        lept_value v;\\\n        lept_init(&v);\\\n        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_STRING, lept_get_type(&v));\\\n        EXPECT_EQ_STRING(expect, lept_get_string(&v), lept_get_string_length(&v));\\\n        lept_free(&v);\\\n    } while(0)\n\nstatic void test_parse_string() {\n    TEST_STRING(\"\", \"\\\"\\\"\");\n    TEST_STRING(\"Hello\", \"\\\"Hello\\\"\");\n    TEST_STRING(\"Hello\\nWorld\", \"\\\"Hello\\\\nWorld\\\"\");\n    TEST_STRING(\"\\\" \\\\ / \\b \\f \\n \\r \\t\", \"\\\"\\\\\\\" \\\\\\\\ \\\\/ \\\\b \\\\f \\\\n \\\\r \\\\t\\\"\");\n    TEST_STRING(\"Hello\\0World\", \"\\\"Hello\\\\u0000World\\\"\");\n    TEST_STRING(\"\\x24\", \"\\\"\\\\u0024\\\"\");         /* Dollar sign U+0024 */\n    TEST_STRING(\"\\xC2\\xA2\", \"\\\"\\\\u00A2\\\"\");     /* Cents sign U+00A2 */\n    TEST_STRING(\"\\xE2\\x82\\xAC\", \"\\\"\\\\u20AC\\\"\"); /* Euro sign U+20AC */\n    TEST_STRING(\"\\xF0\\x9D\\x84\\x9E\", \"\\\"\\\\uD834\\\\uDD1E\\\"\");  /* G clef sign U+1D11E */\n    TEST_STRING(\"\\xF0\\x9D\\x84\\x9E\", \"\\\"\\\\ud834\\\\udd1e\\\"\");  /* G clef sign U+1D11E */\n}\n\n#define TEST_ERROR(error, json)\\\n    do {\\\n        lept_value v;\\\n        lept_init(&v);\\\n        v.type = LEPT_FALSE;\\\n        EXPECT_EQ_INT(error, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\\\n        lept_free(&v);\\\n    } while(0)\n\nstatic void test_parse_expect_value() {\n    TEST_ERROR(LEPT_PARSE_EXPECT_VALUE, \"\");\n    TEST_ERROR(LEPT_PARSE_EXPECT_VALUE, \" \");\n}\n\nstatic void test_parse_invalid_value() {\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"nul\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"?\");\n\n    /* invalid number */\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"+0\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"+1\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \".123\"); /* at least one digit before '.' */\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"1.\");   /* at least one digit after '.' */\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"INF\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"inf\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"NAN\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"nan\");\n}\n\nstatic void test_parse_root_not_singular() {\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"null x\");\n\n    /* invalid number */\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0123\"); /* after zero should be '.' or nothing */\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0x0\");\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0x123\");\n}\n\nstatic void test_parse_number_too_big() {\n    TEST_ERROR(LEPT_PARSE_NUMBER_TOO_BIG, \"1e309\");\n    TEST_ERROR(LEPT_PARSE_NUMBER_TOO_BIG, \"-1e309\");\n}\n\nstatic void test_parse_missing_quotation_mark() {\n    TEST_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK, \"\\\"\");\n    TEST_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK, \"\\\"abc\");\n}\n\nstatic void test_parse_invalid_string_escape() {\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\v\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\'\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\0\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\x12\\\"\");\n}\n\nstatic void test_parse_invalid_string_char() {\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_CHAR, \"\\\"\\x01\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_CHAR, \"\\\"\\x1F\\\"\");\n}\n\nstatic void test_parse_invalid_unicode_hex() {\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u0\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u01\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u012\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u/000\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\uG000\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u0/00\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u0G00\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u00/0\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u00G0\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u000/\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u000G\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u 123\\\"\");\n}\n\nstatic void test_parse_invalid_unicode_surrogate() {\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uD800\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uDBFF\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uD800\\\\\\\\\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uD800\\\\uDBFF\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uD800\\\\uE000\\\"\");\n}\n\nstatic void test_parse() {\n    test_parse_null();\n    test_parse_true();\n    test_parse_false();\n    test_parse_number();\n    test_parse_string();\n    test_parse_expect_value();\n    test_parse_invalid_value();\n    test_parse_root_not_singular();\n    test_parse_number_too_big();\n    test_parse_missing_quotation_mark();\n    test_parse_invalid_string_escape();\n    test_parse_invalid_string_char();\n    test_parse_invalid_unicode_hex();\n    test_parse_invalid_unicode_surrogate();\n}\n\nstatic void test_access_null() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"a\", 1);\n    lept_set_null(&v);\n    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\n    lept_free(&v);\n}\n\nstatic void test_access_boolean() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"a\", 1);\n    lept_set_boolean(&v, 1);\n    EXPECT_TRUE(lept_get_boolean(&v));\n    lept_set_boolean(&v, 0);\n    EXPECT_FALSE(lept_get_boolean(&v));\n    lept_free(&v);\n}\n\nstatic void test_access_number() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"a\", 1);\n    lept_set_number(&v, 1234.5);\n    EXPECT_EQ_DOUBLE(1234.5, lept_get_number(&v));\n    lept_free(&v);\n}\n\nstatic void test_access_string() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"\", 0);\n    EXPECT_EQ_STRING(\"\", lept_get_string(&v), lept_get_string_length(&v));\n    lept_set_string(&v, \"Hello\", 5);\n    EXPECT_EQ_STRING(\"Hello\", lept_get_string(&v), lept_get_string_length(&v));\n    lept_free(&v);\n}\n\nstatic void test_access() {\n    test_access_null();\n    test_access_boolean();\n    test_access_number();\n    test_access_string();\n}\n\nint main() {\n#ifdef _WINDOWS\n    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);\n#endif\n    test_parse();\n    test_access();\n    printf(\"%d/%d (%3.2f%%) passed\\n\", test_pass, test_count, test_pass * 100.0 / test_count);\n    return main_ret;\n}\n"
  },
  {
    "path": "tutorial04_answer/tutorial04_answer.md",
    "content": "# 从零开始的 JSON 库教程（四）：Unicode 解答篇\n\n* Milo Yip\n* 2016/10/6\n\n本文是[《从零开始的 JSON 库教程》](https://zhuanlan.zhihu.com/json-tutorial)的第四个单元解答篇。解答代码位于 [json-tutorial/tutorial04_answer](https://github.com/miloyip/json-tutorial/blob/master/tutorial04_answer)。\n\n## 1. 实现 `lept_parse_hex4()`\n\n这个函数只是读 4 位 16 进制数字，可以简单地自行实现：\n\n~~~c\nstatic const char* lept_parse_hex4(const char* p, unsigned* u) {\n    int i;\n    *u = 0;\n    for (i = 0; i < 4; i++) {\n        char ch = *p++;\n        *u <<= 4;\n        if      (ch >= '0' && ch <= '9')  *u |= ch - '0';\n        else if (ch >= 'A' && ch <= 'F')  *u |= ch - ('A' - 10);\n        else if (ch >= 'a' && ch <= 'f')  *u |= ch - ('a' - 10);\n        else return NULL;\n    }\n    return p;\n}\n~~~\n\n可能有同学想到用标准库的 [`strtol()`](https://en.cppreference.com/w/c/string/byte/strtol)，因为它也能解析 16 进制数字，那么可以简短的写成：\n\n~~~c\nstatic const char* lept_parse_hex4(const char* p, unsigned* u) {\n    char* end;\n    *u = (unsigned)strtol(p, &end, 16);\n    return end == p + 4 ? end : NULL;\n}\n~~~\n\n但这个实现会错误地接受 `\"\\u 123\"` 这种不合法的 JSON，因为 `strtol()` 会跳过开始的空白。要解决的话，还需要检测第一个字符是否 `[0-9A-Fa-f]`，或者 `!isspace(*p)`。但为了 `strtol()` 做多余的检测，而且自行实现也很简单，我个人会选择首个方案。（前两个单元用 `strtod()` 就没办法，因为它的实现要复杂得多。）\n\n## 2. 实现 `lept_encode_utf8()`\n\n这个函数只需要根据那个 UTF-8 编码表就可以实现：\n\n~~~c\nstatic void lept_encode_utf8(lept_context* c, unsigned u) {\n    if (u <= 0x7F) \n        PUTC(c, u & 0xFF);\n    else if (u <= 0x7FF) {\n        PUTC(c, 0xC0 | ((u >> 6) & 0xFF));\n        PUTC(c, 0x80 | ( u       & 0x3F));\n    }\n    else if (u <= 0xFFFF) {\n        PUTC(c, 0xE0 | ((u >> 12) & 0xFF));\n        PUTC(c, 0x80 | ((u >>  6) & 0x3F));\n        PUTC(c, 0x80 | ( u        & 0x3F));\n    }\n    else {\n        assert(u <= 0x10FFFF);\n        PUTC(c, 0xF0 | ((u >> 18) & 0xFF));\n        PUTC(c, 0x80 | ((u >> 12) & 0x3F));\n        PUTC(c, 0x80 | ((u >>  6) & 0x3F));\n        PUTC(c, 0x80 | ( u        & 0x3F));\n    }\n}\n~~~\n\n有同学可能觉得奇怪，最终也是写进一个 `char`，为什么要做 `x & 0xFF` 这种操作呢？这是因为 `u` 是 `unsigned` 类型，一些编译器可能会警告这个转型可能会截断数据。但实际上，配合了范围的检测然后右移之后，可以保证写入的是 0~255 内的值。为了避免一些编译器的警告误判，我们加上 `x & 0xFF`。一般来说，编译器在优化之后，这与操作是会被消去的，不会影响性能。\n\n其实超过 1 个字符输出时，可以只调用 1 次 `lept_context_push()`。这里全用 `PUTC()` 只是为了代码看上去简单一点。\n\n## 3. 代理对的处理\n\n遇到高代理项，就需要把低代理项 `\\uxxxx` 也解析进来，然后用这两个项去计算出码点：\n\n~~~c\ncase 'u':\n    if (!(p = lept_parse_hex4(p, &u)))\n        STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX);\n    if (u >= 0xD800 && u <= 0xDBFF) { /* surrogate pair */\n        if (*p++ != '\\\\')\n            STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE);\n        if (*p++ != 'u')\n            STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE);\n        if (!(p = lept_parse_hex4(p, &u2)))\n            STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX);\n        if (u2 < 0xDC00 || u2 > 0xDFFF)\n            STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE);\n        u = (((u - 0xD800) << 10) | (u2 - 0xDC00)) + 0x10000;\n    }\n    lept_encode_utf8(c, u);\n    break;\n~~~\n\n## 4. 总结\n\nJSON 的字符串解析终于完成了。但更重要的是，同学通过教程和练习后，应该对于 Unicode 和 UTF-8 编码有基本了解。使用 Unicode 标准去处理文本数据已是世界潮流。虽然 C11/C++11 引入了 Unicode 字符串字面量及少量函数，但仍然有很多不足，一般需要借助第三方库。\n\n我们在稍后的单元还要处理生成时的 Unicode 问题，接下来我们要继续讨论数组和对象的解析。\n\n如果你遇到问题，有不理解的地方，或是有建议，都欢迎在评论或 [issue](https://github.com/miloyip/json-tutorial/issues) 中提出，让所有人一起讨论。\n"
  },
  {
    "path": "tutorial05/CMakeLists.txt",
    "content": "cmake_minimum_required (VERSION 2.6)\nproject (leptjson_test C)\n\nif (CMAKE_C_COMPILER_ID MATCHES \"GNU|Clang\")\n    set(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -ansi -pedantic -Wall\")\nendif()\n\nadd_library(leptjson leptjson.c)\nadd_executable(leptjson_test test.c)\ntarget_link_libraries(leptjson_test leptjson)\n"
  },
  {
    "path": "tutorial05/images/makefile",
    "content": "%.png: %.dot\n\tdot $< -Tpng -o $@\n\nDOTFILES = $(basename $(wildcard *.dot))\nall: $(addsuffix .png, $(DOTFILES))\n"
  },
  {
    "path": "tutorial05/images/parse_array01.dot",
    "content": "digraph {\n    rankdir=TB\n    compound=true\n    fontname=\"Inconsolata, Consolas\"\n    fontsize=10\n    margin=\"0,0\"\n    ranksep=0.3\n    nodesep=1\n    penwidth=0.5\n    \n    node [fontname=\"Inconsolata, Consolas\", fontsize=10, penwidth=0.5, colorscheme=spectral7]\n    edge [fontname=\"Inconsolata, Consolas\", fontsize=10, penwidth=0.5]\n\n    {\n        node [shape=record, style=filled, margin=0.1, height=0.3]\n        json [fillcolor=3, label=\"<j>[|\\\"|a|b|c|\\\"|,|[|1|,|2|]|,|3|]|\\\\0\"]\n        stack [fillcolor=4, label=\"<t> | | | | | |\"]\n    }\n    {\n        node [shape=plaintext, margin=0]\n\n        cjson [label=\"c->json\"]\n        ctop [label=\"c->top\"]\n        desc [style=solid,label=\"\\l1. lept_parse()\\l  2. lept_parse_value() \\l    3. lept_parse_array()\"]\n    }\n\n    cjson -> json:j\n    ctop -> stack:t\n    json -> desc [style=invis]\n}"
  },
  {
    "path": "tutorial05/images/parse_array02.dot",
    "content": "digraph {\n    rankdir=TB\n    compound=true\n    fontname=\"Inconsolata, Consolas\"\n    fontsize=10\n    margin=\"0,0\"\n    ranksep=0.3\n    nodesep=1\n    penwidth=0.5\n    \n    node [fontname=\"Inconsolata, Consolas\", fontsize=10, penwidth=0.5, colorscheme=spectral7]\n    edge [fontname=\"Inconsolata, Consolas\", fontsize=10, penwidth=0.5]\n\n    {\n        node [shape=record, style=filled, margin=0.1, height=0.3]\n        json [fillcolor=3, label=\"[|<j>\\\"|a|b|c|\\\"|,|[|1|,|2|]|,|3|]|\\\\0\"]\n        stack [fillcolor=4, label=\"<t>| | | | | |\"]\n    }\n    {\n        node [shape=plaintext, margin=0]\n\n        cjson [label=\"c->json\"]\n        ctop [label=\"c->top\"]\n        desc [style=solid,label=\"\\l1. lept_parse()\\l  2. lept_parse_value()\\l    3. lept_parse_array()\\l      4. lept_parse_value()\\l        5. lept_parse_string()\"]\n    }\n    {\n        node [shape=Mrecord,style=filled]\n\n        e [fillcolor=5,label=\"{null|}\"]\n    }\n\n    cjson -> json:j\n    ctop -> stack:t\n    json -> desc [style=invis]\n    stack -> e [style=invis]\n }"
  },
  {
    "path": "tutorial05/images/parse_array03.dot",
    "content": "digraph {\n    rankdir=TB\n    compound=true\n    fontname=\"Inconsolata, Consolas\"\n    fontsize=10\n    margin=\"0,0\"\n    ranksep=0.3\n    nodesep=1\n    penwidth=0.5\n    \n    node [fontname=\"Inconsolata, Consolas\", fontsize=10, penwidth=0.5, colorscheme=spectral7]\n    edge [fontname=\"Inconsolata, Consolas\", fontsize=10, penwidth=0.5]\n\n    {\n        node [shape=record, style=filled, margin=0.1, height=0.3]\n        json [fillcolor=3, label=\"[|\\\"|a|b|c|<j>\\\"|,|[|1|,|2|]|,|3|]|\\\\0\"]\n        stack [fillcolor=4, label=\"a|b|c|<t>| | |\"]\n    }\n    {\n        node [shape=plaintext, margin=0]\n\n        cjson [label=\"c->json\"]\n        ctop [label=\"c->top\"]\n        desc [style=solid,label=\"\\l1. lept_parse()\\l  2. lept_parse_value()\\l    3. lept_parse_array()\\l      4. lept_parse_value()\\l        5. lept_parse_string()\"]\n    }\n\n    {\n        node [shape=Mrecord,style=filled]\n\n        e [fillcolor=5,label=\"{null|}\"]\n    }\n\n    cjson -> json:j\n    ctop -> stack:t\n    json -> desc [style=invis]\n    stack -> e [style=invis]\n }"
  },
  {
    "path": "tutorial05/images/parse_array04.dot",
    "content": "digraph {\n    rankdir=TB\n    compound=true\n    fontname=\"Inconsolata, Consolas\"\n    fontsize=10\n    margin=\"0,0\"\n    ranksep=0.3\n    nodesep=1\n    penwidth=0.5\n    \n    node [fontname=\"Inconsolata, Consolas\", fontsize=10, penwidth=0.5, colorscheme=spectral7]\n    edge [fontname=\"Inconsolata, Consolas\", fontsize=10, penwidth=0.5]\n\n    {\n        node [shape=record, style=filled, margin=0.1, height=0.3]\n        json [fillcolor=3, label=\"[|\\\"|a|b|c|<j>\\\"|,|[|1|,|2|]|,|3|]|\\\\0\"]\n        stack [fillcolor=4, label=\"<t> | | | | | |\"]\n        abc [fillcolor=3, label=\"<h>a|b|c|\\\\0\"]\n    }\n    {\n        node [shape=plaintext, margin=0]\n\n        cjson [label=\"c->json\"]\n        ctop [label=\"c->top\"]\n        desc [style=solid,label=\"\\l1. lept_parse()\\l  2. lept_parse_value()\\l    3. lept_parse_array()\\l      4. lept_parse_value()\\l        5. lept_parse_string()\"]\n    }\n\n    {\n        node [shape=Mrecord,style=filled]\n\n        s [fillcolor=6,label=\"{string|<s>s|len=3}\"]\n    }\n\n    cjson -> json:j\n    ctop -> stack:t\n    json -> desc [style=invis]\n    stack -> s [style=invis]\n    s:s -> abc:h\n }"
  },
  {
    "path": "tutorial05/images/parse_array05.dot",
    "content": "digraph {\n    rankdir=TB\n    compound=true\n    fontname=\"Inconsolata, Consolas\"\n    fontsize=10\n    margin=\"0,0\"\n    ranksep=0.3\n    nodesep=1\n    penwidth=0.5\n    \n    node [fontname=\"Inconsolata, Consolas\", fontsize=10, penwidth=0.5, colorscheme=spectral7]\n    edge [fontname=\"Inconsolata, Consolas\", fontsize=10, penwidth=0.5]\n\n    {\n        node [shape=record, style=filled, margin=0.1, height=0.3]\n        json [fillcolor=3, label=\"[|\\\"|a|b|c|\\\"|<j>,|[|1|,|2|]|,|3|]|\\\\0\"]\n        stack [fillcolor=4, label=\"{string|<s>s|len=3}|<t> | | | | |\"]\n        abc [fillcolor=3, label=\"<h>a|b|c|\\\\0\"]\n    }\n    {\n        node [shape=plaintext, margin=0]\n\n        cjson [label=\"c->json\"]\n        ctop [label=\"c->top\"]\n        desc [style=solid,label=\"\\l1. lept_parse()\\l  2. lept_parse_value()\\l    3. lept_parse_array()\"]\n    }\n\n    cjson -> json:j\n    ctop -> stack:t\n    json -> desc [style=invis]\n    stack:s -> abc:h\n }"
  },
  {
    "path": "tutorial05/images/parse_array06.dot",
    "content": "digraph {\n    rankdir=TB\n    compound=true\n    fontname=\"Inconsolata, Consolas\"\n    fontsize=10\n    margin=\"0,0\"\n    ranksep=0.3\n    nodesep=1\n    penwidth=0.5\n    \n    node [fontname=\"Inconsolata, Consolas\", fontsize=10, penwidth=0.5, colorscheme=spectral7]\n    edge [fontname=\"Inconsolata, Consolas\", fontsize=10, penwidth=0.5]\n\n    {\n        node [shape=record, style=filled, margin=0.1, height=0.3]\n        json [fillcolor=3, label=\"[|\\\"|a|b|c|\\\"|,|[|1|<j>,|2|]|,|3|]|\\\\0\"]\n        stack [fillcolor=4, label=\"{string|<s>s|len=3}|<t> | | | | |\"]\n        abc [fillcolor=3, label=\"<h>a|b|c|\\\\0\"]\n    }\n    {\n        node [shape=plaintext, margin=0]\n\n        cjson [label=\"c->json\"]\n        ctop [label=\"c->top\"]\n        desc [style=solid,label=\"\\l1. lept_parse()\\l  2. lept_parse_value()\\l    3. lept_parse_array()\\l      4. lept_parse_value()\\l        5. lept_parse_array()\\l          6. lept_parse_value()\\l            7. lept_parse_number()\"]\n    }\n\n    {\n        node [shape=Mrecord,style=filled]\n\n        n1 [fillcolor=7,label=\"{number|n=1}\"]\n    }\n\n    cjson -> json:j\n    ctop -> stack:t\n    json -> desc [style=invis]\n    stack:s -> abc:h\n    stack -> n1 [style=invis]\n }"
  },
  {
    "path": "tutorial05/images/parse_array07.dot",
    "content": "digraph {\n    rankdir=TB\n    compound=true\n    fontname=\"Inconsolata, Consolas\"\n    fontsize=10\n    margin=\"0,0\"\n    ranksep=0.3\n    nodesep=1\n    penwidth=0.5\n    \n    node [fontname=\"Inconsolata, Consolas\", fontsize=10, penwidth=0.5, colorscheme=spectral7]\n    edge [fontname=\"Inconsolata, Consolas\", fontsize=10, penwidth=0.5]\n\n    {\n        node [shape=record, style=filled, margin=0.1, height=0.3]\n        json [fillcolor=3, label=\"[|\\\"|a|b|c|\\\"|,|[|1|<j>,|2|]|,|3|]|\\\\0\"]\n        stack [fillcolor=4, label=\"{string|<s>s|len=3}|{number|n=1}|<t> | | | |\"]\n        abc [fillcolor=3, label=\"<h>a|b|c|\\\\0\"]\n    }\n    {\n        node [shape=plaintext, margin=0]\n\n        cjson [label=\"c->json\"]\n        ctop [label=\"c->top\"]\n        desc [style=solid,label=\"\\l1. lept_parse()\\l  2. lept_parse_value()\\l    3. lept_parse_array()\\l      4. lept_parse_value()\\l        5. lept_parse_array()\"]\n    }\n\n    cjson -> json:j\n    ctop -> stack:t\n    json -> desc [style=invis]\n    stack:s -> abc:h\n }"
  },
  {
    "path": "tutorial05/images/parse_array08.dot",
    "content": "digraph {\n    rankdir=TB\n    compound=true\n    fontname=\"Inconsolata, Consolas\"\n    fontsize=10\n    margin=\"0,0\"\n    ranksep=0.3\n    nodesep=1\n    penwidth=0.5\n    \n    node [fontname=\"Inconsolata, Consolas\", fontsize=10, penwidth=0.5, colorscheme=spectral7]\n    edge [fontname=\"Inconsolata, Consolas\", fontsize=10, penwidth=0.5]\n\n    {\n        node [shape=record, style=filled, margin=0.1, height=0.3]\n        json [fillcolor=3, label=\"[|\\\"|a|b|c|\\\"|,|[|1|,|2|]|<j>,|3|]|\\\\0\"]\n        stack [fillcolor=4, label=\"{string|<s>s|len=3}|<t> | | | | |\"]\n        abc [fillcolor=3, label=\"<h>a|b|c|\\\\0\"]\n    }\n    {\n        node [shape=plaintext, margin=0]\n\n        cjson [label=\"c->json\"]\n        ctop [label=\"c->top\"]\n        desc [style=solid,label=\"\\l1. lept_parse()\\l  2. lept_parse_value()\\l    3. lept_parse_array()\\l\"]\n    }\n\n    {\n        node [shape=Mrecord,style=filled]\n\n        a2 [fillcolor=2,label=\"{array|<e>e|size=2}\"]\n        n1 [fillcolor=7,label=\"{number|n=1}\"]\n        n2 [fillcolor=7,label=\"{number|n=2}\"]\n    }\n\n    cjson -> json:j\n    ctop -> stack:t\n    json -> desc [style=invis]\n    stack:s -> abc:h\n    a2:e -> n1;\n    a2 -> n2 [style=invis]\n    n1 -> n2 [style=dashed,constraint=false]\n    stack -> a2 [style=invis]\n }"
  },
  {
    "path": "tutorial05/images/parse_array09.dot",
    "content": "digraph {\n    rankdir=TB\n    compound=true\n    fontname=\"Inconsolata, Consolas\"\n    fontsize=10\n    margin=\"0,0\"\n    ranksep=0.3\n    nodesep=1\n    penwidth=0.5\n    \n    node [fontname=\"Inconsolata, Consolas\", fontsize=10, penwidth=0.5, colorscheme=spectral7]\n    edge [fontname=\"Inconsolata, Consolas\", fontsize=10, penwidth=0.5]\n\n    {\n        node [shape=record, style=filled, margin=0.1, height=0.3]\n        json [fillcolor=3, label=\"[|\\\"|a|b|c|\\\"|,|[|1|,|2|]|<j>,|3|]|\\\\0\"]\n        stack [fillcolor=4, label=\"{string|<s>s|len=3}|{array|<e>e|size=2}|<t> | | | |\"]\n        abc [fillcolor=3, label=\"<h>a|b|c|\\\\0\"]\n    }\n    {\n        node [shape=plaintext, margin=0]\n\n        cjson [label=\"c->json\"]\n        ctop [label=\"c->top\"]\n        desc [style=solid,label=\"\\l1. lept_parse()\\l  2. lept_parse_value()\\l    3. lept_parse_array()\\l\"]\n    }\n\n    {\n        node [shape=Mrecord,style=filled]\n\n        n1 [fillcolor=7,label=\"{number|n=1}\"]\n        n2 [fillcolor=7,label=\"{number|n=2}\"]\n    }\n\n    cjson -> json:j\n    ctop -> stack:t\n    json -> desc [style=invis]\n    stack:s -> abc:h\n    stack:e -> n1;\n    stack:e -> n2 [style=invis]\n    n1 -> n2 [style=dashed,constraint=false]\n }"
  },
  {
    "path": "tutorial05/images/parse_array10.dot",
    "content": "digraph {\n    rankdir=TB\n    compound=true\n    fontname=\"Inconsolata, Consolas\"\n    fontsize=10\n    margin=\"0,0\"\n    ranksep=0.3\n    nodesep=1\n    penwidth=0.5\n    \n    node [fontname=\"Inconsolata, Consolas\", fontsize=10, penwidth=0.5, colorscheme=spectral7]\n    edge [fontname=\"Inconsolata, Consolas\", fontsize=10, penwidth=0.5]\n\n    {\n        node [shape=record, style=filled, margin=0.1, height=0.3]\n        json [fillcolor=3, label=\"[|\\\"|a|b|c|\\\"|,|[|1|,|2|]|,|3|]|<j>\\\\0\"]\n        stack [fillcolor=4, label=\"<t> | | | | | |\"]\n        abc [fillcolor=3, label=\"<h>a|b|c|\\\\0\"]\n    }\n    {\n        node [shape=plaintext, margin=0]\n\n        cjson [label=\"c->json\"]\n        ctop [label=\"c->top\"]\n        desc [style=solid,label=\"\\l1. lept_parse()\\l  2. lept_parse_value()\\l    3. lept_parse_array()\\l\"]\n    }\n\n    {\n        node [shape=Mrecord,style=filled]\n\n        a1 [fillcolor=2,label=\"{array|<e>e|size=3}\"]\n        s [fillcolor=6,label=\"{string|<s>s|len=3}\"]\n        a2 [fillcolor=2,label=\"{array|<e>e|size=2}\"]\n        n1 [fillcolor=7,label=\"{number|n=1}\"]\n        n2 [fillcolor=7,label=\"{number|n=2}\"]\n        n3 [fillcolor=7,label=\"{number|n=3}\"]\n    }\n\n    cjson -> json:j\n    ctop -> stack:t\n    json -> desc [style=invis]\n    stack -> a1 [style=invis]\n    a1:e -> s\n    s:s -> abc:h\n    a2:e -> n1;\n    a1 -> { a2; n3 } [style=invis]\n    a2:e -> n2 [style=invis]\n    n1 -> n2 [style=dashed,constraint=false]\n    s -> a2 -> n3 [style=dashed,constraint=false]\n }"
  },
  {
    "path": "tutorial05/leptjson.c",
    "content": "#ifdef _WINDOWS\n#define _CRTDBG_MAP_ALLOC\n#include <crtdbg.h>\n#endif\n#include \"leptjson.h\"\n#include <assert.h>  /* assert() */\n#include <errno.h>   /* errno, ERANGE */\n#include <math.h>    /* HUGE_VAL */\n#include <stdlib.h>  /* NULL, malloc(), realloc(), free(), strtod() */\n#include <string.h>  /* memcpy() */\n\n#ifndef LEPT_PARSE_STACK_INIT_SIZE\n#define LEPT_PARSE_STACK_INIT_SIZE 256\n#endif\n\n#define EXPECT(c, ch)       do { assert(*c->json == (ch)); c->json++; } while(0)\n#define ISDIGIT(ch)         ((ch) >= '0' && (ch) <= '9')\n#define ISDIGIT1TO9(ch)     ((ch) >= '1' && (ch) <= '9')\n#define PUTC(c, ch)         do { *(char*)lept_context_push(c, sizeof(char)) = (ch); } while(0)\n\ntypedef struct {\n    const char* json;\n    char* stack;\n    size_t size, top;\n}lept_context;\n\nstatic void* lept_context_push(lept_context* c, size_t size) {\n    void* ret;\n    assert(size > 0);\n    if (c->top + size >= c->size) {\n        if (c->size == 0)\n            c->size = LEPT_PARSE_STACK_INIT_SIZE;\n        while (c->top + size >= c->size)\n            c->size += c->size >> 1;  /* c->size * 1.5 */\n        c->stack = (char*)realloc(c->stack, c->size);\n    }\n    ret = c->stack + c->top;\n    c->top += size;\n    return ret;\n}\n\nstatic void* lept_context_pop(lept_context* c, size_t size) {\n    assert(c->top >= size);\n    return c->stack + (c->top -= size);\n}\n\nstatic void lept_parse_whitespace(lept_context* c) {\n    const char *p = c->json;\n    while (*p == ' ' || *p == '\\t' || *p == '\\n' || *p == '\\r')\n        p++;\n    c->json = p;\n}\n\nstatic int lept_parse_literal(lept_context* c, lept_value* v, const char* literal, lept_type type) {\n    size_t i;\n    EXPECT(c, literal[0]);\n    for (i = 0; literal[i + 1]; i++)\n        if (c->json[i] != literal[i + 1])\n            return LEPT_PARSE_INVALID_VALUE;\n    c->json += i;\n    v->type = type;\n    return LEPT_PARSE_OK;\n}\n\nstatic int lept_parse_number(lept_context* c, lept_value* v) {\n    const char* p = c->json;\n    if (*p == '-') p++;\n    if (*p == '0') p++;\n    else {\n        if (!ISDIGIT1TO9(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n    if (*p == '.') {\n        p++;\n        if (!ISDIGIT(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n    if (*p == 'e' || *p == 'E') {\n        p++;\n        if (*p == '+' || *p == '-') p++;\n        if (!ISDIGIT(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n    errno = 0;\n    v->u.n = strtod(c->json, NULL);\n    if (errno == ERANGE && (v->u.n == HUGE_VAL || v->u.n == -HUGE_VAL))\n        return LEPT_PARSE_NUMBER_TOO_BIG;\n    v->type = LEPT_NUMBER;\n    c->json = p;\n    return LEPT_PARSE_OK;\n}\n\nstatic const char* lept_parse_hex4(const char* p, unsigned* u) {\n    int i;\n    *u = 0;\n    for (i = 0; i < 4; i++) {\n        char ch = *p++;\n        *u <<= 4;\n        if      (ch >= '0' && ch <= '9')  *u |= ch - '0';\n        else if (ch >= 'A' && ch <= 'F')  *u |= ch - ('A' - 10);\n        else if (ch >= 'a' && ch <= 'f')  *u |= ch - ('a' - 10);\n        else return NULL;\n    }\n    return p;\n}\n\nstatic void lept_encode_utf8(lept_context* c, unsigned u) {\n    if (u <= 0x7F) \n        PUTC(c, u & 0xFF);\n    else if (u <= 0x7FF) {\n        PUTC(c, 0xC0 | ((u >> 6) & 0xFF));\n        PUTC(c, 0x80 | ( u       & 0x3F));\n    }\n    else if (u <= 0xFFFF) {\n        PUTC(c, 0xE0 | ((u >> 12) & 0xFF));\n        PUTC(c, 0x80 | ((u >>  6) & 0x3F));\n        PUTC(c, 0x80 | ( u        & 0x3F));\n    }\n    else {\n        assert(u <= 0x10FFFF);\n        PUTC(c, 0xF0 | ((u >> 18) & 0xFF));\n        PUTC(c, 0x80 | ((u >> 12) & 0x3F));\n        PUTC(c, 0x80 | ((u >>  6) & 0x3F));\n        PUTC(c, 0x80 | ( u        & 0x3F));\n    }\n}\n\n#define STRING_ERROR(ret) do { c->top = head; return ret; } while(0)\n\nstatic int lept_parse_string(lept_context* c, lept_value* v) {\n    size_t head = c->top, len;\n    unsigned u, u2;\n    const char* p;\n    EXPECT(c, '\\\"');\n    p = c->json;\n    for (;;) {\n        char ch = *p++;\n        switch (ch) {\n            case '\\\"':\n                len = c->top - head;\n                lept_set_string(v, (const char*)lept_context_pop(c, len), len);\n                c->json = p;\n                return LEPT_PARSE_OK;\n            case '\\\\':\n                switch (*p++) {\n                    case '\\\"': PUTC(c, '\\\"'); break;\n                    case '\\\\': PUTC(c, '\\\\'); break;\n                    case '/':  PUTC(c, '/' ); break;\n                    case 'b':  PUTC(c, '\\b'); break;\n                    case 'f':  PUTC(c, '\\f'); break;\n                    case 'n':  PUTC(c, '\\n'); break;\n                    case 'r':  PUTC(c, '\\r'); break;\n                    case 't':  PUTC(c, '\\t'); break;\n                    case 'u':\n                        if (!(p = lept_parse_hex4(p, &u)))\n                            STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX);\n                        if (u >= 0xD800 && u <= 0xDBFF) { /* surrogate pair */\n                            if (*p++ != '\\\\')\n                                STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE);\n                            if (*p++ != 'u')\n                                STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE);\n                            if (!(p = lept_parse_hex4(p, &u2)))\n                                STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX);\n                            if (u2 < 0xDC00 || u2 > 0xDFFF)\n                                STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE);\n                            u = (((u - 0xD800) << 10) | (u2 - 0xDC00)) + 0x10000;\n                        }\n                        lept_encode_utf8(c, u);\n                        break;\n                    default:\n                        STRING_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE);\n                }\n                break;\n            case '\\0':\n                STRING_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK);\n            default:\n                if ((unsigned char)ch < 0x20)\n                    STRING_ERROR(LEPT_PARSE_INVALID_STRING_CHAR);\n                PUTC(c, ch);\n        }\n    }\n}\n\nstatic int lept_parse_value(lept_context* c, lept_value* v);\n\nstatic int lept_parse_array(lept_context* c, lept_value* v) {\n    size_t size = 0;\n    int ret;\n    EXPECT(c, '[');\n    if (*c->json == ']') {\n        c->json++;\n        v->type = LEPT_ARRAY;\n        v->u.a.size = 0;\n        v->u.a.e = NULL;\n        return LEPT_PARSE_OK;\n    }\n    for (;;) {\n        lept_value e;\n        lept_init(&e);\n        if ((ret = lept_parse_value(c, &e)) != LEPT_PARSE_OK)\n            return ret;\n        memcpy(lept_context_push(c, sizeof(lept_value)), &e, sizeof(lept_value));\n        size++;\n        if (*c->json == ',')\n            c->json++;\n        else if (*c->json == ']') {\n            c->json++;\n            v->type = LEPT_ARRAY;\n            v->u.a.size = size;\n            size *= sizeof(lept_value);\n            memcpy(v->u.a.e = (lept_value*)malloc(size), lept_context_pop(c, size), size);\n            return LEPT_PARSE_OK;\n        }\n        else\n            return LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET;\n    }\n}\n\nstatic int lept_parse_value(lept_context* c, lept_value* v) {\n    switch (*c->json) {\n        case 't':  return lept_parse_literal(c, v, \"true\", LEPT_TRUE);\n        case 'f':  return lept_parse_literal(c, v, \"false\", LEPT_FALSE);\n        case 'n':  return lept_parse_literal(c, v, \"null\", LEPT_NULL);\n        default:   return lept_parse_number(c, v);\n        case '\"':  return lept_parse_string(c, v);\n        case '[':  return lept_parse_array(c, v);\n        case '\\0': return LEPT_PARSE_EXPECT_VALUE;\n    }\n}\n\nint lept_parse(lept_value* v, const char* json) {\n    lept_context c;\n    int ret;\n    assert(v != NULL);\n    c.json = json;\n    c.stack = NULL;\n    c.size = c.top = 0;\n    lept_init(v);\n    lept_parse_whitespace(&c);\n    if ((ret = lept_parse_value(&c, v)) == LEPT_PARSE_OK) {\n        lept_parse_whitespace(&c);\n        if (*c.json != '\\0') {\n            v->type = LEPT_NULL;\n            ret = LEPT_PARSE_ROOT_NOT_SINGULAR;\n        }\n    }\n    assert(c.top == 0);\n    free(c.stack);\n    return ret;\n}\n\nvoid lept_free(lept_value* v) {\n    assert(v != NULL);\n    if (v->type == LEPT_STRING)\n        free(v->u.s.s);\n    v->type = LEPT_NULL;\n}\n\nlept_type lept_get_type(const lept_value* v) {\n    assert(v != NULL);\n    return v->type;\n}\n\nint lept_get_boolean(const lept_value* v) {\n    assert(v != NULL && (v->type == LEPT_TRUE || v->type == LEPT_FALSE));\n    return v->type == LEPT_TRUE;\n}\n\nvoid lept_set_boolean(lept_value* v, int b) {\n    lept_free(v);\n    v->type = b ? LEPT_TRUE : LEPT_FALSE;\n}\n\ndouble lept_get_number(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_NUMBER);\n    return v->u.n;\n}\n\nvoid lept_set_number(lept_value* v, double n) {\n    lept_free(v);\n    v->u.n = n;\n    v->type = LEPT_NUMBER;\n}\n\nconst char* lept_get_string(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_STRING);\n    return v->u.s.s;\n}\n\nsize_t lept_get_string_length(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_STRING);\n    return v->u.s.len;\n}\n\nvoid lept_set_string(lept_value* v, const char* s, size_t len) {\n    assert(v != NULL && (s != NULL || len == 0));\n    lept_free(v);\n    v->u.s.s = (char*)malloc(len + 1);\n    memcpy(v->u.s.s, s, len);\n    v->u.s.s[len] = '\\0';\n    v->u.s.len = len;\n    v->type = LEPT_STRING;\n}\n\nsize_t lept_get_array_size(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_ARRAY);\n    return v->u.a.size;\n}\n\nlept_value* lept_get_array_element(const lept_value* v, size_t index) {\n    assert(v != NULL && v->type == LEPT_ARRAY);\n    assert(index < v->u.a.size);\n    return &v->u.a.e[index];\n}\n"
  },
  {
    "path": "tutorial05/leptjson.h",
    "content": "#ifndef LEPTJSON_H__\n#define LEPTJSON_H__\n\n#include <stddef.h> /* size_t */\n\ntypedef enum { LEPT_NULL, LEPT_FALSE, LEPT_TRUE, LEPT_NUMBER, LEPT_STRING, LEPT_ARRAY, LEPT_OBJECT } lept_type;\n\ntypedef struct lept_value lept_value;\n\nstruct lept_value {\n    union {\n        struct { lept_value* e; size_t size; }a;    /* array:  elements, element count */\n        struct { char* s; size_t len; }s;           /* string: null-terminated string, string length */\n        double n;                                   /* number */\n    }u;\n    lept_type type;\n};\n\nenum {\n    LEPT_PARSE_OK = 0,\n    LEPT_PARSE_EXPECT_VALUE,\n    LEPT_PARSE_INVALID_VALUE,\n    LEPT_PARSE_ROOT_NOT_SINGULAR,\n    LEPT_PARSE_NUMBER_TOO_BIG,\n    LEPT_PARSE_MISS_QUOTATION_MARK,\n    LEPT_PARSE_INVALID_STRING_ESCAPE,\n    LEPT_PARSE_INVALID_STRING_CHAR,\n    LEPT_PARSE_INVALID_UNICODE_HEX,\n    LEPT_PARSE_INVALID_UNICODE_SURROGATE,\n    LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET\n};\n\n#define lept_init(v) do { (v)->type = LEPT_NULL; } while(0)\n\nint lept_parse(lept_value* v, const char* json);\n\nvoid lept_free(lept_value* v);\n\nlept_type lept_get_type(const lept_value* v);\n\n#define lept_set_null(v) lept_free(v)\n\nint lept_get_boolean(const lept_value* v);\nvoid lept_set_boolean(lept_value* v, int b);\n\ndouble lept_get_number(const lept_value* v);\nvoid lept_set_number(lept_value* v, double n);\n\nconst char* lept_get_string(const lept_value* v);\nsize_t lept_get_string_length(const lept_value* v);\nvoid lept_set_string(lept_value* v, const char* s, size_t len);\n\nsize_t lept_get_array_size(const lept_value* v);\nlept_value* lept_get_array_element(const lept_value* v, size_t index);\n\n#endif /* LEPTJSON_H__ */\n"
  },
  {
    "path": "tutorial05/test.c",
    "content": "#ifdef _WINDOWS\n#define _CRTDBG_MAP_ALLOC\n#include <crtdbg.h>\n#endif\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"leptjson.h\"\n\nstatic int main_ret = 0;\nstatic int test_count = 0;\nstatic int test_pass = 0;\n\n#define EXPECT_EQ_BASE(equality, expect, actual, format) \\\n    do {\\\n        test_count++;\\\n        if (equality)\\\n            test_pass++;\\\n        else {\\\n            fprintf(stderr, \"%s:%d: expect: \" format \" actual: \" format \"\\n\", __FILE__, __LINE__, expect, actual);\\\n            main_ret = 1;\\\n        }\\\n    } while(0)\n\n#define EXPECT_EQ_INT(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, \"%d\")\n#define EXPECT_EQ_DOUBLE(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, \"%.17g\")\n#define EXPECT_EQ_STRING(expect, actual, alength) \\\n    EXPECT_EQ_BASE(sizeof(expect) - 1 == alength && memcmp(expect, actual, alength) == 0, expect, actual, \"%s\")\n#define EXPECT_TRUE(actual) EXPECT_EQ_BASE((actual) != 0, \"true\", \"false\", \"%s\")\n#define EXPECT_FALSE(actual) EXPECT_EQ_BASE((actual) == 0, \"false\", \"true\", \"%s\")\n\n#if defined(_MSC_VER)\n#define EXPECT_EQ_SIZE_T(expect, actual) EXPECT_EQ_BASE((expect) == (actual), (size_t)expect, (size_t)actual, \"%Iu\")\n#else\n#define EXPECT_EQ_SIZE_T(expect, actual) EXPECT_EQ_BASE((expect) == (actual), (size_t)expect, (size_t)actual, \"%zu\")\n#endif\n\nstatic void test_parse_null() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_boolean(&v, 0);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"null\"));\n    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\n    lept_free(&v);\n}\n\nstatic void test_parse_true() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_boolean(&v, 0);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"true\"));\n    EXPECT_EQ_INT(LEPT_TRUE, lept_get_type(&v));\n    lept_free(&v);\n}\n\nstatic void test_parse_false() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_boolean(&v, 1);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"false\"));\n    EXPECT_EQ_INT(LEPT_FALSE, lept_get_type(&v));\n    lept_free(&v);\n}\n\n#define TEST_NUMBER(expect, json)\\\n    do {\\\n        lept_value v;\\\n        lept_init(&v);\\\n        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(&v));\\\n        EXPECT_EQ_DOUBLE(expect, lept_get_number(&v));\\\n        lept_free(&v);\\\n    } while(0)\n\nstatic void test_parse_number() {\n    TEST_NUMBER(0.0, \"0\");\n    TEST_NUMBER(0.0, \"-0\");\n    TEST_NUMBER(0.0, \"-0.0\");\n    TEST_NUMBER(1.0, \"1\");\n    TEST_NUMBER(-1.0, \"-1\");\n    TEST_NUMBER(1.5, \"1.5\");\n    TEST_NUMBER(-1.5, \"-1.5\");\n    TEST_NUMBER(3.1416, \"3.1416\");\n    TEST_NUMBER(1E10, \"1E10\");\n    TEST_NUMBER(1e10, \"1e10\");\n    TEST_NUMBER(1E+10, \"1E+10\");\n    TEST_NUMBER(1E-10, \"1E-10\");\n    TEST_NUMBER(-1E10, \"-1E10\");\n    TEST_NUMBER(-1e10, \"-1e10\");\n    TEST_NUMBER(-1E+10, \"-1E+10\");\n    TEST_NUMBER(-1E-10, \"-1E-10\");\n    TEST_NUMBER(1.234E+10, \"1.234E+10\");\n    TEST_NUMBER(1.234E-10, \"1.234E-10\");\n    TEST_NUMBER(0.0, \"1e-10000\"); /* must underflow */\n\n    TEST_NUMBER(1.0000000000000002, \"1.0000000000000002\"); /* the smallest number > 1 */\n    TEST_NUMBER( 4.9406564584124654e-324, \"4.9406564584124654e-324\"); /* minimum denormal */\n    TEST_NUMBER(-4.9406564584124654e-324, \"-4.9406564584124654e-324\");\n    TEST_NUMBER( 2.2250738585072009e-308, \"2.2250738585072009e-308\");  /* Max subnormal double */\n    TEST_NUMBER(-2.2250738585072009e-308, \"-2.2250738585072009e-308\");\n    TEST_NUMBER( 2.2250738585072014e-308, \"2.2250738585072014e-308\");  /* Min normal positive double */\n    TEST_NUMBER(-2.2250738585072014e-308, \"-2.2250738585072014e-308\");\n    TEST_NUMBER( 1.7976931348623157e+308, \"1.7976931348623157e+308\");  /* Max double */\n    TEST_NUMBER(-1.7976931348623157e+308, \"-1.7976931348623157e+308\");\n}\n\n#define TEST_STRING(expect, json)\\\n    do {\\\n        lept_value v;\\\n        lept_init(&v);\\\n        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_STRING, lept_get_type(&v));\\\n        EXPECT_EQ_STRING(expect, lept_get_string(&v), lept_get_string_length(&v));\\\n        lept_free(&v);\\\n    } while(0)\n\nstatic void test_parse_string() {\n    TEST_STRING(\"\", \"\\\"\\\"\");\n    TEST_STRING(\"Hello\", \"\\\"Hello\\\"\");\n    TEST_STRING(\"Hello\\nWorld\", \"\\\"Hello\\\\nWorld\\\"\");\n    TEST_STRING(\"\\\" \\\\ / \\b \\f \\n \\r \\t\", \"\\\"\\\\\\\" \\\\\\\\ \\\\/ \\\\b \\\\f \\\\n \\\\r \\\\t\\\"\");\n    TEST_STRING(\"Hello\\0World\", \"\\\"Hello\\\\u0000World\\\"\");\n    TEST_STRING(\"\\x24\", \"\\\"\\\\u0024\\\"\");         /* Dollar sign U+0024 */\n    TEST_STRING(\"\\xC2\\xA2\", \"\\\"\\\\u00A2\\\"\");     /* Cents sign U+00A2 */\n    TEST_STRING(\"\\xE2\\x82\\xAC\", \"\\\"\\\\u20AC\\\"\"); /* Euro sign U+20AC */\n    TEST_STRING(\"\\xF0\\x9D\\x84\\x9E\", \"\\\"\\\\uD834\\\\uDD1E\\\"\");  /* G clef sign U+1D11E */\n    TEST_STRING(\"\\xF0\\x9D\\x84\\x9E\", \"\\\"\\\\ud834\\\\udd1e\\\"\");  /* G clef sign U+1D11E */\n}\n\nstatic void test_parse_array() {\n    lept_value v;\n\n    lept_init(&v);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"[ ]\"));\n    EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(&v));\n    EXPECT_EQ_SIZE_T(0, lept_get_array_size(&v));\n    lept_free(&v);\n}\n\n#define TEST_ERROR(error, json)\\\n    do {\\\n        lept_value v;\\\n        lept_init(&v);\\\n        v.type = LEPT_FALSE;\\\n        EXPECT_EQ_INT(error, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\\\n        lept_free(&v);\\\n    } while(0)\n\nstatic void test_parse_expect_value() {\n    TEST_ERROR(LEPT_PARSE_EXPECT_VALUE, \"\");\n    TEST_ERROR(LEPT_PARSE_EXPECT_VALUE, \" \");\n}\n\nstatic void test_parse_invalid_value() {\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"nul\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"?\");\n\n    /* invalid number */\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"+0\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"+1\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \".123\"); /* at least one digit before '.' */\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"1.\");   /* at least one digit after '.' */\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"INF\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"inf\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"NAN\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"nan\");\n\n    /* invalid value in array */\n#if 0\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"[1,]\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"[\\\"a\\\", nul]\");\n#endif\n}\n\nstatic void test_parse_root_not_singular() {\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"null x\");\n\n    /* invalid number */\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0123\"); /* after zero should be '.' or nothing */\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0x0\");\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0x123\");\n}\n\nstatic void test_parse_number_too_big() {\n    TEST_ERROR(LEPT_PARSE_NUMBER_TOO_BIG, \"1e309\");\n    TEST_ERROR(LEPT_PARSE_NUMBER_TOO_BIG, \"-1e309\");\n}\n\nstatic void test_parse_miss_quotation_mark() {\n    TEST_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK, \"\\\"\");\n    TEST_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK, \"\\\"abc\");\n}\n\nstatic void test_parse_invalid_string_escape() {\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\v\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\'\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\0\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\x12\\\"\");\n}\n\nstatic void test_parse_invalid_string_char() {\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_CHAR, \"\\\"\\x01\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_CHAR, \"\\\"\\x1F\\\"\");\n}\n\nstatic void test_parse_invalid_unicode_hex() {\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u0\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u01\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u012\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u/000\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\uG000\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u0/00\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u0G00\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u00/0\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u00G0\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u000/\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u000G\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u 123\\\"\");\n}\n\nstatic void test_parse_invalid_unicode_surrogate() {\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uD800\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uDBFF\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uD800\\\\\\\\\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uD800\\\\uDBFF\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uD800\\\\uE000\\\"\");\n}\n\nstatic void test_parse_miss_comma_or_square_bracket() {\n#if 0\n    TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, \"[1\");\n    TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, \"[1}\");\n    TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, \"[1 2\");\n    TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, \"[[]\");\n#endif\n}\n\nstatic void test_parse() {\n    test_parse_null();\n    test_parse_true();\n    test_parse_false();\n    test_parse_number();\n    test_parse_string();\n    test_parse_array();\n    test_parse_expect_value();\n    test_parse_invalid_value();\n    test_parse_root_not_singular();\n    test_parse_number_too_big();\n    test_parse_miss_quotation_mark();\n    test_parse_invalid_string_escape();\n    test_parse_invalid_string_char();\n    test_parse_invalid_unicode_hex();\n    test_parse_invalid_unicode_surrogate();\n    test_parse_miss_comma_or_square_bracket();\n}\n\nstatic void test_access_null() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"a\", 1);\n    lept_set_null(&v);\n    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\n    lept_free(&v);\n}\n\nstatic void test_access_boolean() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"a\", 1);\n    lept_set_boolean(&v, 1);\n    EXPECT_TRUE(lept_get_boolean(&v));\n    lept_set_boolean(&v, 0);\n    EXPECT_FALSE(lept_get_boolean(&v));\n    lept_free(&v);\n}\n\nstatic void test_access_number() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"a\", 1);\n    lept_set_number(&v, 1234.5);\n    EXPECT_EQ_DOUBLE(1234.5, lept_get_number(&v));\n    lept_free(&v);\n}\n\nstatic void test_access_string() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"\", 0);\n    EXPECT_EQ_STRING(\"\", lept_get_string(&v), lept_get_string_length(&v));\n    lept_set_string(&v, \"Hello\", 5);\n    EXPECT_EQ_STRING(\"Hello\", lept_get_string(&v), lept_get_string_length(&v));\n    lept_free(&v);\n}\n\nstatic void test_access() {\n    test_access_null();\n    test_access_boolean();\n    test_access_number();\n    test_access_string();\n}\n\nint main() {\n#ifdef _WINDOWS\n    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);\n#endif\n    test_parse();\n    test_access();\n    printf(\"%d/%d (%3.2f%%) passed\\n\", test_pass, test_count, test_pass * 100.0 / test_count);\n    return main_ret;\n}\n"
  },
  {
    "path": "tutorial05/tutorial05.md",
    "content": "# 从零开始的 JSON 库教程（五）：解析数组\n\n* Milo Yip\n* 2016/10/7\n\n本文是[《从零开始的 JSON 库教程》](https://zhuanlan.zhihu.com/json-tutorial)的第五个单元。代码位于 [json-tutorial/tutorial05](https://github.com/miloyip/json-tutorial/blob/master/tutorial05)。\n\n本单元内容：\n\n1. [JSON 数组](#1-json-数组)\n2. [数据结构](#2-数据结构)\n3. [解析过程](#3-解析过程)\n4. [实现](#4-实现)\n5. [总结与练习](#5-总结与练习)\n\n## 1. JSON 数组\n\n从零到这第五单元，我们终于要解析一个 JSON 的复合数据类型了。一个 JSON 数组可以包含零至多个元素，而这些元素也可以是数组类型。换句话说，我们可以表示嵌套（nested）的数据结构。先来看看 JSON 数组的语法：\n\n~~~\narray = %x5B ws [ value *( ws %x2C ws value ) ] ws %x5D\n~~~\n\n当中，`%x5B` 是左中括号 `[`，`%x2C` 是逗号 `,`，`%x5D` 是右中括号 `]` ，`ws` 是空白字符。一个数组可以包含零至多个值，以逗号分隔，例如 `[]`、`[1,2,true]`、`[[1,2],[3,4],\"abc\"]` 都是合法的数组。但注意 JSON 不接受末端额外的逗号，例如 `[1,2,]` 是不合法的（许多编程语言如 C/C++、Javascript、Java、C# 都容许数组初始值包含末端逗号）。\n\nJSON 数组的语法很简单，实现的难点不在语法上，而是怎样管理内存。\n\n## 2. 数据结构\n\n首先，我们需要设计存储 JSON 数组类型的数据结构。\n\nJSON 数组存储零至多个元素，最简单就是使用 C 语言的数组。数组最大的好处是能以 $O(1)$ 用索引访问任意元素，次要好处是内存布局紧凑，省内存之余还有高缓存一致性（cache coherence）。但数组的缺点是不能快速插入元素，而且我们在解析 JSON 数组的时候，还不知道应该分配多大的数组才合适。\n\n另一个选择是链表（linked list），它的最大优点是可快速地插入元素（开端、末端或中间），但需要以 $O(n)$ 时间去经索引取得内容。如果我们只需顺序遍历，那么是没有问题的。还有一个小缺点，就是相对数组而言，链表在存储每个元素时有额外内存开销（存储下一节点的指针），而且遍历时元素所在的内存可能不连续，令缓存不命中（cache miss）的机会上升。\n\n我见过一些 JSON 库选择了链表，而这里则选择了数组。我们将会通过之前在解析字符串时实现的堆栈，来解决解析 JSON 数组时未知数组大小的问题。\n\n决定之后，我们在 `lept_value` 的 `union` 中加入数组的结构：\n\n~~~c\ntypedef struct lept_value lept_value;\n\nstruct lept_value {\n    union {\n        struct { lept_value* e; size_t size; }a; /* array */\n        struct { char* s; size_t len; }s;\n        double n;\n    }u;\n    lept_type type;\n};\n~~~\n\n由于 `lept_value` 内使用了自身类型的指针，我们必须前向声明（forward declare）此类型。\n\n另外，注意这里 `size` 是元素的个数，不是字节单位。我们增加两个 API 去访问 JSON 数组类型的值：\n\n~~~c\nsize_t lept_get_array_size(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_ARRAY);\n    return v->u.a.size;\n}\n\nlept_value* lept_get_array_element(const lept_value* v, size_t index) {\n    assert(v != NULL && v->type == LEPT_ARRAY);\n    assert(index < v->u.a.size);\n    return &v->u.a.e[index];\n}\n~~~\n\n暂时我们不考虑增删数组元素，这些功能留待第八单元讨论。\n\n然后，我们写一个单元测试去试用这些 API（练习需要更多测试）。\n\n~~~c\n#if defined(_MSC_VER)\n#define EXPECT_EQ_SIZE_T(expect, actual) EXPECT_EQ_BASE((expect) == (actual), (size_t)expect, (size_t)actual, \"%Iu\")\n#else\n#define EXPECT_EQ_SIZE_T(expect, actual) EXPECT_EQ_BASE((expect) == (actual), (size_t)expect, (size_t)actual, \"%zu\")\n#endif\n\nstatic void test_parse_array() {\n    lept_value v;\n\n    lept_init(&v);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"[ ]\"));\n    EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(&v));\n    EXPECT_EQ_SIZE_T(0, lept_get_array_size(&v));\n    lept_free(&v);\n}\n~~~\n\n在之前的单元中，作者已多次重申，C 语言的数组大小应该使用 `size_t` 类型。因为我们要验证 `lept_get_array_size()` 返回值是否正确，所以再为单元测试框架添加一个宏 `EXPECT_EQ_SIZE_T`。麻烦之处在于，ANSI C（C89）并没有的 `size_t` 打印方法，在 C99 则加入了 `\"%zu\"`，但 VS2015 中才有，之前的 VC 版本使用非标准的 `\"%Iu\"`。因此，上面的代码使用条件编译去区分 VC 和其他编译器。虽然这部分不跨平台也不是 ANSI C 标准，但它只在测试程序中，不太影响程序库的跨平台性。\n\n## 3. 解析过程\n\n我们在解析 JSON 字符串时，因为在开始时不能知道字符串的长度，而又需要进行转义，所以需要一个临时缓冲区去存储解析后的结果。我们为此实现了一个动态增长的堆栈，可以不断压入字符，最后一次性把整个字符串弹出，复制至新分配的内存之中。\n\n对于 JSON 数组，我们也可以用相同的方法，而且，我们可以用同一个堆栈！我们只需要把每个解析好的元素压入堆栈，解析到数组结束时，再一次性把所有元素弹出，复制至新分配的内存之中。\n\n但和字符串有点不一样，如果把 JSON 当作一棵树的数据结构，JSON 字符串是叶节点，而 JSON 数组是中间节点。在叶节点的解析函数中，我们怎样使用那个堆栈也可以，只要最后还原就好了。但对于数组这样的中间节点，共用这个堆栈没问题么？\n\n答案是：只要在解析函数结束时还原堆栈的状态，就没有问题。为了直观地了解这个解析过程，我们用连环图去展示 `[\"abc\",[1,2],3]` 的解析过程。\n\n首先，我们遇到 `[`，进入 `lept_parse_array()`：\n\n![ ](images/parse_array01.png)\n\n生成一个临时的 `lept_value`，用于存储之后的元素。我们再调用 `lept_parse_value()` 去解析这个元素值，因为遇到 `\"` 进入 `lept_parse_string()`：\n\n![ ](images/parse_array02.png)\n\n在 `lept_parse_string()` 中，不断解析字符直至遇到 `\"`，过程中把每个字符压栈：\n\n![ ](images/parse_array03.png)\n\n最后在 `lept_parse_string()` 中，把栈上 3 个字符弹出，分配内存，生成字符串值：\n\n![ ](images/parse_array04.png)\n\n返回上一层 `lept_parse_array()`，把临时元素压栈：\n\n![ ](images/parse_array05.png)\n\n然后我们再遇到 `[`，进入另一个 `lept_parse_array()`。它发现第一个元素是数字类型，所认调用 `lept_parse_number()`，生成一个临时的元素值：\n\n![ ](images/parse_array06.png)\n\n之后把该临时的元素值压栈：\n\n![ ](images/parse_array07.png)\n\n接着再解析第二个元素。我们遇到了 `]`，从栈上弹出 2 个元素，分配内存，生成数组（虚线代表是连续的内存）：\n\n![ ](images/parse_array08.png)\n\n那个数组是上层数组的元素，我们把它压栈。现时栈内已有两个元素，我们再继续解析下一个元素：\n\n![ ](images/parse_array09.png)\n\n最后，遇到了 `]`，可以弹出栈内 3 个元素，分配内存，生成数组：\n\n![ ](images/parse_array10.png)\n\n## 4. 实现\n\n经过这个详细的图解，实现 `lept_parse_array()` 应该没有难度。以下是半制成品：\n\n~~~c\nstatic int lept_parse_value(lept_context* c, lept_value* v); /* 前向声明 */\n\nstatic int lept_parse_array(lept_context* c, lept_value* v) {\n    size_t size = 0;\n    int ret;\n    EXPECT(c, '[');\n    if (*c->json == ']') {\n        c->json++;\n        v->type = LEPT_ARRAY;\n        v->u.a.size = 0;\n        v->u.a.e = NULL;\n        return LEPT_PARSE_OK;\n    }\n    for (;;) {\n        lept_value e;\n        lept_init(&e);\n        if ((ret = lept_parse_value(c, &e)) != LEPT_PARSE_OK)\n            return ret;\n        memcpy(lept_context_push(c, sizeof(lept_value)), &e, sizeof(lept_value));\n        size++;\n        if (*c->json == ',')\n            c->json++;\n        else if (*c->json == ']') {\n            c->json++;\n            v->type = LEPT_ARRAY;\n            v->u.a.size = size;\n            size *= sizeof(lept_value);\n            memcpy(v->u.a.e = (lept_value*)malloc(size), lept_context_pop(c, size), size);\n            return LEPT_PARSE_OK;\n        }\n        else\n            return LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET;\n    }\n}\n\nstatic int lept_parse_value(lept_context* c, lept_value* v) {\n    switch (*c->json) {\n        /* ... */\n        case '[':  return lept_parse_array(c, v);\n    }\n}\n~~~\n\n简单说明的话，就是在循环中建立一个临时值（`lept_value e`），然后调用 `lept_parse_value()` 去把元素解析至这个临时值，完成后把临时值压栈。当遇到 `]`，把栈内的元素弹出，分配内存，生成数组值。\n\n注意到，`lept_parse_value()` 会调用 `lept_parse_array()`，而 `lept_parse_array()` 又会调用 `lept_parse_value()`，这是互相引用，所以必须要加入函数前向声明。\n\n最后，我想告诉同学，实现这个函数时，我曾经制造一个不明显的 bug。这个函数有两个 `memcpy()`，第一个「似乎」是可以避免的，先压栈取得元素的指针，给 `lept_parse_value`：\n\n~~~c\n    for (;;) {\n        /* bug! */\n        lept_value* e = lept_context_push(c, sizeof(lept_value));\n        lept_init(e);\n        size++;\n        if ((ret = lept_parse_value(c, e)) != LEPT_PARSE_OK)\n            return ret;\n        /* ... */\n    }\n~~~\n\n这种写法为什么会有 bug？这是第 5 条练习题。\n\n## 5. 总结与练习\n\n1. 编写 `test_parse_array()` 单元测试，解析以下 2 个 JSON。由于数组是复合的类型，不能使用一个宏去测试结果，请使用各个 API 检查解析后的内容。\n\n~~~js\n[ null , false , true , 123 , \"abc\" ]\n[ [ ] , [ 0 ] , [ 0 , 1 ] , [ 0 , 1 , 2 ] ]\n~~~\n\n2. 现时的测试结果应该是失败的，因为 `lept_parse_array()` 里没有处理空白字符，加进合适的 `lept_parse_whitespace()` 令测试通过。\n\n3. 使用[第三单元解答篇](../tutorial03_answer/tutorial03_answer.md)介绍的检测内存泄漏工具，会发现测试中有内存泄漏。很明显在 `lept_parse_array()` 中使用到 `malloc()` 分配内存，但却没有对应的 `free()`。应该在哪里释放内存？修改代码，使工具不再检测到相关的内存泄漏。\n\n4. 开启 test.c 中两处被 `#if 0 ... #endif` 关闭的测试，本来 `lept_parse_array()` 已经能处理这些测试。然而，运行时会发现 `Assertion failed: (c.top == 0)` 断言失败。这是由于，当错误发生时，仍然有一些临时值在堆栈里，既没有放进数组，也没有被释放。修改 `lept_parse_array()`，当遇到错误时，从堆栈中弹出并释放那些临时值，然后才返回错误码。\n\n5. 第 4 节那段代码为什么会有 bug？\n\n如果你遇到问题，有不理解的地方，或是有建议，都欢迎在评论或 [issue](https://github.com/miloyip/json-tutorial/issues) 中提出，让所有人一起讨论。\n"
  },
  {
    "path": "tutorial05_answer/CMakeLists.txt",
    "content": "cmake_minimum_required (VERSION 2.6)\nproject (leptjson_test C)\n\nif (CMAKE_C_COMPILER_ID MATCHES \"GNU|Clang\")\n    set(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -ansi -pedantic -Wall\")\nendif()\n\nadd_library(leptjson leptjson.c)\nadd_executable(leptjson_test test.c)\ntarget_link_libraries(leptjson_test leptjson)\n"
  },
  {
    "path": "tutorial05_answer/leptjson.c",
    "content": "#ifdef _WINDOWS\n#define _CRTDBG_MAP_ALLOC\n#include <crtdbg.h>\n#endif\n#include \"leptjson.h\"\n#include <assert.h>  /* assert() */\n#include <errno.h>   /* errno, ERANGE */\n#include <math.h>    /* HUGE_VAL */\n#include <stdlib.h>  /* NULL, malloc(), realloc(), free(), strtod() */\n#include <string.h>  /* memcpy() */\n\n#ifndef LEPT_PARSE_STACK_INIT_SIZE\n#define LEPT_PARSE_STACK_INIT_SIZE 256\n#endif\n\n#define EXPECT(c, ch)       do { assert(*c->json == (ch)); c->json++; } while(0)\n#define ISDIGIT(ch)         ((ch) >= '0' && (ch) <= '9')\n#define ISDIGIT1TO9(ch)     ((ch) >= '1' && (ch) <= '9')\n#define PUTC(c, ch)         do { *(char*)lept_context_push(c, sizeof(char)) = (ch); } while(0)\n\ntypedef struct {\n    const char* json;\n    char* stack;\n    size_t size, top;\n}lept_context;\n\nstatic void* lept_context_push(lept_context* c, size_t size) {\n    void* ret;\n    assert(size > 0);\n    if (c->top + size >= c->size) {\n        if (c->size == 0)\n            c->size = LEPT_PARSE_STACK_INIT_SIZE;\n        while (c->top + size >= c->size)\n            c->size += c->size >> 1;  /* c->size * 1.5 */\n        c->stack = (char*)realloc(c->stack, c->size);\n    }\n    ret = c->stack + c->top;\n    c->top += size;\n    return ret;\n}\n\nstatic void* lept_context_pop(lept_context* c, size_t size) {\n    assert(c->top >= size);\n    return c->stack + (c->top -= size);\n}\n\nstatic void lept_parse_whitespace(lept_context* c) {\n    const char *p = c->json;\n    while (*p == ' ' || *p == '\\t' || *p == '\\n' || *p == '\\r')\n        p++;\n    c->json = p;\n}\n\nstatic int lept_parse_literal(lept_context* c, lept_value* v, const char* literal, lept_type type) {\n    size_t i;\n    EXPECT(c, literal[0]);\n    for (i = 0; literal[i + 1]; i++)\n        if (c->json[i] != literal[i + 1])\n            return LEPT_PARSE_INVALID_VALUE;\n    c->json += i;\n    v->type = type;\n    return LEPT_PARSE_OK;\n}\n\nstatic int lept_parse_number(lept_context* c, lept_value* v) {\n    const char* p = c->json;\n    if (*p == '-') p++;\n    if (*p == '0') p++;\n    else {\n        if (!ISDIGIT1TO9(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n    if (*p == '.') {\n        p++;\n        if (!ISDIGIT(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n    if (*p == 'e' || *p == 'E') {\n        p++;\n        if (*p == '+' || *p == '-') p++;\n        if (!ISDIGIT(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n    errno = 0;\n    v->u.n = strtod(c->json, NULL);\n    if (errno == ERANGE && (v->u.n == HUGE_VAL || v->u.n == -HUGE_VAL))\n        return LEPT_PARSE_NUMBER_TOO_BIG;\n    v->type = LEPT_NUMBER;\n    c->json = p;\n    return LEPT_PARSE_OK;\n}\n\nstatic const char* lept_parse_hex4(const char* p, unsigned* u) {\n    int i;\n    *u = 0;\n    for (i = 0; i < 4; i++) {\n        char ch = *p++;\n        *u <<= 4;\n        if      (ch >= '0' && ch <= '9')  *u |= ch - '0';\n        else if (ch >= 'A' && ch <= 'F')  *u |= ch - ('A' - 10);\n        else if (ch >= 'a' && ch <= 'f')  *u |= ch - ('a' - 10);\n        else return NULL;\n    }\n    return p;\n}\n\nstatic void lept_encode_utf8(lept_context* c, unsigned u) {\n    if (u <= 0x7F) \n        PUTC(c, u & 0xFF);\n    else if (u <= 0x7FF) {\n        PUTC(c, 0xC0 | ((u >> 6) & 0xFF));\n        PUTC(c, 0x80 | ( u       & 0x3F));\n    }\n    else if (u <= 0xFFFF) {\n        PUTC(c, 0xE0 | ((u >> 12) & 0xFF));\n        PUTC(c, 0x80 | ((u >>  6) & 0x3F));\n        PUTC(c, 0x80 | ( u        & 0x3F));\n    }\n    else {\n        assert(u <= 0x10FFFF);\n        PUTC(c, 0xF0 | ((u >> 18) & 0xFF));\n        PUTC(c, 0x80 | ((u >> 12) & 0x3F));\n        PUTC(c, 0x80 | ((u >>  6) & 0x3F));\n        PUTC(c, 0x80 | ( u        & 0x3F));\n    }\n}\n\n#define STRING_ERROR(ret) do { c->top = head; return ret; } while(0)\n\nstatic int lept_parse_string(lept_context* c, lept_value* v) {\n    size_t head = c->top, len;\n    unsigned u, u2;\n    const char* p;\n    EXPECT(c, '\\\"');\n    p = c->json;\n    for (;;) {\n        char ch = *p++;\n        switch (ch) {\n            case '\\\"':\n                len = c->top - head;\n                lept_set_string(v, (const char*)lept_context_pop(c, len), len);\n                c->json = p;\n                return LEPT_PARSE_OK;\n            case '\\\\':\n                switch (*p++) {\n                    case '\\\"': PUTC(c, '\\\"'); break;\n                    case '\\\\': PUTC(c, '\\\\'); break;\n                    case '/':  PUTC(c, '/' ); break;\n                    case 'b':  PUTC(c, '\\b'); break;\n                    case 'f':  PUTC(c, '\\f'); break;\n                    case 'n':  PUTC(c, '\\n'); break;\n                    case 'r':  PUTC(c, '\\r'); break;\n                    case 't':  PUTC(c, '\\t'); break;\n                    case 'u':\n                        if (!(p = lept_parse_hex4(p, &u)))\n                            STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX);\n                        if (u >= 0xD800 && u <= 0xDBFF) { /* surrogate pair */\n                            if (*p++ != '\\\\')\n                                STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE);\n                            if (*p++ != 'u')\n                                STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE);\n                            if (!(p = lept_parse_hex4(p, &u2)))\n                                STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX);\n                            if (u2 < 0xDC00 || u2 > 0xDFFF)\n                                STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE);\n                            u = (((u - 0xD800) << 10) | (u2 - 0xDC00)) + 0x10000;\n                        }\n                        lept_encode_utf8(c, u);\n                        break;\n                    default:\n                        STRING_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE);\n                }\n                break;\n            case '\\0':\n                STRING_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK);\n            default:\n                if ((unsigned char)ch < 0x20)\n                    STRING_ERROR(LEPT_PARSE_INVALID_STRING_CHAR);\n                PUTC(c, ch);\n        }\n    }\n}\n\nstatic int lept_parse_value(lept_context* c, lept_value* v);\n\nstatic int lept_parse_array(lept_context* c, lept_value* v) {\n    size_t i, size = 0;\n    int ret;\n    EXPECT(c, '[');\n    lept_parse_whitespace(c);\n    if (*c->json == ']') {\n        c->json++;\n        v->type = LEPT_ARRAY;\n        v->u.a.size = 0;\n        v->u.a.e = NULL;\n        return LEPT_PARSE_OK;\n    }\n    for (;;) {\n        lept_value e;\n        lept_init(&e);\n        if ((ret = lept_parse_value(c, &e)) != LEPT_PARSE_OK)\n            break;\n        memcpy(lept_context_push(c, sizeof(lept_value)), &e, sizeof(lept_value));\n        size++;\n        lept_parse_whitespace(c);\n        if (*c->json == ',') {\n            c->json++;\n            lept_parse_whitespace(c);\n        }\n        else if (*c->json == ']') {\n            c->json++;\n            v->type = LEPT_ARRAY;\n            v->u.a.size = size;\n            size *= sizeof(lept_value);\n            memcpy(v->u.a.e = (lept_value*)malloc(size), lept_context_pop(c, size), size);\n            return LEPT_PARSE_OK;\n        }\n        else {\n            ret = LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET;\n            break;\n        }\n    }\n    /* Pop and free values on the stack */\n    for (i = 0; i < size; i++)\n        lept_free((lept_value*)lept_context_pop(c, sizeof(lept_value)));\n    return ret;\n}\n\nstatic int lept_parse_value(lept_context* c, lept_value* v) {\n    switch (*c->json) {\n        case 't':  return lept_parse_literal(c, v, \"true\", LEPT_TRUE);\n        case 'f':  return lept_parse_literal(c, v, \"false\", LEPT_FALSE);\n        case 'n':  return lept_parse_literal(c, v, \"null\", LEPT_NULL);\n        default:   return lept_parse_number(c, v);\n        case '\"':  return lept_parse_string(c, v);\n        case '[':  return lept_parse_array(c, v);\n        case '\\0': return LEPT_PARSE_EXPECT_VALUE;\n    }\n}\n\nint lept_parse(lept_value* v, const char* json) {\n    lept_context c;\n    int ret;\n    assert(v != NULL);\n    c.json = json;\n    c.stack = NULL;\n    c.size = c.top = 0;\n    lept_init(v);\n    lept_parse_whitespace(&c);\n    if ((ret = lept_parse_value(&c, v)) == LEPT_PARSE_OK) {\n        lept_parse_whitespace(&c);\n        if (*c.json != '\\0') {\n            v->type = LEPT_NULL;\n            ret = LEPT_PARSE_ROOT_NOT_SINGULAR;\n        }\n    }\n    assert(c.top == 0);\n    free(c.stack);\n    return ret;\n}\n\nvoid lept_free(lept_value* v) {\n    size_t i;\n    assert(v != NULL);\n    switch (v->type) {\n        case LEPT_STRING:\n            free(v->u.s.s);\n            break;\n        case LEPT_ARRAY:\n            for (i = 0; i < v->u.a.size; i++)\n                lept_free(&v->u.a.e[i]);\n            free(v->u.a.e);\n            break;\n        default: break;\n    }\n    v->type = LEPT_NULL;\n}\n\nlept_type lept_get_type(const lept_value* v) {\n    assert(v != NULL);\n    return v->type;\n}\n\nint lept_get_boolean(const lept_value* v) {\n    assert(v != NULL && (v->type == LEPT_TRUE || v->type == LEPT_FALSE));\n    return v->type == LEPT_TRUE;\n}\n\nvoid lept_set_boolean(lept_value* v, int b) {\n    lept_free(v);\n    v->type = b ? LEPT_TRUE : LEPT_FALSE;\n}\n\ndouble lept_get_number(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_NUMBER);\n    return v->u.n;\n}\n\nvoid lept_set_number(lept_value* v, double n) {\n    lept_free(v);\n    v->u.n = n;\n    v->type = LEPT_NUMBER;\n}\n\nconst char* lept_get_string(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_STRING);\n    return v->u.s.s;\n}\n\nsize_t lept_get_string_length(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_STRING);\n    return v->u.s.len;\n}\n\nvoid lept_set_string(lept_value* v, const char* s, size_t len) {\n    assert(v != NULL && (s != NULL || len == 0));\n    lept_free(v);\n    v->u.s.s = (char*)malloc(len + 1);\n    memcpy(v->u.s.s, s, len);\n    v->u.s.s[len] = '\\0';\n    v->u.s.len = len;\n    v->type = LEPT_STRING;\n}\n\nsize_t lept_get_array_size(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_ARRAY);\n    return v->u.a.size;\n}\n\nlept_value* lept_get_array_element(const lept_value* v, size_t index) {\n    assert(v != NULL && v->type == LEPT_ARRAY);\n    assert(index < v->u.a.size);\n    return &v->u.a.e[index];\n}\n"
  },
  {
    "path": "tutorial05_answer/leptjson.h",
    "content": "#ifndef LEPTJSON_H__\n#define LEPTJSON_H__\n\n#include <stddef.h> /* size_t */\n\ntypedef enum { LEPT_NULL, LEPT_FALSE, LEPT_TRUE, LEPT_NUMBER, LEPT_STRING, LEPT_ARRAY, LEPT_OBJECT } lept_type;\n\ntypedef struct lept_value lept_value;\n\nstruct lept_value {\n    union {\n        struct { lept_value* e; size_t size; }a;    /* array:  elements, element count */\n        struct { char* s; size_t len; }s;           /* string: null-terminated string, string length */\n        double n;                                   /* number */\n    }u;\n    lept_type type;\n};\n\nenum {\n    LEPT_PARSE_OK = 0,\n    LEPT_PARSE_EXPECT_VALUE,\n    LEPT_PARSE_INVALID_VALUE,\n    LEPT_PARSE_ROOT_NOT_SINGULAR,\n    LEPT_PARSE_NUMBER_TOO_BIG,\n    LEPT_PARSE_MISS_QUOTATION_MARK,\n    LEPT_PARSE_INVALID_STRING_ESCAPE,\n    LEPT_PARSE_INVALID_STRING_CHAR,\n    LEPT_PARSE_INVALID_UNICODE_HEX,\n    LEPT_PARSE_INVALID_UNICODE_SURROGATE,\n    LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET\n};\n\n#define lept_init(v) do { (v)->type = LEPT_NULL; } while(0)\n\nint lept_parse(lept_value* v, const char* json);\n\nvoid lept_free(lept_value* v);\n\nlept_type lept_get_type(const lept_value* v);\n\n#define lept_set_null(v) lept_free(v)\n\nint lept_get_boolean(const lept_value* v);\nvoid lept_set_boolean(lept_value* v, int b);\n\ndouble lept_get_number(const lept_value* v);\nvoid lept_set_number(lept_value* v, double n);\n\nconst char* lept_get_string(const lept_value* v);\nsize_t lept_get_string_length(const lept_value* v);\nvoid lept_set_string(lept_value* v, const char* s, size_t len);\n\nsize_t lept_get_array_size(const lept_value* v);\nlept_value* lept_get_array_element(const lept_value* v, size_t index);\n\n#endif /* LEPTJSON_H__ */\n"
  },
  {
    "path": "tutorial05_answer/test.c",
    "content": "#ifdef _WINDOWS\n#define _CRTDBG_MAP_ALLOC\n#include <crtdbg.h>\n#endif\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"leptjson.h\"\n\nstatic int main_ret = 0;\nstatic int test_count = 0;\nstatic int test_pass = 0;\n\n#define EXPECT_EQ_BASE(equality, expect, actual, format) \\\n    do {\\\n        test_count++;\\\n        if (equality)\\\n            test_pass++;\\\n        else {\\\n            fprintf(stderr, \"%s:%d: expect: \" format \" actual: \" format \"\\n\", __FILE__, __LINE__, expect, actual);\\\n            main_ret = 1;\\\n        }\\\n    } while(0)\n\n#define EXPECT_EQ_INT(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, \"%d\")\n#define EXPECT_EQ_DOUBLE(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, \"%.17g\")\n#define EXPECT_EQ_STRING(expect, actual, alength) \\\n    EXPECT_EQ_BASE(sizeof(expect) - 1 == alength && memcmp(expect, actual, alength) == 0, expect, actual, \"%s\")\n#define EXPECT_TRUE(actual) EXPECT_EQ_BASE((actual) != 0, \"true\", \"false\", \"%s\")\n#define EXPECT_FALSE(actual) EXPECT_EQ_BASE((actual) == 0, \"false\", \"true\", \"%s\")\n\n#if defined(_MSC_VER)\n#define EXPECT_EQ_SIZE_T(expect, actual) EXPECT_EQ_BASE((expect) == (actual), (size_t)expect, (size_t)actual, \"%Iu\")\n#else\n#define EXPECT_EQ_SIZE_T(expect, actual) EXPECT_EQ_BASE((expect) == (actual), (size_t)expect, (size_t)actual, \"%zu\")\n#endif\n\nstatic void test_parse_null() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_boolean(&v, 0);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"null\"));\n    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\n    lept_free(&v);\n}\n\nstatic void test_parse_true() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_boolean(&v, 0);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"true\"));\n    EXPECT_EQ_INT(LEPT_TRUE, lept_get_type(&v));\n    lept_free(&v);\n}\n\nstatic void test_parse_false() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_boolean(&v, 1);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"false\"));\n    EXPECT_EQ_INT(LEPT_FALSE, lept_get_type(&v));\n    lept_free(&v);\n}\n\n#define TEST_NUMBER(expect, json)\\\n    do {\\\n        lept_value v;\\\n        lept_init(&v);\\\n        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(&v));\\\n        EXPECT_EQ_DOUBLE(expect, lept_get_number(&v));\\\n        lept_free(&v);\\\n    } while(0)\n\nstatic void test_parse_number() {\n    TEST_NUMBER(0.0, \"0\");\n    TEST_NUMBER(0.0, \"-0\");\n    TEST_NUMBER(0.0, \"-0.0\");\n    TEST_NUMBER(1.0, \"1\");\n    TEST_NUMBER(-1.0, \"-1\");\n    TEST_NUMBER(1.5, \"1.5\");\n    TEST_NUMBER(-1.5, \"-1.5\");\n    TEST_NUMBER(3.1416, \"3.1416\");\n    TEST_NUMBER(1E10, \"1E10\");\n    TEST_NUMBER(1e10, \"1e10\");\n    TEST_NUMBER(1E+10, \"1E+10\");\n    TEST_NUMBER(1E-10, \"1E-10\");\n    TEST_NUMBER(-1E10, \"-1E10\");\n    TEST_NUMBER(-1e10, \"-1e10\");\n    TEST_NUMBER(-1E+10, \"-1E+10\");\n    TEST_NUMBER(-1E-10, \"-1E-10\");\n    TEST_NUMBER(1.234E+10, \"1.234E+10\");\n    TEST_NUMBER(1.234E-10, \"1.234E-10\");\n    TEST_NUMBER(0.0, \"1e-10000\"); /* must underflow */\n\n    TEST_NUMBER(1.0000000000000002, \"1.0000000000000002\"); /* the smallest number > 1 */\n    TEST_NUMBER( 4.9406564584124654e-324, \"4.9406564584124654e-324\"); /* minimum denormal */\n    TEST_NUMBER(-4.9406564584124654e-324, \"-4.9406564584124654e-324\");\n    TEST_NUMBER( 2.2250738585072009e-308, \"2.2250738585072009e-308\");  /* Max subnormal double */\n    TEST_NUMBER(-2.2250738585072009e-308, \"-2.2250738585072009e-308\");\n    TEST_NUMBER( 2.2250738585072014e-308, \"2.2250738585072014e-308\");  /* Min normal positive double */\n    TEST_NUMBER(-2.2250738585072014e-308, \"-2.2250738585072014e-308\");\n    TEST_NUMBER( 1.7976931348623157e+308, \"1.7976931348623157e+308\");  /* Max double */\n    TEST_NUMBER(-1.7976931348623157e+308, \"-1.7976931348623157e+308\");\n}\n\n#define TEST_STRING(expect, json)\\\n    do {\\\n        lept_value v;\\\n        lept_init(&v);\\\n        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_STRING, lept_get_type(&v));\\\n        EXPECT_EQ_STRING(expect, lept_get_string(&v), lept_get_string_length(&v));\\\n        lept_free(&v);\\\n    } while(0)\n\nstatic void test_parse_string() {\n    TEST_STRING(\"\", \"\\\"\\\"\");\n    TEST_STRING(\"Hello\", \"\\\"Hello\\\"\");\n    TEST_STRING(\"Hello\\nWorld\", \"\\\"Hello\\\\nWorld\\\"\");\n    TEST_STRING(\"\\\" \\\\ / \\b \\f \\n \\r \\t\", \"\\\"\\\\\\\" \\\\\\\\ \\\\/ \\\\b \\\\f \\\\n \\\\r \\\\t\\\"\");\n    TEST_STRING(\"Hello\\0World\", \"\\\"Hello\\\\u0000World\\\"\");\n    TEST_STRING(\"\\x24\", \"\\\"\\\\u0024\\\"\");         /* Dollar sign U+0024 */\n    TEST_STRING(\"\\xC2\\xA2\", \"\\\"\\\\u00A2\\\"\");     /* Cents sign U+00A2 */\n    TEST_STRING(\"\\xE2\\x82\\xAC\", \"\\\"\\\\u20AC\\\"\"); /* Euro sign U+20AC */\n    TEST_STRING(\"\\xF0\\x9D\\x84\\x9E\", \"\\\"\\\\uD834\\\\uDD1E\\\"\");  /* G clef sign U+1D11E */\n    TEST_STRING(\"\\xF0\\x9D\\x84\\x9E\", \"\\\"\\\\ud834\\\\udd1e\\\"\");  /* G clef sign U+1D11E */\n}\n\nstatic void test_parse_array() {\n    size_t i, j;\n    lept_value v;\n\n    lept_init(&v);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"[ ]\"));\n    EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(&v));\n    EXPECT_EQ_SIZE_T(0, lept_get_array_size(&v));\n    lept_free(&v);\n\n    lept_init(&v);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"[ null , false , true , 123 , \\\"abc\\\" ]\"));\n    EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(&v));\n    EXPECT_EQ_SIZE_T(5, lept_get_array_size(&v));\n    EXPECT_EQ_INT(LEPT_NULL,   lept_get_type(lept_get_array_element(&v, 0)));\n    EXPECT_EQ_INT(LEPT_FALSE,  lept_get_type(lept_get_array_element(&v, 1)));\n    EXPECT_EQ_INT(LEPT_TRUE,   lept_get_type(lept_get_array_element(&v, 2)));\n    EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(lept_get_array_element(&v, 3)));\n    EXPECT_EQ_INT(LEPT_STRING, lept_get_type(lept_get_array_element(&v, 4)));\n    EXPECT_EQ_DOUBLE(123.0, lept_get_number(lept_get_array_element(&v, 3)));\n    EXPECT_EQ_STRING(\"abc\", lept_get_string(lept_get_array_element(&v, 4)), lept_get_string_length(lept_get_array_element(&v, 4)));\n    lept_free(&v);\n\n    lept_init(&v);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"[ [ ] , [ 0 ] , [ 0 , 1 ] , [ 0 , 1 , 2 ] ]\"));\n    EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(&v));\n    EXPECT_EQ_SIZE_T(4, lept_get_array_size(&v));\n    for (i = 0; i < 4; i++) {\n        lept_value* a = lept_get_array_element(&v, i);\n        EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(a));\n        EXPECT_EQ_SIZE_T(i, lept_get_array_size(a));\n        for (j = 0; j < i; j++) {\n            lept_value* e = lept_get_array_element(a, j);\n            EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(e));\n            EXPECT_EQ_DOUBLE((double)j, lept_get_number(e));\n        }\n    }\n    lept_free(&v);\n}\n\n#define TEST_ERROR(error, json)\\\n    do {\\\n        lept_value v;\\\n        lept_init(&v);\\\n        v.type = LEPT_FALSE;\\\n        EXPECT_EQ_INT(error, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\\\n        lept_free(&v);\\\n    } while(0)\n\nstatic void test_parse_expect_value() {\n    TEST_ERROR(LEPT_PARSE_EXPECT_VALUE, \"\");\n    TEST_ERROR(LEPT_PARSE_EXPECT_VALUE, \" \");\n}\n\nstatic void test_parse_invalid_value() {\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"nul\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"?\");\n\n    /* invalid number */\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"+0\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"+1\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \".123\"); /* at least one digit before '.' */\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"1.\");   /* at least one digit after '.' */\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"INF\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"inf\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"NAN\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"nan\");\n\n    /* invalid value in array */\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"[1,]\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"[\\\"a\\\", nul]\");\n}\n\nstatic void test_parse_root_not_singular() {\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"null x\");\n\n    /* invalid number */\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0123\"); /* after zero should be '.' or nothing */\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0x0\");\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0x123\");\n}\n\nstatic void test_parse_number_too_big() {\n    TEST_ERROR(LEPT_PARSE_NUMBER_TOO_BIG, \"1e309\");\n    TEST_ERROR(LEPT_PARSE_NUMBER_TOO_BIG, \"-1e309\");\n}\n\nstatic void test_parse_miss_quotation_mark() {\n    TEST_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK, \"\\\"\");\n    TEST_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK, \"\\\"abc\");\n}\n\nstatic void test_parse_invalid_string_escape() {\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\v\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\'\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\0\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\x12\\\"\");\n}\n\nstatic void test_parse_invalid_string_char() {\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_CHAR, \"\\\"\\x01\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_CHAR, \"\\\"\\x1F\\\"\");\n}\n\nstatic void test_parse_invalid_unicode_hex() {\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u0\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u01\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u012\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u/000\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\uG000\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u0/00\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u0G00\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u00/0\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u00G0\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u000/\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u000G\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u 123\\\"\");\n}\n\nstatic void test_parse_invalid_unicode_surrogate() {\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uD800\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uDBFF\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uD800\\\\\\\\\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uD800\\\\uDBFF\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uD800\\\\uE000\\\"\");\n}\n\nstatic void test_parse_miss_comma_or_square_bracket() {\n    TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, \"[1\");\n    TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, \"[1}\");\n    TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, \"[1 2\");\n    TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, \"[[]\");\n}\n\nstatic void test_parse() {\n    test_parse_null();\n    test_parse_true();\n    test_parse_false();\n    test_parse_number();\n    test_parse_string();\n    test_parse_array();\n    test_parse_expect_value();\n    test_parse_invalid_value();\n    test_parse_root_not_singular();\n    test_parse_number_too_big();\n    test_parse_miss_quotation_mark();\n    test_parse_invalid_string_escape();\n    test_parse_invalid_string_char();\n    test_parse_invalid_unicode_hex();\n    test_parse_invalid_unicode_surrogate();\n    test_parse_miss_comma_or_square_bracket();\n}\n\nstatic void test_access_null() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"a\", 1);\n    lept_set_null(&v);\n    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\n    lept_free(&v);\n}\n\nstatic void test_access_boolean() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"a\", 1);\n    lept_set_boolean(&v, 1);\n    EXPECT_TRUE(lept_get_boolean(&v));\n    lept_set_boolean(&v, 0);\n    EXPECT_FALSE(lept_get_boolean(&v));\n    lept_free(&v);\n}\n\nstatic void test_access_number() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"a\", 1);\n    lept_set_number(&v, 1234.5);\n    EXPECT_EQ_DOUBLE(1234.5, lept_get_number(&v));\n    lept_free(&v);\n}\n\nstatic void test_access_string() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"\", 0);\n    EXPECT_EQ_STRING(\"\", lept_get_string(&v), lept_get_string_length(&v));\n    lept_set_string(&v, \"Hello\", 5);\n    EXPECT_EQ_STRING(\"Hello\", lept_get_string(&v), lept_get_string_length(&v));\n    lept_free(&v);\n}\n\nstatic void test_access() {\n    test_access_null();\n    test_access_boolean();\n    test_access_number();\n    test_access_string();\n}\n\nint main() {\n#ifdef _WINDOWS\n    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);\n#endif\n    test_parse();\n    test_access();\n    printf(\"%d/%d (%3.2f%%) passed\\n\", test_pass, test_count, test_pass * 100.0 / test_count);\n    return main_ret;\n}\n"
  },
  {
    "path": "tutorial05_answer/tutorial05_answer.md",
    "content": "# 从零开始的 JSON 库教程（五）：解析数组解答篇\n\n* Milo Yip\n* 2016/10/13\n\n本文是[《从零开始的 JSON 库教程》](https://zhuanlan.zhihu.com/json-tutorial)的第五个单元解答篇。解答代码位于 [json-tutorial/tutorial05_answer](https://github.com/miloyip/json-tutorial/blob/master/tutorial05_answer)。\n\n## 1. 编写 `test_parse_array()` 单元测试\n\n这个练习纯粹为了熟习数组的访问 API。新增的第一个 JSON 只需平凡的检测。第二个 JSON 有特定模式，第 i 个子数组的长度为 i，每个子数组的第 j 个元素是数字值 j，所以可用两层 for 循环测试。\n\n~~~c\nstatic void test_parse_array() {\n    size_t i, j;\n    lept_value v;\n\n    /* ... */\n\n    lept_init(&v);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"[ null , false , true , 123 , \\\"abc\\\" ]\"));\n    EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(&v));\n    EXPECT_EQ_SIZE_T(5, lept_get_array_size(&v));\n    EXPECT_EQ_INT(LEPT_NULL,   lept_get_type(lept_get_array_element(&v, 0)));\n    EXPECT_EQ_INT(LEPT_FALSE,  lept_get_type(lept_get_array_element(&v, 1)));\n    EXPECT_EQ_INT(LEPT_TRUE,   lept_get_type(lept_get_array_element(&v, 2)));\n    EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(lept_get_array_element(&v, 3)));\n    EXPECT_EQ_INT(LEPT_STRING, lept_get_type(lept_get_array_element(&v, 4)));\n    EXPECT_EQ_DOUBLE(123.0, lept_get_number(lept_get_array_element(&v, 3)));\n    EXPECT_EQ_STRING(\"abc\", lept_get_string(lept_get_array_element(&v, 4)), lept_get_string_length(lept_get_array_element(&v, 4)));\n    lept_free(&v);\n\n    lept_init(&v);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"[ [ ] , [ 0 ] , [ 0 , 1 ] , [ 0 , 1 , 2 ] ]\"));\n    EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(&v));\n    EXPECT_EQ_SIZE_T(4, lept_get_array_size(&v));\n    for (i = 0; i < 4; i++) {\n        lept_value* a = lept_get_array_element(&v, i);\n        EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(a));\n        EXPECT_EQ_SIZE_T(i, lept_get_array_size(a));\n        for (j = 0; j < i; j++) {\n            lept_value* e = lept_get_array_element(a, j);\n            EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(e));\n            EXPECT_EQ_DOUBLE((double)j, lept_get_number(e));\n        }\n    }\n    lept_free(&v);\n}\n~~~\n\n## 2. 解析空白字符\n\n按现时的 `lept_parse_array()` 的编写方式，需要加入 3 个 `lept_parse_whitespace()` 调用，分别是解析 `[` 之后，元素之后，以及 `,` 之后：\n\n~~~c\nstatic int lept_parse_array(lept_context* c, lept_value* v) {\n    /* ... */\n    EXPECT(c, '[');\n    lept_parse_whitespace(c);\n    /* ... */\n    for (;;) {\n        /* ... */\n        if ((ret = lept_parse_value(c, &e)) != LEPT_PARSE_OK)\n            return ret;\n        /* ... */\n        lept_parse_whitespace(c);\n        if (*c->json == ',') {\n            c->json++;\n            lept_parse_whitespace(c);\n        }\n        /* ... */\n    }\n}\n~~~\n\n## 3. 内存泄漏\n\n成功测试那 3 个 JSON 后，使用内存泄漏检测工具会发现 `lept_parse_array()` 用 `malloc()`分配的内存没有被释放：\n\n~~~\n==154== 124 (120 direct, 4 indirect) bytes in 1 blocks are definitely lost in loss record 2 of 4\n==154==    at 0x4C28C20: malloc (vg_replace_malloc.c:296)\n==154==    by 0x409D82: lept_parse_array (in /json-tutorial/tutorial05/build/leptjson_test)\n==154==    by 0x409E91: lept_parse_value (in /json-tutorial/tutorial05/build/leptjson_test)\n==154==    by 0x409F14: lept_parse (in /json-tutorial/tutorial05/build/leptjson_test)\n==154==    by 0x405261: test_parse_array (in /json-tutorial/tutorial05/build/leptjson_test)\n==154==    by 0x408C72: test_parse (in /json-tutorial/tutorial05/build/leptjson_test)\n==154==    by 0x40916A: main (in /json-tutorial/tutorial05/build/leptjson_test)\n==154== \n==154== 240 (96 direct, 144 indirect) bytes in 1 blocks are definitely lost in loss record 4 of 4\n==154==    at 0x4C28C20: malloc (vg_replace_malloc.c:296)\n==154==    by 0x409D82: lept_parse_array (in /json-tutorial/tutorial05/build/leptjson_test)\n==154==    by 0x409E91: lept_parse_value (in /json-tutorial/tutorial05/build/leptjson_test)\n==154==    by 0x409F14: lept_parse (in /json-tutorial/tutorial05/build/leptjson_test)\n==154==    by 0x40582C: test_parse_array (in /json-tutorial/tutorial05/build/leptjson_test)\n==154==    by 0x408C72: test_parse (in /json-tutorial/tutorial05/build/leptjson_test)\n==154==    by 0x40916A: main (in /json-tutorial/tutorial05/build/leptjson_test)\n~~~\n\n很明显，有 `malloc()` 就要有对应的 `free()`。正确的释放位置应该放置在 `lept_free()`，当值被释放时，该值拥有的内存也在那里释放。之前字符串的释放也是放在这里：\n\n~~~c\nvoid lept_free(lept_value* v) {\n    assert(v != NULL);\n    if (v->type == LEPT_STRING)\n        free(v->u.s.s);\n    v->type = LEPT_NULL;\n}\n~~~\n\n但对于数组，我们应该先把数组内的元素通过递归调用 `lept_free()` 释放，然后才释放本身的 `v->u.a.e`：\n\n~~~c\nvoid lept_free(lept_value* v) {\n    size_t i;\n    assert(v != NULL);\n    switch (v->type) {\n        case LEPT_STRING:\n            free(v->u.s.s);\n            break;\n        case LEPT_ARRAY:\n            for (i = 0; i < v->u.a.size; i++)\n                lept_free(&v->u.a.e[i]);\n            free(v->u.a.e);\n            break;\n        default: break;\n    }\n    v->type = LEPT_NULL;\n}\n~~~\n\n修改之后，再运行内存泄漏检测工具，确保问题已被修正。\n\n## 4. 解析错误时的内存处理\n\n遇到解析错误时，我们可能在之前已压入了一些值在自定义堆栈上。如果没有处理，最后会在 `lept_parse()` 中发现堆栈上还有一些值，做成断言失败。所以，遇到解析错误时，我们必须弹出并释放那些值。\n\n在 `lept_parse_array` 中，原本遇到解析失败时，会直接返回错误码。我们把它改为 `break` 离开循环，在循环结束后的地方用 `lept_free()` 释放从堆栈弹出的值，然后才返回错误码：\n\n~~~c\nstatic int lept_parse_array(lept_context* c, lept_value* v) {\n    /* ... */\n    for (;;) {\n        /* ... */\n        if ((ret = lept_parse_value(c, &e)) != LEPT_PARSE_OK)\n            break;\n        /* ... */\n        if (*c->json == ',') {\n            /* ... */\n        }\n        else if (*c->json == ']') {\n            /* ... */\n        }\n        else {\n            ret = LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET;\n            break;\n        }\n    }\n    /* Pop and free values on the stack */\n    for (i = 0; i < size; i++)\n        lept_free((lept_value*)lept_context_pop(c, sizeof(lept_value)));\n    return ret;\n}\n~~~\n\n## 5. bug 的解释\n\n这个 bug 源于压栈时，会获得一个指针 `e`，指向从堆栈分配到的空间：\n\n~~~c\n    for (;;) {\n        /* bug! */\n        lept_value* e = lept_context_push(c, sizeof(lept_value));\n        lept_init(e);\n        size++;\n        if ((ret = lept_parse_value(c, e)) != LEPT_PARSE_OK)\n            return ret;\n        /* ... */\n    }\n~~~\n\n然后，我们把这个指针调用 `lept_parse_value(c, e)`，这里会出现问题，因为 `lept_parse_value()` 及之下的函数都需要调用 `lept_context_push()`，而 `lept_context_push()` 在发现栈满了的时候会用 `realloc()` 扩容。这时候，我们上层的 `e` 就会失效，变成一个悬挂指针（dangling pointer），而且 `lept_parse_value(c, e)` 会通过这个指针写入解析结果，造成非法访问。\n\n在使用 C++ 容器时，也会遇到类似的问题。从容器中取得的迭代器（iterator）后，如果改动容器内容，之前的迭代器会失效。这里的悬挂指针问题也是相同的。\n\n但这种 bug 有时可能在简单测试中不能自动发现，因为问题只有堆栈满了才会出现。从测试的角度看，我们需要一些压力测试（stress test），测试更大更复杂的数据。但从编程的角度看，我们要谨慎考虑变量的生命周期，尽量从编程阶段避免出现问题。例如把 `lept_context_push()` 的 API 改为：\n\n~~~c\nstatic void lept_context_push(lept_context* c, const void* data, size_t size);\n~~~\n\n这样就确把数据压入栈内，避免了返回指针的生命周期问题。但我们之后会发现，原来的 API 设计在一些情况会更方便一些，例如在把字符串值转化（stringify）为 JSON 时，我们可以预先在堆栈分配字符串所需的最大空间，而当时是未有数据填充进去的。\n\n无论如何，我们编程时都要考虑清楚变量的生命周期，特别是指针的生命周期。\n\n## 6. 总结\n\n经过对数组的解析，我们也了解到如何利用递归处理复合型的数据类型解析。与一些用链表或自动扩展的动态数组的实现比较，我们利用了自定义堆栈作为缓冲区，能分配最紧凑的数组作存储之用，会比其他实现更省内存。我们完成了数组类型后，只余下对象类型了。\n\n如果你遇到问题，有不理解的地方，或是有建议，都欢迎在评论或 [issue](https://github.com/miloyip/json-tutorial/issues) 中提出，让所有人一起讨论。\n"
  },
  {
    "path": "tutorial06/CMakeLists.txt",
    "content": "cmake_minimum_required (VERSION 2.6)\nproject (leptjson_test C)\n\nif (CMAKE_C_COMPILER_ID MATCHES \"GNU|Clang\")\n    set(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -ansi -pedantic -Wall\")\nendif()\n\nadd_library(leptjson leptjson.c)\nadd_executable(leptjson_test test.c)\ntarget_link_libraries(leptjson_test leptjson)\n"
  },
  {
    "path": "tutorial06/leptjson.c",
    "content": "#ifdef _WINDOWS\n#define _CRTDBG_MAP_ALLOC\n#include <crtdbg.h>\n#endif\n#include \"leptjson.h\"\n#include <assert.h>  /* assert() */\n#include <errno.h>   /* errno, ERANGE */\n#include <math.h>    /* HUGE_VAL */\n#include <stdlib.h>  /* NULL, malloc(), realloc(), free(), strtod() */\n#include <string.h>  /* memcpy() */\n\n#ifndef LEPT_PARSE_STACK_INIT_SIZE\n#define LEPT_PARSE_STACK_INIT_SIZE 256\n#endif\n\n#define EXPECT(c, ch)       do { assert(*c->json == (ch)); c->json++; } while(0)\n#define ISDIGIT(ch)         ((ch) >= '0' && (ch) <= '9')\n#define ISDIGIT1TO9(ch)     ((ch) >= '1' && (ch) <= '9')\n#define PUTC(c, ch)         do { *(char*)lept_context_push(c, sizeof(char)) = (ch); } while(0)\n\ntypedef struct {\n    const char* json;\n    char* stack;\n    size_t size, top;\n}lept_context;\n\nstatic void* lept_context_push(lept_context* c, size_t size) {\n    void* ret;\n    assert(size > 0);\n    if (c->top + size >= c->size) {\n        if (c->size == 0)\n            c->size = LEPT_PARSE_STACK_INIT_SIZE;\n        while (c->top + size >= c->size)\n            c->size += c->size >> 1;  /* c->size * 1.5 */\n        c->stack = (char*)realloc(c->stack, c->size);\n    }\n    ret = c->stack + c->top;\n    c->top += size;\n    return ret;\n}\n\nstatic void* lept_context_pop(lept_context* c, size_t size) {\n    assert(c->top >= size);\n    return c->stack + (c->top -= size);\n}\n\nstatic void lept_parse_whitespace(lept_context* c) {\n    const char *p = c->json;\n    while (*p == ' ' || *p == '\\t' || *p == '\\n' || *p == '\\r')\n        p++;\n    c->json = p;\n}\n\nstatic int lept_parse_literal(lept_context* c, lept_value* v, const char* literal, lept_type type) {\n    size_t i;\n    EXPECT(c, literal[0]);\n    for (i = 0; literal[i + 1]; i++)\n        if (c->json[i] != literal[i + 1])\n            return LEPT_PARSE_INVALID_VALUE;\n    c->json += i;\n    v->type = type;\n    return LEPT_PARSE_OK;\n}\n\nstatic int lept_parse_number(lept_context* c, lept_value* v) {\n    const char* p = c->json;\n    if (*p == '-') p++;\n    if (*p == '0') p++;\n    else {\n        if (!ISDIGIT1TO9(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n    if (*p == '.') {\n        p++;\n        if (!ISDIGIT(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n    if (*p == 'e' || *p == 'E') {\n        p++;\n        if (*p == '+' || *p == '-') p++;\n        if (!ISDIGIT(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n    errno = 0;\n    v->u.n = strtod(c->json, NULL);\n    if (errno == ERANGE && (v->u.n == HUGE_VAL || v->u.n == -HUGE_VAL))\n        return LEPT_PARSE_NUMBER_TOO_BIG;\n    v->type = LEPT_NUMBER;\n    c->json = p;\n    return LEPT_PARSE_OK;\n}\n\nstatic const char* lept_parse_hex4(const char* p, unsigned* u) {\n    int i;\n    *u = 0;\n    for (i = 0; i < 4; i++) {\n        char ch = *p++;\n        *u <<= 4;\n        if      (ch >= '0' && ch <= '9')  *u |= ch - '0';\n        else if (ch >= 'A' && ch <= 'F')  *u |= ch - ('A' - 10);\n        else if (ch >= 'a' && ch <= 'f')  *u |= ch - ('a' - 10);\n        else return NULL;\n    }\n    return p;\n}\n\nstatic void lept_encode_utf8(lept_context* c, unsigned u) {\n    if (u <= 0x7F) \n        PUTC(c, u & 0xFF);\n    else if (u <= 0x7FF) {\n        PUTC(c, 0xC0 | ((u >> 6) & 0xFF));\n        PUTC(c, 0x80 | ( u       & 0x3F));\n    }\n    else if (u <= 0xFFFF) {\n        PUTC(c, 0xE0 | ((u >> 12) & 0xFF));\n        PUTC(c, 0x80 | ((u >>  6) & 0x3F));\n        PUTC(c, 0x80 | ( u        & 0x3F));\n    }\n    else {\n        assert(u <= 0x10FFFF);\n        PUTC(c, 0xF0 | ((u >> 18) & 0xFF));\n        PUTC(c, 0x80 | ((u >> 12) & 0x3F));\n        PUTC(c, 0x80 | ((u >>  6) & 0x3F));\n        PUTC(c, 0x80 | ( u        & 0x3F));\n    }\n}\n\n#define STRING_ERROR(ret) do { c->top = head; return ret; } while(0)\n\nstatic int lept_parse_string(lept_context* c, lept_value* v) {\n    size_t head = c->top, len;\n    unsigned u, u2;\n    const char* p;\n    EXPECT(c, '\\\"');\n    p = c->json;\n    for (;;) {\n        char ch = *p++;\n        switch (ch) {\n            case '\\\"':\n                len = c->top - head;\n                lept_set_string(v, (const char*)lept_context_pop(c, len), len);\n                c->json = p;\n                return LEPT_PARSE_OK;\n            case '\\\\':\n                switch (*p++) {\n                    case '\\\"': PUTC(c, '\\\"'); break;\n                    case '\\\\': PUTC(c, '\\\\'); break;\n                    case '/':  PUTC(c, '/' ); break;\n                    case 'b':  PUTC(c, '\\b'); break;\n                    case 'f':  PUTC(c, '\\f'); break;\n                    case 'n':  PUTC(c, '\\n'); break;\n                    case 'r':  PUTC(c, '\\r'); break;\n                    case 't':  PUTC(c, '\\t'); break;\n                    case 'u':\n                        if (!(p = lept_parse_hex4(p, &u)))\n                            STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX);\n                        if (u >= 0xD800 && u <= 0xDBFF) { /* surrogate pair */\n                            if (*p++ != '\\\\')\n                                STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE);\n                            if (*p++ != 'u')\n                                STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE);\n                            if (!(p = lept_parse_hex4(p, &u2)))\n                                STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX);\n                            if (u2 < 0xDC00 || u2 > 0xDFFF)\n                                STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE);\n                            u = (((u - 0xD800) << 10) | (u2 - 0xDC00)) + 0x10000;\n                        }\n                        lept_encode_utf8(c, u);\n                        break;\n                    default:\n                        STRING_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE);\n                }\n                break;\n            case '\\0':\n                STRING_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK);\n            default:\n                if ((unsigned char)ch < 0x20)\n                    STRING_ERROR(LEPT_PARSE_INVALID_STRING_CHAR);\n                PUTC(c, ch);\n        }\n    }\n}\n\nstatic int lept_parse_value(lept_context* c, lept_value* v);\n\nstatic int lept_parse_array(lept_context* c, lept_value* v) {\n    size_t i, size = 0;\n    int ret;\n    EXPECT(c, '[');\n    lept_parse_whitespace(c);\n    if (*c->json == ']') {\n        c->json++;\n        v->type = LEPT_ARRAY;\n        v->u.a.size = 0;\n        v->u.a.e = NULL;\n        return LEPT_PARSE_OK;\n    }\n    for (;;) {\n        lept_value e;\n        lept_init(&e);\n        if ((ret = lept_parse_value(c, &e)) != LEPT_PARSE_OK)\n            break;\n        memcpy(lept_context_push(c, sizeof(lept_value)), &e, sizeof(lept_value));\n        size++;\n        lept_parse_whitespace(c);\n        if (*c->json == ',') {\n            c->json++;\n            lept_parse_whitespace(c);\n        }\n        else if (*c->json == ']') {\n            c->json++;\n            v->type = LEPT_ARRAY;\n            v->u.a.size = size;\n            size *= sizeof(lept_value);\n            memcpy(v->u.a.e = (lept_value*)malloc(size), lept_context_pop(c, size), size);\n            return LEPT_PARSE_OK;\n        }\n        else {\n            ret = LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET;\n            break;\n        }\n    }\n    /* Pop and free values on the stack */\n    for (i = 0; i < size; i++)\n        lept_free((lept_value*)lept_context_pop(c, sizeof(lept_value)));\n    return ret;\n}\n\nstatic int lept_parse_object(lept_context* c, lept_value* v) {\n    size_t size;\n    lept_member m;\n    int ret;\n    EXPECT(c, '{');\n    lept_parse_whitespace(c);\n    if (*c->json == '}') {\n        c->json++;\n        v->type = LEPT_OBJECT;\n        v->u.o.m = 0;\n        v->u.o.size = 0;\n        return LEPT_PARSE_OK;\n    }\n    m.k = NULL;\n    size = 0;\n    for (;;) {\n        lept_init(&m.v);\n        /* \\todo parse key to m.k, m.klen */\n        /* \\todo parse ws colon ws */\n        /* parse value */\n        if ((ret = lept_parse_value(c, &m.v)) != LEPT_PARSE_OK)\n            break;\n        memcpy(lept_context_push(c, sizeof(lept_member)), &m, sizeof(lept_member));\n        size++;\n        m.k = NULL; /* ownership is transferred to member on stack */\n        /* \\todo parse ws [comma | right-curly-brace] ws */\n    }\n    /* \\todo Pop and free members on the stack */\n    return ret;\n}\n\nstatic int lept_parse_value(lept_context* c, lept_value* v) {\n    switch (*c->json) {\n        case 't':  return lept_parse_literal(c, v, \"true\", LEPT_TRUE);\n        case 'f':  return lept_parse_literal(c, v, \"false\", LEPT_FALSE);\n        case 'n':  return lept_parse_literal(c, v, \"null\", LEPT_NULL);\n        default:   return lept_parse_number(c, v);\n        case '\"':  return lept_parse_string(c, v);\n        case '[':  return lept_parse_array(c, v);\n        case '{':  return lept_parse_object(c, v);\n        case '\\0': return LEPT_PARSE_EXPECT_VALUE;\n    }\n}\n\nint lept_parse(lept_value* v, const char* json) {\n    lept_context c;\n    int ret;\n    assert(v != NULL);\n    c.json = json;\n    c.stack = NULL;\n    c.size = c.top = 0;\n    lept_init(v);\n    lept_parse_whitespace(&c);\n    if ((ret = lept_parse_value(&c, v)) == LEPT_PARSE_OK) {\n        lept_parse_whitespace(&c);\n        if (*c.json != '\\0') {\n            v->type = LEPT_NULL;\n            ret = LEPT_PARSE_ROOT_NOT_SINGULAR;\n        }\n    }\n    assert(c.top == 0);\n    free(c.stack);\n    return ret;\n}\n\nvoid lept_free(lept_value* v) {\n    size_t i;\n    assert(v != NULL);\n    switch (v->type) {\n        case LEPT_STRING:\n            free(v->u.s.s);\n            break;\n        case LEPT_ARRAY:\n            for (i = 0; i < v->u.a.size; i++)\n                lept_free(&v->u.a.e[i]);\n            free(v->u.a.e);\n            break;\n        default: break;\n    }\n    v->type = LEPT_NULL;\n}\n\nlept_type lept_get_type(const lept_value* v) {\n    assert(v != NULL);\n    return v->type;\n}\n\nint lept_get_boolean(const lept_value* v) {\n    assert(v != NULL && (v->type == LEPT_TRUE || v->type == LEPT_FALSE));\n    return v->type == LEPT_TRUE;\n}\n\nvoid lept_set_boolean(lept_value* v, int b) {\n    lept_free(v);\n    v->type = b ? LEPT_TRUE : LEPT_FALSE;\n}\n\ndouble lept_get_number(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_NUMBER);\n    return v->u.n;\n}\n\nvoid lept_set_number(lept_value* v, double n) {\n    lept_free(v);\n    v->u.n = n;\n    v->type = LEPT_NUMBER;\n}\n\nconst char* lept_get_string(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_STRING);\n    return v->u.s.s;\n}\n\nsize_t lept_get_string_length(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_STRING);\n    return v->u.s.len;\n}\n\nvoid lept_set_string(lept_value* v, const char* s, size_t len) {\n    assert(v != NULL && (s != NULL || len == 0));\n    lept_free(v);\n    v->u.s.s = (char*)malloc(len + 1);\n    memcpy(v->u.s.s, s, len);\n    v->u.s.s[len] = '\\0';\n    v->u.s.len = len;\n    v->type = LEPT_STRING;\n}\n\nsize_t lept_get_array_size(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_ARRAY);\n    return v->u.a.size;\n}\n\nlept_value* lept_get_array_element(const lept_value* v, size_t index) {\n    assert(v != NULL && v->type == LEPT_ARRAY);\n    assert(index < v->u.a.size);\n    return &v->u.a.e[index];\n}\n\nsize_t lept_get_object_size(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_OBJECT);\n    return v->u.o.size;\n}\n\nconst char* lept_get_object_key(const lept_value* v, size_t index) {\n    assert(v != NULL && v->type == LEPT_OBJECT);\n    assert(index < v->u.o.size);\n    return v->u.o.m[index].k;\n}\n\nsize_t lept_get_object_key_length(const lept_value* v, size_t index) {\n    assert(v != NULL && v->type == LEPT_OBJECT);\n    assert(index < v->u.o.size);\n    return v->u.o.m[index].klen;\n}\n\nlept_value* lept_get_object_value(const lept_value* v, size_t index) {\n    assert(v != NULL && v->type == LEPT_OBJECT);\n    assert(index < v->u.o.size);\n    return &v->u.o.m[index].v;\n}\n"
  },
  {
    "path": "tutorial06/leptjson.h",
    "content": "#ifndef LEPTJSON_H__\n#define LEPTJSON_H__\n\n#include <stddef.h> /* size_t */\n\ntypedef enum { LEPT_NULL, LEPT_FALSE, LEPT_TRUE, LEPT_NUMBER, LEPT_STRING, LEPT_ARRAY, LEPT_OBJECT } lept_type;\n\ntypedef struct lept_value lept_value;\ntypedef struct lept_member lept_member;\n\nstruct lept_value {\n    union {\n        struct { lept_member* m; size_t size; }o;   /* object: members, member count */\n        struct { lept_value* e; size_t size; }a;    /* array:  elements, element count */\n        struct { char* s; size_t len; }s;           /* string: null-terminated string, string length */\n        double n;                                   /* number */\n    }u;\n    lept_type type;\n};\n\nstruct lept_member {\n    char* k; size_t klen;   /* member key string, key string length */\n    lept_value v;           /* member value */\n};\n\nenum {\n    LEPT_PARSE_OK = 0,\n    LEPT_PARSE_EXPECT_VALUE,\n    LEPT_PARSE_INVALID_VALUE,\n    LEPT_PARSE_ROOT_NOT_SINGULAR,\n    LEPT_PARSE_NUMBER_TOO_BIG,\n    LEPT_PARSE_MISS_QUOTATION_MARK,\n    LEPT_PARSE_INVALID_STRING_ESCAPE,\n    LEPT_PARSE_INVALID_STRING_CHAR,\n    LEPT_PARSE_INVALID_UNICODE_HEX,\n    LEPT_PARSE_INVALID_UNICODE_SURROGATE,\n    LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET,\n    LEPT_PARSE_MISS_KEY,\n    LEPT_PARSE_MISS_COLON,\n    LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET\n};\n\n#define lept_init(v) do { (v)->type = LEPT_NULL; } while(0)\n\nint lept_parse(lept_value* v, const char* json);\n\nvoid lept_free(lept_value* v);\n\nlept_type lept_get_type(const lept_value* v);\n\n#define lept_set_null(v) lept_free(v)\n\nint lept_get_boolean(const lept_value* v);\nvoid lept_set_boolean(lept_value* v, int b);\n\ndouble lept_get_number(const lept_value* v);\nvoid lept_set_number(lept_value* v, double n);\n\nconst char* lept_get_string(const lept_value* v);\nsize_t lept_get_string_length(const lept_value* v);\nvoid lept_set_string(lept_value* v, const char* s, size_t len);\n\nsize_t lept_get_array_size(const lept_value* v);\nlept_value* lept_get_array_element(const lept_value* v, size_t index);\n\nsize_t lept_get_object_size(const lept_value* v);\nconst char* lept_get_object_key(const lept_value* v, size_t index);\nsize_t lept_get_object_key_length(const lept_value* v, size_t index);\nlept_value* lept_get_object_value(const lept_value* v, size_t index);\n\n#endif /* LEPTJSON_H__ */\n"
  },
  {
    "path": "tutorial06/test.c",
    "content": "#ifdef _WINDOWS\n#define _CRTDBG_MAP_ALLOC\n#include <crtdbg.h>\n#endif\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"leptjson.h\"\n\nstatic int main_ret = 0;\nstatic int test_count = 0;\nstatic int test_pass = 0;\n\n#define EXPECT_EQ_BASE(equality, expect, actual, format) \\\n    do {\\\n        test_count++;\\\n        if (equality)\\\n            test_pass++;\\\n        else {\\\n            fprintf(stderr, \"%s:%d: expect: \" format \" actual: \" format \"\\n\", __FILE__, __LINE__, expect, actual);\\\n            main_ret = 1;\\\n        }\\\n    } while(0)\n\n#define EXPECT_EQ_INT(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, \"%d\")\n#define EXPECT_EQ_DOUBLE(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, \"%.17g\")\n#define EXPECT_EQ_STRING(expect, actual, alength) \\\n    EXPECT_EQ_BASE(sizeof(expect) - 1 == alength && memcmp(expect, actual, alength) == 0, expect, actual, \"%s\")\n#define EXPECT_TRUE(actual) EXPECT_EQ_BASE((actual) != 0, \"true\", \"false\", \"%s\")\n#define EXPECT_FALSE(actual) EXPECT_EQ_BASE((actual) == 0, \"false\", \"true\", \"%s\")\n\n#if defined(_MSC_VER)\n#define EXPECT_EQ_SIZE_T(expect, actual) EXPECT_EQ_BASE((expect) == (actual), (size_t)expect, (size_t)actual, \"%Iu\")\n#else\n#define EXPECT_EQ_SIZE_T(expect, actual) EXPECT_EQ_BASE((expect) == (actual), (size_t)expect, (size_t)actual, \"%zu\")\n#endif\n\nstatic void test_parse_null() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_boolean(&v, 0);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"null\"));\n    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\n    lept_free(&v);\n}\n\nstatic void test_parse_true() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_boolean(&v, 0);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"true\"));\n    EXPECT_EQ_INT(LEPT_TRUE, lept_get_type(&v));\n    lept_free(&v);\n}\n\nstatic void test_parse_false() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_boolean(&v, 1);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"false\"));\n    EXPECT_EQ_INT(LEPT_FALSE, lept_get_type(&v));\n    lept_free(&v);\n}\n\n#define TEST_NUMBER(expect, json)\\\n    do {\\\n        lept_value v;\\\n        lept_init(&v);\\\n        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(&v));\\\n        EXPECT_EQ_DOUBLE(expect, lept_get_number(&v));\\\n        lept_free(&v);\\\n    } while(0)\n\nstatic void test_parse_number() {\n    TEST_NUMBER(0.0, \"0\");\n    TEST_NUMBER(0.0, \"-0\");\n    TEST_NUMBER(0.0, \"-0.0\");\n    TEST_NUMBER(1.0, \"1\");\n    TEST_NUMBER(-1.0, \"-1\");\n    TEST_NUMBER(1.5, \"1.5\");\n    TEST_NUMBER(-1.5, \"-1.5\");\n    TEST_NUMBER(3.1416, \"3.1416\");\n    TEST_NUMBER(1E10, \"1E10\");\n    TEST_NUMBER(1e10, \"1e10\");\n    TEST_NUMBER(1E+10, \"1E+10\");\n    TEST_NUMBER(1E-10, \"1E-10\");\n    TEST_NUMBER(-1E10, \"-1E10\");\n    TEST_NUMBER(-1e10, \"-1e10\");\n    TEST_NUMBER(-1E+10, \"-1E+10\");\n    TEST_NUMBER(-1E-10, \"-1E-10\");\n    TEST_NUMBER(1.234E+10, \"1.234E+10\");\n    TEST_NUMBER(1.234E-10, \"1.234E-10\");\n    TEST_NUMBER(0.0, \"1e-10000\"); /* must underflow */\n\n    TEST_NUMBER(1.0000000000000002, \"1.0000000000000002\"); /* the smallest number > 1 */\n    TEST_NUMBER( 4.9406564584124654e-324, \"4.9406564584124654e-324\"); /* minimum denormal */\n    TEST_NUMBER(-4.9406564584124654e-324, \"-4.9406564584124654e-324\");\n    TEST_NUMBER( 2.2250738585072009e-308, \"2.2250738585072009e-308\");  /* Max subnormal double */\n    TEST_NUMBER(-2.2250738585072009e-308, \"-2.2250738585072009e-308\");\n    TEST_NUMBER( 2.2250738585072014e-308, \"2.2250738585072014e-308\");  /* Min normal positive double */\n    TEST_NUMBER(-2.2250738585072014e-308, \"-2.2250738585072014e-308\");\n    TEST_NUMBER( 1.7976931348623157e+308, \"1.7976931348623157e+308\");  /* Max double */\n    TEST_NUMBER(-1.7976931348623157e+308, \"-1.7976931348623157e+308\");\n}\n\n#define TEST_STRING(expect, json)\\\n    do {\\\n        lept_value v;\\\n        lept_init(&v);\\\n        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_STRING, lept_get_type(&v));\\\n        EXPECT_EQ_STRING(expect, lept_get_string(&v), lept_get_string_length(&v));\\\n        lept_free(&v);\\\n    } while(0)\n\nstatic void test_parse_string() {\n    TEST_STRING(\"\", \"\\\"\\\"\");\n    TEST_STRING(\"Hello\", \"\\\"Hello\\\"\");\n    TEST_STRING(\"Hello\\nWorld\", \"\\\"Hello\\\\nWorld\\\"\");\n    TEST_STRING(\"\\\" \\\\ / \\b \\f \\n \\r \\t\", \"\\\"\\\\\\\" \\\\\\\\ \\\\/ \\\\b \\\\f \\\\n \\\\r \\\\t\\\"\");\n    TEST_STRING(\"Hello\\0World\", \"\\\"Hello\\\\u0000World\\\"\");\n    TEST_STRING(\"\\x24\", \"\\\"\\\\u0024\\\"\");         /* Dollar sign U+0024 */\n    TEST_STRING(\"\\xC2\\xA2\", \"\\\"\\\\u00A2\\\"\");     /* Cents sign U+00A2 */\n    TEST_STRING(\"\\xE2\\x82\\xAC\", \"\\\"\\\\u20AC\\\"\"); /* Euro sign U+20AC */\n    TEST_STRING(\"\\xF0\\x9D\\x84\\x9E\", \"\\\"\\\\uD834\\\\uDD1E\\\"\");  /* G clef sign U+1D11E */\n    TEST_STRING(\"\\xF0\\x9D\\x84\\x9E\", \"\\\"\\\\ud834\\\\udd1e\\\"\");  /* G clef sign U+1D11E */\n}\n\nstatic void test_parse_array() {\n    size_t i, j;\n    lept_value v;\n\n    lept_init(&v);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"[ ]\"));\n    EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(&v));\n    EXPECT_EQ_SIZE_T(0, lept_get_array_size(&v));\n    lept_free(&v);\n\n    lept_init(&v);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"[ null , false , true , 123 , \\\"abc\\\" ]\"));\n    EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(&v));\n    EXPECT_EQ_SIZE_T(5, lept_get_array_size(&v));\n    EXPECT_EQ_INT(LEPT_NULL,   lept_get_type(lept_get_array_element(&v, 0)));\n    EXPECT_EQ_INT(LEPT_FALSE,  lept_get_type(lept_get_array_element(&v, 1)));\n    EXPECT_EQ_INT(LEPT_TRUE,   lept_get_type(lept_get_array_element(&v, 2)));\n    EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(lept_get_array_element(&v, 3)));\n    EXPECT_EQ_INT(LEPT_STRING, lept_get_type(lept_get_array_element(&v, 4)));\n    EXPECT_EQ_DOUBLE(123.0, lept_get_number(lept_get_array_element(&v, 3)));\n    EXPECT_EQ_STRING(\"abc\", lept_get_string(lept_get_array_element(&v, 4)), lept_get_string_length(lept_get_array_element(&v, 4)));\n    lept_free(&v);\n\n    lept_init(&v);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"[ [ ] , [ 0 ] , [ 0 , 1 ] , [ 0 , 1 , 2 ] ]\"));\n    EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(&v));\n    EXPECT_EQ_SIZE_T(4, lept_get_array_size(&v));\n    for (i = 0; i < 4; i++) {\n        lept_value* a = lept_get_array_element(&v, i);\n        EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(a));\n        EXPECT_EQ_SIZE_T(i, lept_get_array_size(a));\n        for (j = 0; j < i; j++) {\n            lept_value* e = lept_get_array_element(a, j);\n            EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(e));\n            EXPECT_EQ_DOUBLE((double)j, lept_get_number(e));\n        }\n    }\n    lept_free(&v);\n}\n\nstatic void test_parse_object() {\n    lept_value v;\n    size_t i;\n\n    lept_init(&v);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \" { } \"));\n    EXPECT_EQ_INT(LEPT_OBJECT, lept_get_type(&v));\n    EXPECT_EQ_SIZE_T(0, lept_get_object_size(&v));\n    lept_free(&v);\n\n    lept_init(&v);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v,\n        \" { \"\n        \"\\\"n\\\" : null , \"\n        \"\\\"f\\\" : false , \"\n        \"\\\"t\\\" : true , \"\n        \"\\\"i\\\" : 123 , \"\n        \"\\\"s\\\" : \\\"abc\\\", \"\n        \"\\\"a\\\" : [ 1, 2, 3 ],\"\n        \"\\\"o\\\" : { \\\"1\\\" : 1, \\\"2\\\" : 2, \\\"3\\\" : 3 }\"\n        \" } \"\n    ));\n    EXPECT_EQ_INT(LEPT_OBJECT, lept_get_type(&v));\n    EXPECT_EQ_SIZE_T(7, lept_get_object_size(&v));\n    EXPECT_EQ_STRING(\"n\", lept_get_object_key(&v, 0), lept_get_object_key_length(&v, 0));\n    EXPECT_EQ_INT(LEPT_NULL,   lept_get_type(lept_get_object_value(&v, 0)));\n    EXPECT_EQ_STRING(\"f\", lept_get_object_key(&v, 1), lept_get_object_key_length(&v, 1));\n    EXPECT_EQ_INT(LEPT_FALSE,  lept_get_type(lept_get_object_value(&v, 1)));\n    EXPECT_EQ_STRING(\"t\", lept_get_object_key(&v, 2), lept_get_object_key_length(&v, 2));\n    EXPECT_EQ_INT(LEPT_TRUE,   lept_get_type(lept_get_object_value(&v, 2)));\n    EXPECT_EQ_STRING(\"i\", lept_get_object_key(&v, 3), lept_get_object_key_length(&v, 3));\n    EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(lept_get_object_value(&v, 3)));\n    EXPECT_EQ_DOUBLE(123.0, lept_get_number(lept_get_object_value(&v, 3)));\n    EXPECT_EQ_STRING(\"s\", lept_get_object_key(&v, 4), lept_get_object_key_length(&v, 4));\n    EXPECT_EQ_INT(LEPT_STRING, lept_get_type(lept_get_object_value(&v, 4)));\n    EXPECT_EQ_STRING(\"abc\", lept_get_string(lept_get_object_value(&v, 4)), lept_get_string_length(lept_get_object_value(&v, 4)));\n    EXPECT_EQ_STRING(\"a\", lept_get_object_key(&v, 5), lept_get_object_key_length(&v, 5));\n    EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(lept_get_object_value(&v, 5)));\n    EXPECT_EQ_SIZE_T(3, lept_get_array_size(lept_get_object_value(&v, 5)));\n    for (i = 0; i < 3; i++) {\n        lept_value* e = lept_get_array_element(lept_get_object_value(&v, 5), i);\n        EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(e));\n        EXPECT_EQ_DOUBLE(i + 1.0, lept_get_number(e));\n    }\n    EXPECT_EQ_STRING(\"o\", lept_get_object_key(&v, 6), lept_get_object_key_length(&v, 6));\n    {\n        lept_value* o = lept_get_object_value(&v, 6);\n        EXPECT_EQ_INT(LEPT_OBJECT, lept_get_type(o));\n        for (i = 0; i < 3; i++) {\n            lept_value* ov = lept_get_object_value(o, i);\n            EXPECT_TRUE('1' + i == lept_get_object_key(o, i)[0]);\n            EXPECT_EQ_SIZE_T(1, lept_get_object_key_length(o, i));\n            EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(ov));\n            EXPECT_EQ_DOUBLE(i + 1.0, lept_get_number(ov));\n        }\n    }\n    lept_free(&v);\n}\n\n#define TEST_ERROR(error, json)\\\n    do {\\\n        lept_value v;\\\n        lept_init(&v);\\\n        v.type = LEPT_FALSE;\\\n        EXPECT_EQ_INT(error, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\\\n        lept_free(&v);\\\n    } while(0)\n\nstatic void test_parse_expect_value() {\n    TEST_ERROR(LEPT_PARSE_EXPECT_VALUE, \"\");\n    TEST_ERROR(LEPT_PARSE_EXPECT_VALUE, \" \");\n}\n\nstatic void test_parse_invalid_value() {\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"nul\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"?\");\n\n    /* invalid number */\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"+0\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"+1\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \".123\"); /* at least one digit before '.' */\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"1.\");   /* at least one digit after '.' */\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"INF\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"inf\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"NAN\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"nan\");\n\n    /* invalid value in array */\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"[1,]\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"[\\\"a\\\", nul]\");\n}\n\nstatic void test_parse_root_not_singular() {\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"null x\");\n\n    /* invalid number */\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0123\"); /* after zero should be '.' or nothing */\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0x0\");\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0x123\");\n}\n\nstatic void test_parse_number_too_big() {\n    TEST_ERROR(LEPT_PARSE_NUMBER_TOO_BIG, \"1e309\");\n    TEST_ERROR(LEPT_PARSE_NUMBER_TOO_BIG, \"-1e309\");\n}\n\nstatic void test_parse_miss_quotation_mark() {\n    TEST_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK, \"\\\"\");\n    TEST_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK, \"\\\"abc\");\n}\n\nstatic void test_parse_invalid_string_escape() {\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\v\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\'\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\0\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\x12\\\"\");\n}\n\nstatic void test_parse_invalid_string_char() {\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_CHAR, \"\\\"\\x01\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_CHAR, \"\\\"\\x1F\\\"\");\n}\n\nstatic void test_parse_invalid_unicode_hex() {\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u0\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u01\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u012\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u/000\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\uG000\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u0/00\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u0G00\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u00/0\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u00G0\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u000/\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u000G\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u 123\\\"\");\n}\n\nstatic void test_parse_invalid_unicode_surrogate() {\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uD800\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uDBFF\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uD800\\\\\\\\\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uD800\\\\uDBFF\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uD800\\\\uE000\\\"\");\n}\n\nstatic void test_parse_miss_comma_or_square_bracket() {\n    TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, \"[1\");\n    TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, \"[1}\");\n    TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, \"[1 2\");\n    TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, \"[[]\");\n}\n\nstatic void test_parse_miss_key() {\n    TEST_ERROR(LEPT_PARSE_MISS_KEY, \"{:1,\");\n    TEST_ERROR(LEPT_PARSE_MISS_KEY, \"{1:1,\");\n    TEST_ERROR(LEPT_PARSE_MISS_KEY, \"{true:1,\");\n    TEST_ERROR(LEPT_PARSE_MISS_KEY, \"{false:1,\");\n    TEST_ERROR(LEPT_PARSE_MISS_KEY, \"{null:1,\");\n    TEST_ERROR(LEPT_PARSE_MISS_KEY, \"{[]:1,\");\n    TEST_ERROR(LEPT_PARSE_MISS_KEY, \"{{}:1,\");\n    TEST_ERROR(LEPT_PARSE_MISS_KEY, \"{\\\"a\\\":1,\");\n}\n\nstatic void test_parse_miss_colon() {\n    TEST_ERROR(LEPT_PARSE_MISS_COLON, \"{\\\"a\\\"}\");\n    TEST_ERROR(LEPT_PARSE_MISS_COLON, \"{\\\"a\\\",\\\"b\\\"}\");\n}\n\nstatic void test_parse_miss_comma_or_curly_bracket() {\n    TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET, \"{\\\"a\\\":1\");\n    TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET, \"{\\\"a\\\":1]\");\n    TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET, \"{\\\"a\\\":1 \\\"b\\\"\");\n    TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET, \"{\\\"a\\\":{}\");\n}\n\nstatic void test_parse() {\n    test_parse_null();\n    test_parse_true();\n    test_parse_false();\n    test_parse_number();\n    test_parse_string();\n    test_parse_array();\n#if 0\n    test_parse_object();\n#endif\n\n    test_parse_expect_value();\n    test_parse_invalid_value();\n    test_parse_root_not_singular();\n    test_parse_number_too_big();\n    test_parse_miss_quotation_mark();\n    test_parse_invalid_string_escape();\n    test_parse_invalid_string_char();\n    test_parse_invalid_unicode_hex();\n    test_parse_invalid_unicode_surrogate();\n    test_parse_miss_comma_or_square_bracket();\n#if 0\n    test_parse_miss_key();\n    test_parse_miss_colon();\n    test_parse_miss_comma_or_curly_bracket();\n#endif\n}\n\nstatic void test_access_null() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"a\", 1);\n    lept_set_null(&v);\n    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\n    lept_free(&v);\n}\n\nstatic void test_access_boolean() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"a\", 1);\n    lept_set_boolean(&v, 1);\n    EXPECT_TRUE(lept_get_boolean(&v));\n    lept_set_boolean(&v, 0);\n    EXPECT_FALSE(lept_get_boolean(&v));\n    lept_free(&v);\n}\n\nstatic void test_access_number() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"a\", 1);\n    lept_set_number(&v, 1234.5);\n    EXPECT_EQ_DOUBLE(1234.5, lept_get_number(&v));\n    lept_free(&v);\n}\n\nstatic void test_access_string() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"\", 0);\n    EXPECT_EQ_STRING(\"\", lept_get_string(&v), lept_get_string_length(&v));\n    lept_set_string(&v, \"Hello\", 5);\n    EXPECT_EQ_STRING(\"Hello\", lept_get_string(&v), lept_get_string_length(&v));\n    lept_free(&v);\n}\n\nstatic void test_access() {\n    test_access_null();\n    test_access_boolean();\n    test_access_number();\n    test_access_string();\n}\n\nint main() {\n#ifdef _WINDOWS\n    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);\n#endif\n    test_parse();\n    test_access();\n    printf(\"%d/%d (%3.2f%%) passed\\n\", test_pass, test_count, test_pass * 100.0 / test_count);\n    return main_ret;\n}\n"
  },
  {
    "path": "tutorial06/tutorial06.md",
    "content": "# 从零开始的 JSON 库教程（六）：解析对象\n\n* Milo Yip\n* 2016/10/29\n\n本文是[《从零开始的 JSON 库教程》](https://zhuanlan.zhihu.com/json-tutorial)的第六个单元。代码位于 [json-tutorial/tutorial06](https://github.com/miloyip/json-tutorial/blob/master/tutorial06)。\n\n本单元内容：\n\n1. [JSON 对象](#1-json-对象)\n2. [数据结构](#2-数据结构)\n3. [重构字符串解析](#3-重构字符串解析)\n4. [实现](#4-实现)\n5. [总结与练习](#5-总结与练习)\n\n## 1. JSON 对象\n\n此单元是本教程最后一个关于 JSON 解析器的部分。JSON 对象和 JSON 数组非常相似，区别包括 JSON 对象以花括号 `{}`（`U+007B`、`U+007D`）包裹表示，另外 JSON 对象由对象成员（member）组成，而 JSON 数组由 JSON 值组成。所谓对象成员，就是键值对，键必须为 JSON 字符串，然后值是任何 JSON 值，中间以冒号 `:`（`U+003A`）分隔。完整语法如下：\n\n~~~\nmember = string ws %x3A ws value\nobject = %x7B ws [ member *( ws %x2C ws member ) ] ws %x7D\n~~~\n\n## 2. 数据结构\n\n要表示键值对的集合，有很多数据结构可供选择，例如：\n\n* 动态数组（dynamic array）：可扩展容量的数组，如 C++ 的 [`std::vector`](https://en.cppreference.com/w/cpp/container/vector)。\n* 有序动态数组（sorted dynamic array）：和动态数组相同，但保证元素已排序，可用二分搜寻查询成员。\n* 平衡树（balanced tree）：平衡二叉树可有序地遍历成员，如红黑树和 C++ 的 [`std::map`](https://en.cppreference.com/w/cpp/container/map)（[`std::multi_map`](https://en.cppreference.com/w/cpp/container/multimap) 支持重复键）。\n* 哈希表（hash table）：通过哈希函数能实现平均 O(1) 查询，如 C++11 的 [`std::unordered_map`](https://en.cppreference.com/w/cpp/container/unordered_map)（[`unordered_multimap`](https://en.cppreference.com/w/cpp/container/unordered_multimap) 支持重复键）。\n\n设一个对象有 n 个成员，数据结构的容量是 m，n ⩽ m，那么一些常用操作的时间／空间复杂度如下：\n\n|               |动态数组 |有序动态数组|平衡树    |哈希表                |\n|---------------|:-------:|:----------:|:--------:|:--------------------:|\n|有序           |否       |是          |是        |否                    |\n|自定成员次序   |可       |否          |否        |否                    |\n|初始化 n 个成员|O(n)     |O(n log n)  |O(n log n)|平均 O(n)、最坏 O(n^2)|\n|加入成员       |分摊 O(1)|O(n)        |O(log n)  |平均 O(1)、最坏 O(n)  |\n|移除成员       |O(n)     |O(n)        |O(log n)  |平均 O(1)、最坏 O(n)  |\n|查询成员       |O(n)     |O(log n)    |O(log n)  |平均 O(1)、最坏 O(n)  |\n|遍历成员       |O(n)     |O(n)        |O(n)      |O(m)                  |\n|检测对象相等   |O(n^2)   |O(n)        |O(n)      |平均 O(n)、最坏 O(n^2)|\n|空间           |O(m)     |O(m)        |O(n)      |O(m)                  |\n\n在 ECMA-404 标准中，并没有规定对象中每个成员的键一定要唯一的，也没有规定是否需要维持成员的次序。\n\n为了简单起见，我们的 leptjson 选择用动态数组的方案。我们将在单元八才加入动态功能，所以这单元中，每个对象仅仅是成员的数组。那么它跟上一单元的数组非常接近：\n\n~~~c\ntypedef struct lept_value lept_value;\ntypedef struct lept_member lept_member;\n\nstruct lept_value {\n    union {\n        struct { lept_member* m; size_t size; }o;\n        struct { lept_value* e; size_t size; }a;\n        struct { char* s; size_t len; }s;\n        double n;\n    }u;\n    lept_type type;\n};\n\nstruct lept_member {\n    char* k; size_t klen;   /* member key string, key string length */\n    lept_value v;           /* member value */\n};\n~~~\n\n成员结构 `lept_member` 是一个 `lept_value` 加上键的字符串。如同 JSON 字符串的值，我们也需要同时保留字符串的长度，因为字符串本身可能包含空字符 `\\u0000`。\n\n在这单元中，我们仅添加了最基本的访问函数，用于撰写单元测试：\n\n~~~c\nsize_t lept_get_object_size(const lept_value* v);\nconst char* lept_get_object_key(const lept_value* v, size_t index);\nsize_t lept_get_object_key_length(const lept_value* v, size_t index);\nlept_value* lept_get_object_value(const lept_value* v, size_t index);\n~~~\n\n在软件开发过程中，许多时候，选择合适的数据结构后已等于完成一半工作。没有完美的数据结构，所以最好考虑多一些应用的场合，看看时间／空间复杂度以至相关系数是否合适。\n\n接下来，我们就可以着手实现。\n\n## 3. 重构字符串解析\n\n在软件工程中，[代码重构](https://zh.wikipedia.org/wiki/%E4%BB%A3%E7%A0%81%E9%87%8D%E6%9E%84)（code refactoring）是指在不改变软件外在行为时，修改代码以改进结构。代码重构十分依赖于单元测试，因为我们是通过单元测试去维护代码的正确性。有了足够的单元测试，我们可以放胆去重构，尝试并评估不同的改进方式，找到合乎心意而且能通过单元测试的改动，我们才提交它。\n\n我们知道，成员的键也是一个 JSON 字符串，然而，我们不使用 `lept_value` 存储键，因为这样会浪费了当中 `type` 这个无用的字段。由于 `lept_parse_string()` 是直接地把解析的结果写进一个 `lept_value`，所以我们先用「提取方法（extract method，见下注）」的重构方式，把解析 JSON 字符串及写入 `lept_value` 分拆成两部分：\n\n~~~c\n/* 解析 JSON 字符串，把结果写入 str 和 len */\n/* str 指向 c->stack 中的元素，需要在 c->stack  */\nstatic int lept_parse_string_raw(lept_context* c, char** str, size_t* len) {\n    /* \\todo */\n}\n\nstatic int lept_parse_string(lept_context* c, lept_value* v) {\n    int ret;\n    char* s;\n    size_t len;\n    if ((ret = lept_parse_string_raw(c, &s, &len)) == LEPT_PARSE_OK)\n        lept_set_string(v, s, len);\n    return ret;\n}\n~~~\n\n这样的话，我们实现对象的解析时，就可以使用 `lept_parse_string_raw()`　来解析 JSON 字符串，然后把结果复制至 `lept_member` 的 `k` 和 `klen` 字段。\n\n注：在 Fowler 的经典著作 [1] 中，把各种重构方式分门别类，每个方式都有详细的步骤说明。由于书中以 Java 为例子，所以方式的名称使用了 Java 的述语，例如方法（method）。在 C 语言中，「提取方法」其实应该称为「提取函数」。\n\n[1] Fowler, Martin. Refactoring: improving the design of existing code. Pearson Education India, 2009. 中译本：熊节译，《重构——改善既有代码的设计》，人民邮电出版社，2010年。\n\n## 4. 实现\n\n解析对象与解析数组非常相似，所以我留空了几段作为练习。在解析数组时，我们把当前的元素以 `lept_value` 压入栈中，而在这里，我们则是以 `lept_member` 压入：\n\n~~~c\nstatic int lept_parse_object(lept_context* c, lept_value* v) {\n    size_t size;\n    lept_member m;\n    int ret;\n    EXPECT(c, '{');\n    lept_parse_whitespace(c);\n    if (*c->json == '}') {\n        c->json++;\n        v->type = LEPT_OBJECT;\n        v->u.o.m = 0;\n        v->u.o.size = 0;\n        return LEPT_PARSE_OK;\n    }\n    m.k = NULL;\n    size = 0;\n    for (;;) {\n        lept_init(&m.v);\n        /* \\todo parse key to m.k, m.klen */\n        /* \\todo parse ws colon ws */\n        /* parse value */\n        if ((ret = lept_parse_value(c, &m.v)) != LEPT_PARSE_OK)\n            break;\n        memcpy(lept_context_push(c, sizeof(lept_member)), &m, sizeof(lept_member));\n        size++;\n        m.k = NULL; /* ownership is transferred to member on stack */\n        /* \\todo parse ws [comma | right-curly-brace] ws */\n    }\n    /* \\todo Pop and free members on the stack */\n    return ret;\n}\n~~~\n\n要注意的是，我们要为 `m.k` 分配内存去存储键的字符串，若在整个对象解析时发生错误，也要记得释放栈中的 `lept_member` 的 `k`。\n\n我们为解析对象定义了几个新的错误码：\n\n~~~c\nenum {\n    /* ... */\n    LEPT_PARSE_MISS_KEY,\n    LEPT_PARSE_MISS_COLON,\n    LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET\n};\n~~~\n\n在此不再赘述它们的意义了，可从以下的单元测试看到例子：\n\n~~~c\nstatic void test_parse_miss_key() {\n    TEST_ERROR(LEPT_PARSE_MISS_KEY, \"{:1,\");\n    TEST_ERROR(LEPT_PARSE_MISS_KEY, \"{1:1,\");\n    TEST_ERROR(LEPT_PARSE_MISS_KEY, \"{true:1,\");\n    TEST_ERROR(LEPT_PARSE_MISS_KEY, \"{false:1,\");\n    TEST_ERROR(LEPT_PARSE_MISS_KEY, \"{null:1,\");\n    TEST_ERROR(LEPT_PARSE_MISS_KEY, \"{[]:1,\");\n    TEST_ERROR(LEPT_PARSE_MISS_KEY, \"{{}:1,\");\n    TEST_ERROR(LEPT_PARSE_MISS_KEY, \"{\\\"a\\\":1,\");\n}\n\nstatic void test_parse_miss_colon() {\n    TEST_ERROR(LEPT_PARSE_MISS_COLON, \"{\\\"a\\\"}\");\n    TEST_ERROR(LEPT_PARSE_MISS_COLON, \"{\\\"a\\\",\\\"b\\\"}\");\n}\n\nstatic void test_parse_miss_comma_or_curly_bracket() {\n    TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET, \"{\\\"a\\\":1\");\n    TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET, \"{\\\"a\\\":1]\");\n    TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET, \"{\\\"a\\\":1 \\\"b\\\"\");\n    TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET, \"{\\\"a\\\":{}\");\n}\n~~~\n\n## 5. 总结与练习\n\n在本单元中，除了谈及 JSON 对象的语法、可选的数据结构、实现方式，我们也轻轻谈及了重构的概念。有赖于测试驱动开发（TDD），我们可以不断重塑软件的内部结构。\n\n完成这次练习之后，恭喜你，你已经完整地实现了一个符合标准的 JSON 解析器了。之后我们会完成更简单的生成器及其他访问功能。\n\n由于对象和数组的相似性，此单元留空了较多实现部分作为练习：\n\n1. 依第 3 节所述，重构 `lept_parse_string()`。重构前运行单元测试，重构后确保单元测试仍保持通过。\n2. 打开 `test.c` 中两个 `#if 0`，运行单元测试，证实单元测试不通过。然后实现 `lept_parse_object()` 中的 `\\todo` 部分。验证实现能通过单元测试。\n3. 使用工具检测内存泄漏，解决它们。\n\n如果你遇到问题，有不理解的地方，或是有建议，都欢迎在评论或 [issue](https://github.com/miloyip/json-tutorial/issues) 中提出，让所有人一起讨论。\n"
  },
  {
    "path": "tutorial06_answer/CMakeLists.txt",
    "content": "cmake_minimum_required (VERSION 2.6)\nproject (leptjson_test C)\n\nif (CMAKE_C_COMPILER_ID MATCHES \"GNU|Clang\")\n    set(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -ansi -pedantic -Wall\")\nendif()\n\nadd_library(leptjson leptjson.c)\nadd_executable(leptjson_test test.c)\ntarget_link_libraries(leptjson_test leptjson)\n"
  },
  {
    "path": "tutorial06_answer/leptjson.c",
    "content": "#ifdef _WINDOWS\n#define _CRTDBG_MAP_ALLOC\n#include <crtdbg.h>\n#endif\n#include \"leptjson.h\"\n#include <assert.h>  /* assert() */\n#include <errno.h>   /* errno, ERANGE */\n#include <math.h>    /* HUGE_VAL */\n#include <stdlib.h>  /* NULL, malloc(), realloc(), free(), strtod() */\n#include <string.h>  /* memcpy() */\n\n#ifndef LEPT_PARSE_STACK_INIT_SIZE\n#define LEPT_PARSE_STACK_INIT_SIZE 256\n#endif\n\n#define EXPECT(c, ch)       do { assert(*c->json == (ch)); c->json++; } while(0)\n#define ISDIGIT(ch)         ((ch) >= '0' && (ch) <= '9')\n#define ISDIGIT1TO9(ch)     ((ch) >= '1' && (ch) <= '9')\n#define PUTC(c, ch)         do { *(char*)lept_context_push(c, sizeof(char)) = (ch); } while(0)\n\ntypedef struct {\n    const char* json;\n    char* stack;\n    size_t size, top;\n}lept_context;\n\nstatic void* lept_context_push(lept_context* c, size_t size) {\n    void* ret;\n    assert(size > 0);\n    if (c->top + size >= c->size) {\n        if (c->size == 0)\n            c->size = LEPT_PARSE_STACK_INIT_SIZE;\n        while (c->top + size >= c->size)\n            c->size += c->size >> 1;  /* c->size * 1.5 */\n        c->stack = (char*)realloc(c->stack, c->size);\n    }\n    ret = c->stack + c->top;\n    c->top += size;\n    return ret;\n}\n\nstatic void* lept_context_pop(lept_context* c, size_t size) {\n    assert(c->top >= size);\n    return c->stack + (c->top -= size);\n}\n\nstatic void lept_parse_whitespace(lept_context* c) {\n    const char *p = c->json;\n    while (*p == ' ' || *p == '\\t' || *p == '\\n' || *p == '\\r')\n        p++;\n    c->json = p;\n}\n\nstatic int lept_parse_literal(lept_context* c, lept_value* v, const char* literal, lept_type type) {\n    size_t i;\n    EXPECT(c, literal[0]);\n    for (i = 0; literal[i + 1]; i++)\n        if (c->json[i] != literal[i + 1])\n            return LEPT_PARSE_INVALID_VALUE;\n    c->json += i;\n    v->type = type;\n    return LEPT_PARSE_OK;\n}\n\nstatic int lept_parse_number(lept_context* c, lept_value* v) {\n    const char* p = c->json;\n    if (*p == '-') p++;\n    if (*p == '0') p++;\n    else {\n        if (!ISDIGIT1TO9(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n    if (*p == '.') {\n        p++;\n        if (!ISDIGIT(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n    if (*p == 'e' || *p == 'E') {\n        p++;\n        if (*p == '+' || *p == '-') p++;\n        if (!ISDIGIT(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n    errno = 0;\n    v->u.n = strtod(c->json, NULL);\n    if (errno == ERANGE && (v->u.n == HUGE_VAL || v->u.n == -HUGE_VAL))\n        return LEPT_PARSE_NUMBER_TOO_BIG;\n    v->type = LEPT_NUMBER;\n    c->json = p;\n    return LEPT_PARSE_OK;\n}\n\nstatic const char* lept_parse_hex4(const char* p, unsigned* u) {\n    int i;\n    *u = 0;\n    for (i = 0; i < 4; i++) {\n        char ch = *p++;\n        *u <<= 4;\n        if      (ch >= '0' && ch <= '9')  *u |= ch - '0';\n        else if (ch >= 'A' && ch <= 'F')  *u |= ch - ('A' - 10);\n        else if (ch >= 'a' && ch <= 'f')  *u |= ch - ('a' - 10);\n        else return NULL;\n    }\n    return p;\n}\n\nstatic void lept_encode_utf8(lept_context* c, unsigned u) {\n    if (u <= 0x7F) \n        PUTC(c, u & 0xFF);\n    else if (u <= 0x7FF) {\n        PUTC(c, 0xC0 | ((u >> 6) & 0xFF));\n        PUTC(c, 0x80 | ( u       & 0x3F));\n    }\n    else if (u <= 0xFFFF) {\n        PUTC(c, 0xE0 | ((u >> 12) & 0xFF));\n        PUTC(c, 0x80 | ((u >>  6) & 0x3F));\n        PUTC(c, 0x80 | ( u        & 0x3F));\n    }\n    else {\n        assert(u <= 0x10FFFF);\n        PUTC(c, 0xF0 | ((u >> 18) & 0xFF));\n        PUTC(c, 0x80 | ((u >> 12) & 0x3F));\n        PUTC(c, 0x80 | ((u >>  6) & 0x3F));\n        PUTC(c, 0x80 | ( u        & 0x3F));\n    }\n}\n\n#define STRING_ERROR(ret) do { c->top = head; return ret; } while(0)\n\nstatic int lept_parse_string_raw(lept_context* c, char** str, size_t* len) {\n    size_t head = c->top;\n    unsigned u, u2;\n    const char* p;\n    EXPECT(c, '\\\"');\n    p = c->json;\n    for (;;) {\n        char ch = *p++;\n        switch (ch) {\n            case '\\\"':\n                *len = c->top - head;\n                *str = lept_context_pop(c, *len);\n                c->json = p;\n                return LEPT_PARSE_OK;\n            case '\\\\':\n                switch (*p++) {\n                    case '\\\"': PUTC(c, '\\\"'); break;\n                    case '\\\\': PUTC(c, '\\\\'); break;\n                    case '/':  PUTC(c, '/' ); break;\n                    case 'b':  PUTC(c, '\\b'); break;\n                    case 'f':  PUTC(c, '\\f'); break;\n                    case 'n':  PUTC(c, '\\n'); break;\n                    case 'r':  PUTC(c, '\\r'); break;\n                    case 't':  PUTC(c, '\\t'); break;\n                    case 'u':\n                        if (!(p = lept_parse_hex4(p, &u)))\n                            STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX);\n                        if (u >= 0xD800 && u <= 0xDBFF) { /* surrogate pair */\n                            if (*p++ != '\\\\')\n                                STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE);\n                            if (*p++ != 'u')\n                                STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE);\n                            if (!(p = lept_parse_hex4(p, &u2)))\n                                STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX);\n                            if (u2 < 0xDC00 || u2 > 0xDFFF)\n                                STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE);\n                            u = (((u - 0xD800) << 10) | (u2 - 0xDC00)) + 0x10000;\n                        }\n                        lept_encode_utf8(c, u);\n                        break;\n                    default:\n                        STRING_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE);\n                }\n                break;\n            case '\\0':\n                STRING_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK);\n            default:\n                if ((unsigned char)ch < 0x20)\n                    STRING_ERROR(LEPT_PARSE_INVALID_STRING_CHAR);\n                PUTC(c, ch);\n        }\n    }\n}\n\nstatic int lept_parse_string(lept_context* c, lept_value* v) {\n    int ret;\n    char* s;\n    size_t len;\n    if ((ret = lept_parse_string_raw(c, &s, &len)) == LEPT_PARSE_OK)\n        lept_set_string(v, s, len);\n    return ret;\n}\n\nstatic int lept_parse_value(lept_context* c, lept_value* v);\n\nstatic int lept_parse_array(lept_context* c, lept_value* v) {\n    size_t i, size = 0;\n    int ret;\n    EXPECT(c, '[');\n    lept_parse_whitespace(c);\n    if (*c->json == ']') {\n        c->json++;\n        v->type = LEPT_ARRAY;\n        v->u.a.size = 0;\n        v->u.a.e = NULL;\n        return LEPT_PARSE_OK;\n    }\n    for (;;) {\n        lept_value e;\n        lept_init(&e);\n        if ((ret = lept_parse_value(c, &e)) != LEPT_PARSE_OK)\n            break;\n        memcpy(lept_context_push(c, sizeof(lept_value)), &e, sizeof(lept_value));\n        size++;\n        lept_parse_whitespace(c);\n        if (*c->json == ',') {\n            c->json++;\n            lept_parse_whitespace(c);\n        }\n        else if (*c->json == ']') {\n            c->json++;\n            v->type = LEPT_ARRAY;\n            v->u.a.size = size;\n            size *= sizeof(lept_value);\n            memcpy(v->u.a.e = (lept_value*)malloc(size), lept_context_pop(c, size), size);\n            return LEPT_PARSE_OK;\n        }\n        else {\n            ret = LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET;\n            break;\n        }\n    }\n    /* Pop and free values on the stack */\n    for (i = 0; i < size; i++)\n        lept_free((lept_value*)lept_context_pop(c, sizeof(lept_value)));\n    return ret;\n}\n\nstatic int lept_parse_object(lept_context* c, lept_value* v) {\n    size_t i, size;\n    lept_member m;\n    int ret;\n    EXPECT(c, '{');\n    lept_parse_whitespace(c);\n    if (*c->json == '}') {\n        c->json++;\n        v->type = LEPT_OBJECT;\n        v->u.o.m = 0;\n        v->u.o.size = 0;\n        return LEPT_PARSE_OK;\n    }\n    m.k = NULL;\n    size = 0;\n    for (;;) {\n        char* str;\n        lept_init(&m.v);\n        /* parse key */\n        if (*c->json != '\"') {\n            ret = LEPT_PARSE_MISS_KEY;\n            break;\n        }\n        if ((ret = lept_parse_string_raw(c, &str, &m.klen)) != LEPT_PARSE_OK)\n            break;\n        memcpy(m.k = (char*)malloc(m.klen + 1), str, m.klen);\n        m.k[m.klen] = '\\0';\n        /* parse ws colon ws */\n        lept_parse_whitespace(c);\n        if (*c->json != ':') {\n            ret = LEPT_PARSE_MISS_COLON;\n            break;\n        }\n        c->json++;\n        lept_parse_whitespace(c);\n        /* parse value */\n        if ((ret = lept_parse_value(c, &m.v)) != LEPT_PARSE_OK)\n            break;\n        memcpy(lept_context_push(c, sizeof(lept_member)), &m, sizeof(lept_member));\n        size++;\n        m.k = NULL; /* ownership is transferred to member on stack */\n        /* parse ws [comma | right-curly-brace] ws */\n        lept_parse_whitespace(c);\n        if (*c->json == ',') {\n            c->json++;\n            lept_parse_whitespace(c);\n        }\n        else if (*c->json == '}') {\n            size_t s = sizeof(lept_member) * size;\n            c->json++;\n            v->type = LEPT_OBJECT;\n            v->u.o.size = size;\n            memcpy(v->u.o.m = (lept_member*)malloc(s), lept_context_pop(c, s), s);\n            return LEPT_PARSE_OK;\n        }\n        else {\n            ret = LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET;\n            break;\n        }\n    }\n    /* Pop and free members on the stack */\n    free(m.k);\n    for (i = 0; i < size; i++) {\n        lept_member* m = (lept_member*)lept_context_pop(c, sizeof(lept_member));\n        free(m->k);\n        lept_free(&m->v);\n    }\n    v->type = LEPT_NULL;\n    return ret;\n}\n\nstatic int lept_parse_value(lept_context* c, lept_value* v) {\n    switch (*c->json) {\n        case 't':  return lept_parse_literal(c, v, \"true\", LEPT_TRUE);\n        case 'f':  return lept_parse_literal(c, v, \"false\", LEPT_FALSE);\n        case 'n':  return lept_parse_literal(c, v, \"null\", LEPT_NULL);\n        default:   return lept_parse_number(c, v);\n        case '\"':  return lept_parse_string(c, v);\n        case '[':  return lept_parse_array(c, v);\n        case '{':  return lept_parse_object(c, v);\n        case '\\0': return LEPT_PARSE_EXPECT_VALUE;\n    }\n}\n\nint lept_parse(lept_value* v, const char* json) {\n    lept_context c;\n    int ret;\n    assert(v != NULL);\n    c.json = json;\n    c.stack = NULL;\n    c.size = c.top = 0;\n    lept_init(v);\n    lept_parse_whitespace(&c);\n    if ((ret = lept_parse_value(&c, v)) == LEPT_PARSE_OK) {\n        lept_parse_whitespace(&c);\n        if (*c.json != '\\0') {\n            v->type = LEPT_NULL;\n            ret = LEPT_PARSE_ROOT_NOT_SINGULAR;\n        }\n    }\n    assert(c.top == 0);\n    free(c.stack);\n    return ret;\n}\n\nvoid lept_free(lept_value* v) {\n    size_t i;\n    assert(v != NULL);\n    switch (v->type) {\n        case LEPT_STRING:\n            free(v->u.s.s);\n            break;\n        case LEPT_ARRAY:\n            for (i = 0; i < v->u.a.size; i++)\n                lept_free(&v->u.a.e[i]);\n            free(v->u.a.e);\n            break;\n        case LEPT_OBJECT:\n            for (i = 0; i < v->u.o.size; i++) {\n                free(v->u.o.m[i].k);\n                lept_free(&v->u.o.m[i].v);\n            }\n            free(v->u.o.m);\n            break;\n        default: break;\n    }\n    v->type = LEPT_NULL;\n}\n\nlept_type lept_get_type(const lept_value* v) {\n    assert(v != NULL);\n    return v->type;\n}\n\nint lept_get_boolean(const lept_value* v) {\n    assert(v != NULL && (v->type == LEPT_TRUE || v->type == LEPT_FALSE));\n    return v->type == LEPT_TRUE;\n}\n\nvoid lept_set_boolean(lept_value* v, int b) {\n    lept_free(v);\n    v->type = b ? LEPT_TRUE : LEPT_FALSE;\n}\n\ndouble lept_get_number(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_NUMBER);\n    return v->u.n;\n}\n\nvoid lept_set_number(lept_value* v, double n) {\n    lept_free(v);\n    v->u.n = n;\n    v->type = LEPT_NUMBER;\n}\n\nconst char* lept_get_string(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_STRING);\n    return v->u.s.s;\n}\n\nsize_t lept_get_string_length(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_STRING);\n    return v->u.s.len;\n}\n\nvoid lept_set_string(lept_value* v, const char* s, size_t len) {\n    assert(v != NULL && (s != NULL || len == 0));\n    lept_free(v);\n    v->u.s.s = (char*)malloc(len + 1);\n    memcpy(v->u.s.s, s, len);\n    v->u.s.s[len] = '\\0';\n    v->u.s.len = len;\n    v->type = LEPT_STRING;\n}\n\nsize_t lept_get_array_size(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_ARRAY);\n    return v->u.a.size;\n}\n\nlept_value* lept_get_array_element(const lept_value* v, size_t index) {\n    assert(v != NULL && v->type == LEPT_ARRAY);\n    assert(index < v->u.a.size);\n    return &v->u.a.e[index];\n}\n\nsize_t lept_get_object_size(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_OBJECT);\n    return v->u.o.size;\n}\n\nconst char* lept_get_object_key(const lept_value* v, size_t index) {\n    assert(v != NULL && v->type == LEPT_OBJECT);\n    assert(index < v->u.o.size);\n    return v->u.o.m[index].k;\n}\n\nsize_t lept_get_object_key_length(const lept_value* v, size_t index) {\n    assert(v != NULL && v->type == LEPT_OBJECT);\n    assert(index < v->u.o.size);\n    return v->u.o.m[index].klen;\n}\n\nlept_value* lept_get_object_value(const lept_value* v, size_t index) {\n    assert(v != NULL && v->type == LEPT_OBJECT);\n    assert(index < v->u.o.size);\n    return &v->u.o.m[index].v;\n}\n"
  },
  {
    "path": "tutorial06_answer/leptjson.h",
    "content": "#ifndef LEPTJSON_H__\n#define LEPTJSON_H__\n\n#include <stddef.h> /* size_t */\n\ntypedef enum { LEPT_NULL, LEPT_FALSE, LEPT_TRUE, LEPT_NUMBER, LEPT_STRING, LEPT_ARRAY, LEPT_OBJECT } lept_type;\n\ntypedef struct lept_value lept_value;\ntypedef struct lept_member lept_member;\n\nstruct lept_value {\n    union {\n        struct { lept_member* m; size_t size; }o;   /* object: members, member count */\n        struct { lept_value* e; size_t size; }a;    /* array:  elements, element count */\n        struct { char* s; size_t len; }s;           /* string: null-terminated string, string length */\n        double n;                                   /* number */\n    }u;\n    lept_type type;\n};\n\nstruct lept_member {\n    char* k; size_t klen;   /* member key string, key string length */\n    lept_value v;           /* member value */\n};\n\nenum {\n    LEPT_PARSE_OK = 0,\n    LEPT_PARSE_EXPECT_VALUE,\n    LEPT_PARSE_INVALID_VALUE,\n    LEPT_PARSE_ROOT_NOT_SINGULAR,\n    LEPT_PARSE_NUMBER_TOO_BIG,\n    LEPT_PARSE_MISS_QUOTATION_MARK,\n    LEPT_PARSE_INVALID_STRING_ESCAPE,\n    LEPT_PARSE_INVALID_STRING_CHAR,\n    LEPT_PARSE_INVALID_UNICODE_HEX,\n    LEPT_PARSE_INVALID_UNICODE_SURROGATE,\n    LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET,\n    LEPT_PARSE_MISS_KEY,\n    LEPT_PARSE_MISS_COLON,\n    LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET\n};\n\n#define lept_init(v) do { (v)->type = LEPT_NULL; } while(0)\n\nint lept_parse(lept_value* v, const char* json);\n\nvoid lept_free(lept_value* v);\n\nlept_type lept_get_type(const lept_value* v);\n\n#define lept_set_null(v) lept_free(v)\n\nint lept_get_boolean(const lept_value* v);\nvoid lept_set_boolean(lept_value* v, int b);\n\ndouble lept_get_number(const lept_value* v);\nvoid lept_set_number(lept_value* v, double n);\n\nconst char* lept_get_string(const lept_value* v);\nsize_t lept_get_string_length(const lept_value* v);\nvoid lept_set_string(lept_value* v, const char* s, size_t len);\n\nsize_t lept_get_array_size(const lept_value* v);\nlept_value* lept_get_array_element(const lept_value* v, size_t index);\n\nsize_t lept_get_object_size(const lept_value* v);\nconst char* lept_get_object_key(const lept_value* v, size_t index);\nsize_t lept_get_object_key_length(const lept_value* v, size_t index);\nlept_value* lept_get_object_value(const lept_value* v, size_t index);\n\n#endif /* LEPTJSON_H__ */\n"
  },
  {
    "path": "tutorial06_answer/test.c",
    "content": "#ifdef _WINDOWS\n#define _CRTDBG_MAP_ALLOC\n#include <crtdbg.h>\n#endif\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"leptjson.h\"\n\nstatic int main_ret = 0;\nstatic int test_count = 0;\nstatic int test_pass = 0;\n\n#define EXPECT_EQ_BASE(equality, expect, actual, format) \\\n    do {\\\n        test_count++;\\\n        if (equality)\\\n            test_pass++;\\\n        else {\\\n            fprintf(stderr, \"%s:%d: expect: \" format \" actual: \" format \"\\n\", __FILE__, __LINE__, expect, actual);\\\n            main_ret = 1;\\\n        }\\\n    } while(0)\n\n#define EXPECT_EQ_INT(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, \"%d\")\n#define EXPECT_EQ_DOUBLE(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, \"%.17g\")\n#define EXPECT_EQ_STRING(expect, actual, alength) \\\n    EXPECT_EQ_BASE(sizeof(expect) - 1 == alength && memcmp(expect, actual, alength + 1) == 0, expect, actual, \"%s\")\n#define EXPECT_TRUE(actual) EXPECT_EQ_BASE((actual) != 0, \"true\", \"false\", \"%s\")\n#define EXPECT_FALSE(actual) EXPECT_EQ_BASE((actual) == 0, \"false\", \"true\", \"%s\")\n\n#if defined(_MSC_VER)\n#define EXPECT_EQ_SIZE_T(expect, actual) EXPECT_EQ_BASE((expect) == (actual), (size_t)expect, (size_t)actual, \"%Iu\")\n#else\n#define EXPECT_EQ_SIZE_T(expect, actual) EXPECT_EQ_BASE((expect) == (actual), (size_t)expect, (size_t)actual, \"%zu\")\n#endif\n\nstatic void test_parse_null() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_boolean(&v, 0);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"null\"));\n    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\n    lept_free(&v);\n}\n\nstatic void test_parse_true() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_boolean(&v, 0);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"true\"));\n    EXPECT_EQ_INT(LEPT_TRUE, lept_get_type(&v));\n    lept_free(&v);\n}\n\nstatic void test_parse_false() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_boolean(&v, 1);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"false\"));\n    EXPECT_EQ_INT(LEPT_FALSE, lept_get_type(&v));\n    lept_free(&v);\n}\n\n#define TEST_NUMBER(expect, json)\\\n    do {\\\n        lept_value v;\\\n        lept_init(&v);\\\n        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(&v));\\\n        EXPECT_EQ_DOUBLE(expect, lept_get_number(&v));\\\n        lept_free(&v);\\\n    } while(0)\n\nstatic void test_parse_number() {\n    TEST_NUMBER(0.0, \"0\");\n    TEST_NUMBER(0.0, \"-0\");\n    TEST_NUMBER(0.0, \"-0.0\");\n    TEST_NUMBER(1.0, \"1\");\n    TEST_NUMBER(-1.0, \"-1\");\n    TEST_NUMBER(1.5, \"1.5\");\n    TEST_NUMBER(-1.5, \"-1.5\");\n    TEST_NUMBER(3.1416, \"3.1416\");\n    TEST_NUMBER(1E10, \"1E10\");\n    TEST_NUMBER(1e10, \"1e10\");\n    TEST_NUMBER(1E+10, \"1E+10\");\n    TEST_NUMBER(1E-10, \"1E-10\");\n    TEST_NUMBER(-1E10, \"-1E10\");\n    TEST_NUMBER(-1e10, \"-1e10\");\n    TEST_NUMBER(-1E+10, \"-1E+10\");\n    TEST_NUMBER(-1E-10, \"-1E-10\");\n    TEST_NUMBER(1.234E+10, \"1.234E+10\");\n    TEST_NUMBER(1.234E-10, \"1.234E-10\");\n    TEST_NUMBER(0.0, \"1e-10000\"); /* must underflow */\n\n    TEST_NUMBER(1.0000000000000002, \"1.0000000000000002\"); /* the smallest number > 1 */\n    TEST_NUMBER( 4.9406564584124654e-324, \"4.9406564584124654e-324\"); /* minimum denormal */\n    TEST_NUMBER(-4.9406564584124654e-324, \"-4.9406564584124654e-324\");\n    TEST_NUMBER( 2.2250738585072009e-308, \"2.2250738585072009e-308\");  /* Max subnormal double */\n    TEST_NUMBER(-2.2250738585072009e-308, \"-2.2250738585072009e-308\");\n    TEST_NUMBER( 2.2250738585072014e-308, \"2.2250738585072014e-308\");  /* Min normal positive double */\n    TEST_NUMBER(-2.2250738585072014e-308, \"-2.2250738585072014e-308\");\n    TEST_NUMBER( 1.7976931348623157e+308, \"1.7976931348623157e+308\");  /* Max double */\n    TEST_NUMBER(-1.7976931348623157e+308, \"-1.7976931348623157e+308\");\n}\n\n#define TEST_STRING(expect, json)\\\n    do {\\\n        lept_value v;\\\n        lept_init(&v);\\\n        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_STRING, lept_get_type(&v));\\\n        EXPECT_EQ_STRING(expect, lept_get_string(&v), lept_get_string_length(&v));\\\n        lept_free(&v);\\\n    } while(0)\n\nstatic void test_parse_string() {\n    TEST_STRING(\"\", \"\\\"\\\"\");\n    TEST_STRING(\"Hello\", \"\\\"Hello\\\"\");\n    TEST_STRING(\"Hello\\nWorld\", \"\\\"Hello\\\\nWorld\\\"\");\n    TEST_STRING(\"\\\" \\\\ / \\b \\f \\n \\r \\t\", \"\\\"\\\\\\\" \\\\\\\\ \\\\/ \\\\b \\\\f \\\\n \\\\r \\\\t\\\"\");\n    TEST_STRING(\"Hello\\0World\", \"\\\"Hello\\\\u0000World\\\"\");\n    TEST_STRING(\"\\x24\", \"\\\"\\\\u0024\\\"\");         /* Dollar sign U+0024 */\n    TEST_STRING(\"\\xC2\\xA2\", \"\\\"\\\\u00A2\\\"\");     /* Cents sign U+00A2 */\n    TEST_STRING(\"\\xE2\\x82\\xAC\", \"\\\"\\\\u20AC\\\"\"); /* Euro sign U+20AC */\n    TEST_STRING(\"\\xF0\\x9D\\x84\\x9E\", \"\\\"\\\\uD834\\\\uDD1E\\\"\");  /* G clef sign U+1D11E */\n    TEST_STRING(\"\\xF0\\x9D\\x84\\x9E\", \"\\\"\\\\ud834\\\\udd1e\\\"\");  /* G clef sign U+1D11E */\n}\n\nstatic void test_parse_array() {\n    size_t i, j;\n    lept_value v;\n\n    lept_init(&v);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"[ ]\"));\n    EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(&v));\n    EXPECT_EQ_SIZE_T(0, lept_get_array_size(&v));\n    lept_free(&v);\n\n    lept_init(&v);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"[ null , false , true , 123 , \\\"abc\\\" ]\"));\n    EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(&v));\n    EXPECT_EQ_SIZE_T(5, lept_get_array_size(&v));\n    EXPECT_EQ_INT(LEPT_NULL,   lept_get_type(lept_get_array_element(&v, 0)));\n    EXPECT_EQ_INT(LEPT_FALSE,  lept_get_type(lept_get_array_element(&v, 1)));\n    EXPECT_EQ_INT(LEPT_TRUE,   lept_get_type(lept_get_array_element(&v, 2)));\n    EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(lept_get_array_element(&v, 3)));\n    EXPECT_EQ_INT(LEPT_STRING, lept_get_type(lept_get_array_element(&v, 4)));\n    EXPECT_EQ_DOUBLE(123.0, lept_get_number(lept_get_array_element(&v, 3)));\n    EXPECT_EQ_STRING(\"abc\", lept_get_string(lept_get_array_element(&v, 4)), lept_get_string_length(lept_get_array_element(&v, 4)));\n    lept_free(&v);\n\n    lept_init(&v);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"[ [ ] , [ 0 ] , [ 0 , 1 ] , [ 0 , 1 , 2 ] ]\"));\n    EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(&v));\n    EXPECT_EQ_SIZE_T(4, lept_get_array_size(&v));\n    for (i = 0; i < 4; i++) {\n        lept_value* a = lept_get_array_element(&v, i);\n        EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(a));\n        EXPECT_EQ_SIZE_T(i, lept_get_array_size(a));\n        for (j = 0; j < i; j++) {\n            lept_value* e = lept_get_array_element(a, j);\n            EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(e));\n            EXPECT_EQ_DOUBLE((double)j, lept_get_number(e));\n        }\n    }\n    lept_free(&v);\n}\n\nstatic void test_parse_object() {\n    lept_value v;\n    size_t i;\n\n    lept_init(&v);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \" { } \"));\n    EXPECT_EQ_INT(LEPT_OBJECT, lept_get_type(&v));\n    EXPECT_EQ_SIZE_T(0, lept_get_object_size(&v));\n    lept_free(&v);\n\n    lept_init(&v);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v,\n        \" { \"\n        \"\\\"n\\\" : null , \"\n        \"\\\"f\\\" : false , \"\n        \"\\\"t\\\" : true , \"\n        \"\\\"i\\\" : 123 , \"\n        \"\\\"s\\\" : \\\"abc\\\", \"\n        \"\\\"a\\\" : [ 1, 2, 3 ],\"\n        \"\\\"o\\\" : { \\\"1\\\" : 1, \\\"2\\\" : 2, \\\"3\\\" : 3 }\"\n        \" } \"\n    ));\n    EXPECT_EQ_INT(LEPT_OBJECT, lept_get_type(&v));\n    EXPECT_EQ_SIZE_T(7, lept_get_object_size(&v));\n    EXPECT_EQ_STRING(\"n\", lept_get_object_key(&v, 0), lept_get_object_key_length(&v, 0));\n    EXPECT_EQ_INT(LEPT_NULL,   lept_get_type(lept_get_object_value(&v, 0)));\n    EXPECT_EQ_STRING(\"f\", lept_get_object_key(&v, 1), lept_get_object_key_length(&v, 1));\n    EXPECT_EQ_INT(LEPT_FALSE,  lept_get_type(lept_get_object_value(&v, 1)));\n    EXPECT_EQ_STRING(\"t\", lept_get_object_key(&v, 2), lept_get_object_key_length(&v, 2));\n    EXPECT_EQ_INT(LEPT_TRUE,   lept_get_type(lept_get_object_value(&v, 2)));\n    EXPECT_EQ_STRING(\"i\", lept_get_object_key(&v, 3), lept_get_object_key_length(&v, 3));\n    EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(lept_get_object_value(&v, 3)));\n    EXPECT_EQ_DOUBLE(123.0, lept_get_number(lept_get_object_value(&v, 3)));\n    EXPECT_EQ_STRING(\"s\", lept_get_object_key(&v, 4), lept_get_object_key_length(&v, 4));\n    EXPECT_EQ_INT(LEPT_STRING, lept_get_type(lept_get_object_value(&v, 4)));\n    EXPECT_EQ_STRING(\"abc\", lept_get_string(lept_get_object_value(&v, 4)), lept_get_string_length(lept_get_object_value(&v, 4)));\n    EXPECT_EQ_STRING(\"a\", lept_get_object_key(&v, 5), lept_get_object_key_length(&v, 5));\n    EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(lept_get_object_value(&v, 5)));\n    EXPECT_EQ_SIZE_T(3, lept_get_array_size(lept_get_object_value(&v, 5)));\n    for (i = 0; i < 3; i++) {\n        lept_value* e = lept_get_array_element(lept_get_object_value(&v, 5), i);\n        EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(e));\n        EXPECT_EQ_DOUBLE(i + 1.0, lept_get_number(e));\n    }\n    EXPECT_EQ_STRING(\"o\", lept_get_object_key(&v, 6), lept_get_object_key_length(&v, 6));\n    {\n        lept_value* o = lept_get_object_value(&v, 6);\n        EXPECT_EQ_INT(LEPT_OBJECT, lept_get_type(o));\n        for (i = 0; i < 3; i++) {\n            lept_value* ov = lept_get_object_value(o, i);\n            EXPECT_TRUE('1' + i == lept_get_object_key(o, i)[0]);\n            EXPECT_EQ_SIZE_T(1, lept_get_object_key_length(o, i));\n            EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(ov));\n            EXPECT_EQ_DOUBLE(i + 1.0, lept_get_number(ov));\n        }\n    }\n    lept_free(&v);\n}\n\n#define TEST_ERROR(error, json)\\\n    do {\\\n        lept_value v;\\\n        lept_init(&v);\\\n        v.type = LEPT_FALSE;\\\n        EXPECT_EQ_INT(error, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\\\n        lept_free(&v);\\\n    } while(0)\n\nstatic void test_parse_expect_value() {\n    TEST_ERROR(LEPT_PARSE_EXPECT_VALUE, \"\");\n    TEST_ERROR(LEPT_PARSE_EXPECT_VALUE, \" \");\n}\n\nstatic void test_parse_invalid_value() {\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"nul\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"?\");\n\n    /* invalid number */\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"+0\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"+1\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \".123\"); /* at least one digit before '.' */\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"1.\");   /* at least one digit after '.' */\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"INF\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"inf\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"NAN\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"nan\");\n\n    /* invalid value in array */\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"[1,]\");\n    TEST_ERROR(LEPT_PARSE_INVALID_VALUE, \"[\\\"a\\\", nul]\");\n}\n\nstatic void test_parse_root_not_singular() {\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"null x\");\n\n    /* invalid number */\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0123\"); /* after zero should be '.' or nothing */\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0x0\");\n    TEST_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0x123\");\n}\n\nstatic void test_parse_number_too_big() {\n    TEST_ERROR(LEPT_PARSE_NUMBER_TOO_BIG, \"1e309\");\n    TEST_ERROR(LEPT_PARSE_NUMBER_TOO_BIG, \"-1e309\");\n}\n\nstatic void test_parse_miss_quotation_mark() {\n    TEST_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK, \"\\\"\");\n    TEST_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK, \"\\\"abc\");\n}\n\nstatic void test_parse_invalid_string_escape() {\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\v\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\'\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\0\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\x12\\\"\");\n}\n\nstatic void test_parse_invalid_string_char() {\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_CHAR, \"\\\"\\x01\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_STRING_CHAR, \"\\\"\\x1F\\\"\");\n}\n\nstatic void test_parse_invalid_unicode_hex() {\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u0\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u01\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u012\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u/000\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\uG000\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u0/00\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u0G00\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u00/0\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u00G0\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u000/\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u000G\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u 123\\\"\");\n}\n\nstatic void test_parse_invalid_unicode_surrogate() {\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uD800\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uDBFF\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uD800\\\\\\\\\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uD800\\\\uDBFF\\\"\");\n    TEST_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uD800\\\\uE000\\\"\");\n}\n\nstatic void test_parse_miss_comma_or_square_bracket() {\n    TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, \"[1\");\n    TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, \"[1}\");\n    TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, \"[1 2\");\n    TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, \"[[]\");\n}\n\nstatic void test_parse_miss_key() {\n    TEST_ERROR(LEPT_PARSE_MISS_KEY, \"{:1,\");\n    TEST_ERROR(LEPT_PARSE_MISS_KEY, \"{1:1,\");\n    TEST_ERROR(LEPT_PARSE_MISS_KEY, \"{true:1,\");\n    TEST_ERROR(LEPT_PARSE_MISS_KEY, \"{false:1,\");\n    TEST_ERROR(LEPT_PARSE_MISS_KEY, \"{null:1,\");\n    TEST_ERROR(LEPT_PARSE_MISS_KEY, \"{[]:1,\");\n    TEST_ERROR(LEPT_PARSE_MISS_KEY, \"{{}:1,\");\n    TEST_ERROR(LEPT_PARSE_MISS_KEY, \"{\\\"a\\\":1,\");\n}\n\nstatic void test_parse_miss_colon() {\n    TEST_ERROR(LEPT_PARSE_MISS_COLON, \"{\\\"a\\\"}\");\n    TEST_ERROR(LEPT_PARSE_MISS_COLON, \"{\\\"a\\\",\\\"b\\\"}\");\n}\n\nstatic void test_parse_miss_comma_or_curly_bracket() {\n    TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET, \"{\\\"a\\\":1\");\n    TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET, \"{\\\"a\\\":1]\");\n    TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET, \"{\\\"a\\\":1 \\\"b\\\"\");\n    TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET, \"{\\\"a\\\":{}\");\n}\n\nstatic void test_parse() {\n    test_parse_null();\n    test_parse_true();\n    test_parse_false();\n    test_parse_number();\n    test_parse_string();\n    test_parse_array();\n    test_parse_object();\n\n    test_parse_expect_value();\n    test_parse_invalid_value();\n    test_parse_root_not_singular();\n    test_parse_number_too_big();\n    test_parse_miss_quotation_mark();\n    test_parse_invalid_string_escape();\n    test_parse_invalid_string_char();\n    test_parse_invalid_unicode_hex();\n    test_parse_invalid_unicode_surrogate();\n    test_parse_miss_comma_or_square_bracket();\n    test_parse_miss_key();\n    test_parse_miss_colon();\n    test_parse_miss_comma_or_curly_bracket();\n}\n\nstatic void test_access_null() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"a\", 1);\n    lept_set_null(&v);\n    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\n    lept_free(&v);\n}\n\nstatic void test_access_boolean() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"a\", 1);\n    lept_set_boolean(&v, 1);\n    EXPECT_TRUE(lept_get_boolean(&v));\n    lept_set_boolean(&v, 0);\n    EXPECT_FALSE(lept_get_boolean(&v));\n    lept_free(&v);\n}\n\nstatic void test_access_number() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"a\", 1);\n    lept_set_number(&v, 1234.5);\n    EXPECT_EQ_DOUBLE(1234.5, lept_get_number(&v));\n    lept_free(&v);\n}\n\nstatic void test_access_string() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"\", 0);\n    EXPECT_EQ_STRING(\"\", lept_get_string(&v), lept_get_string_length(&v));\n    lept_set_string(&v, \"Hello\", 5);\n    EXPECT_EQ_STRING(\"Hello\", lept_get_string(&v), lept_get_string_length(&v));\n    lept_free(&v);\n}\n\nstatic void test_access() {\n    test_access_null();\n    test_access_boolean();\n    test_access_number();\n    test_access_string();\n}\n\nint main() {\n#ifdef _WINDOWS\n    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);\n#endif\n    test_parse();\n    test_access();\n    printf(\"%d/%d (%3.2f%%) passed\\n\", test_pass, test_count, test_pass * 100.0 / test_count);\n    return main_ret;\n}\n"
  },
  {
    "path": "tutorial06_answer/tutorial06_answer.md",
    "content": "# 从零开始的 JSON 库教程（六）：解析对象解答篇\n\n* Milo Yip\n* 2016/11/15\n\n本文是[《从零开始的 JSON 库教程》](https://zhuanlan.zhihu.com/json-tutorial)的第六个单元解答篇。解答代码位于 [json-tutorial/tutorial06_answer](https://github.com/miloyip/json-tutorial/blob/master/tutorial06_answer)。\n\n## 1. 重构 `lept_parse_string()`\n\n这个「提取方法」重构练习很简单，只需要把原来调用 `lept_set_string` 的地方，改为写入参数变量。因此，原来的 `lept_parse_string()` 和 答案中的 `lept_parse_string_raw()` 的 diff 仅是两处：\n\n~~~\n130,131c130,131\n< static int lept_parse_string(lept_context* c, lept_value* v) {\n<     size_t head = c->top, len;\n---\n> static int lept_parse_string_raw(lept_context* c, char** str, size_t* len) {\n>     size_t head = c->top;\n140,141c140,141\n<                 len = c->top - head;\n<                 lept_set_string(v, (const char*)lept_context_pop(c, len), len);\n---\n>                 *len = c->top - head;\n>                 *str = lept_context_pop(c, *len);\n~~~\n\n以 TDD 方式开发软件，因为有单元测试确保软件的正确性，面对新需求可以安心重构，改善软件架构。\n\n## 2. 实现 `lept_parse_object()`\n\n有了 `lept_parse_array()` 的经验，实现 `lept_parse_object()` 几乎是一样的，分别只是每个迭代要多处理一个键和冒号。我们把这个实现过程分为 5 步曲。\n\n第 1 步是利用刚才重构出来的 `lept_parse_string_raw()` 去解析键的字符串。由于 `lept_parse_string_raw()` 假设第一个字符为 `\"`，我们要先作校检，失败则要返回 `LEPT_PARSE_MISS_KEY` 错误。若字符串解析成功，它会把结果存储在我们的栈之中，需要把结果写入临时 `lept_member` 的 `k` 和 `klen` 字段中：\n\n~~~c\nstatic int lept_parse_object(lept_context* c, lept_value* v) {\n    size_t i, size;\n    lept_member m;\n    int ret;\n    /* ... */\n    m.k = NULL;\n    size = 0;\n    for (;;) {\n        char* str;\n        lept_init(&m.v);\n        /* 1. parse key */\n        if (*c->json != '\"') {\n            ret = LEPT_PARSE_MISS_KEY;\n            break;\n        }\n        if ((ret = lept_parse_string_raw(c, &str, &m.klen)) != LEPT_PARSE_OK)\n            break;\n        memcpy(m.k = (char*)malloc(m.klen + 1), str, m.klen);\n        m.k[m.klen] = '\\0';\n        /* 2. parse ws colon ws */\n        /* ... */\n    }\n    /* 5. Pop and free members on the stack */\n    /* ... */\n}\n~~~\n\n第 2 步是解析冒号，冒号前后可有空白字符：\n\n~~~c\n        /* 2. parse ws colon ws */\n        lept_parse_whitespace(c);\n        if (*c->json != ':') {\n            ret = LEPT_PARSE_MISS_COLON;\n            break;\n        }\n        c->json++;\n        lept_parse_whitespace(c);\n~~~\n\n第 3 步是解析任意的 JSON 值。这部分与解析数组一样，递归调用 `lept_parse_value()`，把结果写入临时 `lept_member` 的 `v` 字段，然后把整个 `lept_member` 压入栈：\n\n~~~c\n        /* 3. parse value */\n        if ((ret = lept_parse_value(c, &m.v)) != LEPT_PARSE_OK)\n            break;\n        memcpy(lept_context_push(c, sizeof(lept_member)), &m, sizeof(lept_member));\n        size++;\n        m.k = NULL; /* ownership is transferred to member on stack */\n~~~\n\n但有一点要注意，如果之前缺乏冒号，或是这里解析值失败，在函数返回前我们要释放 `m.k`。如果我们成功地解析整个成员，那么就要把 `m.k` 设为空指针，其意义是说明该键的字符串的拥有权已转移至栈，之后如遇到错误，我们不会重覆释放栈里成员的键和这个临时成员的键。\n\n第 4 步，解析逗号或右花括号。遇上右花括号的话，当前的 JSON 对象就解析完结了，我们把栈上的成员复制至结果，并直接返回：\n\n~~~c\n        /* 4. parse ws [comma | right-curly-brace] ws */\n        lept_parse_whitespace(c);\n        if (*c->json == ',') {\n            c->json++;\n            lept_parse_whitespace(c);\n        }\n        else if (*c->json == '}') {\n            size_t s = sizeof(lept_member) * size;\n            c->json++;\n            v->type = LEPT_OBJECT;\n            v->u.o.size = size;\n            memcpy(v->u.o.m = (lept_member*)malloc(s), lept_context_pop(c, s), s);\n            return LEPT_PARSE_OK;\n        }\n        else {\n            ret = LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET;\n            break;\n        }\n~~~\n\n最后，当 `for (;;)` 中遇到任何错误便会到达这第 5 步，要释放临时的 key 字符串及栈上的成员：\n\n~~~c\n    /* 5. Pop and free members on the stack */\n    free(m.k);\n    for (i = 0; i < size; i++) {\n        lept_member* m = (lept_member*)lept_context_pop(c, sizeof(lept_member));\n        free(m->k);\n        lept_free(&m->v);\n    }\n    v->type = LEPT_NULL;\n    return ret;\n~~~\n\n注意我们不需要先检查 `m.k != NULL`，因为 `free(NULL)` 是完全合法的。\n\n## 3. 释放内存\n\n使用工具检测内存泄漏时，我们会发现以下这行代码造成内存泄漏：\n\n~~~c\nmemcpy(v->u.o.m = (lept_member*)malloc(s), lept_context_pop(c, s), s);\n~~~\n\n类似数组，我们也需要在 `lept_free()` 释放 JSON 对象的成员（包括键及值）：\n\n~~~c\nvoid lept_free(lept_value* v) {\n    size_t i;\n    assert(v != NULL);\n    switch (v->type) {\n        /* ... */\n        case LEPT_OBJECT:\n            for (i = 0; i < v->u.o.size; i++) {\n                free(v->u.o.m[i].k);\n                lept_free(&v->u.o.m[i].v);\n            }\n            free(v->u.o.m);\n            break;\n        default: break;\n    }\n    v->type = LEPT_NULL;\n}\n~~~\n\n## 4. 总结\n\n至此，你已实现一个完整的 JSON 解析器，可解析任何合法的 JSON。统计一下，不计算空行及注释，现时 `leptjson.c` 只有 405 行代码，`lept_json.h` 54 行，`test.c` 309 行。\n\n另一方面，一些程序也需要生成 JSON。也许最初读者会以为生成 JSON 只需直接 `sprintf()/fprintf()` 就可以，但深入了解 JSON 的语法之后，我们应该知道 JSON 语法还是需做一些处理，例如字符串的转义、数字的格式等。然而，实现生成器还是要比解析器容易得多。而且，假设我们有一个正确的解析器，可以简单使用 roundtrip 方式实现测试。请期待下回分解。\n\n如果你遇到问题，有不理解的地方，或是有建议，都欢迎在评论或 [issue](https://github.com/miloyip/json-tutorial/issues) 中提出，让所有人一起讨论。\n"
  },
  {
    "path": "tutorial07/CMakeLists.txt",
    "content": "cmake_minimum_required (VERSION 2.6)\nproject (leptjson_test C)\n\nif (CMAKE_C_COMPILER_ID MATCHES \"GNU|Clang\")\n    set(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -ansi -pedantic -Wall\")\nendif()\n\nadd_library(leptjson leptjson.c)\nadd_executable(leptjson_test test.c)\ntarget_link_libraries(leptjson_test leptjson)\n"
  },
  {
    "path": "tutorial07/images/makefile",
    "content": "%.png: %.dot\n\tdot $< -Tpng -o $@\n\nDOTFILES = $(basename $(wildcard *.dot))\nall: $(addsuffix .png, $(DOTFILES))\n"
  },
  {
    "path": "tutorial07/images/parse_stringify.dot",
    "content": "digraph {\n    compound=true\n    fontname=\"Inconsolata, Consolas\"\n    fontsize=10\n    margin=\"0,0\"\n    ranksep=0.3\n    penwidth=0.5\n    \n    node [fontname=\"Inconsolata, Consolas\", fontsize=10, penwidth=0.5]\n    edge [fontname=\"Inconsolata, Consolas\", fontsize=10, arrowhead=normal]\n\n    {\n        node [shape=record, fontsize=\"8\", margin=\"0.04\", height=0.2, color=gray]\n        json [label=\"\\{|\\\"|p|r|o|j|e|c|t|\\\"|:|\\\"|l|e|p|t|j|s|o|n|\\\"|,|\\\"|s|t|a|r|s|\\\"|:|1|0|\\}\"]\n    }\n    \n    subgraph cluster1 {\n        margin=\"10,10\"\n        labeljust=\"left\"\n        label = \"lept_value tree\"\n        style=filled\n        fillcolor=gray95\n        node [shape=Mrecord, style=filled, colorscheme=spectral7]\n        \n        root [label=\"{object|}\", fillcolor=3]\n\n        {           \n            project [label=\"{key|\\\"project\\\"}\", fillcolor=5]\n            leptjson [label=\"{string|\\\"leptjson\\\"}\", fillcolor=5]\n            stars [label=\"{key|\\\"stars\\\"}\", fillcolor=5]\n            ten [label=\"{number|10}\", fillcolor=6]\n        }\n\n        edge [arrowhead=vee]\n        root -> { project; stars }\n\n        edge [arrowhead=\"none\"]\n        project -> leptjson\n        stars -> ten\n    }\n\n    json -> root [label=\" lept_parse()   \", lhead=\"cluster1\"]\n    root -> json [label=\" lept_stringify() \", ltail=\"cluster1\"]\n}"
  },
  {
    "path": "tutorial07/leptjson.c",
    "content": "#ifdef _WINDOWS\n#define _CRTDBG_MAP_ALLOC\n#include <crtdbg.h>\n#endif\n#include \"leptjson.h\"\n#include <assert.h>  /* assert() */\n#include <errno.h>   /* errno, ERANGE */\n#include <math.h>    /* HUGE_VAL */\n#include <stdio.h>   /* sprintf() */\n#include <stdlib.h>  /* NULL, malloc(), realloc(), free(), strtod() */\n#include <string.h>  /* memcpy() */\n\n#ifndef LEPT_PARSE_STACK_INIT_SIZE\n#define LEPT_PARSE_STACK_INIT_SIZE 256\n#endif\n\n#ifndef LEPT_PARSE_STRINGIFY_INIT_SIZE\n#define LEPT_PARSE_STRINGIFY_INIT_SIZE 256\n#endif\n\n#define EXPECT(c, ch)       do { assert(*c->json == (ch)); c->json++; } while(0)\n#define ISDIGIT(ch)         ((ch) >= '0' && (ch) <= '9')\n#define ISDIGIT1TO9(ch)     ((ch) >= '1' && (ch) <= '9')\n#define PUTC(c, ch)         do { *(char*)lept_context_push(c, sizeof(char)) = (ch); } while(0)\n#define PUTS(c, s, len)     memcpy(lept_context_push(c, len), s, len)\n\ntypedef struct {\n    const char* json;\n    char* stack;\n    size_t size, top;\n}lept_context;\n\nstatic void* lept_context_push(lept_context* c, size_t size) {\n    void* ret;\n    assert(size > 0);\n    if (c->top + size >= c->size) {\n        if (c->size == 0)\n            c->size = LEPT_PARSE_STACK_INIT_SIZE;\n        while (c->top + size >= c->size)\n            c->size += c->size >> 1;  /* c->size * 1.5 */\n        c->stack = (char*)realloc(c->stack, c->size);\n    }\n    ret = c->stack + c->top;\n    c->top += size;\n    return ret;\n}\n\nstatic void* lept_context_pop(lept_context* c, size_t size) {\n    assert(c->top >= size);\n    return c->stack + (c->top -= size);\n}\n\nstatic void lept_parse_whitespace(lept_context* c) {\n    const char *p = c->json;\n    while (*p == ' ' || *p == '\\t' || *p == '\\n' || *p == '\\r')\n        p++;\n    c->json = p;\n}\n\nstatic int lept_parse_literal(lept_context* c, lept_value* v, const char* literal, lept_type type) {\n    size_t i;\n    EXPECT(c, literal[0]);\n    for (i = 0; literal[i + 1]; i++)\n        if (c->json[i] != literal[i + 1])\n            return LEPT_PARSE_INVALID_VALUE;\n    c->json += i;\n    v->type = type;\n    return LEPT_PARSE_OK;\n}\n\nstatic int lept_parse_number(lept_context* c, lept_value* v) {\n    const char* p = c->json;\n    if (*p == '-') p++;\n    if (*p == '0') p++;\n    else {\n        if (!ISDIGIT1TO9(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n    if (*p == '.') {\n        p++;\n        if (!ISDIGIT(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n    if (*p == 'e' || *p == 'E') {\n        p++;\n        if (*p == '+' || *p == '-') p++;\n        if (!ISDIGIT(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n    errno = 0;\n    v->u.n = strtod(c->json, NULL);\n    if (errno == ERANGE && (v->u.n == HUGE_VAL || v->u.n == -HUGE_VAL))\n        return LEPT_PARSE_NUMBER_TOO_BIG;\n    v->type = LEPT_NUMBER;\n    c->json = p;\n    return LEPT_PARSE_OK;\n}\n\nstatic const char* lept_parse_hex4(const char* p, unsigned* u) {\n    int i;\n    *u = 0;\n    for (i = 0; i < 4; i++) {\n        char ch = *p++;\n        *u <<= 4;\n        if      (ch >= '0' && ch <= '9')  *u |= ch - '0';\n        else if (ch >= 'A' && ch <= 'F')  *u |= ch - ('A' - 10);\n        else if (ch >= 'a' && ch <= 'f')  *u |= ch - ('a' - 10);\n        else return NULL;\n    }\n    return p;\n}\n\nstatic void lept_encode_utf8(lept_context* c, unsigned u) {\n    if (u <= 0x7F) \n        PUTC(c, u & 0xFF);\n    else if (u <= 0x7FF) {\n        PUTC(c, 0xC0 | ((u >> 6) & 0xFF));\n        PUTC(c, 0x80 | ( u       & 0x3F));\n    }\n    else if (u <= 0xFFFF) {\n        PUTC(c, 0xE0 | ((u >> 12) & 0xFF));\n        PUTC(c, 0x80 | ((u >>  6) & 0x3F));\n        PUTC(c, 0x80 | ( u        & 0x3F));\n    }\n    else {\n        assert(u <= 0x10FFFF);\n        PUTC(c, 0xF0 | ((u >> 18) & 0xFF));\n        PUTC(c, 0x80 | ((u >> 12) & 0x3F));\n        PUTC(c, 0x80 | ((u >>  6) & 0x3F));\n        PUTC(c, 0x80 | ( u        & 0x3F));\n    }\n}\n\n#define STRING_ERROR(ret) do { c->top = head; return ret; } while(0)\n\nstatic int lept_parse_string_raw(lept_context* c, char** str, size_t* len) {\n    size_t head = c->top;\n    unsigned u, u2;\n    const char* p;\n    EXPECT(c, '\\\"');\n    p = c->json;\n    for (;;) {\n        char ch = *p++;\n        switch (ch) {\n            case '\\\"':\n                *len = c->top - head;\n                *str = lept_context_pop(c, *len);\n                c->json = p;\n                return LEPT_PARSE_OK;\n            case '\\\\':\n                switch (*p++) {\n                    case '\\\"': PUTC(c, '\\\"'); break;\n                    case '\\\\': PUTC(c, '\\\\'); break;\n                    case '/':  PUTC(c, '/' ); break;\n                    case 'b':  PUTC(c, '\\b'); break;\n                    case 'f':  PUTC(c, '\\f'); break;\n                    case 'n':  PUTC(c, '\\n'); break;\n                    case 'r':  PUTC(c, '\\r'); break;\n                    case 't':  PUTC(c, '\\t'); break;\n                    case 'u':\n                        if (!(p = lept_parse_hex4(p, &u)))\n                            STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX);\n                        if (u >= 0xD800 && u <= 0xDBFF) { /* surrogate pair */\n                            if (*p++ != '\\\\')\n                                STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE);\n                            if (*p++ != 'u')\n                                STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE);\n                            if (!(p = lept_parse_hex4(p, &u2)))\n                                STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX);\n                            if (u2 < 0xDC00 || u2 > 0xDFFF)\n                                STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE);\n                            u = (((u - 0xD800) << 10) | (u2 - 0xDC00)) + 0x10000;\n                        }\n                        lept_encode_utf8(c, u);\n                        break;\n                    default:\n                        STRING_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE);\n                }\n                break;\n            case '\\0':\n                STRING_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK);\n            default:\n                if ((unsigned char)ch < 0x20)\n                    STRING_ERROR(LEPT_PARSE_INVALID_STRING_CHAR);\n                PUTC(c, ch);\n        }\n    }\n}\n\nstatic int lept_parse_string(lept_context* c, lept_value* v) {\n    int ret;\n    char* s;\n    size_t len;\n    if ((ret = lept_parse_string_raw(c, &s, &len)) == LEPT_PARSE_OK)\n        lept_set_string(v, s, len);\n    return ret;\n}\n\nstatic int lept_parse_value(lept_context* c, lept_value* v);\n\nstatic int lept_parse_array(lept_context* c, lept_value* v) {\n    size_t i, size = 0;\n    int ret;\n    EXPECT(c, '[');\n    lept_parse_whitespace(c);\n    if (*c->json == ']') {\n        c->json++;\n        v->type = LEPT_ARRAY;\n        v->u.a.size = 0;\n        v->u.a.e = NULL;\n        return LEPT_PARSE_OK;\n    }\n    for (;;) {\n        lept_value e;\n        lept_init(&e);\n        if ((ret = lept_parse_value(c, &e)) != LEPT_PARSE_OK)\n            break;\n        memcpy(lept_context_push(c, sizeof(lept_value)), &e, sizeof(lept_value));\n        size++;\n        lept_parse_whitespace(c);\n        if (*c->json == ',') {\n            c->json++;\n            lept_parse_whitespace(c);\n        }\n        else if (*c->json == ']') {\n            c->json++;\n            v->type = LEPT_ARRAY;\n            v->u.a.size = size;\n            size *= sizeof(lept_value);\n            memcpy(v->u.a.e = (lept_value*)malloc(size), lept_context_pop(c, size), size);\n            return LEPT_PARSE_OK;\n        }\n        else {\n            ret = LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET;\n            break;\n        }\n    }\n    /* Pop and free values on the stack */\n    for (i = 0; i < size; i++)\n        lept_free((lept_value*)lept_context_pop(c, sizeof(lept_value)));\n    return ret;\n}\n\nstatic int lept_parse_object(lept_context* c, lept_value* v) {\n    size_t i, size;\n    lept_member m;\n    int ret;\n    EXPECT(c, '{');\n    lept_parse_whitespace(c);\n    if (*c->json == '}') {\n        c->json++;\n        v->type = LEPT_OBJECT;\n        v->u.o.m = 0;\n        v->u.o.size = 0;\n        return LEPT_PARSE_OK;\n    }\n    m.k = NULL;\n    size = 0;\n    for (;;) {\n        char* str;\n        lept_init(&m.v);\n        /* parse key */\n        if (*c->json != '\"') {\n            ret = LEPT_PARSE_MISS_KEY;\n            break;\n        }\n        if ((ret = lept_parse_string_raw(c, &str, &m.klen)) != LEPT_PARSE_OK)\n            break;\n        memcpy(m.k = (char*)malloc(m.klen + 1), str, m.klen);\n        m.k[m.klen] = '\\0';\n        /* parse ws colon ws */\n        lept_parse_whitespace(c);\n        if (*c->json != ':') {\n            ret = LEPT_PARSE_MISS_COLON;\n            break;\n        }\n        c->json++;\n        lept_parse_whitespace(c);\n        /* parse value */\n        if ((ret = lept_parse_value(c, &m.v)) != LEPT_PARSE_OK)\n            break;\n        memcpy(lept_context_push(c, sizeof(lept_member)), &m, sizeof(lept_member));\n        size++;\n        m.k = NULL; /* ownership is transferred to member on stack */\n        /* parse ws [comma | right-curly-brace] ws */\n        lept_parse_whitespace(c);\n        if (*c->json == ',') {\n            c->json++;\n            lept_parse_whitespace(c);\n        }\n        else if (*c->json == '}') {\n            size_t s = sizeof(lept_member) * size;\n            c->json++;\n            v->type = LEPT_OBJECT;\n            v->u.o.size = size;\n            memcpy(v->u.o.m = (lept_member*)malloc(s), lept_context_pop(c, s), s);\n            return LEPT_PARSE_OK;\n        }\n        else {\n            ret = LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET;\n            break;\n        }\n    }\n    /* Pop and free members on the stack */\n    free(m.k);\n    for (i = 0; i < size; i++) {\n        lept_member* m = (lept_member*)lept_context_pop(c, sizeof(lept_member));\n        free(m->k);\n        lept_free(&m->v);\n    }\n    v->type = LEPT_NULL;\n    return ret;\n}\n\nstatic int lept_parse_value(lept_context* c, lept_value* v) {\n    switch (*c->json) {\n        case 't':  return lept_parse_literal(c, v, \"true\", LEPT_TRUE);\n        case 'f':  return lept_parse_literal(c, v, \"false\", LEPT_FALSE);\n        case 'n':  return lept_parse_literal(c, v, \"null\", LEPT_NULL);\n        default:   return lept_parse_number(c, v);\n        case '\"':  return lept_parse_string(c, v);\n        case '[':  return lept_parse_array(c, v);\n        case '{':  return lept_parse_object(c, v);\n        case '\\0': return LEPT_PARSE_EXPECT_VALUE;\n    }\n}\n\nint lept_parse(lept_value* v, const char* json) {\n    lept_context c;\n    int ret;\n    assert(v != NULL);\n    c.json = json;\n    c.stack = NULL;\n    c.size = c.top = 0;\n    lept_init(v);\n    lept_parse_whitespace(&c);\n    if ((ret = lept_parse_value(&c, v)) == LEPT_PARSE_OK) {\n        lept_parse_whitespace(&c);\n        if (*c.json != '\\0') {\n            v->type = LEPT_NULL;\n            ret = LEPT_PARSE_ROOT_NOT_SINGULAR;\n        }\n    }\n    assert(c.top == 0);\n    free(c.stack);\n    return ret;\n}\n\nstatic void lept_stringify_string(lept_context* c, const char* s, size_t len) {\n    /* ... */\n}\n\nstatic void lept_stringify_value(lept_context* c, const lept_value* v) {\n    switch (v->type) {\n        case LEPT_NULL:   PUTS(c, \"null\",  4); break;\n        case LEPT_FALSE:  PUTS(c, \"false\", 5); break;\n        case LEPT_TRUE:   PUTS(c, \"true\",  4); break;\n        case LEPT_NUMBER: c->top -= 32 - sprintf(lept_context_push(c, 32), \"%.17g\", v->u.n); break;\n        case LEPT_STRING: lept_stringify_string(c, v->u.s.s, v->u.s.len); break;\n        case LEPT_ARRAY:\n            /* ... */\n            break;\n        case LEPT_OBJECT:\n            /* ... */\n            break;\n        default: assert(0 && \"invalid type\");\n    }\n}\n\nchar* lept_stringify(const lept_value* v, size_t* length) {\n    lept_context c;\n    assert(v != NULL);\n    c.stack = (char*)malloc(c.size = LEPT_PARSE_STRINGIFY_INIT_SIZE);\n    c.top = 0;\n    lept_stringify_value(&c, v);\n    if (length)\n        *length = c.top;\n    PUTC(&c, '\\0');\n    return c.stack;\n}\n\nvoid lept_free(lept_value* v) {\n    size_t i;\n    assert(v != NULL);\n    switch (v->type) {\n        case LEPT_STRING:\n            free(v->u.s.s);\n            break;\n        case LEPT_ARRAY:\n            for (i = 0; i < v->u.a.size; i++)\n                lept_free(&v->u.a.e[i]);\n            free(v->u.a.e);\n            break;\n        case LEPT_OBJECT:\n            for (i = 0; i < v->u.o.size; i++) {\n                free(v->u.o.m[i].k);\n                lept_free(&v->u.o.m[i].v);\n            }\n            free(v->u.o.m);\n            break;\n        default: break;\n    }\n    v->type = LEPT_NULL;\n}\n\nlept_type lept_get_type(const lept_value* v) {\n    assert(v != NULL);\n    return v->type;\n}\n\nint lept_get_boolean(const lept_value* v) {\n    assert(v != NULL && (v->type == LEPT_TRUE || v->type == LEPT_FALSE));\n    return v->type == LEPT_TRUE;\n}\n\nvoid lept_set_boolean(lept_value* v, int b) {\n    lept_free(v);\n    v->type = b ? LEPT_TRUE : LEPT_FALSE;\n}\n\ndouble lept_get_number(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_NUMBER);\n    return v->u.n;\n}\n\nvoid lept_set_number(lept_value* v, double n) {\n    lept_free(v);\n    v->u.n = n;\n    v->type = LEPT_NUMBER;\n}\n\nconst char* lept_get_string(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_STRING);\n    return v->u.s.s;\n}\n\nsize_t lept_get_string_length(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_STRING);\n    return v->u.s.len;\n}\n\nvoid lept_set_string(lept_value* v, const char* s, size_t len) {\n    assert(v != NULL && (s != NULL || len == 0));\n    lept_free(v);\n    v->u.s.s = (char*)malloc(len + 1);\n    memcpy(v->u.s.s, s, len);\n    v->u.s.s[len] = '\\0';\n    v->u.s.len = len;\n    v->type = LEPT_STRING;\n}\n\nsize_t lept_get_array_size(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_ARRAY);\n    return v->u.a.size;\n}\n\nlept_value* lept_get_array_element(const lept_value* v, size_t index) {\n    assert(v != NULL && v->type == LEPT_ARRAY);\n    assert(index < v->u.a.size);\n    return &v->u.a.e[index];\n}\n\nsize_t lept_get_object_size(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_OBJECT);\n    return v->u.o.size;\n}\n\nconst char* lept_get_object_key(const lept_value* v, size_t index) {\n    assert(v != NULL && v->type == LEPT_OBJECT);\n    assert(index < v->u.o.size);\n    return v->u.o.m[index].k;\n}\n\nsize_t lept_get_object_key_length(const lept_value* v, size_t index) {\n    assert(v != NULL && v->type == LEPT_OBJECT);\n    assert(index < v->u.o.size);\n    return v->u.o.m[index].klen;\n}\n\nlept_value* lept_get_object_value(const lept_value* v, size_t index) {\n    assert(v != NULL && v->type == LEPT_OBJECT);\n    assert(index < v->u.o.size);\n    return &v->u.o.m[index].v;\n}\n"
  },
  {
    "path": "tutorial07/leptjson.h",
    "content": "#ifndef LEPTJSON_H__\n#define LEPTJSON_H__\n\n#include <stddef.h> /* size_t */\n\ntypedef enum { LEPT_NULL, LEPT_FALSE, LEPT_TRUE, LEPT_NUMBER, LEPT_STRING, LEPT_ARRAY, LEPT_OBJECT } lept_type;\n\ntypedef struct lept_value lept_value;\ntypedef struct lept_member lept_member;\n\nstruct lept_value {\n    union {\n        struct { lept_member* m; size_t size; }o;   /* object: members, member count */\n        struct { lept_value* e; size_t size; }a;    /* array:  elements, element count */\n        struct { char* s; size_t len; }s;           /* string: null-terminated string, string length */\n        double n;                                   /* number */\n    }u;\n    lept_type type;\n};\n\nstruct lept_member {\n    char* k; size_t klen;   /* member key string, key string length */\n    lept_value v;           /* member value */\n};\n\nenum {\n    LEPT_PARSE_OK = 0,\n    LEPT_PARSE_EXPECT_VALUE,\n    LEPT_PARSE_INVALID_VALUE,\n    LEPT_PARSE_ROOT_NOT_SINGULAR,\n    LEPT_PARSE_NUMBER_TOO_BIG,\n    LEPT_PARSE_MISS_QUOTATION_MARK,\n    LEPT_PARSE_INVALID_STRING_ESCAPE,\n    LEPT_PARSE_INVALID_STRING_CHAR,\n    LEPT_PARSE_INVALID_UNICODE_HEX,\n    LEPT_PARSE_INVALID_UNICODE_SURROGATE,\n    LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET,\n    LEPT_PARSE_MISS_KEY,\n    LEPT_PARSE_MISS_COLON,\n    LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET\n};\n\n#define lept_init(v) do { (v)->type = LEPT_NULL; } while(0)\n\nint lept_parse(lept_value* v, const char* json);\nchar* lept_stringify(const lept_value* v, size_t* length);\n\nvoid lept_free(lept_value* v);\n\nlept_type lept_get_type(const lept_value* v);\n\n#define lept_set_null(v) lept_free(v)\n\nint lept_get_boolean(const lept_value* v);\nvoid lept_set_boolean(lept_value* v, int b);\n\ndouble lept_get_number(const lept_value* v);\nvoid lept_set_number(lept_value* v, double n);\n\nconst char* lept_get_string(const lept_value* v);\nsize_t lept_get_string_length(const lept_value* v);\nvoid lept_set_string(lept_value* v, const char* s, size_t len);\n\nsize_t lept_get_array_size(const lept_value* v);\nlept_value* lept_get_array_element(const lept_value* v, size_t index);\n\nsize_t lept_get_object_size(const lept_value* v);\nconst char* lept_get_object_key(const lept_value* v, size_t index);\nsize_t lept_get_object_key_length(const lept_value* v, size_t index);\nlept_value* lept_get_object_value(const lept_value* v, size_t index);\n\n#endif /* LEPTJSON_H__ */\n"
  },
  {
    "path": "tutorial07/test.c",
    "content": "#ifdef _WINDOWS\n#define _CRTDBG_MAP_ALLOC\n#include <crtdbg.h>\n#endif\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"leptjson.h\"\n\nstatic int main_ret = 0;\nstatic int test_count = 0;\nstatic int test_pass = 0;\n\n#define EXPECT_EQ_BASE(equality, expect, actual, format) \\\n    do {\\\n        test_count++;\\\n        if (equality)\\\n            test_pass++;\\\n        else {\\\n            fprintf(stderr, \"%s:%d: expect: \" format \" actual: \" format \"\\n\", __FILE__, __LINE__, expect, actual);\\\n            main_ret = 1;\\\n        }\\\n    } while(0)\n\n#define EXPECT_EQ_INT(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, \"%d\")\n#define EXPECT_EQ_DOUBLE(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, \"%.17g\")\n#define EXPECT_EQ_STRING(expect, actual, alength) \\\n    EXPECT_EQ_BASE(sizeof(expect) - 1 == alength && memcmp(expect, actual, alength + 1) == 0, expect, actual, \"%s\")\n#define EXPECT_TRUE(actual) EXPECT_EQ_BASE((actual) != 0, \"true\", \"false\", \"%s\")\n#define EXPECT_FALSE(actual) EXPECT_EQ_BASE((actual) == 0, \"false\", \"true\", \"%s\")\n\n#if defined(_MSC_VER)\n#define EXPECT_EQ_SIZE_T(expect, actual) EXPECT_EQ_BASE((expect) == (actual), (size_t)expect, (size_t)actual, \"%Iu\")\n#else\n#define EXPECT_EQ_SIZE_T(expect, actual) EXPECT_EQ_BASE((expect) == (actual), (size_t)expect, (size_t)actual, \"%zu\")\n#endif\n\nstatic void test_parse_null() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_boolean(&v, 0);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"null\"));\n    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\n    lept_free(&v);\n}\n\nstatic void test_parse_true() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_boolean(&v, 0);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"true\"));\n    EXPECT_EQ_INT(LEPT_TRUE, lept_get_type(&v));\n    lept_free(&v);\n}\n\nstatic void test_parse_false() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_boolean(&v, 1);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"false\"));\n    EXPECT_EQ_INT(LEPT_FALSE, lept_get_type(&v));\n    lept_free(&v);\n}\n\n#define TEST_NUMBER(expect, json)\\\n    do {\\\n        lept_value v;\\\n        lept_init(&v);\\\n        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(&v));\\\n        EXPECT_EQ_DOUBLE(expect, lept_get_number(&v));\\\n        lept_free(&v);\\\n    } while(0)\n\nstatic void test_parse_number() {\n    TEST_NUMBER(0.0, \"0\");\n    TEST_NUMBER(0.0, \"-0\");\n    TEST_NUMBER(0.0, \"-0.0\");\n    TEST_NUMBER(1.0, \"1\");\n    TEST_NUMBER(-1.0, \"-1\");\n    TEST_NUMBER(1.5, \"1.5\");\n    TEST_NUMBER(-1.5, \"-1.5\");\n    TEST_NUMBER(3.1416, \"3.1416\");\n    TEST_NUMBER(1E10, \"1E10\");\n    TEST_NUMBER(1e10, \"1e10\");\n    TEST_NUMBER(1E+10, \"1E+10\");\n    TEST_NUMBER(1E-10, \"1E-10\");\n    TEST_NUMBER(-1E10, \"-1E10\");\n    TEST_NUMBER(-1e10, \"-1e10\");\n    TEST_NUMBER(-1E+10, \"-1E+10\");\n    TEST_NUMBER(-1E-10, \"-1E-10\");\n    TEST_NUMBER(1.234E+10, \"1.234E+10\");\n    TEST_NUMBER(1.234E-10, \"1.234E-10\");\n    TEST_NUMBER(0.0, \"1e-10000\"); /* must underflow */\n\n    TEST_NUMBER(1.0000000000000002, \"1.0000000000000002\"); /* the smallest number > 1 */\n    TEST_NUMBER( 4.9406564584124654e-324, \"4.9406564584124654e-324\"); /* minimum denormal */\n    TEST_NUMBER(-4.9406564584124654e-324, \"-4.9406564584124654e-324\");\n    TEST_NUMBER( 2.2250738585072009e-308, \"2.2250738585072009e-308\");  /* Max subnormal double */\n    TEST_NUMBER(-2.2250738585072009e-308, \"-2.2250738585072009e-308\");\n    TEST_NUMBER( 2.2250738585072014e-308, \"2.2250738585072014e-308\");  /* Min normal positive double */\n    TEST_NUMBER(-2.2250738585072014e-308, \"-2.2250738585072014e-308\");\n    TEST_NUMBER( 1.7976931348623157e+308, \"1.7976931348623157e+308\");  /* Max double */\n    TEST_NUMBER(-1.7976931348623157e+308, \"-1.7976931348623157e+308\");\n}\n\n#define TEST_STRING(expect, json)\\\n    do {\\\n        lept_value v;\\\n        lept_init(&v);\\\n        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_STRING, lept_get_type(&v));\\\n        EXPECT_EQ_STRING(expect, lept_get_string(&v), lept_get_string_length(&v));\\\n        lept_free(&v);\\\n    } while(0)\n\nstatic void test_parse_string() {\n    TEST_STRING(\"\", \"\\\"\\\"\");\n    TEST_STRING(\"Hello\", \"\\\"Hello\\\"\");\n    TEST_STRING(\"Hello\\nWorld\", \"\\\"Hello\\\\nWorld\\\"\");\n    TEST_STRING(\"\\\" \\\\ / \\b \\f \\n \\r \\t\", \"\\\"\\\\\\\" \\\\\\\\ \\\\/ \\\\b \\\\f \\\\n \\\\r \\\\t\\\"\");\n    TEST_STRING(\"Hello\\0World\", \"\\\"Hello\\\\u0000World\\\"\");\n    TEST_STRING(\"\\x24\", \"\\\"\\\\u0024\\\"\");         /* Dollar sign U+0024 */\n    TEST_STRING(\"\\xC2\\xA2\", \"\\\"\\\\u00A2\\\"\");     /* Cents sign U+00A2 */\n    TEST_STRING(\"\\xE2\\x82\\xAC\", \"\\\"\\\\u20AC\\\"\"); /* Euro sign U+20AC */\n    TEST_STRING(\"\\xF0\\x9D\\x84\\x9E\", \"\\\"\\\\uD834\\\\uDD1E\\\"\");  /* G clef sign U+1D11E */\n    TEST_STRING(\"\\xF0\\x9D\\x84\\x9E\", \"\\\"\\\\ud834\\\\udd1e\\\"\");  /* G clef sign U+1D11E */\n}\n\nstatic void test_parse_array() {\n    size_t i, j;\n    lept_value v;\n\n    lept_init(&v);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"[ ]\"));\n    EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(&v));\n    EXPECT_EQ_SIZE_T(0, lept_get_array_size(&v));\n    lept_free(&v);\n\n    lept_init(&v);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"[ null , false , true , 123 , \\\"abc\\\" ]\"));\n    EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(&v));\n    EXPECT_EQ_SIZE_T(5, lept_get_array_size(&v));\n    EXPECT_EQ_INT(LEPT_NULL,   lept_get_type(lept_get_array_element(&v, 0)));\n    EXPECT_EQ_INT(LEPT_FALSE,  lept_get_type(lept_get_array_element(&v, 1)));\n    EXPECT_EQ_INT(LEPT_TRUE,   lept_get_type(lept_get_array_element(&v, 2)));\n    EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(lept_get_array_element(&v, 3)));\n    EXPECT_EQ_INT(LEPT_STRING, lept_get_type(lept_get_array_element(&v, 4)));\n    EXPECT_EQ_DOUBLE(123.0, lept_get_number(lept_get_array_element(&v, 3)));\n    EXPECT_EQ_STRING(\"abc\", lept_get_string(lept_get_array_element(&v, 4)), lept_get_string_length(lept_get_array_element(&v, 4)));\n    lept_free(&v);\n\n    lept_init(&v);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"[ [ ] , [ 0 ] , [ 0 , 1 ] , [ 0 , 1 , 2 ] ]\"));\n    EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(&v));\n    EXPECT_EQ_SIZE_T(4, lept_get_array_size(&v));\n    for (i = 0; i < 4; i++) {\n        lept_value* a = lept_get_array_element(&v, i);\n        EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(a));\n        EXPECT_EQ_SIZE_T(i, lept_get_array_size(a));\n        for (j = 0; j < i; j++) {\n            lept_value* e = lept_get_array_element(a, j);\n            EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(e));\n            EXPECT_EQ_DOUBLE((double)j, lept_get_number(e));\n        }\n    }\n    lept_free(&v);\n}\n\nstatic void test_parse_object() {\n    lept_value v;\n    size_t i;\n\n    lept_init(&v);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \" { } \"));\n    EXPECT_EQ_INT(LEPT_OBJECT, lept_get_type(&v));\n    EXPECT_EQ_SIZE_T(0, lept_get_object_size(&v));\n    lept_free(&v);\n\n    lept_init(&v);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v,\n        \" { \"\n        \"\\\"n\\\" : null , \"\n        \"\\\"f\\\" : false , \"\n        \"\\\"t\\\" : true , \"\n        \"\\\"i\\\" : 123 , \"\n        \"\\\"s\\\" : \\\"abc\\\", \"\n        \"\\\"a\\\" : [ 1, 2, 3 ],\"\n        \"\\\"o\\\" : { \\\"1\\\" : 1, \\\"2\\\" : 2, \\\"3\\\" : 3 }\"\n        \" } \"\n    ));\n    EXPECT_EQ_INT(LEPT_OBJECT, lept_get_type(&v));\n    EXPECT_EQ_SIZE_T(7, lept_get_object_size(&v));\n    EXPECT_EQ_STRING(\"n\", lept_get_object_key(&v, 0), lept_get_object_key_length(&v, 0));\n    EXPECT_EQ_INT(LEPT_NULL,   lept_get_type(lept_get_object_value(&v, 0)));\n    EXPECT_EQ_STRING(\"f\", lept_get_object_key(&v, 1), lept_get_object_key_length(&v, 1));\n    EXPECT_EQ_INT(LEPT_FALSE,  lept_get_type(lept_get_object_value(&v, 1)));\n    EXPECT_EQ_STRING(\"t\", lept_get_object_key(&v, 2), lept_get_object_key_length(&v, 2));\n    EXPECT_EQ_INT(LEPT_TRUE,   lept_get_type(lept_get_object_value(&v, 2)));\n    EXPECT_EQ_STRING(\"i\", lept_get_object_key(&v, 3), lept_get_object_key_length(&v, 3));\n    EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(lept_get_object_value(&v, 3)));\n    EXPECT_EQ_DOUBLE(123.0, lept_get_number(lept_get_object_value(&v, 3)));\n    EXPECT_EQ_STRING(\"s\", lept_get_object_key(&v, 4), lept_get_object_key_length(&v, 4));\n    EXPECT_EQ_INT(LEPT_STRING, lept_get_type(lept_get_object_value(&v, 4)));\n    EXPECT_EQ_STRING(\"abc\", lept_get_string(lept_get_object_value(&v, 4)), lept_get_string_length(lept_get_object_value(&v, 4)));\n    EXPECT_EQ_STRING(\"a\", lept_get_object_key(&v, 5), lept_get_object_key_length(&v, 5));\n    EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(lept_get_object_value(&v, 5)));\n    EXPECT_EQ_SIZE_T(3, lept_get_array_size(lept_get_object_value(&v, 5)));\n    for (i = 0; i < 3; i++) {\n        lept_value* e = lept_get_array_element(lept_get_object_value(&v, 5), i);\n        EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(e));\n        EXPECT_EQ_DOUBLE(i + 1.0, lept_get_number(e));\n    }\n    EXPECT_EQ_STRING(\"o\", lept_get_object_key(&v, 6), lept_get_object_key_length(&v, 6));\n    {\n        lept_value* o = lept_get_object_value(&v, 6);\n        EXPECT_EQ_INT(LEPT_OBJECT, lept_get_type(o));\n        for (i = 0; i < 3; i++) {\n            lept_value* ov = lept_get_object_value(o, i);\n            EXPECT_TRUE('1' + i == lept_get_object_key(o, i)[0]);\n            EXPECT_EQ_SIZE_T(1, lept_get_object_key_length(o, i));\n            EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(ov));\n            EXPECT_EQ_DOUBLE(i + 1.0, lept_get_number(ov));\n        }\n    }\n    lept_free(&v);\n}\n\n#define TEST_PARSE_ERROR(error, json)\\\n    do {\\\n        lept_value v;\\\n        lept_init(&v);\\\n        v.type = LEPT_FALSE;\\\n        EXPECT_EQ_INT(error, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\\\n        lept_free(&v);\\\n    } while(0)\n\nstatic void test_parse_expect_value() {\n    TEST_PARSE_ERROR(LEPT_PARSE_EXPECT_VALUE, \"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_EXPECT_VALUE, \" \");\n}\n\nstatic void test_parse_invalid_value() {\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, \"nul\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, \"?\");\n\n    /* invalid number */\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, \"+0\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, \"+1\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, \".123\"); /* at least one digit before '.' */\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, \"1.\");   /* at least one digit after '.' */\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, \"INF\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, \"inf\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, \"NAN\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, \"nan\");\n\n    /* invalid value in array */\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, \"[1,]\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, \"[\\\"a\\\", nul]\");\n}\n\nstatic void test_parse_root_not_singular() {\n    TEST_PARSE_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"null x\");\n\n    /* invalid number */\n    TEST_PARSE_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0123\"); /* after zero should be '.' or nothing */\n    TEST_PARSE_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0x0\");\n    TEST_PARSE_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0x123\");\n}\n\nstatic void test_parse_number_too_big() {\n    TEST_PARSE_ERROR(LEPT_PARSE_NUMBER_TOO_BIG, \"1e309\");\n    TEST_PARSE_ERROR(LEPT_PARSE_NUMBER_TOO_BIG, \"-1e309\");\n}\n\nstatic void test_parse_miss_quotation_mark() {\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK, \"\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK, \"\\\"abc\");\n}\n\nstatic void test_parse_invalid_string_escape() {\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\v\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\'\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\0\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\x12\\\"\");\n}\n\nstatic void test_parse_invalid_string_char() {\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_STRING_CHAR, \"\\\"\\x01\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_STRING_CHAR, \"\\\"\\x1F\\\"\");\n}\n\nstatic void test_parse_invalid_unicode_hex() {\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u0\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u01\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u012\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u/000\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\uG000\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u0/00\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u0G00\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u0/00\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u00G0\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u000/\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u000G\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u 123\\\"\");\n}\n\nstatic void test_parse_invalid_unicode_surrogate() {\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uD800\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uDBFF\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uD800\\\\\\\\\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uD800\\\\uDBFF\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uD800\\\\uE000\\\"\");\n}\n\nstatic void test_parse_miss_comma_or_square_bracket() {\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, \"[1\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, \"[1}\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, \"[1 2\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, \"[[]\");\n}\n\nstatic void test_parse_miss_key() {\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_KEY, \"{:1,\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_KEY, \"{1:1,\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_KEY, \"{true:1,\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_KEY, \"{false:1,\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_KEY, \"{null:1,\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_KEY, \"{[]:1,\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_KEY, \"{{}:1,\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_KEY, \"{\\\"a\\\":1,\");\n}\n\nstatic void test_parse_miss_colon() {\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_COLON, \"{\\\"a\\\"}\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_COLON, \"{\\\"a\\\",\\\"b\\\"}\");\n}\n\nstatic void test_parse_miss_comma_or_curly_bracket() {\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET, \"{\\\"a\\\":1\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET, \"{\\\"a\\\":1]\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET, \"{\\\"a\\\":1 \\\"b\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET, \"{\\\"a\\\":{}\");\n}\n\nstatic void test_parse() {\n    test_parse_null();\n    test_parse_true();\n    test_parse_false();\n    test_parse_number();\n    test_parse_string();\n    test_parse_array();\n    test_parse_object();\n\n    test_parse_expect_value();\n    test_parse_invalid_value();\n    test_parse_root_not_singular();\n    test_parse_number_too_big();\n    test_parse_miss_quotation_mark();\n    test_parse_invalid_string_escape();\n    test_parse_invalid_string_char();\n    test_parse_invalid_unicode_hex();\n    test_parse_invalid_unicode_surrogate();\n    test_parse_miss_comma_or_square_bracket();\n    test_parse_miss_key();\n    test_parse_miss_colon();\n    test_parse_miss_comma_or_curly_bracket();\n}\n\n#define TEST_ROUNDTRIP(json)\\\n    do {\\\n        lept_value v;\\\n        char* json2;\\\n        size_t length;\\\n        lept_init(&v);\\\n        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));\\\n        json2 = lept_stringify(&v, &length);\\\n        EXPECT_EQ_STRING(json, json2, length);\\\n        lept_free(&v);\\\n        free(json2);\\\n    } while(0)\n\nstatic void test_stringify_number() {\n    TEST_ROUNDTRIP(\"0\");\n    TEST_ROUNDTRIP(\"-0\");\n    TEST_ROUNDTRIP(\"1\");\n    TEST_ROUNDTRIP(\"-1\");\n    TEST_ROUNDTRIP(\"1.5\");\n    TEST_ROUNDTRIP(\"-1.5\");\n    TEST_ROUNDTRIP(\"3.25\");\n    TEST_ROUNDTRIP(\"1e+20\");\n    TEST_ROUNDTRIP(\"1.234e+20\");\n    TEST_ROUNDTRIP(\"1.234e-20\");\n\n    TEST_ROUNDTRIP(\"1.0000000000000002\"); /* the smallest number > 1 */\n    TEST_ROUNDTRIP(\"4.9406564584124654e-324\"); /* minimum denormal */\n    TEST_ROUNDTRIP(\"-4.9406564584124654e-324\");\n    TEST_ROUNDTRIP(\"2.2250738585072009e-308\");  /* Max subnormal double */\n    TEST_ROUNDTRIP(\"-2.2250738585072009e-308\");\n    TEST_ROUNDTRIP(\"2.2250738585072014e-308\");  /* Min normal positive double */\n    TEST_ROUNDTRIP(\"-2.2250738585072014e-308\");\n    TEST_ROUNDTRIP(\"1.7976931348623157e+308\");  /* Max double */\n    TEST_ROUNDTRIP(\"-1.7976931348623157e+308\");\n}\n\nstatic void test_stringify_string() {\n    TEST_ROUNDTRIP(\"\\\"\\\"\");\n    TEST_ROUNDTRIP(\"\\\"Hello\\\"\");\n    TEST_ROUNDTRIP(\"\\\"Hello\\\\nWorld\\\"\");\n    TEST_ROUNDTRIP(\"\\\"\\\\\\\" \\\\\\\\ / \\\\b \\\\f \\\\n \\\\r \\\\t\\\"\");\n    TEST_ROUNDTRIP(\"\\\"Hello\\\\u0000World\\\"\");\n}\n\nstatic void test_stringify_array() {\n    TEST_ROUNDTRIP(\"[]\");\n    TEST_ROUNDTRIP(\"[null,false,true,123,\\\"abc\\\",[1,2,3]]\");\n}\n\nstatic void test_stringify_object() {\n    TEST_ROUNDTRIP(\"{}\");\n    TEST_ROUNDTRIP(\"{\\\"n\\\":null,\\\"f\\\":false,\\\"t\\\":true,\\\"i\\\":123,\\\"s\\\":\\\"abc\\\",\\\"a\\\":[1,2,3],\\\"o\\\":{\\\"1\\\":1,\\\"2\\\":2,\\\"3\\\":3}}\");\n}\n\nstatic void test_stringify() {\n    TEST_ROUNDTRIP(\"null\");\n    TEST_ROUNDTRIP(\"false\");\n    TEST_ROUNDTRIP(\"true\");\n    test_stringify_number();\n    test_stringify_string();\n    test_stringify_array();\n    test_stringify_object();\n}\n\nstatic void test_access_null() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"a\", 1);\n    lept_set_null(&v);\n    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\n    lept_free(&v);\n}\n\nstatic void test_access_boolean() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"a\", 1);\n    lept_set_boolean(&v, 1);\n    EXPECT_TRUE(lept_get_boolean(&v));\n    lept_set_boolean(&v, 0);\n    EXPECT_FALSE(lept_get_boolean(&v));\n    lept_free(&v);\n}\n\nstatic void test_access_number() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"a\", 1);\n    lept_set_number(&v, 1234.5);\n    EXPECT_EQ_DOUBLE(1234.5, lept_get_number(&v));\n    lept_free(&v);\n}\n\nstatic void test_access_string() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"\", 0);\n    EXPECT_EQ_STRING(\"\", lept_get_string(&v), lept_get_string_length(&v));\n    lept_set_string(&v, \"Hello\", 5);\n    EXPECT_EQ_STRING(\"Hello\", lept_get_string(&v), lept_get_string_length(&v));\n    lept_free(&v);\n}\n\nstatic void test_access() {\n    test_access_null();\n    test_access_boolean();\n    test_access_number();\n    test_access_string();\n}\n\nint main() {\n#ifdef _WINDOWS\n    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);\n#endif\n    test_parse();\n    test_stringify();\n    test_access();\n    printf(\"%d/%d (%3.2f%%) passed\\n\", test_pass, test_count, test_pass * 100.0 / test_count);\n    return main_ret;\n}\n"
  },
  {
    "path": "tutorial07/tutorial07.md",
    "content": "# 从零开始的 JSON 库教程（七）：生成器\n\n* Milo Yip\n* 2016/12/20\n\n本文是[《从零开始的 JSON 库教程》](https://zhuanlan.zhihu.com/json-tutorial)的第七个单元。代码位于 [json-tutorial/tutorial07](https://github.com/miloyip/json-tutorial/blob/master/tutorial07)。\n\n本单元内容：\n\n1. [JSON 生成器](#1-json-生成器)\n2. [再利用 lept_context 做动态数组](#2-再利用-lept_context-做动态数组)\n3. [生成 null、false 和 true](#3-生成-nullfalse-和-true)\n4. [生成数字](#4-生成数字)\n5. [总结与练习](#5-总结与练习)\n\n## 1. JSON 生成器\n\n我们在前 6 个单元实现了一个合乎标准的 JSON 解析器，它把 JSON 文本解析成一个树形数据结构，整个结构以 `lept_value` 的节点组成。\n\nJSON 生成器（generator）负责相反的事情，就是把树形数据结构转换成 JSON 文本。这个过程又称为「字符串化（stringify）」。\n\n![JSON 的解析与生成](images/parse_stringify.png)\n\n相对于解析器，通常生成器更容易实现，而且生成器几乎不会造成运行时错误。因此，生成器的 API 设计为以下形式，直接返回 JSON 的字符串：\n\n~~~c\nchar* lept_stringify(const lept_value* v, size_t* length);\n~~~\n\n`length` 参数是可选的，它会存储 JSON 的长度，传入 `NULL` 可忽略此参数。使用方需负责用 `free()` 释放内存。\n\n为了简单起见，我们不做换行、缩进等美化（prettify）处理，因此它生成的 JSON 会是单行、无空白字符的最紧凑形式。\n\n## 2. 再利用 lept_context 做动态数组\n\n在实现 JSON 解析时，我们加入了一个动态变长的堆栈，用于存储临时的解析结果。而现在，我们也需要存储生成的结果，所以最简单是再利用该数据结构，作为输出缓冲区。\n\n~~~c\n#ifndef LEPT_PARSE_STRINGIFY_INIT_SIZE\n#define LEPT_PARSE_STRINGIFY_INIT_SIZE 256\n#endif\n\nint lept_stringify(const lept_value* v, char** json, size_t* length) {\n    lept_context c;\n    int ret;\n    assert(v != NULL);\n    assert(json != NULL);\n    c.stack = (char*)malloc(c.size = LEPT_PARSE_STRINGIFY_INIT_SIZE);\n    c.top = 0;\n    if ((ret = lept_stringify_value(&c, v)) != LEPT_STRINGIFY_OK) {\n        free(c.stack);\n        *json = NULL;\n        return ret;\n    }\n    if (length)\n        *length = c.top;\n    PUTC(&c, '\\0');\n    *json = c.stack;\n    return LEPT_STRINGIFY_OK;\n}\n~~~\n\n生成根节点的值之后，我们还需要加入一个空字符作结尾。\n\n如前所述，此 API 还提供了 `length` 可选参数，当传入非空指针时，就能获得生成 JSON 的长度。或许读者会疑问，为什么需要获得长度，我们不是可以用 `strlen()` 获得么？是的，因为 JSON 不会含有空字符（若 JSON 字符串中含空字符，必须转义为 `\\u0000`），用 `strlen()` 是没有问题的。但这样做会带来不必要的性能消耗，理想地是避免调用方有额外消耗。\n\n## 3. 生成 null、false 和 true\n\n接下来，我们生成最简单的 JSON 类型，就是 3 种 JSON 字面值。为贯彻 TDD，先写测试：\n\n~~~c\n#define TEST_ROUNDTRIP(json)\\\n    do {\\\n        lept_value v;\\\n        char* json2;\\\n        size_t length;\\\n        lept_init(&v);\\\n        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_STRINGIFY_OK, lept_stringify(&v, &json2, &length));\\\n        EXPECT_EQ_STRING(json, json2, length);\\\n        lept_free(&v);\\\n        free(json2);\\\n    } while(0)\n\nstatic void test_stringify() {\n    TEST_ROUNDTRIP(\"null\");\n    TEST_ROUNDTRIP(\"false\");\n    TEST_ROUNDTRIP(\"true\");\n    /* ... */\n}\n~~~\n\n这里我们采用一个最简单的测试方式，把一个 JSON 解析，然后再生成另一 JSON，逐字符比较两个 JSON 是否一模一样。这种测试可称为往返（roundtrip）测试。但需要注意，同一个 JSON 的内容可以有多种不同的表示方式，例如可以插入不定数量的空白字符，数字 `1.0` 和 `1` 也是等价的。所以另一种测试方式，是比较两次解析的结果（`lept_value` 的树）是否相同，此功能将会在下一单元讲解。\n\n然后，我们实现 `lept_stringify_value`，加入一个 `PUTS()` 宏去输出字符串：\n\n~~~c\n#define PUTS(c, s, len)     memcpy(lept_context_push(c, len), s, len)\n\nstatic int lept_stringify_value(lept_context* c, const lept_value* v) {\n    size_t i;\n    int ret;\n    switch (v->type) {\n        case LEPT_NULL:   PUTS(c, \"null\",  4); break;\n        case LEPT_FALSE:  PUTS(c, \"false\", 5); break;\n        case LEPT_TRUE:   PUTS(c, \"true\",  4); break;\n        /* ... */\n    }\n    return LEPT_STRINGIFY_OK;\n}\n~~~\n\n## 4. 生成数字\n\n为了简单起见，我们使用 `sprintf(\"%.17g\", ...)` 来把浮点数转换成文本。`\"%.17g\"` 是足够把双精度浮点转换成可还原的文本。\n\n最简单的实现方式可能是这样的：\n\n~~~c\n        case LEPT_NUMBER:\n            {\n                char buffer[32];\n                int length = sprintf(buffer, \"%.17g\", v->u.n);\n                PUTS(c, buffer, length);\n            }\n            break;\n~~~\n\n但这样需要在 `PUTS()` 中做一次 `memcpy()`，实际上我们可以避免这次复制，只需要生成的时候直接写进 `c` 里的堆栈，然后再按实际长度调查 `c->top`：\n\n~~~c\n        case LEPT_NUMBER:\n            {\n                char* buffer = lept_context_push(c, 32);\n                int length = sprintf(buffer, \"%.17g\", v->u.n);\n                c->top -= 32 - length;\n            }\n            break;\n~~~\n\n因每个临时变量只用了一次，我们可以把代码压缩成一行：\n\n~~~c\n        case LEPT_NUMBER:\n            c->top -= 32 - sprintf(lept_context_push(c, 32), \"%.17g\", v->u.n);\n            break;\n~~~\n\n## 5. 总结与练习\n\n我们在此单元中简介了 JSON 的生成功能和 leptjson 中的实现方式。\n\nleptjson 重复利用了 `lept_context` 中的数据结构作为输出缓冲，可以节省代码量。\n\n生成通常比解析简单（一个例外是 RapidJSON 自行实现了浮点数至字符串的算法），余下的 3 种 JSON 类型就当作练习吧：\n\n1. 由于有两个地方需要生成字符串（JSON 字符串和对象类型），所以先实现 `lept_stringify_string()`。注意，字符串的语法比较复杂，一些字符必须转义，其他少于 `0x20` 的字符需要转义为 `\\u00xx` 形式。\n\n2. 直接在 `lept_stringify_value()` 的 `switch` 内实现 JSON 数组和对象类型的生成。这些实现里都会递归调用 `lept_stringify_value()`。\n\n3. 在你的 `lept_stringify_string()` 是否使用了多次 `PUTC()`？如果是，它每次输出一个字符时，都要检测缓冲区是否有足够空间（不够时需扩展）。能否优化这部分的性能？这种优化有什么代价么？\n\n如果你遇到问题，有不理解的地方，或是有建议，都欢迎在评论或 [issue](https://github.com/miloyip/json-tutorial/issues) 中提出，让所有人一起讨论。\n"
  },
  {
    "path": "tutorial07_answer/CMakeLists.txt",
    "content": "cmake_minimum_required (VERSION 2.6)\nproject (leptjson_test C)\n\nif (CMAKE_C_COMPILER_ID MATCHES \"GNU|Clang\")\n    set(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -ansi -pedantic -Wall\")\nendif()\n\nadd_library(leptjson leptjson.c)\nadd_executable(leptjson_test test.c)\ntarget_link_libraries(leptjson_test leptjson)\n"
  },
  {
    "path": "tutorial07_answer/leptjson.c",
    "content": "#ifdef _WINDOWS\n#define _CRTDBG_MAP_ALLOC\n#include <crtdbg.h>\n#endif\n#include \"leptjson.h\"\n#include <assert.h>  /* assert() */\n#include <errno.h>   /* errno, ERANGE */\n#include <math.h>    /* HUGE_VAL */\n#include <stdio.h>   /* sprintf() */\n#include <stdlib.h>  /* NULL, malloc(), realloc(), free(), strtod() */\n#include <string.h>  /* memcpy() */\n\n#ifndef LEPT_PARSE_STACK_INIT_SIZE\n#define LEPT_PARSE_STACK_INIT_SIZE 256\n#endif\n\n#ifndef LEPT_PARSE_STRINGIFY_INIT_SIZE\n#define LEPT_PARSE_STRINGIFY_INIT_SIZE 256\n#endif\n\n#define EXPECT(c, ch)       do { assert(*c->json == (ch)); c->json++; } while(0)\n#define ISDIGIT(ch)         ((ch) >= '0' && (ch) <= '9')\n#define ISDIGIT1TO9(ch)     ((ch) >= '1' && (ch) <= '9')\n#define PUTC(c, ch)         do { *(char*)lept_context_push(c, sizeof(char)) = (ch); } while(0)\n#define PUTS(c, s, len)     memcpy(lept_context_push(c, len), s, len)\n\ntypedef struct {\n    const char* json;\n    char* stack;\n    size_t size, top;\n}lept_context;\n\nstatic void* lept_context_push(lept_context* c, size_t size) {\n    void* ret;\n    assert(size > 0);\n    if (c->top + size >= c->size) {\n        if (c->size == 0)\n            c->size = LEPT_PARSE_STACK_INIT_SIZE;\n        while (c->top + size >= c->size)\n            c->size += c->size >> 1;  /* c->size * 1.5 */\n        c->stack = (char*)realloc(c->stack, c->size);\n    }\n    ret = c->stack + c->top;\n    c->top += size;\n    return ret;\n}\n\nstatic void* lept_context_pop(lept_context* c, size_t size) {\n    assert(c->top >= size);\n    return c->stack + (c->top -= size);\n}\n\nstatic void lept_parse_whitespace(lept_context* c) {\n    const char *p = c->json;\n    while (*p == ' ' || *p == '\\t' || *p == '\\n' || *p == '\\r')\n        p++;\n    c->json = p;\n}\n\nstatic int lept_parse_literal(lept_context* c, lept_value* v, const char* literal, lept_type type) {\n    size_t i;\n    EXPECT(c, literal[0]);\n    for (i = 0; literal[i + 1]; i++)\n        if (c->json[i] != literal[i + 1])\n            return LEPT_PARSE_INVALID_VALUE;\n    c->json += i;\n    v->type = type;\n    return LEPT_PARSE_OK;\n}\n\nstatic int lept_parse_number(lept_context* c, lept_value* v) {\n    const char* p = c->json;\n    if (*p == '-') p++;\n    if (*p == '0') p++;\n    else {\n        if (!ISDIGIT1TO9(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n    if (*p == '.') {\n        p++;\n        if (!ISDIGIT(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n    if (*p == 'e' || *p == 'E') {\n        p++;\n        if (*p == '+' || *p == '-') p++;\n        if (!ISDIGIT(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n    errno = 0;\n    v->u.n = strtod(c->json, NULL);\n    if (errno == ERANGE && (v->u.n == HUGE_VAL || v->u.n == -HUGE_VAL))\n        return LEPT_PARSE_NUMBER_TOO_BIG;\n    v->type = LEPT_NUMBER;\n    c->json = p;\n    return LEPT_PARSE_OK;\n}\n\nstatic const char* lept_parse_hex4(const char* p, unsigned* u) {\n    int i;\n    *u = 0;\n    for (i = 0; i < 4; i++) {\n        char ch = *p++;\n        *u <<= 4;\n        if      (ch >= '0' && ch <= '9')  *u |= ch - '0';\n        else if (ch >= 'A' && ch <= 'F')  *u |= ch - ('A' - 10);\n        else if (ch >= 'a' && ch <= 'f')  *u |= ch - ('a' - 10);\n        else return NULL;\n    }\n    return p;\n}\n\nstatic void lept_encode_utf8(lept_context* c, unsigned u) {\n    if (u <= 0x7F) \n        PUTC(c, u & 0xFF);\n    else if (u <= 0x7FF) {\n        PUTC(c, 0xC0 | ((u >> 6) & 0xFF));\n        PUTC(c, 0x80 | ( u       & 0x3F));\n    }\n    else if (u <= 0xFFFF) {\n        PUTC(c, 0xE0 | ((u >> 12) & 0xFF));\n        PUTC(c, 0x80 | ((u >>  6) & 0x3F));\n        PUTC(c, 0x80 | ( u        & 0x3F));\n    }\n    else {\n        assert(u <= 0x10FFFF);\n        PUTC(c, 0xF0 | ((u >> 18) & 0xFF));\n        PUTC(c, 0x80 | ((u >> 12) & 0x3F));\n        PUTC(c, 0x80 | ((u >>  6) & 0x3F));\n        PUTC(c, 0x80 | ( u        & 0x3F));\n    }\n}\n\n#define STRING_ERROR(ret) do { c->top = head; return ret; } while(0)\n\nstatic int lept_parse_string_raw(lept_context* c, char** str, size_t* len) {\n    size_t head = c->top;\n    unsigned u, u2;\n    const char* p;\n    EXPECT(c, '\\\"');\n    p = c->json;\n    for (;;) {\n        char ch = *p++;\n        switch (ch) {\n            case '\\\"':\n                *len = c->top - head;\n                *str = lept_context_pop(c, *len);\n                c->json = p;\n                return LEPT_PARSE_OK;\n            case '\\\\':\n                switch (*p++) {\n                    case '\\\"': PUTC(c, '\\\"'); break;\n                    case '\\\\': PUTC(c, '\\\\'); break;\n                    case '/':  PUTC(c, '/' ); break;\n                    case 'b':  PUTC(c, '\\b'); break;\n                    case 'f':  PUTC(c, '\\f'); break;\n                    case 'n':  PUTC(c, '\\n'); break;\n                    case 'r':  PUTC(c, '\\r'); break;\n                    case 't':  PUTC(c, '\\t'); break;\n                    case 'u':\n                        if (!(p = lept_parse_hex4(p, &u)))\n                            STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX);\n                        if (u >= 0xD800 && u <= 0xDBFF) { /* surrogate pair */\n                            if (*p++ != '\\\\')\n                                STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE);\n                            if (*p++ != 'u')\n                                STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE);\n                            if (!(p = lept_parse_hex4(p, &u2)))\n                                STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX);\n                            if (u2 < 0xDC00 || u2 > 0xDFFF)\n                                STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE);\n                            u = (((u - 0xD800) << 10) | (u2 - 0xDC00)) + 0x10000;\n                        }\n                        lept_encode_utf8(c, u);\n                        break;\n                    default:\n                        STRING_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE);\n                }\n                break;\n            case '\\0':\n                STRING_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK);\n            default:\n                if ((unsigned char)ch < 0x20)\n                    STRING_ERROR(LEPT_PARSE_INVALID_STRING_CHAR);\n                PUTC(c, ch);\n        }\n    }\n}\n\nstatic int lept_parse_string(lept_context* c, lept_value* v) {\n    int ret;\n    char* s;\n    size_t len;\n    if ((ret = lept_parse_string_raw(c, &s, &len)) == LEPT_PARSE_OK)\n        lept_set_string(v, s, len);\n    return ret;\n}\n\nstatic int lept_parse_value(lept_context* c, lept_value* v);\n\nstatic int lept_parse_array(lept_context* c, lept_value* v) {\n    size_t i, size = 0;\n    int ret;\n    EXPECT(c, '[');\n    lept_parse_whitespace(c);\n    if (*c->json == ']') {\n        c->json++;\n        v->type = LEPT_ARRAY;\n        v->u.a.size = 0;\n        v->u.a.e = NULL;\n        return LEPT_PARSE_OK;\n    }\n    for (;;) {\n        lept_value e;\n        lept_init(&e);\n        if ((ret = lept_parse_value(c, &e)) != LEPT_PARSE_OK)\n            break;\n        memcpy(lept_context_push(c, sizeof(lept_value)), &e, sizeof(lept_value));\n        size++;\n        lept_parse_whitespace(c);\n        if (*c->json == ',') {\n            c->json++;\n            lept_parse_whitespace(c);\n        }\n        else if (*c->json == ']') {\n            c->json++;\n            v->type = LEPT_ARRAY;\n            v->u.a.size = size;\n            size *= sizeof(lept_value);\n            memcpy(v->u.a.e = (lept_value*)malloc(size), lept_context_pop(c, size), size);\n            return LEPT_PARSE_OK;\n        }\n        else {\n            ret = LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET;\n            break;\n        }\n    }\n    /* Pop and free values on the stack */\n    for (i = 0; i < size; i++)\n        lept_free((lept_value*)lept_context_pop(c, sizeof(lept_value)));\n    return ret;\n}\n\nstatic int lept_parse_object(lept_context* c, lept_value* v) {\n    size_t i, size;\n    lept_member m;\n    int ret;\n    EXPECT(c, '{');\n    lept_parse_whitespace(c);\n    if (*c->json == '}') {\n        c->json++;\n        v->type = LEPT_OBJECT;\n        v->u.o.m = 0;\n        v->u.o.size = 0;\n        return LEPT_PARSE_OK;\n    }\n    m.k = NULL;\n    size = 0;\n    for (;;) {\n        char* str;\n        lept_init(&m.v);\n        /* parse key */\n        if (*c->json != '\"') {\n            ret = LEPT_PARSE_MISS_KEY;\n            break;\n        }\n        if ((ret = lept_parse_string_raw(c, &str, &m.klen)) != LEPT_PARSE_OK)\n            break;\n        memcpy(m.k = (char*)malloc(m.klen + 1), str, m.klen);\n        m.k[m.klen] = '\\0';\n        /* parse ws colon ws */\n        lept_parse_whitespace(c);\n        if (*c->json != ':') {\n            ret = LEPT_PARSE_MISS_COLON;\n            break;\n        }\n        c->json++;\n        lept_parse_whitespace(c);\n        /* parse value */\n        if ((ret = lept_parse_value(c, &m.v)) != LEPT_PARSE_OK)\n            break;\n        memcpy(lept_context_push(c, sizeof(lept_member)), &m, sizeof(lept_member));\n        size++;\n        m.k = NULL; /* ownership is transferred to member on stack */\n        /* parse ws [comma | right-curly-brace] ws */\n        lept_parse_whitespace(c);\n        if (*c->json == ',') {\n            c->json++;\n            lept_parse_whitespace(c);\n        }\n        else if (*c->json == '}') {\n            size_t s = sizeof(lept_member) * size;\n            c->json++;\n            v->type = LEPT_OBJECT;\n            v->u.o.size = size;\n            memcpy(v->u.o.m = (lept_member*)malloc(s), lept_context_pop(c, s), s);\n            return LEPT_PARSE_OK;\n        }\n        else {\n            ret = LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET;\n            break;\n        }\n    }\n    /* Pop and free members on the stack */\n    free(m.k);\n    for (i = 0; i < size; i++) {\n        lept_member* m = (lept_member*)lept_context_pop(c, sizeof(lept_member));\n        free(m->k);\n        lept_free(&m->v);\n    }\n    v->type = LEPT_NULL;\n    return ret;\n}\n\nstatic int lept_parse_value(lept_context* c, lept_value* v) {\n    switch (*c->json) {\n        case 't':  return lept_parse_literal(c, v, \"true\", LEPT_TRUE);\n        case 'f':  return lept_parse_literal(c, v, \"false\", LEPT_FALSE);\n        case 'n':  return lept_parse_literal(c, v, \"null\", LEPT_NULL);\n        default:   return lept_parse_number(c, v);\n        case '\"':  return lept_parse_string(c, v);\n        case '[':  return lept_parse_array(c, v);\n        case '{':  return lept_parse_object(c, v);\n        case '\\0': return LEPT_PARSE_EXPECT_VALUE;\n    }\n}\n\nint lept_parse(lept_value* v, const char* json) {\n    lept_context c;\n    int ret;\n    assert(v != NULL);\n    c.json = json;\n    c.stack = NULL;\n    c.size = c.top = 0;\n    lept_init(v);\n    lept_parse_whitespace(&c);\n    if ((ret = lept_parse_value(&c, v)) == LEPT_PARSE_OK) {\n        lept_parse_whitespace(&c);\n        if (*c.json != '\\0') {\n            v->type = LEPT_NULL;\n            ret = LEPT_PARSE_ROOT_NOT_SINGULAR;\n        }\n    }\n    assert(c.top == 0);\n    free(c.stack);\n    return ret;\n}\n\n#if 0\n// Unoptimized\nstatic void lept_stringify_string(lept_context* c, const char* s, size_t len) {\n    size_t i;\n    assert(s != NULL);\n    PUTC(c, '\"');\n    for (i = 0; i < len; i++) {\n        unsigned char ch = (unsigned char)s[i];\n        switch (ch) {\n            case '\\\"': PUTS(c, \"\\\\\\\"\", 2); break;\n            case '\\\\': PUTS(c, \"\\\\\\\\\", 2); break;\n            case '\\b': PUTS(c, \"\\\\b\",  2); break;\n            case '\\f': PUTS(c, \"\\\\f\",  2); break;\n            case '\\n': PUTS(c, \"\\\\n\",  2); break;\n            case '\\r': PUTS(c, \"\\\\r\",  2); break;\n            case '\\t': PUTS(c, \"\\\\t\",  2); break;\n            default:\n                if (ch < 0x20) {\n                    char buffer[7];\n                    sprintf(buffer, \"\\\\u%04X\", ch);\n                    PUTS(c, buffer, 6);\n                }\n                else\n                    PUTC(c, s[i]);\n        }\n    }\n    PUTC(c, '\"');\n}\n#else\nstatic void lept_stringify_string(lept_context* c, const char* s, size_t len) {\n    static const char hex_digits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };\n    size_t i, size;\n    char* head, *p;\n    assert(s != NULL);\n    p = head = lept_context_push(c, size = len * 6 + 2); /* \"\\u00xx...\" */\n    *p++ = '\"';\n    for (i = 0; i < len; i++) {\n        unsigned char ch = (unsigned char)s[i];\n        switch (ch) {\n            case '\\\"': *p++ = '\\\\'; *p++ = '\\\"'; break;\n            case '\\\\': *p++ = '\\\\'; *p++ = '\\\\'; break;\n            case '\\b': *p++ = '\\\\'; *p++ = 'b';  break;\n            case '\\f': *p++ = '\\\\'; *p++ = 'f';  break;\n            case '\\n': *p++ = '\\\\'; *p++ = 'n';  break;\n            case '\\r': *p++ = '\\\\'; *p++ = 'r';  break;\n            case '\\t': *p++ = '\\\\'; *p++ = 't';  break;\n            default:\n                if (ch < 0x20) {\n                    *p++ = '\\\\'; *p++ = 'u'; *p++ = '0'; *p++ = '0';\n                    *p++ = hex_digits[ch >> 4];\n                    *p++ = hex_digits[ch & 15];\n                }\n                else\n                    *p++ = s[i];\n        }\n    }\n    *p++ = '\"';\n    c->top -= size - (p - head);\n}\n#endif\n\nstatic void lept_stringify_value(lept_context* c, const lept_value* v) {\n    size_t i;\n    switch (v->type) {\n        case LEPT_NULL:   PUTS(c, \"null\",  4); break;\n        case LEPT_FALSE:  PUTS(c, \"false\", 5); break;\n        case LEPT_TRUE:   PUTS(c, \"true\",  4); break;\n        case LEPT_NUMBER: c->top -= 32 - sprintf(lept_context_push(c, 32), \"%.17g\", v->u.n); break;\n        case LEPT_STRING: lept_stringify_string(c, v->u.s.s, v->u.s.len); break;\n        case LEPT_ARRAY:\n            PUTC(c, '[');\n            for (i = 0; i < v->u.a.size; i++) {\n                if (i > 0)\n                    PUTC(c, ',');\n                lept_stringify_value(c, &v->u.a.e[i]);\n            }\n            PUTC(c, ']');\n            break;\n        case LEPT_OBJECT:\n            PUTC(c, '{');\n            for (i = 0; i < v->u.o.size; i++) {\n                if (i > 0)\n                    PUTC(c, ',');\n                lept_stringify_string(c, v->u.o.m[i].k, v->u.o.m[i].klen);\n                PUTC(c, ':');\n                lept_stringify_value(c, &v->u.o.m[i].v);\n            }\n            PUTC(c, '}');\n            break;\n        default: assert(0 && \"invalid type\");\n    }\n}\n\nchar* lept_stringify(const lept_value* v, size_t* length) {\n    lept_context c;\n    assert(v != NULL);\n    c.stack = (char*)malloc(c.size = LEPT_PARSE_STRINGIFY_INIT_SIZE);\n    c.top = 0;\n    lept_stringify_value(&c, v);\n    if (length)\n        *length = c.top;\n    PUTC(&c, '\\0');\n    return c.stack;\n}\n\nvoid lept_free(lept_value* v) {\n    size_t i;\n    assert(v != NULL);\n    switch (v->type) {\n        case LEPT_STRING:\n            free(v->u.s.s);\n            break;\n        case LEPT_ARRAY:\n            for (i = 0; i < v->u.a.size; i++)\n                lept_free(&v->u.a.e[i]);\n            free(v->u.a.e);\n            break;\n        case LEPT_OBJECT:\n            for (i = 0; i < v->u.o.size; i++) {\n                free(v->u.o.m[i].k);\n                lept_free(&v->u.o.m[i].v);\n            }\n            free(v->u.o.m);\n            break;\n        default: break;\n    }\n    v->type = LEPT_NULL;\n}\n\nlept_type lept_get_type(const lept_value* v) {\n    assert(v != NULL);\n    return v->type;\n}\n\nint lept_get_boolean(const lept_value* v) {\n    assert(v != NULL && (v->type == LEPT_TRUE || v->type == LEPT_FALSE));\n    return v->type == LEPT_TRUE;\n}\n\nvoid lept_set_boolean(lept_value* v, int b) {\n    lept_free(v);\n    v->type = b ? LEPT_TRUE : LEPT_FALSE;\n}\n\ndouble lept_get_number(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_NUMBER);\n    return v->u.n;\n}\n\nvoid lept_set_number(lept_value* v, double n) {\n    lept_free(v);\n    v->u.n = n;\n    v->type = LEPT_NUMBER;\n}\n\nconst char* lept_get_string(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_STRING);\n    return v->u.s.s;\n}\n\nsize_t lept_get_string_length(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_STRING);\n    return v->u.s.len;\n}\n\nvoid lept_set_string(lept_value* v, const char* s, size_t len) {\n    assert(v != NULL && (s != NULL || len == 0));\n    lept_free(v);\n    v->u.s.s = (char*)malloc(len + 1);\n    memcpy(v->u.s.s, s, len);\n    v->u.s.s[len] = '\\0';\n    v->u.s.len = len;\n    v->type = LEPT_STRING;\n}\n\nsize_t lept_get_array_size(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_ARRAY);\n    return v->u.a.size;\n}\n\nlept_value* lept_get_array_element(const lept_value* v, size_t index) {\n    assert(v != NULL && v->type == LEPT_ARRAY);\n    assert(index < v->u.a.size);\n    return &v->u.a.e[index];\n}\n\nsize_t lept_get_object_size(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_OBJECT);\n    return v->u.o.size;\n}\n\nconst char* lept_get_object_key(const lept_value* v, size_t index) {\n    assert(v != NULL && v->type == LEPT_OBJECT);\n    assert(index < v->u.o.size);\n    return v->u.o.m[index].k;\n}\n\nsize_t lept_get_object_key_length(const lept_value* v, size_t index) {\n    assert(v != NULL && v->type == LEPT_OBJECT);\n    assert(index < v->u.o.size);\n    return v->u.o.m[index].klen;\n}\n\nlept_value* lept_get_object_value(const lept_value* v, size_t index) {\n    assert(v != NULL && v->type == LEPT_OBJECT);\n    assert(index < v->u.o.size);\n    return &v->u.o.m[index].v;\n}\n"
  },
  {
    "path": "tutorial07_answer/leptjson.h",
    "content": "#ifndef LEPTJSON_H__\n#define LEPTJSON_H__\n\n#include <stddef.h> /* size_t */\n\ntypedef enum { LEPT_NULL, LEPT_FALSE, LEPT_TRUE, LEPT_NUMBER, LEPT_STRING, LEPT_ARRAY, LEPT_OBJECT } lept_type;\n\ntypedef struct lept_value lept_value;\ntypedef struct lept_member lept_member;\n\nstruct lept_value {\n    union {\n        struct { lept_member* m; size_t size; }o;   /* object: members, member count */\n        struct { lept_value* e; size_t size; }a;    /* array:  elements, element count */\n        struct { char* s; size_t len; }s;           /* string: null-terminated string, string length */\n        double n;                                   /* number */\n    }u;\n    lept_type type;\n};\n\nstruct lept_member {\n    char* k; size_t klen;   /* member key string, key string length */\n    lept_value v;           /* member value */\n};\n\nenum {\n    LEPT_PARSE_OK = 0,\n    LEPT_PARSE_EXPECT_VALUE,\n    LEPT_PARSE_INVALID_VALUE,\n    LEPT_PARSE_ROOT_NOT_SINGULAR,\n    LEPT_PARSE_NUMBER_TOO_BIG,\n    LEPT_PARSE_MISS_QUOTATION_MARK,\n    LEPT_PARSE_INVALID_STRING_ESCAPE,\n    LEPT_PARSE_INVALID_STRING_CHAR,\n    LEPT_PARSE_INVALID_UNICODE_HEX,\n    LEPT_PARSE_INVALID_UNICODE_SURROGATE,\n    LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET,\n    LEPT_PARSE_MISS_KEY,\n    LEPT_PARSE_MISS_COLON,\n    LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET\n};\n\n#define lept_init(v) do { (v)->type = LEPT_NULL; } while(0)\n\nint lept_parse(lept_value* v, const char* json);\nchar* lept_stringify(const lept_value* v, size_t* length);\n\nvoid lept_free(lept_value* v);\n\nlept_type lept_get_type(const lept_value* v);\n\n#define lept_set_null(v) lept_free(v)\n\nint lept_get_boolean(const lept_value* v);\nvoid lept_set_boolean(lept_value* v, int b);\n\ndouble lept_get_number(const lept_value* v);\nvoid lept_set_number(lept_value* v, double n);\n\nconst char* lept_get_string(const lept_value* v);\nsize_t lept_get_string_length(const lept_value* v);\nvoid lept_set_string(lept_value* v, const char* s, size_t len);\n\nsize_t lept_get_array_size(const lept_value* v);\nlept_value* lept_get_array_element(const lept_value* v, size_t index);\n\nsize_t lept_get_object_size(const lept_value* v);\nconst char* lept_get_object_key(const lept_value* v, size_t index);\nsize_t lept_get_object_key_length(const lept_value* v, size_t index);\nlept_value* lept_get_object_value(const lept_value* v, size_t index);\n\n#endif /* LEPTJSON_H__ */\n"
  },
  {
    "path": "tutorial07_answer/test.c",
    "content": "#ifdef _WINDOWS\n#define _CRTDBG_MAP_ALLOC\n#include <crtdbg.h>\n#endif\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"leptjson.h\"\n\nstatic int main_ret = 0;\nstatic int test_count = 0;\nstatic int test_pass = 0;\n\n#define EXPECT_EQ_BASE(equality, expect, actual, format) \\\n    do {\\\n        test_count++;\\\n        if (equality)\\\n            test_pass++;\\\n        else {\\\n            fprintf(stderr, \"%s:%d: expect: \" format \" actual: \" format \"\\n\", __FILE__, __LINE__, expect, actual);\\\n            main_ret = 1;\\\n        }\\\n    } while(0)\n\n#define EXPECT_EQ_INT(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, \"%d\")\n#define EXPECT_EQ_DOUBLE(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, \"%.17g\")\n#define EXPECT_EQ_STRING(expect, actual, alength) \\\n    EXPECT_EQ_BASE(sizeof(expect) - 1 == alength && memcmp(expect, actual, alength + 1) == 0, expect, actual, \"%s\")\n#define EXPECT_TRUE(actual) EXPECT_EQ_BASE((actual) != 0, \"true\", \"false\", \"%s\")\n#define EXPECT_FALSE(actual) EXPECT_EQ_BASE((actual) == 0, \"false\", \"true\", \"%s\")\n\n#if defined(_MSC_VER)\n#define EXPECT_EQ_SIZE_T(expect, actual) EXPECT_EQ_BASE((expect) == (actual), (size_t)expect, (size_t)actual, \"%Iu\")\n#else\n#define EXPECT_EQ_SIZE_T(expect, actual) EXPECT_EQ_BASE((expect) == (actual), (size_t)expect, (size_t)actual, \"%zu\")\n#endif\n\nstatic void test_parse_null() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_boolean(&v, 0);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"null\"));\n    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\n    lept_free(&v);\n}\n\nstatic void test_parse_true() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_boolean(&v, 0);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"true\"));\n    EXPECT_EQ_INT(LEPT_TRUE, lept_get_type(&v));\n    lept_free(&v);\n}\n\nstatic void test_parse_false() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_boolean(&v, 1);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"false\"));\n    EXPECT_EQ_INT(LEPT_FALSE, lept_get_type(&v));\n    lept_free(&v);\n}\n\n#define TEST_NUMBER(expect, json)\\\n    do {\\\n        lept_value v;\\\n        lept_init(&v);\\\n        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(&v));\\\n        EXPECT_EQ_DOUBLE(expect, lept_get_number(&v));\\\n        lept_free(&v);\\\n    } while(0)\n\nstatic void test_parse_number() {\n    TEST_NUMBER(0.0, \"0\");\n    TEST_NUMBER(0.0, \"-0\");\n    TEST_NUMBER(0.0, \"-0.0\");\n    TEST_NUMBER(1.0, \"1\");\n    TEST_NUMBER(-1.0, \"-1\");\n    TEST_NUMBER(1.5, \"1.5\");\n    TEST_NUMBER(-1.5, \"-1.5\");\n    TEST_NUMBER(3.1416, \"3.1416\");\n    TEST_NUMBER(1E10, \"1E10\");\n    TEST_NUMBER(1e10, \"1e10\");\n    TEST_NUMBER(1E+10, \"1E+10\");\n    TEST_NUMBER(1E-10, \"1E-10\");\n    TEST_NUMBER(-1E10, \"-1E10\");\n    TEST_NUMBER(-1e10, \"-1e10\");\n    TEST_NUMBER(-1E+10, \"-1E+10\");\n    TEST_NUMBER(-1E-10, \"-1E-10\");\n    TEST_NUMBER(1.234E+10, \"1.234E+10\");\n    TEST_NUMBER(1.234E-10, \"1.234E-10\");\n    TEST_NUMBER(0.0, \"1e-10000\"); /* must underflow */\n\n    TEST_NUMBER(1.0000000000000002, \"1.0000000000000002\"); /* the smallest number > 1 */\n    TEST_NUMBER( 4.9406564584124654e-324, \"4.9406564584124654e-324\"); /* minimum denormal */\n    TEST_NUMBER(-4.9406564584124654e-324, \"-4.9406564584124654e-324\");\n    TEST_NUMBER( 2.2250738585072009e-308, \"2.2250738585072009e-308\");  /* Max subnormal double */\n    TEST_NUMBER(-2.2250738585072009e-308, \"-2.2250738585072009e-308\");\n    TEST_NUMBER( 2.2250738585072014e-308, \"2.2250738585072014e-308\");  /* Min normal positive double */\n    TEST_NUMBER(-2.2250738585072014e-308, \"-2.2250738585072014e-308\");\n    TEST_NUMBER( 1.7976931348623157e+308, \"1.7976931348623157e+308\");  /* Max double */\n    TEST_NUMBER(-1.7976931348623157e+308, \"-1.7976931348623157e+308\");\n}\n\n#define TEST_STRING(expect, json)\\\n    do {\\\n        lept_value v;\\\n        lept_init(&v);\\\n        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_STRING, lept_get_type(&v));\\\n        EXPECT_EQ_STRING(expect, lept_get_string(&v), lept_get_string_length(&v));\\\n        lept_free(&v);\\\n    } while(0)\n\nstatic void test_parse_string() {\n    TEST_STRING(\"\", \"\\\"\\\"\");\n    TEST_STRING(\"Hello\", \"\\\"Hello\\\"\");\n    TEST_STRING(\"Hello\\nWorld\", \"\\\"Hello\\\\nWorld\\\"\");\n    TEST_STRING(\"\\\" \\\\ / \\b \\f \\n \\r \\t\", \"\\\"\\\\\\\" \\\\\\\\ \\\\/ \\\\b \\\\f \\\\n \\\\r \\\\t\\\"\");\n    TEST_STRING(\"Hello\\0World\", \"\\\"Hello\\\\u0000World\\\"\");\n    TEST_STRING(\"\\x24\", \"\\\"\\\\u0024\\\"\");         /* Dollar sign U+0024 */\n    TEST_STRING(\"\\xC2\\xA2\", \"\\\"\\\\u00A2\\\"\");     /* Cents sign U+00A2 */\n    TEST_STRING(\"\\xE2\\x82\\xAC\", \"\\\"\\\\u20AC\\\"\"); /* Euro sign U+20AC */\n    TEST_STRING(\"\\xF0\\x9D\\x84\\x9E\", \"\\\"\\\\uD834\\\\uDD1E\\\"\");  /* G clef sign U+1D11E */\n    TEST_STRING(\"\\xF0\\x9D\\x84\\x9E\", \"\\\"\\\\ud834\\\\udd1e\\\"\");  /* G clef sign U+1D11E */\n}\n\nstatic void test_parse_array() {\n    size_t i, j;\n    lept_value v;\n\n    lept_init(&v);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"[ ]\"));\n    EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(&v));\n    EXPECT_EQ_SIZE_T(0, lept_get_array_size(&v));\n    lept_free(&v);\n\n    lept_init(&v);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"[ null , false , true , 123 , \\\"abc\\\" ]\"));\n    EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(&v));\n    EXPECT_EQ_SIZE_T(5, lept_get_array_size(&v));\n    EXPECT_EQ_INT(LEPT_NULL,   lept_get_type(lept_get_array_element(&v, 0)));\n    EXPECT_EQ_INT(LEPT_FALSE,  lept_get_type(lept_get_array_element(&v, 1)));\n    EXPECT_EQ_INT(LEPT_TRUE,   lept_get_type(lept_get_array_element(&v, 2)));\n    EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(lept_get_array_element(&v, 3)));\n    EXPECT_EQ_INT(LEPT_STRING, lept_get_type(lept_get_array_element(&v, 4)));\n    EXPECT_EQ_DOUBLE(123.0, lept_get_number(lept_get_array_element(&v, 3)));\n    EXPECT_EQ_STRING(\"abc\", lept_get_string(lept_get_array_element(&v, 4)), lept_get_string_length(lept_get_array_element(&v, 4)));\n    lept_free(&v);\n\n    lept_init(&v);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"[ [ ] , [ 0 ] , [ 0 , 1 ] , [ 0 , 1 , 2 ] ]\"));\n    EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(&v));\n    EXPECT_EQ_SIZE_T(4, lept_get_array_size(&v));\n    for (i = 0; i < 4; i++) {\n        lept_value* a = lept_get_array_element(&v, i);\n        EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(a));\n        EXPECT_EQ_SIZE_T(i, lept_get_array_size(a));\n        for (j = 0; j < i; j++) {\n            lept_value* e = lept_get_array_element(a, j);\n            EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(e));\n            EXPECT_EQ_DOUBLE((double)j, lept_get_number(e));\n        }\n    }\n    lept_free(&v);\n}\n\nstatic void test_parse_object() {\n    lept_value v;\n    size_t i;\n\n    lept_init(&v);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \" { } \"));\n    EXPECT_EQ_INT(LEPT_OBJECT, lept_get_type(&v));\n    EXPECT_EQ_SIZE_T(0, lept_get_object_size(&v));\n    lept_free(&v);\n\n    lept_init(&v);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v,\n        \" { \"\n        \"\\\"n\\\" : null , \"\n        \"\\\"f\\\" : false , \"\n        \"\\\"t\\\" : true , \"\n        \"\\\"i\\\" : 123 , \"\n        \"\\\"s\\\" : \\\"abc\\\", \"\n        \"\\\"a\\\" : [ 1, 2, 3 ],\"\n        \"\\\"o\\\" : { \\\"1\\\" : 1, \\\"2\\\" : 2, \\\"3\\\" : 3 }\"\n        \" } \"\n    ));\n    EXPECT_EQ_INT(LEPT_OBJECT, lept_get_type(&v));\n    EXPECT_EQ_SIZE_T(7, lept_get_object_size(&v));\n    EXPECT_EQ_STRING(\"n\", lept_get_object_key(&v, 0), lept_get_object_key_length(&v, 0));\n    EXPECT_EQ_INT(LEPT_NULL,   lept_get_type(lept_get_object_value(&v, 0)));\n    EXPECT_EQ_STRING(\"f\", lept_get_object_key(&v, 1), lept_get_object_key_length(&v, 1));\n    EXPECT_EQ_INT(LEPT_FALSE,  lept_get_type(lept_get_object_value(&v, 1)));\n    EXPECT_EQ_STRING(\"t\", lept_get_object_key(&v, 2), lept_get_object_key_length(&v, 2));\n    EXPECT_EQ_INT(LEPT_TRUE,   lept_get_type(lept_get_object_value(&v, 2)));\n    EXPECT_EQ_STRING(\"i\", lept_get_object_key(&v, 3), lept_get_object_key_length(&v, 3));\n    EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(lept_get_object_value(&v, 3)));\n    EXPECT_EQ_DOUBLE(123.0, lept_get_number(lept_get_object_value(&v, 3)));\n    EXPECT_EQ_STRING(\"s\", lept_get_object_key(&v, 4), lept_get_object_key_length(&v, 4));\n    EXPECT_EQ_INT(LEPT_STRING, lept_get_type(lept_get_object_value(&v, 4)));\n    EXPECT_EQ_STRING(\"abc\", lept_get_string(lept_get_object_value(&v, 4)), lept_get_string_length(lept_get_object_value(&v, 4)));\n    EXPECT_EQ_STRING(\"a\", lept_get_object_key(&v, 5), lept_get_object_key_length(&v, 5));\n    EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(lept_get_object_value(&v, 5)));\n    EXPECT_EQ_SIZE_T(3, lept_get_array_size(lept_get_object_value(&v, 5)));\n    for (i = 0; i < 3; i++) {\n        lept_value* e = lept_get_array_element(lept_get_object_value(&v, 5), i);\n        EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(e));\n        EXPECT_EQ_DOUBLE(i + 1.0, lept_get_number(e));\n    }\n    EXPECT_EQ_STRING(\"o\", lept_get_object_key(&v, 6), lept_get_object_key_length(&v, 6));\n    {\n        lept_value* o = lept_get_object_value(&v, 6);\n        EXPECT_EQ_INT(LEPT_OBJECT, lept_get_type(o));\n        for (i = 0; i < 3; i++) {\n            lept_value* ov = lept_get_object_value(o, i);\n            EXPECT_TRUE('1' + i == lept_get_object_key(o, i)[0]);\n            EXPECT_EQ_SIZE_T(1, lept_get_object_key_length(o, i));\n            EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(ov));\n            EXPECT_EQ_DOUBLE(i + 1.0, lept_get_number(ov));\n        }\n    }\n    lept_free(&v);\n}\n\n#define TEST_PARSE_ERROR(error, json)\\\n    do {\\\n        lept_value v;\\\n        lept_init(&v);\\\n        v.type = LEPT_FALSE;\\\n        EXPECT_EQ_INT(error, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\\\n        lept_free(&v);\\\n    } while(0)\n\nstatic void test_parse_expect_value() {\n    TEST_PARSE_ERROR(LEPT_PARSE_EXPECT_VALUE, \"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_EXPECT_VALUE, \" \");\n}\n\nstatic void test_parse_invalid_value() {\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, \"nul\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, \"?\");\n\n    /* invalid number */\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, \"+0\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, \"+1\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, \".123\"); /* at least one digit before '.' */\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, \"1.\");   /* at least one digit after '.' */\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, \"INF\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, \"inf\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, \"NAN\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, \"nan\");\n\n    /* invalid value in array */\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, \"[1,]\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, \"[\\\"a\\\", nul]\");\n}\n\nstatic void test_parse_root_not_singular() {\n    TEST_PARSE_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"null x\");\n\n    /* invalid number */\n    TEST_PARSE_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0123\"); /* after zero should be '.' or nothing */\n    TEST_PARSE_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0x0\");\n    TEST_PARSE_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0x123\");\n}\n\nstatic void test_parse_number_too_big() {\n    TEST_PARSE_ERROR(LEPT_PARSE_NUMBER_TOO_BIG, \"1e309\");\n    TEST_PARSE_ERROR(LEPT_PARSE_NUMBER_TOO_BIG, \"-1e309\");\n}\n\nstatic void test_parse_miss_quotation_mark() {\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK, \"\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK, \"\\\"abc\");\n}\n\nstatic void test_parse_invalid_string_escape() {\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\v\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\'\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\0\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\x12\\\"\");\n}\n\nstatic void test_parse_invalid_string_char() {\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_STRING_CHAR, \"\\\"\\x01\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_STRING_CHAR, \"\\\"\\x1F\\\"\");\n}\n\nstatic void test_parse_invalid_unicode_hex() {\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u0\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u01\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u012\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u/000\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\uG000\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u0/00\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u0G00\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u0/00\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u00G0\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u000/\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u000G\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u 123\\\"\");\n}\n\nstatic void test_parse_invalid_unicode_surrogate() {\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uD800\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uDBFF\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uD800\\\\\\\\\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uD800\\\\uDBFF\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uD800\\\\uE000\\\"\");\n}\n\nstatic void test_parse_miss_comma_or_square_bracket() {\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, \"[1\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, \"[1}\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, \"[1 2\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, \"[[]\");\n}\n\nstatic void test_parse_miss_key() {\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_KEY, \"{:1,\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_KEY, \"{1:1,\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_KEY, \"{true:1,\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_KEY, \"{false:1,\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_KEY, \"{null:1,\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_KEY, \"{[]:1,\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_KEY, \"{{}:1,\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_KEY, \"{\\\"a\\\":1,\");\n}\n\nstatic void test_parse_miss_colon() {\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_COLON, \"{\\\"a\\\"}\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_COLON, \"{\\\"a\\\",\\\"b\\\"}\");\n}\n\nstatic void test_parse_miss_comma_or_curly_bracket() {\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET, \"{\\\"a\\\":1\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET, \"{\\\"a\\\":1]\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET, \"{\\\"a\\\":1 \\\"b\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET, \"{\\\"a\\\":{}\");\n}\n\nstatic void test_parse() {\n    test_parse_null();\n    test_parse_true();\n    test_parse_false();\n    test_parse_number();\n    test_parse_string();\n    test_parse_array();\n    test_parse_object();\n\n    test_parse_expect_value();\n    test_parse_invalid_value();\n    test_parse_root_not_singular();\n    test_parse_number_too_big();\n    test_parse_miss_quotation_mark();\n    test_parse_invalid_string_escape();\n    test_parse_invalid_string_char();\n    test_parse_invalid_unicode_hex();\n    test_parse_invalid_unicode_surrogate();\n    test_parse_miss_comma_or_square_bracket();\n    test_parse_miss_key();\n    test_parse_miss_colon();\n    test_parse_miss_comma_or_curly_bracket();\n}\n\n#define TEST_ROUNDTRIP(json)\\\n    do {\\\n        lept_value v;\\\n        char* json2;\\\n        size_t length;\\\n        lept_init(&v);\\\n        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));\\\n        json2 = lept_stringify(&v, &length);\\\n        EXPECT_EQ_STRING(json, json2, length);\\\n        lept_free(&v);\\\n        free(json2);\\\n    } while(0)\n\nstatic void test_stringify_number() {\n    TEST_ROUNDTRIP(\"0\");\n    TEST_ROUNDTRIP(\"-0\");\n    TEST_ROUNDTRIP(\"1\");\n    TEST_ROUNDTRIP(\"-1\");\n    TEST_ROUNDTRIP(\"1.5\");\n    TEST_ROUNDTRIP(\"-1.5\");\n    TEST_ROUNDTRIP(\"3.25\");\n    TEST_ROUNDTRIP(\"1e+20\");\n    TEST_ROUNDTRIP(\"1.234e+20\");\n    TEST_ROUNDTRIP(\"1.234e-20\");\n\n    TEST_ROUNDTRIP(\"1.0000000000000002\"); /* the smallest number > 1 */\n    TEST_ROUNDTRIP(\"4.9406564584124654e-324\"); /* minimum denormal */\n    TEST_ROUNDTRIP(\"-4.9406564584124654e-324\");\n    TEST_ROUNDTRIP(\"2.2250738585072009e-308\");  /* Max subnormal double */\n    TEST_ROUNDTRIP(\"-2.2250738585072009e-308\");\n    TEST_ROUNDTRIP(\"2.2250738585072014e-308\");  /* Min normal positive double */\n    TEST_ROUNDTRIP(\"-2.2250738585072014e-308\");\n    TEST_ROUNDTRIP(\"1.7976931348623157e+308\");  /* Max double */\n    TEST_ROUNDTRIP(\"-1.7976931348623157e+308\");\n}\n\nstatic void test_stringify_string() {\n    TEST_ROUNDTRIP(\"\\\"\\\"\");\n    TEST_ROUNDTRIP(\"\\\"Hello\\\"\");\n    TEST_ROUNDTRIP(\"\\\"Hello\\\\nWorld\\\"\");\n    TEST_ROUNDTRIP(\"\\\"\\\\\\\" \\\\\\\\ / \\\\b \\\\f \\\\n \\\\r \\\\t\\\"\");\n    TEST_ROUNDTRIP(\"\\\"Hello\\\\u0000World\\\"\");\n}\n\nstatic void test_stringify_array() {\n    TEST_ROUNDTRIP(\"[]\");\n    TEST_ROUNDTRIP(\"[null,false,true,123,\\\"abc\\\",[1,2,3]]\");\n}\n\nstatic void test_stringify_object() {\n    TEST_ROUNDTRIP(\"{}\");\n    TEST_ROUNDTRIP(\"{\\\"n\\\":null,\\\"f\\\":false,\\\"t\\\":true,\\\"i\\\":123,\\\"s\\\":\\\"abc\\\",\\\"a\\\":[1,2,3],\\\"o\\\":{\\\"1\\\":1,\\\"2\\\":2,\\\"3\\\":3}}\");\n}\n\nstatic void test_stringify() {\n    TEST_ROUNDTRIP(\"null\");\n    TEST_ROUNDTRIP(\"false\");\n    TEST_ROUNDTRIP(\"true\");\n    test_stringify_number();\n    test_stringify_string();\n    test_stringify_array();\n    test_stringify_object();\n}\n\nstatic void test_access_null() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"a\", 1);\n    lept_set_null(&v);\n    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\n    lept_free(&v);\n}\n\nstatic void test_access_boolean() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"a\", 1);\n    lept_set_boolean(&v, 1);\n    EXPECT_TRUE(lept_get_boolean(&v));\n    lept_set_boolean(&v, 0);\n    EXPECT_FALSE(lept_get_boolean(&v));\n    lept_free(&v);\n}\n\nstatic void test_access_number() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"a\", 1);\n    lept_set_number(&v, 1234.5);\n    EXPECT_EQ_DOUBLE(1234.5, lept_get_number(&v));\n    lept_free(&v);\n}\n\nstatic void test_access_string() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"\", 0);\n    EXPECT_EQ_STRING(\"\", lept_get_string(&v), lept_get_string_length(&v));\n    lept_set_string(&v, \"Hello\", 5);\n    EXPECT_EQ_STRING(\"Hello\", lept_get_string(&v), lept_get_string_length(&v));\n    lept_free(&v);\n}\n\nstatic void test_access() {\n    test_access_null();\n    test_access_boolean();\n    test_access_number();\n    test_access_string();\n}\n\nint main() {\n#ifdef _WINDOWS\n    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);\n#endif\n    test_parse();\n    test_stringify();\n    test_access();\n    printf(\"%d/%d (%3.2f%%) passed\\n\", test_pass, test_count, test_pass * 100.0 / test_count);\n    return main_ret;\n}\n"
  },
  {
    "path": "tutorial07_answer/tutorial07_answer.md",
    "content": "# 从零开始的 JSON 库教程（七）：生成器解答篇\n\n* Milo Yip\n* 2017/1/5\n\n本文是[《从零开始的 JSON 库教程》](https://zhuanlan.zhihu.com/json-tutorial)的第七个单元解答篇。解答代码位于 [json-tutorial/tutorial07_answer](https://github.com/miloyip/json-tutorial/blob/master/tutorial07_answer)。\n\n## 1. 生成字符串\n\n我们需要对一些字符进行转义，最简单的实现如下：\n\n~~~c\nstatic void lept_stringify_string(lept_context* c, const char* s, size_t len) {\n    size_t i;\n    assert(s != NULL);\n    PUTC(c, '\"');\n    for (i = 0; i < len; i++) {\n        unsigned char ch = (unsigned char)s[i];\n        switch (ch) {\n            case '\\\"': PUTS(c, \"\\\\\\\"\", 2); break;\n            case '\\\\': PUTS(c, \"\\\\\\\\\", 2); break;\n            case '\\b': PUTS(c, \"\\\\b\",  2); break;\n            case '\\f': PUTS(c, \"\\\\f\",  2); break;\n            case '\\n': PUTS(c, \"\\\\n\",  2); break;\n            case '\\r': PUTS(c, \"\\\\r\",  2); break;\n            case '\\t': PUTS(c, \"\\\\t\",  2); break;\n            default:\n                if (ch < 0x20) {\n                    char buffer[7];\n                    sprintf(buffer, \"\\\\u%04X\", ch);\n                    PUTS(c, buffer, 6);\n                }\n                else\n                    PUTC(c, s[i]);\n        }\n    }\n    PUTC(c, '\"');\n}\n\nstatic void lept_stringify_value(lept_context* c, const lept_value* v) {\n    switch (v->type) {\n        /* ... */\n        case LEPT_STRING: lept_stringify_string(c, v->u.s.s, v->u.s.len); break;\n        /* ... */\n    }\n}\n~~~\n\n注意到，十六进位输出的字母可以用大写或小写，我们这里选择了大写，所以 roundtrip 测试时也用大写。但这个并不是必然的，输出小写（用 `\"\\\\u%04x\"`）也可以。\n\n## 2. 生成数组和对象\n\n生成数组也是非常简单，只要输出 `[` 和 `]`，中间对逐个子值递归调用 `lept_stringify_value()`。只要注意在第一个元素后才加入 `,`。而对象也仅是多了一个键和 `:`。\n\n~~~cs\nstatic void lept_stringify_value(lept_context* c, const lept_value* v) {\n    size_t i;\n    switch (v->type) {\n        /* ... */\n        case LEPT_ARRAY:\n            PUTC(c, '[');\n            for (i = 0; i < v->u.a.size; i++) {\n                if (i > 0)\n                    PUTC(c, ',');\n                lept_stringify_value(c, &v->u.a.e[i]);\n            }\n            PUTC(c, ']');\n            break;\n        case LEPT_OBJECT:\n            PUTC(c, '{');\n            for (i = 0; i < v->u.o.size; i++) {\n                if (i > 0)\n                    PUTC(c, ',');\n                lept_stringify_string(c, v->u.o.m[i].k, v->u.o.m[i].klen);\n                PUTC(c, ':');\n                lept_stringify_value(c, &v->u.o.m[i].v);\n            }\n            PUTC(c, '}');\n            break;\n        /* ... */\n    }\n}\n~~~\n\n## 3. 优化 `lept_stringify_string()`\n\n最后，我们讨论一下优化。上面的 `lept_stringify_string()` 实现中，每次输出一个字符／字符串，都要调用 `lept_context_push()`。如果我们使用一些性能剖测工具，也可能会发现这个函数消耗较多 CPU。\n\n~~~c\nstatic void* lept_context_push(lept_context* c, size_t size) {\n    void* ret;\n    assert(size > 0);\n    if (c->top + size >= c->size) { // (1)\n        if (c->size == 0)\n            c->size = LEPT_PARSE_STACK_INIT_SIZE;\n        while (c->top + size >= c->size)\n            c->size += c->size >> 1;  /* c->size * 1.5 */\n        c->stack = (char*)realloc(c->stack, c->size);\n    }\n    ret = c->stack + c->top;       // (2)\n    c->top += size;                // (3)\n    return ret;                    // (4)\n}\n~~~\n\n中间最花费时间的，应该会是 (1)，需要计算而且作分支检查。即使使用 C99 的 `inline` 关键字（或使用宏）去减少函数调用的开销，这个分支也无法避免。\n\n所以，一个优化的点子是，预先分配足够的内存，每次加入字符就不用做这个检查了。但多大的内存才足够呢？我们可以看到，每个字符可生成最长的形式是 `\\u00XX`，占 6 个字符，再加上前后两个双引号，也就是共 `len * 6 + 2` 个输出字符。那么，使用 `char* p = lept_context_push()` 作一次分配后，便可以用 `*p++ = c` 去输出字符了。最后，再按实际输出量调整堆栈指针。\n\n另一个小优化点，是自行编写十六进位输出，避免了 `printf()` 内解析格式的开销。\n\n~~~c\nstatic void lept_stringify_string(lept_context* c, const char* s, size_t len) {\n    static const char hex_digits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };\n    size_t i, size;\n    char* head, *p;\n    assert(s != NULL);\n    p = head = lept_context_push(c, size = len * 6 + 2); /* \"\\u00xx...\" */\n    *p++ = '\"';\n    for (i = 0; i < len; i++) {\n        unsigned char ch = (unsigned char)s[i];\n        switch (ch) {\n            case '\\\"': *p++ = '\\\\'; *p++ = '\\\"'; break;\n            case '\\\\': *p++ = '\\\\'; *p++ = '\\\\'; break;\n            case '\\b': *p++ = '\\\\'; *p++ = 'b';  break;\n            case '\\f': *p++ = '\\\\'; *p++ = 'f';  break;\n            case '\\n': *p++ = '\\\\'; *p++ = 'n';  break;\n            case '\\r': *p++ = '\\\\'; *p++ = 'r';  break;\n            case '\\t': *p++ = '\\\\'; *p++ = 't';  break;\n            default:\n                if (ch < 0x20) {\n                    *p++ = '\\\\'; *p++ = 'u'; *p++ = '0'; *p++ = '0';\n                    *p++ = hex_digits[ch >> 4];\n                    *p++ = hex_digits[ch & 15];\n                }\n                else\n                    *p++ = s[i];\n        }\n    }\n    *p++ = '\"';\n    c->top -= size - (p - head);\n}\n~~~\n\n要注意的是，很多优化都是有代价的。第一个优化采取空间换时间的策略，对于只含一个字符串的JSON，很可能会分配多 6 倍内存；但对于正常含多个值的 JSON，多分配的内存可在之后的值所利用，不会造成太多浪费。\n\n而第二个优化的缺点，就是有稍增加了一点程序体积。也许有人会问，为什么 `hex_digits` 不用字符串字面量 `\"0123456789ABCDEF\"`？其实是可以的，但这会多浪费 1 个字节（实际因数据对齐可能会浪费 4 个或更多）。\n\n## 4. 总结\n\n我们用 80 行左右的代码就实现了 JSON 生成器，并尝试了做一些简单的优化。除了这种最简单的功能，有一些 JSON 库还会提供一些美化功能，即加入缩进及换行。另外，有一些应用可能需要大量输出数字，那么就可能需要优化数字的输出。这方面可考虑 C++ 开源库 [double-conversion](https://github.com/google/double-conversion)，以及参考本人另一篇文章《[RapidJSON 代码剖析（四）：优化 Grisu](https://zhuanlan.zhihu.com/p/20092285)》。\n\n现时数组和对象类型只有最基本的访问、修改函数，我们会在下一篇补完。\n\n如果你遇到问题，有不理解的地方，或是有建议，都欢迎在评论或 [issue](https://github.com/miloyip/json-tutorial/issues) 中提出，让所有人一起讨论。\n"
  },
  {
    "path": "tutorial08/CMakeLists.txt",
    "content": "cmake_minimum_required (VERSION 2.6)\nproject (leptjson_test C)\n\nif (CMAKE_C_COMPILER_ID MATCHES \"GNU|Clang\")\n    set(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -ansi -pedantic -Wall\")\nendif()\n\nadd_library(leptjson leptjson.c)\nadd_executable(leptjson_test test.c)\ntarget_link_libraries(leptjson_test leptjson)\n"
  },
  {
    "path": "tutorial08/leptjson.c",
    "content": "#ifdef _WINDOWS\n#define _CRTDBG_MAP_ALLOC\n#include <crtdbg.h>\n#endif\n#include \"leptjson.h\"\n#include <assert.h>  /* assert() */\n#include <errno.h>   /* errno, ERANGE */\n#include <math.h>    /* HUGE_VAL */\n#include <stdio.h>   /* sprintf() */\n#include <stdlib.h>  /* NULL, malloc(), realloc(), free(), strtod() */\n#include <string.h>  /* memcpy() */\n\n#ifndef LEPT_PARSE_STACK_INIT_SIZE\n#define LEPT_PARSE_STACK_INIT_SIZE 256\n#endif\n\n#ifndef LEPT_PARSE_STRINGIFY_INIT_SIZE\n#define LEPT_PARSE_STRINGIFY_INIT_SIZE 256\n#endif\n\n#define EXPECT(c, ch)       do { assert(*c->json == (ch)); c->json++; } while(0)\n#define ISDIGIT(ch)         ((ch) >= '0' && (ch) <= '9')\n#define ISDIGIT1TO9(ch)     ((ch) >= '1' && (ch) <= '9')\n#define PUTC(c, ch)         do { *(char*)lept_context_push(c, sizeof(char)) = (ch); } while(0)\n#define PUTS(c, s, len)     memcpy(lept_context_push(c, len), s, len)\n\ntypedef struct {\n    const char* json;\n    char* stack;\n    size_t size, top;\n}lept_context;\n\nstatic void* lept_context_push(lept_context* c, size_t size) {\n    void* ret;\n    assert(size > 0);\n    if (c->top + size >= c->size) {\n        if (c->size == 0)\n            c->size = LEPT_PARSE_STACK_INIT_SIZE;\n        while (c->top + size >= c->size)\n            c->size += c->size >> 1;  /* c->size * 1.5 */\n        c->stack = (char*)realloc(c->stack, c->size);\n    }\n    ret = c->stack + c->top;\n    c->top += size;\n    return ret;\n}\n\nstatic void* lept_context_pop(lept_context* c, size_t size) {\n    assert(c->top >= size);\n    return c->stack + (c->top -= size);\n}\n\nstatic void lept_parse_whitespace(lept_context* c) {\n    const char *p = c->json;\n    while (*p == ' ' || *p == '\\t' || *p == '\\n' || *p == '\\r')\n        p++;\n    c->json = p;\n}\n\nstatic int lept_parse_literal(lept_context* c, lept_value* v, const char* literal, lept_type type) {\n    size_t i;\n    EXPECT(c, literal[0]);\n    for (i = 0; literal[i + 1]; i++)\n        if (c->json[i] != literal[i + 1])\n            return LEPT_PARSE_INVALID_VALUE;\n    c->json += i;\n    v->type = type;\n    return LEPT_PARSE_OK;\n}\n\nstatic int lept_parse_number(lept_context* c, lept_value* v) {\n    const char* p = c->json;\n    if (*p == '-') p++;\n    if (*p == '0') p++;\n    else {\n        if (!ISDIGIT1TO9(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n    if (*p == '.') {\n        p++;\n        if (!ISDIGIT(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n    if (*p == 'e' || *p == 'E') {\n        p++;\n        if (*p == '+' || *p == '-') p++;\n        if (!ISDIGIT(*p)) return LEPT_PARSE_INVALID_VALUE;\n        for (p++; ISDIGIT(*p); p++);\n    }\n    errno = 0;\n    v->u.n = strtod(c->json, NULL);\n    if (errno == ERANGE && (v->u.n == HUGE_VAL || v->u.n == -HUGE_VAL))\n        return LEPT_PARSE_NUMBER_TOO_BIG;\n    v->type = LEPT_NUMBER;\n    c->json = p;\n    return LEPT_PARSE_OK;\n}\n\nstatic const char* lept_parse_hex4(const char* p, unsigned* u) {\n    int i;\n    *u = 0;\n    for (i = 0; i < 4; i++) {\n        char ch = *p++;\n        *u <<= 4;\n        if      (ch >= '0' && ch <= '9')  *u |= ch - '0';\n        else if (ch >= 'A' && ch <= 'F')  *u |= ch - ('A' - 10);\n        else if (ch >= 'a' && ch <= 'f')  *u |= ch - ('a' - 10);\n        else return NULL;\n    }\n    return p;\n}\n\nstatic void lept_encode_utf8(lept_context* c, unsigned u) {\n    if (u <= 0x7F) \n        PUTC(c, u & 0xFF);\n    else if (u <= 0x7FF) {\n        PUTC(c, 0xC0 | ((u >> 6) & 0xFF));\n        PUTC(c, 0x80 | ( u       & 0x3F));\n    }\n    else if (u <= 0xFFFF) {\n        PUTC(c, 0xE0 | ((u >> 12) & 0xFF));\n        PUTC(c, 0x80 | ((u >>  6) & 0x3F));\n        PUTC(c, 0x80 | ( u        & 0x3F));\n    }\n    else {\n        assert(u <= 0x10FFFF);\n        PUTC(c, 0xF0 | ((u >> 18) & 0xFF));\n        PUTC(c, 0x80 | ((u >> 12) & 0x3F));\n        PUTC(c, 0x80 | ((u >>  6) & 0x3F));\n        PUTC(c, 0x80 | ( u        & 0x3F));\n    }\n}\n\n#define STRING_ERROR(ret) do { c->top = head; return ret; } while(0)\n\nstatic int lept_parse_string_raw(lept_context* c, char** str, size_t* len) {\n    size_t head = c->top;\n    unsigned u, u2;\n    const char* p;\n    EXPECT(c, '\\\"');\n    p = c->json;\n    for (;;) {\n        char ch = *p++;\n        switch (ch) {\n            case '\\\"':\n                *len = c->top - head;\n                *str = lept_context_pop(c, *len);\n                c->json = p;\n                return LEPT_PARSE_OK;\n            case '\\\\':\n                switch (*p++) {\n                    case '\\\"': PUTC(c, '\\\"'); break;\n                    case '\\\\': PUTC(c, '\\\\'); break;\n                    case '/':  PUTC(c, '/' ); break;\n                    case 'b':  PUTC(c, '\\b'); break;\n                    case 'f':  PUTC(c, '\\f'); break;\n                    case 'n':  PUTC(c, '\\n'); break;\n                    case 'r':  PUTC(c, '\\r'); break;\n                    case 't':  PUTC(c, '\\t'); break;\n                    case 'u':\n                        if (!(p = lept_parse_hex4(p, &u)))\n                            STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX);\n                        if (u >= 0xD800 && u <= 0xDBFF) { /* surrogate pair */\n                            if (*p++ != '\\\\')\n                                STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE);\n                            if (*p++ != 'u')\n                                STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE);\n                            if (!(p = lept_parse_hex4(p, &u2)))\n                                STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX);\n                            if (u2 < 0xDC00 || u2 > 0xDFFF)\n                                STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE);\n                            u = (((u - 0xD800) << 10) | (u2 - 0xDC00)) + 0x10000;\n                        }\n                        lept_encode_utf8(c, u);\n                        break;\n                    default:\n                        STRING_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE);\n                }\n                break;\n            case '\\0':\n                STRING_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK);\n            default:\n                if ((unsigned char)ch < 0x20)\n                    STRING_ERROR(LEPT_PARSE_INVALID_STRING_CHAR);\n                PUTC(c, ch);\n        }\n    }\n}\n\nstatic int lept_parse_string(lept_context* c, lept_value* v) {\n    int ret;\n    char* s;\n    size_t len;\n    if ((ret = lept_parse_string_raw(c, &s, &len)) == LEPT_PARSE_OK)\n        lept_set_string(v, s, len);\n    return ret;\n}\n\nstatic int lept_parse_value(lept_context* c, lept_value* v);\n\nstatic int lept_parse_array(lept_context* c, lept_value* v) {\n    size_t i, size = 0;\n    int ret;\n    EXPECT(c, '[');\n    lept_parse_whitespace(c);\n    if (*c->json == ']') {\n        c->json++;\n        lept_set_array(v, 0);\n        return LEPT_PARSE_OK;\n    }\n    for (;;) {\n        lept_value e;\n        lept_init(&e);\n        if ((ret = lept_parse_value(c, &e)) != LEPT_PARSE_OK)\n            break;\n        memcpy(lept_context_push(c, sizeof(lept_value)), &e, sizeof(lept_value));\n        size++;\n        lept_parse_whitespace(c);\n        if (*c->json == ',') {\n            c->json++;\n            lept_parse_whitespace(c);\n        }\n        else if (*c->json == ']') {\n            c->json++;\n            lept_set_array(v, size);\n            memcpy(v->u.a.e, lept_context_pop(c, size * sizeof(lept_value)), size * sizeof(lept_value));\n            v->u.a.size = size;\n            return LEPT_PARSE_OK;\n        }\n        else {\n            ret = LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET;\n            break;\n        }\n    }\n    /* Pop and free values on the stack */\n    for (i = 0; i < size; i++)\n        lept_free((lept_value*)lept_context_pop(c, sizeof(lept_value)));\n    return ret;\n}\n\nstatic int lept_parse_object(lept_context* c, lept_value* v) {\n    size_t i, size;\n    lept_member m;\n    int ret;\n    EXPECT(c, '{');\n    lept_parse_whitespace(c);\n    if (*c->json == '}') {\n        c->json++;\n        lept_set_object(v, 0);\n        return LEPT_PARSE_OK;\n    }\n    m.k = NULL;\n    size = 0;\n    for (;;) {\n        char* str;\n        lept_init(&m.v);\n        /* parse key */\n        if (*c->json != '\"') {\n            ret = LEPT_PARSE_MISS_KEY;\n            break;\n        }\n        if ((ret = lept_parse_string_raw(c, &str, &m.klen)) != LEPT_PARSE_OK)\n            break;\n        memcpy(m.k = (char*)malloc(m.klen + 1), str, m.klen);\n        m.k[m.klen] = '\\0';\n        /* parse ws colon ws */\n        lept_parse_whitespace(c);\n        if (*c->json != ':') {\n            ret = LEPT_PARSE_MISS_COLON;\n            break;\n        }\n        c->json++;\n        lept_parse_whitespace(c);\n        /* parse value */\n        if ((ret = lept_parse_value(c, &m.v)) != LEPT_PARSE_OK)\n            break;\n        memcpy(lept_context_push(c, sizeof(lept_member)), &m, sizeof(lept_member));\n        size++;\n        m.k = NULL; /* ownership is transferred to member on stack */\n        /* parse ws [comma | right-curly-brace] ws */\n        lept_parse_whitespace(c);\n        if (*c->json == ',') {\n            c->json++;\n            lept_parse_whitespace(c);\n        }\n        else if (*c->json == '}') {\n            c->json++;\n            lept_set_object(v, size);\n            memcpy(v->u.o.m, lept_context_pop(c, sizeof(lept_member) * size), sizeof(lept_member) * size);\n            v->u.o.size = size;\n            return LEPT_PARSE_OK;\n        }\n        else {\n            ret = LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET;\n            break;\n        }\n    }\n    /* Pop and free members on the stack */\n    free(m.k);\n    for (i = 0; i < size; i++) {\n        lept_member* m = (lept_member*)lept_context_pop(c, sizeof(lept_member));\n        free(m->k);\n        lept_free(&m->v);\n    }\n    v->type = LEPT_NULL;\n    return ret;\n}\n\nstatic int lept_parse_value(lept_context* c, lept_value* v) {\n    switch (*c->json) {\n        case 't':  return lept_parse_literal(c, v, \"true\", LEPT_TRUE);\n        case 'f':  return lept_parse_literal(c, v, \"false\", LEPT_FALSE);\n        case 'n':  return lept_parse_literal(c, v, \"null\", LEPT_NULL);\n        default:   return lept_parse_number(c, v);\n        case '\"':  return lept_parse_string(c, v);\n        case '[':  return lept_parse_array(c, v);\n        case '{':  return lept_parse_object(c, v);\n        case '\\0': return LEPT_PARSE_EXPECT_VALUE;\n    }\n}\n\nint lept_parse(lept_value* v, const char* json) {\n    lept_context c;\n    int ret;\n    assert(v != NULL);\n    c.json = json;\n    c.stack = NULL;\n    c.size = c.top = 0;\n    lept_init(v);\n    lept_parse_whitespace(&c);\n    if ((ret = lept_parse_value(&c, v)) == LEPT_PARSE_OK) {\n        lept_parse_whitespace(&c);\n        if (*c.json != '\\0') {\n            v->type = LEPT_NULL;\n            ret = LEPT_PARSE_ROOT_NOT_SINGULAR;\n        }\n    }\n    assert(c.top == 0);\n    free(c.stack);\n    return ret;\n}\n\nstatic void lept_stringify_string(lept_context* c, const char* s, size_t len) {\n    static const char hex_digits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };\n    size_t i, size;\n    char* head, *p;\n    assert(s != NULL);\n    p = head = lept_context_push(c, size = len * 6 + 2); /* \"\\u00xx...\" */\n    *p++ = '\"';\n    for (i = 0; i < len; i++) {\n        unsigned char ch = (unsigned char)s[i];\n        switch (ch) {\n            case '\\\"': *p++ = '\\\\'; *p++ = '\\\"'; break;\n            case '\\\\': *p++ = '\\\\'; *p++ = '\\\\'; break;\n            case '\\b': *p++ = '\\\\'; *p++ = 'b';  break;\n            case '\\f': *p++ = '\\\\'; *p++ = 'f';  break;\n            case '\\n': *p++ = '\\\\'; *p++ = 'n';  break;\n            case '\\r': *p++ = '\\\\'; *p++ = 'r';  break;\n            case '\\t': *p++ = '\\\\'; *p++ = 't';  break;\n            default:\n                if (ch < 0x20) {\n                    *p++ = '\\\\'; *p++ = 'u'; *p++ = '0'; *p++ = '0';\n                    *p++ = hex_digits[ch >> 4];\n                    *p++ = hex_digits[ch & 15];\n                }\n                else\n                    *p++ = s[i];\n        }\n    }\n    *p++ = '\"';\n    c->top -= size - (p - head);\n}\n\nstatic void lept_stringify_value(lept_context* c, const lept_value* v) {\n    size_t i;\n    switch (v->type) {\n        case LEPT_NULL:   PUTS(c, \"null\",  4); break;\n        case LEPT_FALSE:  PUTS(c, \"false\", 5); break;\n        case LEPT_TRUE:   PUTS(c, \"true\",  4); break;\n        case LEPT_NUMBER: c->top -= 32 - sprintf(lept_context_push(c, 32), \"%.17g\", v->u.n); break;\n        case LEPT_STRING: lept_stringify_string(c, v->u.s.s, v->u.s.len); break;\n        case LEPT_ARRAY:\n            PUTC(c, '[');\n            for (i = 0; i < v->u.a.size; i++) {\n                if (i > 0)\n                    PUTC(c, ',');\n                lept_stringify_value(c, &v->u.a.e[i]);\n            }\n            PUTC(c, ']');\n            break;\n        case LEPT_OBJECT:\n            PUTC(c, '{');\n            for (i = 0; i < v->u.o.size; i++) {\n                if (i > 0)\n                    PUTC(c, ',');\n                lept_stringify_string(c, v->u.o.m[i].k, v->u.o.m[i].klen);\n                PUTC(c, ':');\n                lept_stringify_value(c, &v->u.o.m[i].v);\n            }\n            PUTC(c, '}');\n            break;\n        default: assert(0 && \"invalid type\");\n    }\n}\n\nchar* lept_stringify(const lept_value* v, size_t* length) {\n    lept_context c;\n    assert(v != NULL);\n    c.stack = (char*)malloc(c.size = LEPT_PARSE_STRINGIFY_INIT_SIZE);\n    c.top = 0;\n    lept_stringify_value(&c, v);\n    if (length)\n        *length = c.top;\n    PUTC(&c, '\\0');\n    return c.stack;\n}\n\nvoid lept_copy(lept_value* dst, const lept_value* src) {\n    assert(src != NULL && dst != NULL && src != dst);\n    switch (src->type) {\n        case LEPT_STRING:\n            lept_set_string(dst, src->u.s.s, src->u.s.len);\n            break;\n        case LEPT_ARRAY:\n            /* \\todo */\n            break;\n        case LEPT_OBJECT:\n            /* \\todo */\n            break;\n        default:\n            lept_free(dst);\n            memcpy(dst, src, sizeof(lept_value));\n            break;\n    }\n}\n\nvoid lept_move(lept_value* dst, lept_value* src) {\n    assert(dst != NULL && src != NULL && src != dst);\n    lept_free(dst);\n    memcpy(dst, src, sizeof(lept_value));\n    lept_init(src);\n}\n\nvoid lept_swap(lept_value* lhs, lept_value* rhs) {\n    assert(lhs != NULL && rhs != NULL);\n    if (lhs != rhs) {\n        lept_value temp;\n        memcpy(&temp, lhs, sizeof(lept_value));\n        memcpy(lhs,   rhs, sizeof(lept_value));\n        memcpy(rhs, &temp, sizeof(lept_value));\n    }\n}\n\nvoid lept_free(lept_value* v) {\n    size_t i;\n    assert(v != NULL);\n    switch (v->type) {\n        case LEPT_STRING:\n            free(v->u.s.s);\n            break;\n        case LEPT_ARRAY:\n            for (i = 0; i < v->u.a.size; i++)\n                lept_free(&v->u.a.e[i]);\n            free(v->u.a.e);\n            break;\n        case LEPT_OBJECT:\n            for (i = 0; i < v->u.o.size; i++) {\n                free(v->u.o.m[i].k);\n                lept_free(&v->u.o.m[i].v);\n            }\n            free(v->u.o.m);\n            break;\n        default: break;\n    }\n    v->type = LEPT_NULL;\n}\n\nlept_type lept_get_type(const lept_value* v) {\n    assert(v != NULL);\n    return v->type;\n}\n\nint lept_is_equal(const lept_value* lhs, const lept_value* rhs) {\n    size_t i;\n    assert(lhs != NULL && rhs != NULL);\n    if (lhs->type != rhs->type)\n        return 0;\n    switch (lhs->type) {\n        case LEPT_STRING:\n            return lhs->u.s.len == rhs->u.s.len && \n                memcmp(lhs->u.s.s, rhs->u.s.s, lhs->u.s.len) == 0;\n        case LEPT_NUMBER:\n            return lhs->u.n == rhs->u.n;\n        case LEPT_ARRAY:\n            if (lhs->u.a.size != rhs->u.a.size)\n                return 0;\n            for (i = 0; i < lhs->u.a.size; i++)\n                if (!lept_is_equal(&lhs->u.a.e[i], &rhs->u.a.e[i]))\n                    return 0;\n            return 1;\n        case LEPT_OBJECT:\n            /* \\todo */\n            return 1;\n        default:\n            return 1;\n    }\n}\n\nint lept_get_boolean(const lept_value* v) {\n    assert(v != NULL && (v->type == LEPT_TRUE || v->type == LEPT_FALSE));\n    return v->type == LEPT_TRUE;\n}\n\nvoid lept_set_boolean(lept_value* v, int b) {\n    lept_free(v);\n    v->type = b ? LEPT_TRUE : LEPT_FALSE;\n}\n\ndouble lept_get_number(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_NUMBER);\n    return v->u.n;\n}\n\nvoid lept_set_number(lept_value* v, double n) {\n    lept_free(v);\n    v->u.n = n;\n    v->type = LEPT_NUMBER;\n}\n\nconst char* lept_get_string(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_STRING);\n    return v->u.s.s;\n}\n\nsize_t lept_get_string_length(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_STRING);\n    return v->u.s.len;\n}\n\nvoid lept_set_string(lept_value* v, const char* s, size_t len) {\n    assert(v != NULL && (s != NULL || len == 0));\n    lept_free(v);\n    v->u.s.s = (char*)malloc(len + 1);\n    memcpy(v->u.s.s, s, len);\n    v->u.s.s[len] = '\\0';\n    v->u.s.len = len;\n    v->type = LEPT_STRING;\n}\n\nvoid lept_set_array(lept_value* v, size_t capacity) {\n    assert(v != NULL);\n    lept_free(v);\n    v->type = LEPT_ARRAY;\n    v->u.a.size = 0;\n    v->u.a.capacity = capacity;\n    v->u.a.e = capacity > 0 ? (lept_value*)malloc(capacity * sizeof(lept_value)) : NULL;\n}\n\nsize_t lept_get_array_size(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_ARRAY);\n    return v->u.a.size;\n}\n\nsize_t lept_get_array_capacity(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_ARRAY);\n    return v->u.a.capacity;\n}\n\nvoid lept_reserve_array(lept_value* v, size_t capacity) {\n    assert(v != NULL && v->type == LEPT_ARRAY);\n    if (v->u.a.capacity < capacity) {\n        v->u.a.capacity = capacity;\n        v->u.a.e = (lept_value*)realloc(v->u.a.e, capacity * sizeof(lept_value));\n    }\n}\n\nvoid lept_shrink_array(lept_value* v) {\n    assert(v != NULL && v->type == LEPT_ARRAY);\n    if (v->u.a.capacity > v->u.a.size) {\n        v->u.a.capacity = v->u.a.size;\n        v->u.a.e = (lept_value*)realloc(v->u.a.e, v->u.a.capacity * sizeof(lept_value));\n    }\n}\n\nvoid lept_clear_array(lept_value* v) {\n    assert(v != NULL && v->type == LEPT_ARRAY);\n    lept_erase_array_element(v, 0, v->u.a.size);\n}\n\nlept_value* lept_get_array_element(lept_value* v, size_t index) {\n    assert(v != NULL && v->type == LEPT_ARRAY);\n    assert(index < v->u.a.size);\n    return &v->u.a.e[index];\n}\n\nlept_value* lept_pushback_array_element(lept_value* v) {\n    assert(v != NULL && v->type == LEPT_ARRAY);\n    if (v->u.a.size == v->u.a.capacity)\n        lept_reserve_array(v, v->u.a.capacity == 0 ? 1 : v->u.a.capacity * 2);\n    lept_init(&v->u.a.e[v->u.a.size]);\n    return &v->u.a.e[v->u.a.size++];\n}\n\nvoid lept_popback_array_element(lept_value* v) {\n    assert(v != NULL && v->type == LEPT_ARRAY && v->u.a.size > 0);\n    lept_free(&v->u.a.e[--v->u.a.size]);\n}\n\nlept_value* lept_insert_array_element(lept_value* v, size_t index) {\n    assert(v != NULL && v->type == LEPT_ARRAY && index <= v->u.a.size);\n    /* \\todo */\n    return NULL;\n}\n\nvoid lept_erase_array_element(lept_value* v, size_t index, size_t count) {\n    assert(v != NULL && v->type == LEPT_ARRAY && index + count <= v->u.a.size);\n    /* \\todo */\n}\n\nvoid lept_set_object(lept_value* v, size_t capacity) {\n    assert(v != NULL);\n    lept_free(v);\n    v->type = LEPT_OBJECT;\n    v->u.o.size = 0;\n    v->u.o.capacity = capacity;\n    v->u.o.m = capacity > 0 ? (lept_member*)malloc(capacity * sizeof(lept_member)) : NULL;\n}\n\nsize_t lept_get_object_size(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_OBJECT);\n    return v->u.o.size;\n}\n\nsize_t lept_get_object_capacity(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_OBJECT);\n    /* \\todo */\n    return 0;\n}\n\nvoid lept_reserve_object(lept_value* v, size_t capacity) {\n    assert(v != NULL && v->type == LEPT_OBJECT);\n    /* \\todo */\n}\n\nvoid lept_shrink_object(lept_value* v) {\n    assert(v != NULL && v->type == LEPT_OBJECT);\n    /* \\todo */\n}\n\nvoid lept_clear_object(lept_value* v) {\n    assert(v != NULL && v->type == LEPT_OBJECT);\n    /* \\todo */\n}\n\nconst char* lept_get_object_key(const lept_value* v, size_t index) {\n    assert(v != NULL && v->type == LEPT_OBJECT);\n    assert(index < v->u.o.size);\n    return v->u.o.m[index].k;\n}\n\nsize_t lept_get_object_key_length(const lept_value* v, size_t index) {\n    assert(v != NULL && v->type == LEPT_OBJECT);\n    assert(index < v->u.o.size);\n    return v->u.o.m[index].klen;\n}\n\nlept_value* lept_get_object_value(lept_value* v, size_t index) {\n    assert(v != NULL && v->type == LEPT_OBJECT);\n    assert(index < v->u.o.size);\n    return &v->u.o.m[index].v;\n}\n\nsize_t lept_find_object_index(const lept_value* v, const char* key, size_t klen) {\n    size_t i;\n    assert(v != NULL && v->type == LEPT_OBJECT && key != NULL);\n    for (i = 0; i < v->u.o.size; i++)\n        if (v->u.o.m[i].klen == klen && memcmp(v->u.o.m[i].k, key, klen) == 0)\n            return i;\n    return LEPT_KEY_NOT_EXIST;\n}\n\nlept_value* lept_find_object_value(lept_value* v, const char* key, size_t klen) {\n    size_t index = lept_find_object_index(v, key, klen);\n    return index != LEPT_KEY_NOT_EXIST ? &v->u.o.m[index].v : NULL;\n}\n\nlept_value* lept_set_object_value(lept_value* v, const char* key, size_t klen) {\n    assert(v != NULL && v->type == LEPT_OBJECT && key != NULL);\n    /* \\todo */\n    return NULL;\n}\n\nvoid lept_remove_object_value(lept_value* v, size_t index) {\n    assert(v != NULL && v->type == LEPT_OBJECT && index < v->u.o.size);\n    /* \\todo */\n}\n"
  },
  {
    "path": "tutorial08/leptjson.h",
    "content": "#ifndef LEPTJSON_H__\n#define LEPTJSON_H__\n\n#include <stddef.h> /* size_t */\n\ntypedef enum { LEPT_NULL, LEPT_FALSE, LEPT_TRUE, LEPT_NUMBER, LEPT_STRING, LEPT_ARRAY, LEPT_OBJECT } lept_type;\n\n#define LEPT_KEY_NOT_EXIST ((size_t)-1)\n\ntypedef struct lept_value lept_value;\ntypedef struct lept_member lept_member;\n\nstruct lept_value {\n    union {\n        struct { lept_member* m; size_t size, capacity; }o; /* object: members, member count, capacity */\n        struct { lept_value*  e; size_t size, capacity; }a; /* array:  elements, element count, capacity */\n        struct { char* s; size_t len; }s;                   /* string: null-terminated string, string length */\n        double n;                                           /* number */\n    }u;\n    lept_type type;\n};\n\nstruct lept_member {\n    char* k; size_t klen;   /* member key string, key string length */\n    lept_value v;           /* member value */\n};\n\nenum {\n    LEPT_PARSE_OK = 0,\n    LEPT_PARSE_EXPECT_VALUE,\n    LEPT_PARSE_INVALID_VALUE,\n    LEPT_PARSE_ROOT_NOT_SINGULAR,\n    LEPT_PARSE_NUMBER_TOO_BIG,\n    LEPT_PARSE_MISS_QUOTATION_MARK,\n    LEPT_PARSE_INVALID_STRING_ESCAPE,\n    LEPT_PARSE_INVALID_STRING_CHAR,\n    LEPT_PARSE_INVALID_UNICODE_HEX,\n    LEPT_PARSE_INVALID_UNICODE_SURROGATE,\n    LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET,\n    LEPT_PARSE_MISS_KEY,\n    LEPT_PARSE_MISS_COLON,\n    LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET\n};\n\n#define lept_init(v) do { (v)->type = LEPT_NULL; } while(0)\n\nint lept_parse(lept_value* v, const char* json);\nchar* lept_stringify(const lept_value* v, size_t* length);\n\nvoid lept_copy(lept_value* dst, const lept_value* src);\nvoid lept_move(lept_value* dst, lept_value* src);\nvoid lept_swap(lept_value* lhs, lept_value* rhs);\n\nvoid lept_free(lept_value* v);\n\nlept_type lept_get_type(const lept_value* v);\nint lept_is_equal(const lept_value* lhs, const lept_value* rhs);\n\n#define lept_set_null(v) lept_free(v)\n\nint lept_get_boolean(const lept_value* v);\nvoid lept_set_boolean(lept_value* v, int b);\n\ndouble lept_get_number(const lept_value* v);\nvoid lept_set_number(lept_value* v, double n);\n\nconst char* lept_get_string(const lept_value* v);\nsize_t lept_get_string_length(const lept_value* v);\nvoid lept_set_string(lept_value* v, const char* s, size_t len);\n\nvoid lept_set_array(lept_value* v, size_t capacity);\nsize_t lept_get_array_size(const lept_value* v);\nsize_t lept_get_array_capacity(const lept_value* v);\nvoid lept_reserve_array(lept_value* v, size_t capacity);\nvoid lept_shrink_array(lept_value* v);\nvoid lept_clear_array(lept_value* v);\nlept_value* lept_get_array_element(lept_value* v, size_t index);\nlept_value* lept_pushback_array_element(lept_value* v);\nvoid lept_popback_array_element(lept_value* v);\nlept_value* lept_insert_array_element(lept_value* v, size_t index);\nvoid lept_erase_array_element(lept_value* v, size_t index, size_t count);\n\nvoid lept_set_object(lept_value* v, size_t capacity);\nsize_t lept_get_object_size(const lept_value* v);\nsize_t lept_get_object_capacity(const lept_value* v);\nvoid lept_reserve_object(lept_value* v, size_t capacity);\nvoid lept_shrink_object(lept_value* v);\nvoid lept_clear_object(lept_value* v);\nconst char* lept_get_object_key(const lept_value* v, size_t index);\nsize_t lept_get_object_key_length(const lept_value* v, size_t index);\nlept_value* lept_get_object_value(lept_value* v, size_t index);\nsize_t lept_find_object_index(const lept_value* v, const char* key, size_t klen);\nlept_value* lept_find_object_value(lept_value* v, const char* key, size_t klen);\nlept_value* lept_set_object_value(lept_value* v, const char* key, size_t klen);\nvoid lept_remove_object_value(lept_value* v, size_t index);\n\n#endif /* LEPTJSON_H__ */\n"
  },
  {
    "path": "tutorial08/test.c",
    "content": "#ifdef _WINDOWS\n#define _CRTDBG_MAP_ALLOC\n#include <crtdbg.h>\n#endif\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"leptjson.h\"\n\nstatic int main_ret = 0;\nstatic int test_count = 0;\nstatic int test_pass = 0;\n\n#define EXPECT_EQ_BASE(equality, expect, actual, format) \\\n    do {\\\n        test_count++;\\\n        if (equality)\\\n            test_pass++;\\\n        else {\\\n            fprintf(stderr, \"%s:%d: expect: \" format \" actual: \" format \"\\n\", __FILE__, __LINE__, expect, actual);\\\n            main_ret = 1;\\\n        }\\\n    } while(0)\n\n#define EXPECT_EQ_INT(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, \"%d\")\n#define EXPECT_EQ_DOUBLE(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, \"%.17g\")\n#define EXPECT_EQ_STRING(expect, actual, alength) \\\n    EXPECT_EQ_BASE(sizeof(expect) - 1 == alength && memcmp(expect, actual, alength + 1) == 0, expect, actual, \"%s\")\n#define EXPECT_TRUE(actual) EXPECT_EQ_BASE((actual) != 0, \"true\", \"false\", \"%s\")\n#define EXPECT_FALSE(actual) EXPECT_EQ_BASE((actual) == 0, \"false\", \"true\", \"%s\")\n\n#if defined(_MSC_VER)\n#define EXPECT_EQ_SIZE_T(expect, actual) EXPECT_EQ_BASE((expect) == (actual), (size_t)expect, (size_t)actual, \"%Iu\")\n#else\n#define EXPECT_EQ_SIZE_T(expect, actual) EXPECT_EQ_BASE((expect) == (actual), (size_t)expect, (size_t)actual, \"%zu\")\n#endif\n\nstatic void test_parse_null() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_boolean(&v, 0);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"null\"));\n    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\n    lept_free(&v);\n}\n\nstatic void test_parse_true() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_boolean(&v, 0);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"true\"));\n    EXPECT_EQ_INT(LEPT_TRUE, lept_get_type(&v));\n    lept_free(&v);\n}\n\nstatic void test_parse_false() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_boolean(&v, 1);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"false\"));\n    EXPECT_EQ_INT(LEPT_FALSE, lept_get_type(&v));\n    lept_free(&v);\n}\n\n#define TEST_NUMBER(expect, json)\\\n    do {\\\n        lept_value v;\\\n        lept_init(&v);\\\n        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(&v));\\\n        EXPECT_EQ_DOUBLE(expect, lept_get_number(&v));\\\n        lept_free(&v);\\\n    } while(0)\n\nstatic void test_parse_number() {\n    TEST_NUMBER(0.0, \"0\");\n    TEST_NUMBER(0.0, \"-0\");\n    TEST_NUMBER(0.0, \"-0.0\");\n    TEST_NUMBER(1.0, \"1\");\n    TEST_NUMBER(-1.0, \"-1\");\n    TEST_NUMBER(1.5, \"1.5\");\n    TEST_NUMBER(-1.5, \"-1.5\");\n    TEST_NUMBER(3.1416, \"3.1416\");\n    TEST_NUMBER(1E10, \"1E10\");\n    TEST_NUMBER(1e10, \"1e10\");\n    TEST_NUMBER(1E+10, \"1E+10\");\n    TEST_NUMBER(1E-10, \"1E-10\");\n    TEST_NUMBER(-1E10, \"-1E10\");\n    TEST_NUMBER(-1e10, \"-1e10\");\n    TEST_NUMBER(-1E+10, \"-1E+10\");\n    TEST_NUMBER(-1E-10, \"-1E-10\");\n    TEST_NUMBER(1.234E+10, \"1.234E+10\");\n    TEST_NUMBER(1.234E-10, \"1.234E-10\");\n    TEST_NUMBER(0.0, \"1e-10000\"); /* must underflow */\n\n    TEST_NUMBER(1.0000000000000002, \"1.0000000000000002\"); /* the smallest number > 1 */\n    TEST_NUMBER( 4.9406564584124654e-324, \"4.9406564584124654e-324\"); /* minimum denormal */\n    TEST_NUMBER(-4.9406564584124654e-324, \"-4.9406564584124654e-324\");\n    TEST_NUMBER( 2.2250738585072009e-308, \"2.2250738585072009e-308\");  /* Max subnormal double */\n    TEST_NUMBER(-2.2250738585072009e-308, \"-2.2250738585072009e-308\");\n    TEST_NUMBER( 2.2250738585072014e-308, \"2.2250738585072014e-308\");  /* Min normal positive double */\n    TEST_NUMBER(-2.2250738585072014e-308, \"-2.2250738585072014e-308\");\n    TEST_NUMBER( 1.7976931348623157e+308, \"1.7976931348623157e+308\");  /* Max double */\n    TEST_NUMBER(-1.7976931348623157e+308, \"-1.7976931348623157e+308\");\n}\n\n#define TEST_STRING(expect, json)\\\n    do {\\\n        lept_value v;\\\n        lept_init(&v);\\\n        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_STRING, lept_get_type(&v));\\\n        EXPECT_EQ_STRING(expect, lept_get_string(&v), lept_get_string_length(&v));\\\n        lept_free(&v);\\\n    } while(0)\n\nstatic void test_parse_string() {\n    TEST_STRING(\"\", \"\\\"\\\"\");\n    TEST_STRING(\"Hello\", \"\\\"Hello\\\"\");\n    TEST_STRING(\"Hello\\nWorld\", \"\\\"Hello\\\\nWorld\\\"\");\n    TEST_STRING(\"\\\" \\\\ / \\b \\f \\n \\r \\t\", \"\\\"\\\\\\\" \\\\\\\\ \\\\/ \\\\b \\\\f \\\\n \\\\r \\\\t\\\"\");\n    TEST_STRING(\"Hello\\0World\", \"\\\"Hello\\\\u0000World\\\"\");\n    TEST_STRING(\"\\x24\", \"\\\"\\\\u0024\\\"\");         /* Dollar sign U+0024 */\n    TEST_STRING(\"\\xC2\\xA2\", \"\\\"\\\\u00A2\\\"\");     /* Cents sign U+00A2 */\n    TEST_STRING(\"\\xE2\\x82\\xAC\", \"\\\"\\\\u20AC\\\"\"); /* Euro sign U+20AC */\n    TEST_STRING(\"\\xF0\\x9D\\x84\\x9E\", \"\\\"\\\\uD834\\\\uDD1E\\\"\");  /* G clef sign U+1D11E */\n    TEST_STRING(\"\\xF0\\x9D\\x84\\x9E\", \"\\\"\\\\ud834\\\\udd1e\\\"\");  /* G clef sign U+1D11E */\n}\n\nstatic void test_parse_array() {\n    size_t i, j;\n    lept_value v;\n\n    lept_init(&v);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"[ ]\"));\n    EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(&v));\n    EXPECT_EQ_SIZE_T(0, lept_get_array_size(&v));\n    lept_free(&v);\n\n    lept_init(&v);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"[ null , false , true , 123 , \\\"abc\\\" ]\"));\n    EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(&v));\n    EXPECT_EQ_SIZE_T(5, lept_get_array_size(&v));\n    EXPECT_EQ_INT(LEPT_NULL,   lept_get_type(lept_get_array_element(&v, 0)));\n    EXPECT_EQ_INT(LEPT_FALSE,  lept_get_type(lept_get_array_element(&v, 1)));\n    EXPECT_EQ_INT(LEPT_TRUE,   lept_get_type(lept_get_array_element(&v, 2)));\n    EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(lept_get_array_element(&v, 3)));\n    EXPECT_EQ_INT(LEPT_STRING, lept_get_type(lept_get_array_element(&v, 4)));\n    EXPECT_EQ_DOUBLE(123.0, lept_get_number(lept_get_array_element(&v, 3)));\n    EXPECT_EQ_STRING(\"abc\", lept_get_string(lept_get_array_element(&v, 4)), lept_get_string_length(lept_get_array_element(&v, 4)));\n    lept_free(&v);\n\n    lept_init(&v);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \"[ [ ] , [ 0 ] , [ 0 , 1 ] , [ 0 , 1 , 2 ] ]\"));\n    EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(&v));\n    EXPECT_EQ_SIZE_T(4, lept_get_array_size(&v));\n    for (i = 0; i < 4; i++) {\n        lept_value* a = lept_get_array_element(&v, i);\n        EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(a));\n        EXPECT_EQ_SIZE_T(i, lept_get_array_size(a));\n        for (j = 0; j < i; j++) {\n            lept_value* e = lept_get_array_element(a, j);\n            EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(e));\n            EXPECT_EQ_DOUBLE((double)j, lept_get_number(e));\n        }\n    }\n    lept_free(&v);\n}\n\nstatic void test_parse_object() {\n    lept_value v;\n    size_t i;\n\n    lept_init(&v);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, \" { } \"));\n    EXPECT_EQ_INT(LEPT_OBJECT, lept_get_type(&v));\n    EXPECT_EQ_SIZE_T(0, lept_get_object_size(&v));\n    lept_free(&v);\n\n    lept_init(&v);\n    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v,\n        \" { \"\n        \"\\\"n\\\" : null , \"\n        \"\\\"f\\\" : false , \"\n        \"\\\"t\\\" : true , \"\n        \"\\\"i\\\" : 123 , \"\n        \"\\\"s\\\" : \\\"abc\\\", \"\n        \"\\\"a\\\" : [ 1, 2, 3 ],\"\n        \"\\\"o\\\" : { \\\"1\\\" : 1, \\\"2\\\" : 2, \\\"3\\\" : 3 }\"\n        \" } \"\n    ));\n    EXPECT_EQ_INT(LEPT_OBJECT, lept_get_type(&v));\n    EXPECT_EQ_SIZE_T(7, lept_get_object_size(&v));\n    EXPECT_EQ_STRING(\"n\", lept_get_object_key(&v, 0), lept_get_object_key_length(&v, 0));\n    EXPECT_EQ_INT(LEPT_NULL,   lept_get_type(lept_get_object_value(&v, 0)));\n    EXPECT_EQ_STRING(\"f\", lept_get_object_key(&v, 1), lept_get_object_key_length(&v, 1));\n    EXPECT_EQ_INT(LEPT_FALSE,  lept_get_type(lept_get_object_value(&v, 1)));\n    EXPECT_EQ_STRING(\"t\", lept_get_object_key(&v, 2), lept_get_object_key_length(&v, 2));\n    EXPECT_EQ_INT(LEPT_TRUE,   lept_get_type(lept_get_object_value(&v, 2)));\n    EXPECT_EQ_STRING(\"i\", lept_get_object_key(&v, 3), lept_get_object_key_length(&v, 3));\n    EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(lept_get_object_value(&v, 3)));\n    EXPECT_EQ_DOUBLE(123.0, lept_get_number(lept_get_object_value(&v, 3)));\n    EXPECT_EQ_STRING(\"s\", lept_get_object_key(&v, 4), lept_get_object_key_length(&v, 4));\n    EXPECT_EQ_INT(LEPT_STRING, lept_get_type(lept_get_object_value(&v, 4)));\n    EXPECT_EQ_STRING(\"abc\", lept_get_string(lept_get_object_value(&v, 4)), lept_get_string_length(lept_get_object_value(&v, 4)));\n    EXPECT_EQ_STRING(\"a\", lept_get_object_key(&v, 5), lept_get_object_key_length(&v, 5));\n    EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(lept_get_object_value(&v, 5)));\n    EXPECT_EQ_SIZE_T(3, lept_get_array_size(lept_get_object_value(&v, 5)));\n    for (i = 0; i < 3; i++) {\n        lept_value* e = lept_get_array_element(lept_get_object_value(&v, 5), i);\n        EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(e));\n        EXPECT_EQ_DOUBLE(i + 1.0, lept_get_number(e));\n    }\n    EXPECT_EQ_STRING(\"o\", lept_get_object_key(&v, 6), lept_get_object_key_length(&v, 6));\n    {\n        lept_value* o = lept_get_object_value(&v, 6);\n        EXPECT_EQ_INT(LEPT_OBJECT, lept_get_type(o));\n        for (i = 0; i < 3; i++) {\n            lept_value* ov = lept_get_object_value(o, i);\n            EXPECT_TRUE('1' + i == lept_get_object_key(o, i)[0]);\n            EXPECT_EQ_SIZE_T(1, lept_get_object_key_length(o, i));\n            EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(ov));\n            EXPECT_EQ_DOUBLE(i + 1.0, lept_get_number(ov));\n        }\n    }\n    lept_free(&v);\n}\n\n#define TEST_PARSE_ERROR(error, json)\\\n    do {\\\n        lept_value v;\\\n        lept_init(&v);\\\n        v.type = LEPT_FALSE;\\\n        EXPECT_EQ_INT(error, lept_parse(&v, json));\\\n        EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\\\n        lept_free(&v);\\\n    } while(0)\n\nstatic void test_parse_expect_value() {\n    TEST_PARSE_ERROR(LEPT_PARSE_EXPECT_VALUE, \"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_EXPECT_VALUE, \" \");\n}\n\nstatic void test_parse_invalid_value() {\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, \"nul\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, \"?\");\n\n    /* invalid number */\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, \"+0\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, \"+1\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, \".123\"); /* at least one digit before '.' */\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, \"1.\");   /* at least one digit after '.' */\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, \"INF\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, \"inf\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, \"NAN\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, \"nan\");\n\n    /* invalid value in array */\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, \"[1,]\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, \"[\\\"a\\\", nul]\");\n}\n\nstatic void test_parse_root_not_singular() {\n    TEST_PARSE_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"null x\");\n\n    /* invalid number */\n    TEST_PARSE_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0123\"); /* after zero should be '.' or nothing */\n    TEST_PARSE_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0x0\");\n    TEST_PARSE_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, \"0x123\");\n}\n\nstatic void test_parse_number_too_big() {\n    TEST_PARSE_ERROR(LEPT_PARSE_NUMBER_TOO_BIG, \"1e309\");\n    TEST_PARSE_ERROR(LEPT_PARSE_NUMBER_TOO_BIG, \"-1e309\");\n}\n\nstatic void test_parse_miss_quotation_mark() {\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK, \"\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK, \"\\\"abc\");\n}\n\nstatic void test_parse_invalid_string_escape() {\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\v\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\'\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\0\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, \"\\\"\\\\x12\\\"\");\n}\n\nstatic void test_parse_invalid_string_char() {\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_STRING_CHAR, \"\\\"\\x01\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_STRING_CHAR, \"\\\"\\x1F\\\"\");\n}\n\nstatic void test_parse_invalid_unicode_hex() {\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u0\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u01\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u012\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u/000\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\uG000\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u0/00\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u0G00\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u0/00\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u00G0\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u000/\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u000G\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, \"\\\"\\\\u 123\\\"\");\n}\n\nstatic void test_parse_invalid_unicode_surrogate() {\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uD800\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uDBFF\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uD800\\\\\\\\\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uD800\\\\uDBFF\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, \"\\\"\\\\uD800\\\\uE000\\\"\");\n}\n\nstatic void test_parse_miss_comma_or_square_bracket() {\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, \"[1\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, \"[1}\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, \"[1 2\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, \"[[]\");\n}\n\nstatic void test_parse_miss_key() {\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_KEY, \"{:1,\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_KEY, \"{1:1,\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_KEY, \"{true:1,\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_KEY, \"{false:1,\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_KEY, \"{null:1,\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_KEY, \"{[]:1,\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_KEY, \"{{}:1,\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_KEY, \"{\\\"a\\\":1,\");\n}\n\nstatic void test_parse_miss_colon() {\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_COLON, \"{\\\"a\\\"}\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_COLON, \"{\\\"a\\\",\\\"b\\\"}\");\n}\n\nstatic void test_parse_miss_comma_or_curly_bracket() {\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET, \"{\\\"a\\\":1\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET, \"{\\\"a\\\":1]\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET, \"{\\\"a\\\":1 \\\"b\\\"\");\n    TEST_PARSE_ERROR(LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET, \"{\\\"a\\\":{}\");\n}\n\nstatic void test_parse() {\n    test_parse_null();\n    test_parse_true();\n    test_parse_false();\n    test_parse_number();\n    test_parse_string();\n    test_parse_array();\n    test_parse_object();\n\n    test_parse_expect_value();\n    test_parse_invalid_value();\n    test_parse_root_not_singular();\n    test_parse_number_too_big();\n    test_parse_miss_quotation_mark();\n    test_parse_invalid_string_escape();\n    test_parse_invalid_string_char();\n    test_parse_invalid_unicode_hex();\n    test_parse_invalid_unicode_surrogate();\n    test_parse_miss_comma_or_square_bracket();\n    test_parse_miss_key();\n    test_parse_miss_colon();\n    test_parse_miss_comma_or_curly_bracket();\n}\n\n#define TEST_ROUNDTRIP(json)\\\n    do {\\\n        lept_value v;\\\n        char* json2;\\\n        size_t length;\\\n        lept_init(&v);\\\n        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));\\\n        json2 = lept_stringify(&v, &length);\\\n        EXPECT_EQ_STRING(json, json2, length);\\\n        lept_free(&v);\\\n        free(json2);\\\n    } while(0)\n\nstatic void test_stringify_number() {\n    TEST_ROUNDTRIP(\"0\");\n    TEST_ROUNDTRIP(\"-0\");\n    TEST_ROUNDTRIP(\"1\");\n    TEST_ROUNDTRIP(\"-1\");\n    TEST_ROUNDTRIP(\"1.5\");\n    TEST_ROUNDTRIP(\"-1.5\");\n    TEST_ROUNDTRIP(\"3.25\");\n    TEST_ROUNDTRIP(\"1e+20\");\n    TEST_ROUNDTRIP(\"1.234e+20\");\n    TEST_ROUNDTRIP(\"1.234e-20\");\n\n    TEST_ROUNDTRIP(\"1.0000000000000002\"); /* the smallest number > 1 */\n    TEST_ROUNDTRIP(\"4.9406564584124654e-324\"); /* minimum denormal */\n    TEST_ROUNDTRIP(\"-4.9406564584124654e-324\");\n    TEST_ROUNDTRIP(\"2.2250738585072009e-308\");  /* Max subnormal double */\n    TEST_ROUNDTRIP(\"-2.2250738585072009e-308\");\n    TEST_ROUNDTRIP(\"2.2250738585072014e-308\");  /* Min normal positive double */\n    TEST_ROUNDTRIP(\"-2.2250738585072014e-308\");\n    TEST_ROUNDTRIP(\"1.7976931348623157e+308\");  /* Max double */\n    TEST_ROUNDTRIP(\"-1.7976931348623157e+308\");\n}\n\nstatic void test_stringify_string() {\n    TEST_ROUNDTRIP(\"\\\"\\\"\");\n    TEST_ROUNDTRIP(\"\\\"Hello\\\"\");\n    TEST_ROUNDTRIP(\"\\\"Hello\\\\nWorld\\\"\");\n    TEST_ROUNDTRIP(\"\\\"\\\\\\\" \\\\\\\\ / \\\\b \\\\f \\\\n \\\\r \\\\t\\\"\");\n    TEST_ROUNDTRIP(\"\\\"Hello\\\\u0000World\\\"\");\n}\n\nstatic void test_stringify_array() {\n    TEST_ROUNDTRIP(\"[]\");\n    TEST_ROUNDTRIP(\"[null,false,true,123,\\\"abc\\\",[1,2,3]]\");\n}\n\nstatic void test_stringify_object() {\n    TEST_ROUNDTRIP(\"{}\");\n    TEST_ROUNDTRIP(\"{\\\"n\\\":null,\\\"f\\\":false,\\\"t\\\":true,\\\"i\\\":123,\\\"s\\\":\\\"abc\\\",\\\"a\\\":[1,2,3],\\\"o\\\":{\\\"1\\\":1,\\\"2\\\":2,\\\"3\\\":3}}\");\n}\n\nstatic void test_stringify() {\n    TEST_ROUNDTRIP(\"null\");\n    TEST_ROUNDTRIP(\"false\");\n    TEST_ROUNDTRIP(\"true\");\n    test_stringify_number();\n    test_stringify_string();\n    test_stringify_array();\n    test_stringify_object();\n}\n\n#define TEST_EQUAL(json1, json2, equality) \\\n    do {\\\n        lept_value v1, v2;\\\n        lept_init(&v1);\\\n        lept_init(&v2);\\\n        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v1, json1));\\\n        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v2, json2));\\\n        EXPECT_EQ_INT(equality, lept_is_equal(&v1, &v2));\\\n        lept_free(&v1);\\\n        lept_free(&v2);\\\n    } while(0)\n\nstatic void test_equal() {\n    TEST_EQUAL(\"true\", \"true\", 1);\n    TEST_EQUAL(\"true\", \"false\", 0);\n    TEST_EQUAL(\"false\", \"false\", 1);\n    TEST_EQUAL(\"null\", \"null\", 1);\n    TEST_EQUAL(\"null\", \"0\", 0);\n    TEST_EQUAL(\"123\", \"123\", 1);\n    TEST_EQUAL(\"123\", \"456\", 0);\n    TEST_EQUAL(\"\\\"abc\\\"\", \"\\\"abc\\\"\", 1);\n    TEST_EQUAL(\"\\\"abc\\\"\", \"\\\"abcd\\\"\", 0);\n    TEST_EQUAL(\"[]\", \"[]\", 1);\n    TEST_EQUAL(\"[]\", \"null\", 0);\n    TEST_EQUAL(\"[1,2,3]\", \"[1,2,3]\", 1);\n    TEST_EQUAL(\"[1,2,3]\", \"[1,2,3,4]\", 0);\n    TEST_EQUAL(\"[[]]\", \"[[]]\", 1);\n    TEST_EQUAL(\"{}\", \"{}\", 1);\n    TEST_EQUAL(\"{}\", \"null\", 0);\n    TEST_EQUAL(\"{}\", \"[]\", 0);\n    TEST_EQUAL(\"{\\\"a\\\":1,\\\"b\\\":2}\", \"{\\\"a\\\":1,\\\"b\\\":2}\", 1);\n    TEST_EQUAL(\"{\\\"a\\\":1,\\\"b\\\":2}\", \"{\\\"b\\\":2,\\\"a\\\":1}\", 1);\n    TEST_EQUAL(\"{\\\"a\\\":1,\\\"b\\\":2}\", \"{\\\"a\\\":1,\\\"b\\\":3}\", 0);\n    TEST_EQUAL(\"{\\\"a\\\":1,\\\"b\\\":2}\", \"{\\\"a\\\":1,\\\"b\\\":2,\\\"c\\\":3}\", 0);\n    TEST_EQUAL(\"{\\\"a\\\":{\\\"b\\\":{\\\"c\\\":{}}}}\", \"{\\\"a\\\":{\\\"b\\\":{\\\"c\\\":{}}}}\", 1);\n    TEST_EQUAL(\"{\\\"a\\\":{\\\"b\\\":{\\\"c\\\":{}}}}\", \"{\\\"a\\\":{\\\"b\\\":{\\\"c\\\":[]}}}\", 0);\n}\n\nstatic void test_copy() {\n    lept_value v1, v2;\n    lept_init(&v1);\n    lept_parse(&v1, \"{\\\"t\\\":true,\\\"f\\\":false,\\\"n\\\":null,\\\"d\\\":1.5,\\\"a\\\":[1,2,3]}\");\n    lept_init(&v2);\n    lept_copy(&v2, &v1);\n    EXPECT_TRUE(lept_is_equal(&v2, &v1));\n    lept_free(&v1);\n    lept_free(&v2);\n}\n\nstatic void test_move() {\n    lept_value v1, v2, v3;\n    lept_init(&v1);\n    lept_parse(&v1, \"{\\\"t\\\":true,\\\"f\\\":false,\\\"n\\\":null,\\\"d\\\":1.5,\\\"a\\\":[1,2,3]}\");\n    lept_init(&v2);\n    lept_copy(&v2, &v1);\n    lept_init(&v3);\n    lept_move(&v3, &v2);\n    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v2));\n    EXPECT_TRUE(lept_is_equal(&v3, &v1));\n    lept_free(&v1);\n    lept_free(&v2);\n    lept_free(&v3);\n}\n\nstatic void test_swap() {\n    lept_value v1, v2;\n    lept_init(&v1);\n    lept_init(&v2);\n    lept_set_string(&v1, \"Hello\",  5);\n    lept_set_string(&v2, \"World!\", 6);\n    lept_swap(&v1, &v2);\n    EXPECT_EQ_STRING(\"World!\", lept_get_string(&v1), lept_get_string_length(&v1));\n    EXPECT_EQ_STRING(\"Hello\",  lept_get_string(&v2), lept_get_string_length(&v2));\n    lept_free(&v1);\n    lept_free(&v2);\n}\n\nstatic void test_access_null() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"a\", 1);\n    lept_set_null(&v);\n    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\n    lept_free(&v);\n}\n\nstatic void test_access_boolean() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"a\", 1);\n    lept_set_boolean(&v, 1);\n    EXPECT_TRUE(lept_get_boolean(&v));\n    lept_set_boolean(&v, 0);\n    EXPECT_FALSE(lept_get_boolean(&v));\n    lept_free(&v);\n}\n\nstatic void test_access_number() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"a\", 1);\n    lept_set_number(&v, 1234.5);\n    EXPECT_EQ_DOUBLE(1234.5, lept_get_number(&v));\n    lept_free(&v);\n}\n\nstatic void test_access_string() {\n    lept_value v;\n    lept_init(&v);\n    lept_set_string(&v, \"\", 0);\n    EXPECT_EQ_STRING(\"\", lept_get_string(&v), lept_get_string_length(&v));\n    lept_set_string(&v, \"Hello\", 5);\n    EXPECT_EQ_STRING(\"Hello\", lept_get_string(&v), lept_get_string_length(&v));\n    lept_free(&v);\n}\n\nstatic void test_access_array() {\n    lept_value a, e;\n    size_t i, j;\n\n    lept_init(&a);\n\n    for (j = 0; j <= 5; j += 5) {\n        lept_set_array(&a, j);\n        EXPECT_EQ_SIZE_T(0, lept_get_array_size(&a));\n        EXPECT_EQ_SIZE_T(j, lept_get_array_capacity(&a));\n        for (i = 0; i < 10; i++) {\n            lept_init(&e);\n            lept_set_number(&e, i);\n            lept_move(lept_pushback_array_element(&a), &e);\n            lept_free(&e);\n        }\n\n        EXPECT_EQ_SIZE_T(10, lept_get_array_size(&a));\n        for (i = 0; i < 10; i++)\n            EXPECT_EQ_DOUBLE((double)i, lept_get_number(lept_get_array_element(&a, i)));\n    }\n\n    lept_popback_array_element(&a);\n    EXPECT_EQ_SIZE_T(9, lept_get_array_size(&a));\n    for (i = 0; i < 9; i++)\n        EXPECT_EQ_DOUBLE((double)i, lept_get_number(lept_get_array_element(&a, i)));\n\n    lept_erase_array_element(&a, 4, 0);\n    EXPECT_EQ_SIZE_T(9, lept_get_array_size(&a));\n    for (i = 0; i < 9; i++)\n        EXPECT_EQ_DOUBLE((double)i, lept_get_number(lept_get_array_element(&a, i)));\n\n    lept_erase_array_element(&a, 8, 1);\n    EXPECT_EQ_SIZE_T(8, lept_get_array_size(&a));\n    for (i = 0; i < 8; i++)\n        EXPECT_EQ_DOUBLE((double)i, lept_get_number(lept_get_array_element(&a, i)));\n\n    lept_erase_array_element(&a, 0, 2);\n    EXPECT_EQ_SIZE_T(6, lept_get_array_size(&a));\n    for (i = 0; i < 6; i++)\n        EXPECT_EQ_DOUBLE((double)i + 2, lept_get_number(lept_get_array_element(&a, i)));\n\n#if 0\n    for (i = 0; i < 2; i++) {\n        lept_init(&e);\n        lept_set_number(&e, i);\n        lept_move(lept_insert_array_element(&a, i), &e);\n        lept_free(&e);\n    }\n#endif\n    \n    EXPECT_EQ_SIZE_T(8, lept_get_array_size(&a));\n    for (i = 0; i < 8; i++)\n        EXPECT_EQ_DOUBLE((double)i, lept_get_number(lept_get_array_element(&a, i)));\n\n    EXPECT_TRUE(lept_get_array_capacity(&a) > 8);\n    lept_shrink_array(&a);\n    EXPECT_EQ_SIZE_T(8, lept_get_array_capacity(&a));\n    EXPECT_EQ_SIZE_T(8, lept_get_array_size(&a));\n    for (i = 0; i < 8; i++)\n        EXPECT_EQ_DOUBLE((double)i, lept_get_number(lept_get_array_element(&a, i)));\n\n    lept_set_string(&e, \"Hello\", 5);\n    lept_move(lept_pushback_array_element(&a), &e);     /* Test if element is freed */\n    lept_free(&e);\n\n    i = lept_get_array_capacity(&a);\n    lept_clear_array(&a);\n    EXPECT_EQ_SIZE_T(0, lept_get_array_size(&a));\n    EXPECT_EQ_SIZE_T(i, lept_get_array_capacity(&a));   /* capacity remains unchanged */\n    lept_shrink_array(&a);\n    EXPECT_EQ_SIZE_T(0, lept_get_array_capacity(&a));\n\n    lept_free(&a);\n}\n\nstatic void test_access_object() {\n#if 0\n    lept_value o, v, *pv;\n    size_t i, j, index;\n\n    lept_init(&o);\n\n    for (j = 0; j <= 5; j += 5) {\n        lept_set_object(&o, j);\n        EXPECT_EQ_SIZE_T(0, lept_get_object_size(&o));\n        EXPECT_EQ_SIZE_T(j, lept_get_object_capacity(&o));\n        for (i = 0; i < 10; i++) {\n            char key[2] = \"a\";\n            key[0] += i;\n            lept_init(&v);\n            lept_set_number(&v, i);\n            lept_move(lept_set_object_value(&o, key, 1), &v);\n            lept_free(&v);\n        }\n        EXPECT_EQ_SIZE_T(10, lept_get_object_size(&o));\n        for (i = 0; i < 10; i++) {\n            char key[] = \"a\";\n            key[0] += i;\n            index = lept_find_object_index(&o, key, 1);\n            EXPECT_TRUE(index != LEPT_KEY_NOT_EXIST);\n            pv = lept_get_object_value(&o, index);\n            EXPECT_EQ_DOUBLE((double)i, lept_get_number(pv));\n        }\n    }\n\n    index = lept_find_object_index(&o, \"j\", 1);    \n    EXPECT_TRUE(index != LEPT_KEY_NOT_EXIST);\n    lept_remove_object_value(&o, index);\n    index = lept_find_object_index(&o, \"j\", 1);\n    EXPECT_TRUE(index == LEPT_KEY_NOT_EXIST);\n    EXPECT_EQ_SIZE_T(9, lept_get_object_size(&o));\n\n    index = lept_find_object_index(&o, \"a\", 1);\n    EXPECT_TRUE(index != LEPT_KEY_NOT_EXIST);\n    lept_remove_object_value(&o, index);\n    index = lept_find_object_index(&o, \"a\", 1);\n    EXPECT_TRUE(index == LEPT_KEY_NOT_EXIST);\n    EXPECT_EQ_SIZE_T(8, lept_get_object_size(&o));\n\n    EXPECT_TRUE(lept_get_object_capacity(&o) > 8);\n    lept_shrink_object(&o);\n    EXPECT_EQ_SIZE_T(8, lept_get_object_capacity(&o));\n    EXPECT_EQ_SIZE_T(8, lept_get_object_size(&o));\n    for (i = 0; i < 8; i++) {\n        char key[] = \"a\";\n        key[0] += i + 1;\n        EXPECT_EQ_DOUBLE((double)i + 1, lept_get_number(lept_get_object_value(&o, lept_find_object_index(&o, key, 1))));\n    }\n\n    lept_set_string(&v, \"Hello\", 5);\n    lept_move(lept_set_object_value(&o, \"World\", 5), &v); /* Test if element is freed */\n    lept_free(&v);\n\n    pv = lept_find_object_value(&o, \"World\", 5);\n    EXPECT_TRUE(pv != NULL);\n    EXPECT_EQ_STRING(\"Hello\", lept_get_string(pv), lept_get_string_length(pv));\n\n    i = lept_get_object_capacity(&o);\n    lept_clear_object(&o);\n    EXPECT_EQ_SIZE_T(0, lept_get_object_size(&o));\n    EXPECT_EQ_SIZE_T(i, lept_get_object_capacity(&o)); /* capacity remains unchanged */\n    lept_shrink_object(&o);\n    EXPECT_EQ_SIZE_T(0, lept_get_object_capacity(&o));\n\n    lept_free(&o);\n#endif\n}\n\nstatic void test_access() {\n    test_access_null();\n    test_access_boolean();\n    test_access_number();\n    test_access_string();\n    test_access_array();\n    test_access_object();\n}\n\nint main() {\n#ifdef _WINDOWS\n    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);\n#endif\n    test_parse();\n    test_stringify();\n    test_equal();\n    test_copy();\n    test_move();\n    test_swap();\n    test_access();\n    printf(\"%d/%d (%3.2f%%) passed\\n\", test_pass, test_count, test_pass * 100.0 / test_count);\n    return main_ret;\n}\n"
  },
  {
    "path": "tutorial08/tutorial08.md",
    "content": "# 从零开始的 JSON 库教程（八）：访问与其他功能\n\n* Milo Yip\n* 2018/6/2\n\n本文是[《从零开始的 JSON 库教程》](https://zhuanlan.zhihu.com/json-tutorial)的第八个单元。代码位于 [json-tutorial/tutorial08](https://github.com/miloyip/json-tutorial/blob/master/tutorial08)。\n\n本单元内容：\n\n1. [对象键值查询](#1-对象键值查询)\n2. [相等比较](#2-相等比较)\n3. [复制、移动与交换](#3-复制移动与交换)\n4. [动态数组](#4-动态数组)\n5. [动态对象](#5-动态对象)\n6. [总结与练习](#6-总结与练习)\n\n## 1. 对象键值查询\n\n我们在第六个单元实现了 JSON 对象的数据结构，它仅为一个 `lept_value` 的数组：\n\n~~~c\nstruct lept_value {\n    union {\n        struct { lept_member* m; size_t size; }o;\n        /* ... */\n    }u;\n    lept_type type;\n};\n\nstruct lept_member {\n    char* k; size_t klen;   /* member key string, key string length */\n    lept_value v;           /* member value */\n};\n~~~\n\n为了做相应的解析测试，我们实现了最基本的查询功能：\n\n~~~c\nsize_t lept_get_object_size(const lept_value* v);\nconst char* lept_get_object_key(const lept_value* v, size_t index);\nsize_t lept_get_object_key_length(const lept_value* v, size_t index);\nlept_value* lept_get_object_value(const lept_value* v, size_t index);\n~~~\n\n在实际使用时，我们许多时候需要查询一个键值是否存在，如存在，要获得其相应的值。我们可以提供一个函数，简单地用线性搜寻实现这个查询功能（时间复杂度 $\\mathrm{O}(n)$）：\n\n~~~c\n#define LEPT_KEY_NOT_EXIST ((size_t)-1)\n\nsize_t lept_find_object_index(const lept_value* v, const char* key, size_t klen) {\n    size_t i;\n    assert(v != NULL && v->type == LEPT_OBJECT && key != NULL);\n    for (i = 0; i < v->u.o.size; i++)\n        if (v->u.o.m[i].klen == klen && memcmp(v->u.o.m[i].k, key, klen) == 0)\n            return i;\n    return LEPT_KEY_NOT_EXIST;\n}}\n~~~\n\n若对象内没有所需的键，此函数返回 `LEPT_KEY_NOT_EXIST`。使用时：\n\n~~~c\nlept_value o;\nsize_t index;\nlept_init(&o);\nlept_parse(&o, \"{\\\"name\\\":\\\"Milo\\\", \\\"gender\\\":\\\"M\\\"}\");\nindex = lept_find_object_index(&o, \"name\", 4);\nif (index != LEPT_KEY_NOT_EXIST) {\n    lept_value* v = lept_get_object_value(&o, index);\n    printf(\"%s\\n\", lept_get_string(v));\n}\nlept_free(&o);\n~~~\n\n由于一般也是希望获取键对应的值，而不需要索引，我们再加入一个辅助函数，返回类型改为 `lept_value*`：\n\n~~~c\nlept_value* lept_find_object_value(const lept_value* v, const char* key, size_t klen) {\n    size_t index = lept_find_object_index(v, key, klen);\n    return index != LEPT_KEY_NOT_EXIST ? &v->u.o.m[index].v : NULL;\n}\n~~~\n\n上述例子便可简化为：\n\n~~~c\nlept_value o, *v;\n/* ... */\nif ((v = lept_find_object_value(&o, \"name\", 4)) != NULL)\n    printf(\"%s\\n\", lept_get_string(v));\n~~~\n\n## 2. 相等比较\n\n在实现数组和对象的修改之前，为了测试结果的正确性，我们先实现 `lept_value` 的[相等比较](https://zh.wikipedia.org/zh-cn/%E9%97%9C%E4%BF%82%E9%81%8B%E7%AE%97%E5%AD%90)（equality comparison）。首先，两个值的类型必须相同，对于 true、false、null 这三种类型，比较类型后便完成比较。而对于数字和字符串，需进一步检查是否相等：\n\n~~~c\nint lept_is_equal(const lept_value* lhs, const lept_value* rhs) {\n    assert(lhs != NULL && rhs != NULL);\n    if (lhs->type != rhs->type)\n        return 0;\n    switch (lhs->type) {\n        case LEPT_STRING:\n            return lhs->u.s.len == rhs->u.s.len && \n                memcmp(lhs->u.s.s, rhs->u.s.s, lhs->u.s.len) == 0;\n        case LEPT_NUMBER:\n            return lhs->u.n == rhs->u.n;\n        /* ... */\n        default:\n            return 1;\n    }\n}\n~~~\n\n由于值可能复合类型（数组和对象），也就是一个树形结构。当我们要比较两个树是否相等，可通过递归实现。例如，对于数组，我们先比较元素数目是否相等，然后递归检查对应的元素是否相等：\n\n~~~c\nint lept_is_equal(const lept_value* lhs, const lept_value* rhs) {\n    size_t i;\n    /* ... */\n    switch (lhs->type) {\n        /* ... */\n        case LEPT_ARRAY:\n            if (lhs->u.a.size != rhs->u.a.size)\n                return 0;\n            for (i = 0; i < lhs->u.a.size; i++)\n                if (!lept_is_equal(&lhs->u.a.e[i], &rhs->u.a.e[i]))\n                    return 0;\n            return 1;\n        /* ... */\n    }\n}\n~~~\n\n而对象与数组的不同之处，在于概念上对象的键值对是无序的。例如，`{\"a\":1,\"b\":2}` 和 `{\"b\":2,\"a\":1}` 虽然键值的次序不同，但这两个 JSON 对象是相等的。我们可以简单地利用 `lept_find_object_index()` 去找出对应的值，然后递归作比较。这部分留给读者作为练习。\n\n## 3. 复制、移动与交换\n\n本单元的重点，在于修改数组和对象的内容。我们将会实现一些接口做修改的操作，例如，为对象设置一个键值，我们可能会这么设计：\n\n~~~c\nvoid lept_set_object_value(lept_value* v, const char* key, size_t klen, const lept_value* value);\n\nvoid f() {\n    lept_value v, s;\n    lept_init(&v);\n    lept_parse(&v, \"{}\");\n    lept_init(&s);\n    lept_set_string(&s, \"Hello\", 5);\n    lept_set_object_keyvalue(&v, \"s\", &s); /* {\"s\":\"Hello\"} */\n    lept_free(&v)\n    lept_free(&s);  /* 第二次释放！*/\n}\n~~~\n\n凡涉及赋值，都可能会引起资源拥有权（resource ownership）的问题。值 `s` 并不能以指针方式简单地写入对象 `v`，因为这样便会有两个地方都拥有 `s`，会做成重复释放的 bug。我们有两个选择：\n\n1. 在 `lept_set_object_value()` 中，把参数 `value` [深度复制](https://en.wikipedia.org/wiki/Object_copying#Deep_copy)（deep copy）一个值，即把整个树复制一份，写入其新增的键值对中。\n2. 在 `lept_set_object_value()` 中，把参数 `value` 拥有权转移至新增的键值对，再把 `value` 设置成 null 值。这就是所谓的移动语意（move semantics）。\n\n深度复制是一个常用功能，使用者也可能会用到，例如把一个 JSON 复制一个版本出来修改，保持原来的不变。所以，我们实现一个公开的深度复制函数：\n\n~~~c\nvoid lept_copy(lept_value* dst, const lept_value* src) {\n    size_t i;\n    assert(src != NULL && dst != NULL && src != dst);\n    switch (src->type) {\n        case LEPT_STRING:\n            lept_set_string(dst, src->u.s.s, src->u.s.len);\n            break;\n        case LEPT_ARRAY:\n            /* \\todo */\n            break;\n        case LEPT_OBJECT:\n            /* \\todo */\n            break;\n        default:\n            lept_free(dst);\n            memcpy(dst, src, sizeof(lept_value));\n            break;\n    }\n}\n~~~\n\nC++11 加入了右值引用的功能，可以从语言层面区分复制和移动语意。而在 C 语言中，我们也可以通过实现不同版本的接口（不同名字的函数），实现这两种语意。但为了令接口更简单和正交（orthogonal），我们修改了 `lept_set_object_value()` 的设计，让它返回新增键值对的值指针，所以我们可以用 `lept_copy()` 去复制赋值，也可以简单地改变新增的键值：\n\n~~~c\n/* 返回新增键值对的指针 */\nlept_value* lept_set_object_value(lept_value* v, const char* key, size_t klen);\n\nvoid f() {\n    lept_value v;\n    lept_init(&v);\n    lept_parse(&v, \"{}\");\n    lept_set_string(lept_set_object_value(&v, \"s\"), \"Hello\", 5);\n    /* {\"s\":\"Hello\"} */\n    lept_copy(\n        lept_add_object_keyvalue(&v, \"t\"),\n        lept_get_object_keyvalue(&v, \"s\", 1));\n    /* {\"s\":\"Hello\",\"t\":\"Hello\"} */\n    lept_free(&v);\n}\n~~~\n\n我们还提供了 `lept_move()`，它的实现也非常简单：\n\n~~~c\nvoid lept_move(lept_value* dst, lept_value* src) {\n    assert(dst != NULL && src != NULL && src != dst);\n    lept_free(dst);\n    memcpy(dst, src, sizeof(lept_value));\n    lept_init(src);\n}\n~~~\n\n类似地，我们也实现了一个交换值的接口：\n\n~~~c\nvoid lept_swap(lept_value* lhs, lept_value* rhs) {\n    assert(lhs != NULL && rhs != NULL);\n    if (lhs != rhs) {\n        lept_value temp;\n        memcpy(&temp, lhs, sizeof(lept_value));\n        memcpy(lhs,   rhs, sizeof(lept_value));\n        memcpy(rhs, &temp, sizeof(lept_value));\n    }\n}\n~~~\n\n当我们要修改对象或数组里的值时，我们可以利用这 3 个函数。例如：\n\n~~~c\nconst char* json = \"{\\\"a\\\":[1,2],\\\"b\\\":3}\";\nchar *out;\nlept_value v;\nlept_init(&v);\nlept_parse(&v, json);\nlept_copy(\n    lept_find_object_value(&v, \"b\", 1),\n    lept_find_object_value(&v, \"a\", 1));\nprintf(\"%s\\n\", out = lept_stringify(&v, NULL)); /* {\"a\":[1,2],\"b\":[1,2]} */\nfree(out);\n\nlept_parse(&v, json);\nlept_move(\n    lept_find_object_value(&v, \"b\", 1),\n    lept_find_object_value(&v, \"a\", 1));\nprintf(\"%s\\n\", out = lept_stringify(&v, NULL)); /* {\"a\":null,\"b\":[1,2]} */\nfree(out);\n\nlept_parse(&v, json);\nlept_swap(\n    lept_find_object_value(&v, \"b\", 1),\n    lept_find_object_value(&v, \"a\", 1));\nprintf(\"%s\\n\", out = lept_stringify(&v, NULL)); /* {\"a\":3,\"b\":[1,2]} */\nfree(out);\n\nlept_free(&v);\n~~~\n\n在使用时，可尽量避免 `lept_copy()`，而改用 `lept_move()` 或 `lept_swap()`，因为后者不需要分配内存。当中 `lept_swap()` 更是无须做释放的工作，令它达到 $\\mathrm{O}(1)$ 时间复杂度，其性能与值的内容无关。\n\n## 4. 动态数组\n\n在此单元之前的实现里，每个数组的元素数目在解析后是固定不变的，其数据结构是：\n\n~~~c\nstruct lept_value {\n    union {\n        /* ... */\n        struct { lept_value* e; size_t size; }a; /* array:  elements, element count*/\n        /* ... */\n    }u;\n    lept_type type;\n};\n~~~\n\n用这种数据结构增删元素时，我们需要重新分配一个数组，把适当的旧数据拷贝过去。但这种做法是非常低效的。例如我们想要从一个空的数组加入 $n$ 个元素，便要做 $n(n - 1)/2$ 次元素复制，即 $\\mathrm{O}(n^2)$ 的时间复杂度。\n\n其中一个改进方法，是使用动态数组（dynamic array，或称可增长数组／growable array）的数据结构。C++ STL 标准库中最常用的 `std::vector` 也是使用这种数据结构的容器。\n\n改动也很简单，只需要在数组中加入容量 `capacity` 字段，表示当前已分配的元素数目，而 `size` 则表示现时的有效元素数目：\n\n~~~c\n        /* ... */\n        struct { lept_value* e; size_t size, capacity; }a; /* array:  elements, element count, capacity */\n        /* ... */\n~~~\n\n我们终于提供设置数组的函数，而且它可提供初始的容量：\n\n~~~c\nvoid lept_set_array(lept_value* v, size_t capacity) {\n    assert(v != NULL);\n    lept_free(v);\n    v->type = LEPT_ARRAY;\n    v->u.a.size = 0;\n    v->u.a.capacity = capacity;\n    v->u.a.e = capacity > 0 ? (lept_value*)malloc(capacity * sizeof(lept_value)) : NULL;\n}\n~~~\n\n我们需要稍修改 `lept_parse_array()`，调用 `lept_set_array()` 去设置类型和分配空间。\n\n另外，类似于 `lept_get_array_size()`，也加入获取当前容量的函数：\n\n~~~c\nsize_t lept_get_array_capacity(const lept_value* v) {\n    assert(v != NULL && v->type == LEPT_ARRAY);\n    return v->u.a.capacity;\n}\n~~~\n\n如果当前的容量不足，我们需要扩大容量，标准库的 `realloc()` 可以分配新的内存并把旧的数据拷背过去：\n\n~~~c\nvoid lept_reserve_array(lept_value* v, size_t capacity) {\n    assert(v != NULL && v->type == LEPT_ARRAY);\n    if (v->u.a.capacity < capacity) {\n        v->u.a.capacity = capacity;\n        v->u.a.e = (lept_value*)realloc(v->u.a.e, capacity * sizeof(lept_value));\n    }\n}\n~~~\n\n当数组不需要再修改，可以使用以下的函数，把容量缩小至刚好能放置现有元素：\n\n~~~c\nvoid lept_shrink_array(lept_value* v) {\n    assert(v != NULL && v->type == LEPT_ARRAY);\n    if (v->u.a.capacity > v->u.a.size) {\n        v->u.a.capacity = v->u.a.size;\n        v->u.a.e = (lept_value*)realloc(v->u.a.e, v->u.a.capacity * sizeof(lept_value));\n    }\n}\n~~~\n\n我们不逐一检视每个数组修改函数，仅介绍一下两个例子：\n\n~~~c\nlept_value* lept_pushback_array_element(lept_value* v) {\n    assert(v != NULL && v->type == LEPT_ARRAY);\n    if (v->u.a.size == v->u.a.capacity)\n        lept_reserve_array(v, v->u.a.capacity == 0 ? 1 : v->u.a.capacity * 2);\n    lept_init(&v->u.a.e[v->u.a.size]);\n    return &v->u.a.e[v->u.a.size++];\n}\n\nvoid lept_popback_array_element(lept_value* v) {\n    assert(v != NULL && v->type == LEPT_ARRAY && v->u.a.size > 0);\n    lept_free(&v->u.a.e[--v->u.a.size]);\n}\n~~~\n\n`lept_pushback_array_element()` 在数组末端压入一个元素，返回新的元素指针。如果现有的容量不足，就需要调用 `lept_reserve_array()` 扩容。我们现在用了一个最简单的扩容公式：若容量为 0，则分配 1 个元素；其他情况倍增容量。\n\n`lept_popback_array_element()` 则做相反的工作，记得删去的元素需要调用 `lept_free()`。\n\n下面这 3 个函数留给读者练习：\n\n1. `lept_insert_array_element()` 在 `index` 位置插入一个元素；\n2. `lept_erase_array_element()` 删去在 `index` 位置开始共 `count` 个元素（不改容量）；\n3. `lept_clear_array()` 清除所有元素（不改容量）。\n\n~~~c\nlept_value* lept_insert_array_element(lept_value* v, size_t index);\nvoid lept_erase_array_element(lept_value* v, size_t index, size_t count);\nvoid lept_clear_array(lept_value* v);\n~~~\n\n## 5. 动态对象\n\n动态对象也是采用上述相同的结构，所以直接留给读者修改结构体，并实现以下函数：\n\n~~~c\nvoid lept_set_object(lept_value* v, size_t capacity);\nsize_t lept_get_object_capacity(const lept_value* v);\nvoid lept_reserve_object(lept_value* v, size_t capacity);\nvoid lept_shrink_object(lept_value* v);\nvoid lept_clear_object(lept_value* v);\nlept_value* lept_set_object_value(lept_value* v, const char* key, size_t klen);\nvoid lept_remove_object_value(lept_value* v, size_t index);\n~~~\n\n注意 `lept_set_object_value()` 会先搜寻是否存在现有的键，若存在则直接返回该值的指针，不存在时才新增。\n\n## 6. 总结与练习\n\n本单元主要加入了数组和对象的访问、修改方法。当中的赋值又引申了三种赋值的方式（复制、移动、交换）。这些问题是各种编程语言中都需要考虑的事情，为了减少深度复制的成本，有些程序库或运行时还会采用[写入时复制](https://zh.wikipedia.org/zh-cn/%E5%AF%AB%E5%85%A5%E6%99%82%E8%A4%87%E8%A3%BD)（copy-on-write, COW）。而浅复制（shallow copy）则需要 [引用计数](https://zh.wikipedia.org/wiki/%E5%BC%95%E7%94%A8%E8%AE%A1%E6%95%B0)（reference count）或 [垃圾回收](https://zh.wikipedia.org/zh-cn/%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6_(%E8%A8%88%E7%AE%97%E6%A9%9F%E7%A7%91%E5%AD%B8))（garbage collection, GC）等技术。\n\n另外，我们实现了以动态数组的数据结构，能较高效地对数组和对象进行增删操作。至此，我们已经完成本教程的所有核心功能。做完下面的练习后，我们还会作简单讲解，然后将迎来本教程的最后一个单元。\n\n本单元练习内容：\n\n1. 完成 `lept_is_equal()` 里的对象比较部分。不需要考虑对象内有重复键的情况。\n2. 打开 `test_access_array()` 里的 `#if 0`，实现 `lept_insert_array_element()`、`lept_erase_array_element()` 和 `lept_clear_array()`。\n3. 打开 `test_access_object()` 里的 `#if 0`，参考动态数组，实现第 5 部分列出的所有函数。\n4. 完成 `lept_copy()` 里的数组和对象的复制部分。\n\n如果你遇到问题，有不理解的地方，或是有建议，都欢迎在评论或 [issue](https://github.com/miloyip/json-tutorial/issues) 中提出，让所有人一起讨论。\n"
  }
]