From cde0f0c55eef844195547d92f35a9fbd70f20211 Mon Sep 17 00:00:00 2001 From: sjkxq <1493608547@qq.com> Date: Sat, 6 Sep 2025 21:05:41 +0800 Subject: [PATCH] =?UTF-8?q?feat(tutorial08=5Fanswer):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=20CMakeLists.txt=20=E5=92=8C=20leptjson=20=E6=BA=90=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 CMakeLists.txt 文件,配置项目构建 - 添加 leptjson.c 和 leptjson.h 文件,实现 JSON 解析和操作功能 - 实现了 JSON 数据类型、解析、序列化、复制、移动和比较等操作 - 支持数组和对象的操作,包括插入、删除和查找等 --- tutorial08_answer/CMakeLists.txt | 10 + tutorial08_answer/leptjson.c | 814 +++++++++++++++++++++++++++++++ tutorial08_answer/leptjson.h | 97 ++++ tutorial08_answer/test.c | 721 +++++++++++++++++++++++++++ 4 files changed, 1642 insertions(+) create mode 100644 tutorial08_answer/CMakeLists.txt create mode 100644 tutorial08_answer/leptjson.c create mode 100644 tutorial08_answer/leptjson.h create mode 100644 tutorial08_answer/test.c diff --git a/tutorial08_answer/CMakeLists.txt b/tutorial08_answer/CMakeLists.txt new file mode 100644 index 00000000..49ba19de --- /dev/null +++ b/tutorial08_answer/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required (VERSION 2.6) +project (leptjson_test C) + +if (CMAKE_C_COMPILER_ID MATCHES "GNU|Clang") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ansi -pedantic -Wall") +endif() + +add_library(leptjson leptjson.c) +add_executable(leptjson_test test.c) +target_link_libraries(leptjson_test leptjson) diff --git a/tutorial08_answer/leptjson.c b/tutorial08_answer/leptjson.c new file mode 100644 index 00000000..3ad05eba --- /dev/null +++ b/tutorial08_answer/leptjson.c @@ -0,0 +1,814 @@ +#ifdef _WINDOWS +#define _CRTDBG_MAP_ALLOC +#include +#endif +#include "leptjson.h" +#include /* assert() */ +#include /* errno, ERANGE */ +#include /* HUGE_VAL */ +#include /* sprintf() */ +#include /* NULL, malloc(), realloc(), free(), strtod() */ +#include /* memcpy() */ + +#ifndef LEPT_PARSE_STACK_INIT_SIZE +#define LEPT_PARSE_STACK_INIT_SIZE 256 +#endif + +#ifndef LEPT_PARSE_STRINGIFY_INIT_SIZE +#define LEPT_PARSE_STRINGIFY_INIT_SIZE 256 +#endif + +#define EXPECT(c, ch) do { assert(*c->json == (ch)); c->json++; } while(0) +#define ISDIGIT(ch) ((ch) >= '0' && (ch) <= '9') +#define ISDIGIT1TO9(ch) ((ch) >= '1' && (ch) <= '9') +#define PUTC(c, ch) do { *(char*)lept_context_push(c, sizeof(char)) = (ch); } while(0) +#define PUTS(c, s, len) memcpy(lept_context_push(c, len), s, len) + +typedef struct { + const char* json; + char* stack; + size_t size, top; +}lept_context; + +static void* lept_context_push(lept_context* c, size_t size) { + void* ret; + assert(size > 0); + if (c->top + size >= c->size) { + if (c->size == 0) + c->size = LEPT_PARSE_STACK_INIT_SIZE; + while (c->top + size >= c->size) + c->size += c->size >> 1; /* c->size * 1.5 */ + c->stack = (char*)realloc(c->stack, c->size); + } + ret = c->stack + c->top; + c->top += size; + return ret; +} + +static void* lept_context_pop(lept_context* c, size_t size) { + assert(c->top >= size); + return c->stack + (c->top -= size); +} + +static void lept_parse_whitespace(lept_context* c) { + const char *p = c->json; + while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') + p++; + c->json = p; +} + +static int lept_parse_literal(lept_context* c, lept_value* v, const char* literal, lept_type type) { + size_t i; + EXPECT(c, literal[0]); + for (i = 0; literal[i + 1]; i++) + if (c->json[i] != literal[i + 1]) + return LEPT_PARSE_INVALID_VALUE; + c->json += i; + v->type = type; + return LEPT_PARSE_OK; +} + +static int lept_parse_number(lept_context* c, lept_value* v) { + const char* p = c->json; + if (*p == '-') p++; + if (*p == '0') p++; + else { + if (!ISDIGIT1TO9(*p)) return LEPT_PARSE_INVALID_VALUE; + for (p++; ISDIGIT(*p); p++); + } + if (*p == '.') { + p++; + if (!ISDIGIT(*p)) return LEPT_PARSE_INVALID_VALUE; + for (p++; ISDIGIT(*p); p++); + } + if (*p == 'e' || *p == 'E') { + p++; + if (*p == '+' || *p == '-') p++; + if (!ISDIGIT(*p)) return LEPT_PARSE_INVALID_VALUE; + for (p++; ISDIGIT(*p); p++); + } + errno = 0; + v->u.n = strtod(c->json, NULL); + if (errno == ERANGE && (v->u.n == HUGE_VAL || v->u.n == -HUGE_VAL)) + return LEPT_PARSE_NUMBER_TOO_BIG; + v->type = LEPT_NUMBER; + c->json = p; + return LEPT_PARSE_OK; +} + +static const char* lept_parse_hex4(const char* p, unsigned* u) { + int i; + *u = 0; + for (i = 0; i < 4; i++) { + char ch = *p++; + *u <<= 4; + if (ch >= '0' && ch <= '9') *u |= ch - '0'; + else if (ch >= 'A' && ch <= 'F') *u |= ch - ('A' - 10); + else if (ch >= 'a' && ch <= 'f') *u |= ch - ('a' - 10); + else return NULL; + } + return p; +} + +static void lept_encode_utf8(lept_context* c, unsigned u) { + if (u <= 0x7F) + PUTC(c, u & 0xFF); + else if (u <= 0x7FF) { + PUTC(c, 0xC0 | ((u >> 6) & 0xFF)); + PUTC(c, 0x80 | ( u & 0x3F)); + } + else if (u <= 0xFFFF) { + PUTC(c, 0xE0 | ((u >> 12) & 0xFF)); + PUTC(c, 0x80 | ((u >> 6) & 0x3F)); + PUTC(c, 0x80 | ( u & 0x3F)); + } + else { + assert(u <= 0x10FFFF); + PUTC(c, 0xF0 | ((u >> 18) & 0xFF)); + PUTC(c, 0x80 | ((u >> 12) & 0x3F)); + PUTC(c, 0x80 | ((u >> 6) & 0x3F)); + PUTC(c, 0x80 | ( u & 0x3F)); + } +} + +#define STRING_ERROR(ret) do { c->top = head; return ret; } while(0) + +static int lept_parse_string_raw(lept_context* c, char** str, size_t* len) { + size_t head = c->top; + unsigned u, u2; + const char* p; + EXPECT(c, '\"'); + p = c->json; + for (;;) { + char ch = *p++; + switch (ch) { + case '\"': + *len = c->top - head; + *str = lept_context_pop(c, *len); + c->json = p; + return LEPT_PARSE_OK; + case '\\': + switch (*p++) { + case '\"': PUTC(c, '\"'); break; + case '\\': PUTC(c, '\\'); break; + case '/': PUTC(c, '/' ); break; + case 'b': PUTC(c, '\b'); break; + case 'f': PUTC(c, '\f'); break; + case 'n': PUTC(c, '\n'); break; + case 'r': PUTC(c, '\r'); break; + case 't': PUTC(c, '\t'); break; + case 'u': + if (!(p = lept_parse_hex4(p, &u))) + STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX); + if (u >= 0xD800 && u <= 0xDBFF) { /* surrogate pair */ + if (*p++ != '\\') + STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE); + if (*p++ != 'u') + STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE); + if (!(p = lept_parse_hex4(p, &u2))) + STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX); + if (u2 < 0xDC00 || u2 > 0xDFFF) + STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE); + u = (((u - 0xD800) << 10) | (u2 - 0xDC00)) + 0x10000; + } + lept_encode_utf8(c, u); + break; + default: + STRING_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE); + } + break; + case '\0': + STRING_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK); + default: + if ((unsigned char)ch < 0x20) + STRING_ERROR(LEPT_PARSE_INVALID_STRING_CHAR); + PUTC(c, ch); + } + } +} + +static int lept_parse_string(lept_context* c, lept_value* v) { + int ret; + char* s; + size_t len; + if ((ret = lept_parse_string_raw(c, &s, &len)) == LEPT_PARSE_OK) + lept_set_string(v, s, len); + return ret; +} + +static int lept_parse_value(lept_context* c, lept_value* v); + +static int lept_parse_array(lept_context* c, lept_value* v) { + size_t i, size = 0; + int ret; + EXPECT(c, '['); + lept_parse_whitespace(c); + if (*c->json == ']') { + c->json++; + lept_set_array(v, 0); + return LEPT_PARSE_OK; + } + for (;;) { + lept_value e; + lept_init(&e); + if ((ret = lept_parse_value(c, &e)) != LEPT_PARSE_OK) + break; + memcpy(lept_context_push(c, sizeof(lept_value)), &e, sizeof(lept_value)); + size++; + lept_parse_whitespace(c); + if (*c->json == ',') { + c->json++; + lept_parse_whitespace(c); + } + else if (*c->json == ']') { + c->json++; + lept_set_array(v, size); + memcpy(v->u.a.e, lept_context_pop(c, size * sizeof(lept_value)), size * sizeof(lept_value)); + v->u.a.size = size; + return LEPT_PARSE_OK; + } + else { + ret = LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET; + break; + } + } + /* Pop and free values on the stack */ + for (i = 0; i < size; i++) + lept_free((lept_value*)lept_context_pop(c, sizeof(lept_value))); + return ret; +} + +static int lept_parse_object(lept_context* c, lept_value* v) { + size_t i, size; + lept_member m; + int ret; + EXPECT(c, '{'); + lept_parse_whitespace(c); + if (*c->json == '}') { + c->json++; + lept_set_object(v, 0); + return LEPT_PARSE_OK; + } + m.k = NULL; + size = 0; + for (;;) { + char* str; + lept_init(&m.v); + /* parse key */ + if (*c->json != '"') { + ret = LEPT_PARSE_MISS_KEY; + break; + } + if ((ret = lept_parse_string_raw(c, &str, &m.klen)) != LEPT_PARSE_OK) + break; + memcpy(m.k = (char*)malloc(m.klen + 1), str, m.klen); + m.k[m.klen] = '\0'; + /* parse ws colon ws */ + lept_parse_whitespace(c); + if (*c->json != ':') { + ret = LEPT_PARSE_MISS_COLON; + break; + } + c->json++; + lept_parse_whitespace(c); + /* parse value */ + if ((ret = lept_parse_value(c, &m.v)) != LEPT_PARSE_OK) + break; + memcpy(lept_context_push(c, sizeof(lept_member)), &m, sizeof(lept_member)); + size++; + m.k = NULL; /* ownership is transferred to member on stack */ + /* parse ws [comma | right-curly-brace] ws */ + lept_parse_whitespace(c); + if (*c->json == ',') { + c->json++; + lept_parse_whitespace(c); + } + else if (*c->json == '}') { + c->json++; + lept_set_object(v, size); + memcpy(v->u.o.m, lept_context_pop(c, sizeof(lept_member) * size), sizeof(lept_member) * size); + v->u.o.size = size; + return LEPT_PARSE_OK; + } + else { + ret = LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET; + break; + } + } + /* Pop and free members on the stack */ + free(m.k); + for (i = 0; i < size; i++) { + lept_member* m = (lept_member*)lept_context_pop(c, sizeof(lept_member)); + free(m->k); + lept_free(&m->v); + } + v->type = LEPT_NULL; + return ret; +} + +static int lept_parse_value(lept_context* c, lept_value* v) { + switch (*c->json) { + case 't': return lept_parse_literal(c, v, "true", LEPT_TRUE); + case 'f': return lept_parse_literal(c, v, "false", LEPT_FALSE); + case 'n': return lept_parse_literal(c, v, "null", LEPT_NULL); + default: return lept_parse_number(c, v); + case '"': return lept_parse_string(c, v); + case '[': return lept_parse_array(c, v); + case '{': return lept_parse_object(c, v); + case '\0': return LEPT_PARSE_EXPECT_VALUE; + } +} + +int lept_parse(lept_value* v, const char* json) { + lept_context c; + int ret; + assert(v != NULL); + c.json = json; + c.stack = NULL; + c.size = c.top = 0; + lept_init(v); + lept_parse_whitespace(&c); + if ((ret = lept_parse_value(&c, v)) == LEPT_PARSE_OK) { + lept_parse_whitespace(&c); + if (*c.json != '\0') { + v->type = LEPT_NULL; + ret = LEPT_PARSE_ROOT_NOT_SINGULAR; + } + } + assert(c.top == 0); + free(c.stack); + return ret; +} + +static void lept_stringify_string(lept_context* c, const char* s, size_t len) { + static const char hex_digits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + size_t i, size; + char* head, *p; + assert(s != NULL); + p = head = lept_context_push(c, size = len * 6 + 2); /* "\u00xx..." */ + *p++ = '"'; + for (i = 0; i < len; i++) { + unsigned char ch = (unsigned char)s[i]; + switch (ch) { + case '\"': *p++ = '\\'; *p++ = '\"'; break; + case '\\': *p++ = '\\'; *p++ = '\\'; break; + case '\b': *p++ = '\\'; *p++ = 'b'; break; + case '\f': *p++ = '\\'; *p++ = 'f'; break; + case '\n': *p++ = '\\'; *p++ = 'n'; break; + case '\r': *p++ = '\\'; *p++ = 'r'; break; + case '\t': *p++ = '\\'; *p++ = 't'; break; + default: + if (ch < 0x20) { + *p++ = '\\'; *p++ = 'u'; *p++ = '0'; *p++ = '0'; + *p++ = hex_digits[ch >> 4]; + *p++ = hex_digits[ch & 15]; + } + else + *p++ = s[i]; + } + } + *p++ = '"'; + c->top -= size - (p - head); +} + +static void lept_stringify_value(lept_context* c, const lept_value* v) { + size_t i; + switch (v->type) { + case LEPT_NULL: PUTS(c, "null", 4); break; + case LEPT_FALSE: PUTS(c, "false", 5); break; + case LEPT_TRUE: PUTS(c, "true", 4); break; + case LEPT_NUMBER: c->top -= 32 - sprintf(lept_context_push(c, 32), "%.17g", v->u.n); break; + case LEPT_STRING: lept_stringify_string(c, v->u.s.s, v->u.s.len); break; + case LEPT_ARRAY: + PUTC(c, '['); + for (i = 0; i < v->u.a.size; i++) { + if (i > 0) + PUTC(c, ','); + lept_stringify_value(c, &v->u.a.e[i]); + } + PUTC(c, ']'); + break; + case LEPT_OBJECT: + PUTC(c, '{'); + for (i = 0; i < v->u.o.size; i++) { + if (i > 0) + PUTC(c, ','); + lept_stringify_string(c, v->u.o.m[i].k, v->u.o.m[i].klen); + PUTC(c, ':'); + lept_stringify_value(c, &v->u.o.m[i].v); + } + PUTC(c, '}'); + break; + default: assert(0 && "invalid type"); + } +} + +char* lept_stringify(const lept_value* v, size_t* length) { + lept_context c; + assert(v != NULL); + c.stack = (char*)malloc(c.size = LEPT_PARSE_STRINGIFY_INIT_SIZE); + c.top = 0; + lept_stringify_value(&c, v); + if (length) + *length = c.top; + PUTC(&c, '\0'); + return c.stack; +} + +void lept_copy(lept_value* dst, const lept_value* src) { + size_t i; + assert(src != NULL && dst != NULL && src != dst); + switch (src->type) { + case LEPT_STRING: + lept_set_string(dst, src->u.s.s, src->u.s.len); + break; + case LEPT_ARRAY: + lept_set_array(dst, src->u.a.size); + for (i = 0; i < src->u.a.size; i++) { + lept_value element; + lept_init(&element); + lept_copy(&element, &src->u.a.e[i]); + lept_move(lept_pushback_array_element(dst), &element); + } + break; + case LEPT_OBJECT: + lept_set_object(dst, src->u.o.size); + for (i = 0; i < src->u.o.size; i++) { + lept_value element; + lept_init(&element); + lept_copy(&element, &src->u.o.m[i].v); + lept_move(lept_set_object_value(dst, src->u.o.m[i].k, src->u.o.m[i].klen), &element); + } + break; + default: + lept_free(dst); + memcpy(dst, src, sizeof(lept_value)); + break; + } +} + +void lept_move(lept_value* dst, lept_value* src) { + assert(dst != NULL && src != NULL && src != dst); + lept_free(dst); + memcpy(dst, src, sizeof(lept_value)); + lept_init(src); +} + +void lept_swap(lept_value* lhs, lept_value* rhs) { + assert(lhs != NULL && rhs != NULL); + if (lhs != rhs) { + lept_value temp; + memcpy(&temp, lhs, sizeof(lept_value)); + memcpy(lhs, rhs, sizeof(lept_value)); + memcpy(rhs, &temp, sizeof(lept_value)); + } +} + +void lept_free(lept_value* v) { + size_t i; + assert(v != NULL); + switch (v->type) { + case LEPT_STRING: + free(v->u.s.s); + break; + case LEPT_ARRAY: + for (i = 0; i < v->u.a.size; i++) + lept_free(&v->u.a.e[i]); + free(v->u.a.e); + break; + case LEPT_OBJECT: + for (i = 0; i < v->u.o.size; i++) { + free(v->u.o.m[i].k); + lept_free(&v->u.o.m[i].v); + } + free(v->u.o.m); + break; + default: break; + } + v->type = LEPT_NULL; +} + +lept_type lept_get_type(const lept_value* v) { + assert(v != NULL); + return v->type; +} + +int lept_is_equal(const lept_value* lhs, const lept_value* rhs) { + size_t i, j; + assert(lhs != NULL && rhs != NULL); + if (lhs->type != rhs->type) + return 0; + switch (lhs->type) { + case LEPT_STRING: + return lhs->u.s.len == rhs->u.s.len && + memcmp(lhs->u.s.s, rhs->u.s.s, lhs->u.s.len) == 0; + case LEPT_NUMBER: + return lhs->u.n == rhs->u.n; + case LEPT_ARRAY: + if (lhs->u.a.size != rhs->u.a.size) + return 0; + for (i = 0; i < lhs->u.a.size; i++) + if (!lept_is_equal(&lhs->u.a.e[i], &rhs->u.a.e[i])) + return 0; + return 1; + case LEPT_OBJECT: + if (lhs->u.o.size != rhs->u.o.size) + return 0; + for (i = 0; i < lhs->u.o.size; i++) { + /* 找到对应的键 */ + for (j = 0; j < rhs->u.o.size; j++) { + if (lhs->u.o.m[i].klen == rhs->u.o.m[j].klen && + memcmp(lhs->u.o.m[i].k, rhs->u.o.m[j].k, lhs->u.o.m[i].klen) == 0) { + /* 找到了键,比较值 */ + if (!lept_is_equal(&lhs->u.o.m[i].v, &rhs->u.o.m[j].v)) + return 0; + break; + } + } + /* 没有找到对应的键 */ + if (j == rhs->u.o.size) + return 0; + } + return 1; + default: + return 1; + } +} + +int lept_get_boolean(const lept_value* v) { + assert(v != NULL && (v->type == LEPT_TRUE || v->type == LEPT_FALSE)); + return v->type == LEPT_TRUE; +} + +void lept_set_boolean(lept_value* v, int b) { + lept_free(v); + v->type = b ? LEPT_TRUE : LEPT_FALSE; +} + +double lept_get_number(const lept_value* v) { + assert(v != NULL && v->type == LEPT_NUMBER); + return v->u.n; +} + +void lept_set_number(lept_value* v, double n) { + lept_free(v); + v->u.n = n; + v->type = LEPT_NUMBER; +} + +const char* lept_get_string(const lept_value* v) { + assert(v != NULL && v->type == LEPT_STRING); + return v->u.s.s; +} + +size_t lept_get_string_length(const lept_value* v) { + assert(v != NULL && v->type == LEPT_STRING); + return v->u.s.len; +} + +void lept_set_string(lept_value* v, const char* s, size_t len) { + assert(v != NULL && (s != NULL || len == 0)); + lept_free(v); + v->u.s.s = (char*)malloc(len + 1); + memcpy(v->u.s.s, s, len); + v->u.s.s[len] = '\0'; + v->u.s.len = len; + v->type = LEPT_STRING; +} + +void lept_set_array(lept_value* v, size_t capacity) { + assert(v != NULL); + lept_free(v); + v->type = LEPT_ARRAY; + v->u.a.size = 0; + v->u.a.capacity = capacity; + v->u.a.e = capacity > 0 ? (lept_value*)malloc(capacity * sizeof(lept_value)) : NULL; +} + +size_t lept_get_array_size(const lept_value* v) { + assert(v != NULL && v->type == LEPT_ARRAY); + return v->u.a.size; +} + +size_t lept_get_array_capacity(const lept_value* v) { + assert(v != NULL && v->type == LEPT_ARRAY); + return v->u.a.capacity; +} + +void lept_reserve_array(lept_value* v, size_t capacity) { + assert(v != NULL && v->type == LEPT_ARRAY); + if (v->u.a.capacity < capacity) { + v->u.a.capacity = capacity; + v->u.a.e = (lept_value*)realloc(v->u.a.e, capacity * sizeof(lept_value)); + } +} + +void lept_shrink_array(lept_value* v) { + assert(v != NULL && v->type == LEPT_ARRAY); + if (v->u.a.capacity > v->u.a.size) { + v->u.a.capacity = v->u.a.size; + v->u.a.e = (lept_value*)realloc(v->u.a.e, v->u.a.capacity * sizeof(lept_value)); + } +} + +void lept_clear_array(lept_value* v) { + assert(v != NULL && v->type == LEPT_ARRAY); + lept_erase_array_element(v, 0, v->u.a.size); +} + +lept_value* lept_get_array_element(lept_value* v, size_t index) { + assert(v != NULL && v->type == LEPT_ARRAY); + assert(index < v->u.a.size); + return &v->u.a.e[index]; +} + +lept_value* lept_pushback_array_element(lept_value* v) { + assert(v != NULL && v->type == LEPT_ARRAY); + if (v->u.a.size == v->u.a.capacity) + lept_reserve_array(v, v->u.a.capacity == 0 ? 1 : v->u.a.capacity * 2); + lept_init(&v->u.a.e[v->u.a.size]); + return &v->u.a.e[v->u.a.size++]; +} + +void lept_popback_array_element(lept_value* v) { + assert(v != NULL && v->type == LEPT_ARRAY && v->u.a.size > 0); + lept_free(&v->u.a.e[--v->u.a.size]); +} + +lept_value* lept_insert_array_element(lept_value* v, size_t index) { + assert(v != NULL && v->type == LEPT_ARRAY && index <= v->u.a.size); + + /* 确保数组容量足够 */ + if (v->u.a.size + 1 > v->u.a.capacity) { + size_t new_capacity = v->u.a.capacity == 0 ? 1 : v->u.a.capacity * 2; + lept_value* new_e = (lept_value*)realloc(v->u.a.e, new_capacity * sizeof(lept_value)); + if (new_e == NULL) + return NULL; + v->u.a.e = new_e; + v->u.a.capacity = new_capacity; + } + + /* 将 index 之后的元素向后移动一位 */ + if (index < v->u.a.size) { + memmove( + &v->u.a.e[index + 1], + &v->u.a.e[index], + (v->u.a.size - index) * sizeof(lept_value) + ); + } + + /* 初始化新插入的元素 */ + lept_init(&v->u.a.e[index]); + v->u.a.size++; + + return &v->u.a.e[index]; +} + +void lept_erase_array_element(lept_value* v, size_t index, size_t count) { + size_t i; + assert(v != NULL && v->type == LEPT_ARRAY && index + count <= v->u.a.size); + + /* 释放要删除的元素 */ + for (i = 0; i < count; i++) { + lept_free(&v->u.a.e[index + i]); + } + + /* 将后面的元素向前移动 */ + for (i = index; i + count < v->u.a.size; i++) { + /* 使用 memcpy 而不是赋值,因为 lept_value 可能含有指针,我们只需要复制值本身 */ + memcpy(&v->u.a.e[i], &v->u.a.e[i + count], sizeof(lept_value)); + } + + /* 更新数组大小 */ + v->u.a.size -= count; +} + +void lept_set_object(lept_value* v, size_t capacity) { + assert(v != NULL); + lept_free(v); + v->type = LEPT_OBJECT; + v->u.o.size = 0; + v->u.o.capacity = capacity; + v->u.o.m = capacity > 0 ? (lept_member*)malloc(capacity * sizeof(lept_member)) : NULL; +} + +size_t lept_get_object_size(const lept_value* v) { + assert(v != NULL && v->type == LEPT_OBJECT); + return v->u.o.size; +} + +size_t lept_get_object_capacity(const lept_value* v) { + assert(v != NULL && v->type == LEPT_OBJECT); + /* \todo */ + return 0; +} + +void lept_reserve_object(lept_value* v, size_t capacity) { + assert(v != NULL && v->type == LEPT_OBJECT); + /* \todo */ +} + +void lept_shrink_object(lept_value* v) { + assert(v != NULL && v->type == LEPT_OBJECT); + + /* 如果容量大于大小,则重新分配内存以减少容量 */ + if (v->u.o.capacity > v->u.o.size) { + v->u.o.capacity = v->u.o.size; + v->u.o.m = (lept_member*)realloc(v->u.o.m, v->u.o.capacity * sizeof(lept_member)); + } +} + +void lept_clear_object(lept_value* v) { + size_t i; + assert(v != NULL && v->type == LEPT_OBJECT); + + /* 释放所有成员的内存 */ + for (i = 0; i < v->u.o.size; i++) { + free(v->u.o.m[i].k); + lept_free(&v->u.o.m[i].v); + } + v->u.o.size = 0; +} + +const char* lept_get_object_key(const lept_value* v, size_t index) { + assert(v != NULL && v->type == LEPT_OBJECT); + assert(index < v->u.o.size); + return v->u.o.m[index].k; +} + +size_t lept_get_object_key_length(const lept_value* v, size_t index) { + assert(v != NULL && v->type == LEPT_OBJECT); + assert(index < v->u.o.size); + return v->u.o.m[index].klen; +} + +lept_value* lept_get_object_value(lept_value* v, size_t index) { + assert(v != NULL && v->type == LEPT_OBJECT); + assert(index < v->u.o.size); + return &v->u.o.m[index].v; +} + +size_t lept_find_object_index(const lept_value* v, const char* key, size_t klen) { + size_t i; + assert(v != NULL && v->type == LEPT_OBJECT && key != NULL); + for (i = 0; i < v->u.o.size; i++) + if (v->u.o.m[i].klen == klen && memcmp(v->u.o.m[i].k, key, klen) == 0) + return i; + return LEPT_KEY_NOT_EXIST; +} + +lept_value* lept_find_object_value(lept_value* v, const char* key, size_t klen) { + size_t index = lept_find_object_index(v, key, klen); + return index != LEPT_KEY_NOT_EXIST ? &v->u.o.m[index].v : NULL; +} + +lept_value* lept_set_object_value(lept_value* v, const char* key, size_t klen) { + size_t i, index; + assert(v != NULL && v->type == LEPT_OBJECT && key != NULL); + + /* 先查找是否已存在该键 */ + index = lept_find_object_index(v, key, klen); + if (index != LEPT_KEY_NOT_EXIST) + return &v->u.o.m[index].v; + + /* 如果容量不足,则扩展容量 */ + if (v->u.o.size == v->u.o.capacity) { + size_t new_capacity = v->u.o.capacity == 0 ? 1 : v->u.o.capacity * 2; + lept_member* new_m = (lept_member*)realloc(v->u.o.m, new_capacity * sizeof(lept_member)); + if (new_m == NULL) + return NULL; + v->u.o.m = new_m; + v->u.o.capacity = new_capacity; + } + + /* 创建新的键值对 */ + i = v->u.o.size; + v->u.o.m[i].k = (char*)malloc(klen + 1); + memcpy(v->u.o.m[i].k, key, klen); + v->u.o.m[i].k[klen] = '\0'; + v->u.o.m[i].klen = klen; + lept_init(&v->u.o.m[i].v); + v->u.o.size++; + + return &v->u.o.m[i].v; +} + +void lept_remove_object_value(lept_value* v, size_t index) { + assert(v != NULL && v->type == LEPT_OBJECT && index < v->u.o.size); + + /* 释放要删除的成员 */ + free(v->u.o.m[index].k); + lept_free(&v->u.o.m[index].v); + + /* 将后面的成员向前移动 */ + if (index < v->u.o.size - 1) { + memmove( + &v->u.o.m[index], + &v->u.o.m[index + 1], + (v->u.o.size - index - 1) * sizeof(lept_member) + ); + } + + /* 更新对象大小 */ + v->u.o.size--; +} diff --git a/tutorial08_answer/leptjson.h b/tutorial08_answer/leptjson.h new file mode 100644 index 00000000..5526ded8 --- /dev/null +++ b/tutorial08_answer/leptjson.h @@ -0,0 +1,97 @@ +#ifndef LEPTJSON_H__ +#define LEPTJSON_H__ + +#include /* size_t */ + +typedef enum { LEPT_NULL, LEPT_FALSE, LEPT_TRUE, LEPT_NUMBER, LEPT_STRING, LEPT_ARRAY, LEPT_OBJECT } lept_type; + +#define LEPT_KEY_NOT_EXIST ((size_t)-1) + +typedef struct lept_value lept_value; +typedef struct lept_member lept_member; + +struct lept_value { + union { + struct { lept_member* m; size_t size, capacity; }o; /* object: members, member count, capacity */ + struct { lept_value* e; size_t size, capacity; }a; /* array: elements, element count, capacity */ + struct { char* s; size_t len; }s; /* string: null-terminated string, string length */ + double n; /* number */ + }u; + lept_type type; +}; + +struct lept_member { + char* k; size_t klen; /* member key string, key string length */ + lept_value v; /* member value */ +}; + +enum { + LEPT_PARSE_OK = 0, + LEPT_PARSE_EXPECT_VALUE, + LEPT_PARSE_INVALID_VALUE, + LEPT_PARSE_ROOT_NOT_SINGULAR, + LEPT_PARSE_NUMBER_TOO_BIG, + LEPT_PARSE_MISS_QUOTATION_MARK, + LEPT_PARSE_INVALID_STRING_ESCAPE, + LEPT_PARSE_INVALID_STRING_CHAR, + LEPT_PARSE_INVALID_UNICODE_HEX, + LEPT_PARSE_INVALID_UNICODE_SURROGATE, + LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, + LEPT_PARSE_MISS_KEY, + LEPT_PARSE_MISS_COLON, + LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET +}; + +#define lept_init(v) do { (v)->type = LEPT_NULL; } while(0) + +int lept_parse(lept_value* v, const char* json); +char* lept_stringify(const lept_value* v, size_t* length); + +void lept_copy(lept_value* dst, const lept_value* src); +void lept_move(lept_value* dst, lept_value* src); +void lept_swap(lept_value* lhs, lept_value* rhs); + +void lept_free(lept_value* v); + +lept_type lept_get_type(const lept_value* v); +int lept_is_equal(const lept_value* lhs, const lept_value* rhs); + +#define lept_set_null(v) lept_free(v) + +int lept_get_boolean(const lept_value* v); +void lept_set_boolean(lept_value* v, int b); + +double lept_get_number(const lept_value* v); +void lept_set_number(lept_value* v, double n); + +const char* lept_get_string(const lept_value* v); +size_t lept_get_string_length(const lept_value* v); +void lept_set_string(lept_value* v, const char* s, size_t len); + +void lept_set_array(lept_value* v, size_t capacity); +size_t lept_get_array_size(const lept_value* v); +size_t lept_get_array_capacity(const lept_value* v); +void lept_reserve_array(lept_value* v, size_t capacity); +void lept_shrink_array(lept_value* v); +void lept_clear_array(lept_value* v); +lept_value* lept_get_array_element(lept_value* v, size_t index); +lept_value* lept_pushback_array_element(lept_value* v); +void lept_popback_array_element(lept_value* v); +lept_value* lept_insert_array_element(lept_value* v, size_t index); +void lept_erase_array_element(lept_value* v, size_t index, size_t count); + +void lept_set_object(lept_value* v, size_t capacity); +size_t lept_get_object_size(const lept_value* v); +size_t lept_get_object_capacity(const lept_value* v); +void lept_reserve_object(lept_value* v, size_t capacity); +void lept_shrink_object(lept_value* v); +void lept_clear_object(lept_value* v); +const char* lept_get_object_key(const lept_value* v, size_t index); +size_t lept_get_object_key_length(const lept_value* v, size_t index); +lept_value* lept_get_object_value(lept_value* v, size_t index); +size_t lept_find_object_index(const lept_value* v, const char* key, size_t klen); +lept_value* lept_find_object_value(lept_value* v, const char* key, size_t klen); +lept_value* lept_set_object_value(lept_value* v, const char* key, size_t klen); +void lept_remove_object_value(lept_value* v, size_t index); + +#endif /* LEPTJSON_H__ */ diff --git a/tutorial08_answer/test.c b/tutorial08_answer/test.c new file mode 100644 index 00000000..5f64bd30 --- /dev/null +++ b/tutorial08_answer/test.c @@ -0,0 +1,721 @@ +#ifdef _WINDOWS +#define _CRTDBG_MAP_ALLOC +#include +#endif +#include +#include +#include +#include "leptjson.h" + +static int main_ret = 0; +static int test_count = 0; +static int test_pass = 0; + +#define EXPECT_EQ_BASE(equality, expect, actual, format) \ + do {\ + test_count++;\ + if (equality)\ + test_pass++;\ + else {\ + fprintf(stderr, "%s:%d: expect: " format " actual: " format "\n", __FILE__, __LINE__, expect, actual);\ + main_ret = 1;\ + }\ + } while(0) + +#define EXPECT_EQ_INT(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, "%d") +#define EXPECT_EQ_DOUBLE(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, "%.17g") +#define EXPECT_EQ_STRING(expect, actual, alength) \ + EXPECT_EQ_BASE(sizeof(expect) - 1 == alength && memcmp(expect, actual, alength + 1) == 0, expect, actual, "%s") +#define EXPECT_TRUE(actual) EXPECT_EQ_BASE((actual) != 0, "true", "false", "%s") +#define EXPECT_FALSE(actual) EXPECT_EQ_BASE((actual) == 0, "false", "true", "%s") + +#if defined(_MSC_VER) +#define EXPECT_EQ_SIZE_T(expect, actual) EXPECT_EQ_BASE((expect) == (actual), (size_t)expect, (size_t)actual, "%Iu") +#else +#define EXPECT_EQ_SIZE_T(expect, actual) EXPECT_EQ_BASE((expect) == (actual), (unsigned long)expect, (unsigned long)actual, "%lu") +#endif + +static void test_parse_null() { + lept_value v; + lept_init(&v); + lept_set_boolean(&v, 0); + EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, "null")); + EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v)); + lept_free(&v); +} + +static void test_parse_true() { + lept_value v; + lept_init(&v); + lept_set_boolean(&v, 0); + EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, "true")); + EXPECT_EQ_INT(LEPT_TRUE, lept_get_type(&v)); + lept_free(&v); +} + +static void test_parse_false() { + lept_value v; + lept_init(&v); + lept_set_boolean(&v, 1); + EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, "false")); + EXPECT_EQ_INT(LEPT_FALSE, lept_get_type(&v)); + lept_free(&v); +} + +#define TEST_NUMBER(expect, json)\ + do {\ + lept_value v;\ + lept_init(&v);\ + EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));\ + EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(&v));\ + EXPECT_EQ_DOUBLE(expect, lept_get_number(&v));\ + lept_free(&v);\ + } while(0) + +static void test_parse_number() { + TEST_NUMBER(0.0, "0"); + TEST_NUMBER(0.0, "-0"); + TEST_NUMBER(0.0, "-0.0"); + TEST_NUMBER(1.0, "1"); + TEST_NUMBER(-1.0, "-1"); + TEST_NUMBER(1.5, "1.5"); + TEST_NUMBER(-1.5, "-1.5"); + TEST_NUMBER(3.1416, "3.1416"); + TEST_NUMBER(1E10, "1E10"); + TEST_NUMBER(1e10, "1e10"); + TEST_NUMBER(1E+10, "1E+10"); + TEST_NUMBER(1E-10, "1E-10"); + TEST_NUMBER(-1E10, "-1E10"); + TEST_NUMBER(-1e10, "-1e10"); + TEST_NUMBER(-1E+10, "-1E+10"); + TEST_NUMBER(-1E-10, "-1E-10"); + TEST_NUMBER(1.234E+10, "1.234E+10"); + TEST_NUMBER(1.234E-10, "1.234E-10"); + TEST_NUMBER(0.0, "1e-10000"); /* must underflow */ + + TEST_NUMBER(1.0000000000000002, "1.0000000000000002"); /* the smallest number > 1 */ + TEST_NUMBER( 4.9406564584124654e-324, "4.9406564584124654e-324"); /* minimum denormal */ + TEST_NUMBER(-4.9406564584124654e-324, "-4.9406564584124654e-324"); + TEST_NUMBER( 2.2250738585072009e-308, "2.2250738585072009e-308"); /* Max subnormal double */ + TEST_NUMBER(-2.2250738585072009e-308, "-2.2250738585072009e-308"); + TEST_NUMBER( 2.2250738585072014e-308, "2.2250738585072014e-308"); /* Min normal positive double */ + TEST_NUMBER(-2.2250738585072014e-308, "-2.2250738585072014e-308"); + TEST_NUMBER( 1.7976931348623157e+308, "1.7976931348623157e+308"); /* Max double */ + TEST_NUMBER(-1.7976931348623157e+308, "-1.7976931348623157e+308"); +} + +#define TEST_STRING(expect, json)\ + do {\ + lept_value v;\ + lept_init(&v);\ + EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));\ + EXPECT_EQ_INT(LEPT_STRING, lept_get_type(&v));\ + EXPECT_EQ_STRING(expect, lept_get_string(&v), lept_get_string_length(&v));\ + lept_free(&v);\ + } while(0) + +static void test_parse_string() { + TEST_STRING("", "\"\""); + TEST_STRING("Hello", "\"Hello\""); + TEST_STRING("Hello\nWorld", "\"Hello\\nWorld\""); + TEST_STRING("\" \\ / \b \f \n \r \t", "\"\\\" \\\\ \\/ \\b \\f \\n \\r \\t\""); + TEST_STRING("Hello\0World", "\"Hello\\u0000World\""); + TEST_STRING("\x24", "\"\\u0024\""); /* Dollar sign U+0024 */ + TEST_STRING("\xC2\xA2", "\"\\u00A2\""); /* Cents sign U+00A2 */ + TEST_STRING("\xE2\x82\xAC", "\"\\u20AC\""); /* Euro sign U+20AC */ + TEST_STRING("\xF0\x9D\x84\x9E", "\"\\uD834\\uDD1E\""); /* G clef sign U+1D11E */ + TEST_STRING("\xF0\x9D\x84\x9E", "\"\\ud834\\udd1e\""); /* G clef sign U+1D11E */ +} + +static void test_parse_array() { + size_t i, j; + lept_value v; + + lept_init(&v); + EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, "[ ]")); + EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(&v)); + EXPECT_EQ_SIZE_T(0, lept_get_array_size(&v)); + lept_free(&v); + + lept_init(&v); + EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, "[ null , false , true , 123 , \"abc\" ]")); + EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(&v)); + EXPECT_EQ_SIZE_T(5, lept_get_array_size(&v)); + EXPECT_EQ_INT(LEPT_NULL, lept_get_type(lept_get_array_element(&v, 0))); + EXPECT_EQ_INT(LEPT_FALSE, lept_get_type(lept_get_array_element(&v, 1))); + EXPECT_EQ_INT(LEPT_TRUE, lept_get_type(lept_get_array_element(&v, 2))); + EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(lept_get_array_element(&v, 3))); + EXPECT_EQ_INT(LEPT_STRING, lept_get_type(lept_get_array_element(&v, 4))); + EXPECT_EQ_DOUBLE(123.0, lept_get_number(lept_get_array_element(&v, 3))); + EXPECT_EQ_STRING("abc", lept_get_string(lept_get_array_element(&v, 4)), lept_get_string_length(lept_get_array_element(&v, 4))); + lept_free(&v); + + lept_init(&v); + EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, "[ [ ] , [ 0 ] , [ 0 , 1 ] , [ 0 , 1 , 2 ] ]")); + EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(&v)); + EXPECT_EQ_SIZE_T(4, lept_get_array_size(&v)); + for (i = 0; i < 4; i++) { + lept_value* a = lept_get_array_element(&v, i); + EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(a)); + EXPECT_EQ_SIZE_T(i, lept_get_array_size(a)); + for (j = 0; j < i; j++) { + lept_value* e = lept_get_array_element(a, j); + EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(e)); + EXPECT_EQ_DOUBLE((double)j, lept_get_number(e)); + } + } + lept_free(&v); +} + +static void test_parse_object() { + lept_value v; + size_t i; + + lept_init(&v); + EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, " { } ")); + EXPECT_EQ_INT(LEPT_OBJECT, lept_get_type(&v)); + EXPECT_EQ_SIZE_T(0, lept_get_object_size(&v)); + lept_free(&v); + + lept_init(&v); + EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, + " { " + "\"n\" : null , " + "\"f\" : false , " + "\"t\" : true , " + "\"i\" : 123 , " + "\"s\" : \"abc\", " + "\"a\" : [ 1, 2, 3 ]," + "\"o\" : { \"1\" : 1, \"2\" : 2, \"3\" : 3 }" + " } " + )); + EXPECT_EQ_INT(LEPT_OBJECT, lept_get_type(&v)); + EXPECT_EQ_SIZE_T(7, lept_get_object_size(&v)); + EXPECT_EQ_STRING("n", lept_get_object_key(&v, 0), lept_get_object_key_length(&v, 0)); + EXPECT_EQ_INT(LEPT_NULL, lept_get_type(lept_get_object_value(&v, 0))); + EXPECT_EQ_STRING("f", lept_get_object_key(&v, 1), lept_get_object_key_length(&v, 1)); + EXPECT_EQ_INT(LEPT_FALSE, lept_get_type(lept_get_object_value(&v, 1))); + EXPECT_EQ_STRING("t", lept_get_object_key(&v, 2), lept_get_object_key_length(&v, 2)); + EXPECT_EQ_INT(LEPT_TRUE, lept_get_type(lept_get_object_value(&v, 2))); + EXPECT_EQ_STRING("i", lept_get_object_key(&v, 3), lept_get_object_key_length(&v, 3)); + EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(lept_get_object_value(&v, 3))); + EXPECT_EQ_DOUBLE(123.0, lept_get_number(lept_get_object_value(&v, 3))); + EXPECT_EQ_STRING("s", lept_get_object_key(&v, 4), lept_get_object_key_length(&v, 4)); + EXPECT_EQ_INT(LEPT_STRING, lept_get_type(lept_get_object_value(&v, 4))); + EXPECT_EQ_STRING("abc", lept_get_string(lept_get_object_value(&v, 4)), lept_get_string_length(lept_get_object_value(&v, 4))); + EXPECT_EQ_STRING("a", lept_get_object_key(&v, 5), lept_get_object_key_length(&v, 5)); + EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(lept_get_object_value(&v, 5))); + EXPECT_EQ_SIZE_T(3, lept_get_array_size(lept_get_object_value(&v, 5))); + for (i = 0; i < 3; i++) { + lept_value* e = lept_get_array_element(lept_get_object_value(&v, 5), i); + EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(e)); + EXPECT_EQ_DOUBLE(i + 1.0, lept_get_number(e)); + } + EXPECT_EQ_STRING("o", lept_get_object_key(&v, 6), lept_get_object_key_length(&v, 6)); + { + lept_value* o = lept_get_object_value(&v, 6); + EXPECT_EQ_INT(LEPT_OBJECT, lept_get_type(o)); + for (i = 0; i < 3; i++) { + lept_value* ov = lept_get_object_value(o, i); + EXPECT_TRUE('1' + i == lept_get_object_key(o, i)[0]); + EXPECT_EQ_SIZE_T(1, lept_get_object_key_length(o, i)); + EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(ov)); + EXPECT_EQ_DOUBLE(i + 1.0, lept_get_number(ov)); + } + } + lept_free(&v); +} + +#define TEST_PARSE_ERROR(error, json)\ + do {\ + lept_value v;\ + lept_init(&v);\ + v.type = LEPT_FALSE;\ + EXPECT_EQ_INT(error, lept_parse(&v, json));\ + EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));\ + lept_free(&v);\ + } while(0) + +static void test_parse_expect_value() { + TEST_PARSE_ERROR(LEPT_PARSE_EXPECT_VALUE, ""); + TEST_PARSE_ERROR(LEPT_PARSE_EXPECT_VALUE, " "); +} + +static void test_parse_invalid_value() { + TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, "nul"); + TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, "?"); + + /* invalid number */ + TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, "+0"); + TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, "+1"); + TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, ".123"); /* at least one digit before '.' */ + TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, "1."); /* at least one digit after '.' */ + TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, "INF"); + TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, "inf"); + TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, "NAN"); + TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, "nan"); + + /* invalid value in array */ + TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, "[1,]"); + TEST_PARSE_ERROR(LEPT_PARSE_INVALID_VALUE, "[\"a\", nul]"); +} + +static void test_parse_root_not_singular() { + TEST_PARSE_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, "null x"); + + /* invalid number */ + TEST_PARSE_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, "0123"); /* after zero should be '.' or nothing */ + TEST_PARSE_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, "0x0"); + TEST_PARSE_ERROR(LEPT_PARSE_ROOT_NOT_SINGULAR, "0x123"); +} + +static void test_parse_number_too_big() { + TEST_PARSE_ERROR(LEPT_PARSE_NUMBER_TOO_BIG, "1e309"); + TEST_PARSE_ERROR(LEPT_PARSE_NUMBER_TOO_BIG, "-1e309"); +} + +static void test_parse_miss_quotation_mark() { + TEST_PARSE_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK, "\""); + TEST_PARSE_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK, "\"abc"); +} + +static void test_parse_invalid_string_escape() { + TEST_PARSE_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, "\"\\v\""); + TEST_PARSE_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, "\"\\'\""); + TEST_PARSE_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, "\"\\0\""); + TEST_PARSE_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE, "\"\\x12\""); +} + +static void test_parse_invalid_string_char() { + TEST_PARSE_ERROR(LEPT_PARSE_INVALID_STRING_CHAR, "\"\x01\""); + TEST_PARSE_ERROR(LEPT_PARSE_INVALID_STRING_CHAR, "\"\x1F\""); +} + +static void test_parse_invalid_unicode_hex() { + TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, "\"\\u\""); + TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, "\"\\u0\""); + TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, "\"\\u01\""); + TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, "\"\\u012\""); + TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, "\"\\u/000\""); + TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, "\"\\uG000\""); + TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, "\"\\u0/00\""); + TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, "\"\\u0G00\""); + TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, "\"\\u0/00\""); + TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, "\"\\u00G0\""); + TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, "\"\\u000/\""); + TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, "\"\\u000G\""); + TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX, "\"\\u 123\""); +} + +static void test_parse_invalid_unicode_surrogate() { + TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, "\"\\uD800\""); + TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, "\"\\uDBFF\""); + TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, "\"\\uD800\\\\\""); + TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, "\"\\uD800\\uDBFF\""); + TEST_PARSE_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE, "\"\\uD800\\uE000\""); +} + +static void test_parse_miss_comma_or_square_bracket() { + TEST_PARSE_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, "[1"); + TEST_PARSE_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, "[1}"); + TEST_PARSE_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, "[1 2"); + TEST_PARSE_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, "[[]"); +} + +static void test_parse_miss_key() { + TEST_PARSE_ERROR(LEPT_PARSE_MISS_KEY, "{:1,"); + TEST_PARSE_ERROR(LEPT_PARSE_MISS_KEY, "{1:1,"); + TEST_PARSE_ERROR(LEPT_PARSE_MISS_KEY, "{true:1,"); + TEST_PARSE_ERROR(LEPT_PARSE_MISS_KEY, "{false:1,"); + TEST_PARSE_ERROR(LEPT_PARSE_MISS_KEY, "{null:1,"); + TEST_PARSE_ERROR(LEPT_PARSE_MISS_KEY, "{[]:1,"); + TEST_PARSE_ERROR(LEPT_PARSE_MISS_KEY, "{{}:1,"); + TEST_PARSE_ERROR(LEPT_PARSE_MISS_KEY, "{\"a\":1,"); +} + +static void test_parse_miss_colon() { + TEST_PARSE_ERROR(LEPT_PARSE_MISS_COLON, "{\"a\"}"); + TEST_PARSE_ERROR(LEPT_PARSE_MISS_COLON, "{\"a\",\"b\"}"); +} + +static void test_parse_miss_comma_or_curly_bracket() { + TEST_PARSE_ERROR(LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET, "{\"a\":1"); + TEST_PARSE_ERROR(LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET, "{\"a\":1]"); + TEST_PARSE_ERROR(LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET, "{\"a\":1 \"b\""); + TEST_PARSE_ERROR(LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET, "{\"a\":{}"); +} + +static void test_parse() { + test_parse_null(); + test_parse_true(); + test_parse_false(); + test_parse_number(); + test_parse_string(); + test_parse_array(); + test_parse_object(); + + test_parse_expect_value(); + test_parse_invalid_value(); + test_parse_root_not_singular(); + test_parse_number_too_big(); + test_parse_miss_quotation_mark(); + test_parse_invalid_string_escape(); + test_parse_invalid_string_char(); + test_parse_invalid_unicode_hex(); + test_parse_invalid_unicode_surrogate(); + test_parse_miss_comma_or_square_bracket(); + test_parse_miss_key(); + test_parse_miss_colon(); + test_parse_miss_comma_or_curly_bracket(); +} + +#define TEST_ROUNDTRIP(json)\ + do {\ + lept_value v;\ + char* json2;\ + size_t length;\ + lept_init(&v);\ + EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));\ + json2 = lept_stringify(&v, &length);\ + EXPECT_EQ_STRING(json, json2, length);\ + lept_free(&v);\ + free(json2);\ + } while(0) + +static void test_stringify_number() { + TEST_ROUNDTRIP("0"); + TEST_ROUNDTRIP("-0"); + TEST_ROUNDTRIP("1"); + TEST_ROUNDTRIP("-1"); + TEST_ROUNDTRIP("1.5"); + TEST_ROUNDTRIP("-1.5"); + TEST_ROUNDTRIP("3.25"); + TEST_ROUNDTRIP("1e+20"); + TEST_ROUNDTRIP("1.234e+20"); + TEST_ROUNDTRIP("1.234e-20"); + + TEST_ROUNDTRIP("1.0000000000000002"); /* the smallest number > 1 */ + TEST_ROUNDTRIP("4.9406564584124654e-324"); /* minimum denormal */ + TEST_ROUNDTRIP("-4.9406564584124654e-324"); + TEST_ROUNDTRIP("2.2250738585072009e-308"); /* Max subnormal double */ + TEST_ROUNDTRIP("-2.2250738585072009e-308"); + TEST_ROUNDTRIP("2.2250738585072014e-308"); /* Min normal positive double */ + TEST_ROUNDTRIP("-2.2250738585072014e-308"); + TEST_ROUNDTRIP("1.7976931348623157e+308"); /* Max double */ + TEST_ROUNDTRIP("-1.7976931348623157e+308"); +} + +static void test_stringify_string() { + TEST_ROUNDTRIP("\"\""); + TEST_ROUNDTRIP("\"Hello\""); + TEST_ROUNDTRIP("\"Hello\\nWorld\""); + TEST_ROUNDTRIP("\"\\\" \\\\ / \\b \\f \\n \\r \\t\""); + TEST_ROUNDTRIP("\"Hello\\u0000World\""); +} + +static void test_stringify_array() { + TEST_ROUNDTRIP("[]"); + TEST_ROUNDTRIP("[null,false,true,123,\"abc\",[1,2,3]]"); +} + +static void test_stringify_object() { + TEST_ROUNDTRIP("{}"); + TEST_ROUNDTRIP("{\"n\":null,\"f\":false,\"t\":true,\"i\":123,\"s\":\"abc\",\"a\":[1,2,3],\"o\":{\"1\":1,\"2\":2,\"3\":3}}"); +} + +static void test_stringify() { + TEST_ROUNDTRIP("null"); + TEST_ROUNDTRIP("false"); + TEST_ROUNDTRIP("true"); + test_stringify_number(); + test_stringify_string(); + test_stringify_array(); + test_stringify_object(); +} + +#define TEST_EQUAL(json1, json2, equality) \ + do {\ + lept_value v1, v2;\ + lept_init(&v1);\ + lept_init(&v2);\ + EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v1, json1));\ + EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v2, json2));\ + EXPECT_EQ_INT(equality, lept_is_equal(&v1, &v2));\ + lept_free(&v1);\ + lept_free(&v2);\ + } while(0) + +static void test_equal() { + TEST_EQUAL("true", "true", 1); + TEST_EQUAL("true", "false", 0); + TEST_EQUAL("false", "false", 1); + TEST_EQUAL("null", "null", 1); + TEST_EQUAL("null", "0", 0); + TEST_EQUAL("123", "123", 1); + TEST_EQUAL("123", "456", 0); + TEST_EQUAL("\"abc\"", "\"abc\"", 1); + TEST_EQUAL("\"abc\"", "\"abcd\"", 0); + TEST_EQUAL("[]", "[]", 1); + TEST_EQUAL("[]", "null", 0); + TEST_EQUAL("[1,2,3]", "[1,2,3]", 1); + TEST_EQUAL("[1,2,3]", "[1,2,3,4]", 0); + TEST_EQUAL("[[]]", "[[]]", 1); + TEST_EQUAL("{}", "{}", 1); + TEST_EQUAL("{}", "null", 0); + TEST_EQUAL("{}", "[]", 0); + TEST_EQUAL("{\"a\":1,\"b\":2}", "{\"a\":1,\"b\":2}", 1); + TEST_EQUAL("{\"a\":1,\"b\":2}", "{\"b\":2,\"a\":1}", 1); + TEST_EQUAL("{\"a\":1,\"b\":2}", "{\"a\":1,\"b\":3}", 0); + TEST_EQUAL("{\"a\":1,\"b\":2}", "{\"a\":1,\"b\":2,\"c\":3}", 0); + TEST_EQUAL("{\"a\":{\"b\":{\"c\":{}}}}", "{\"a\":{\"b\":{\"c\":{}}}}", 1); + TEST_EQUAL("{\"a\":{\"b\":{\"c\":{}}}}", "{\"a\":{\"b\":{\"c\":[]}}}", 0); +} + +static void test_copy() { + lept_value v1, v2; + lept_init(&v1); + lept_parse(&v1, "{\"t\":true,\"f\":false,\"n\":null,\"d\":1.5,\"a\":[1,2,3]}"); + lept_init(&v2); + lept_copy(&v2, &v1); + EXPECT_TRUE(lept_is_equal(&v2, &v1)); + lept_free(&v1); + lept_free(&v2); +} + +static void test_move() { + lept_value v1, v2, v3; + lept_init(&v1); + lept_parse(&v1, "{\"t\":true,\"f\":false,\"n\":null,\"d\":1.5,\"a\":[1,2,3]}"); + lept_init(&v2); + lept_copy(&v2, &v1); + lept_init(&v3); + lept_move(&v3, &v2); + EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v2)); + EXPECT_TRUE(lept_is_equal(&v3, &v1)); + lept_free(&v1); + lept_free(&v2); + lept_free(&v3); +} + +static void test_swap() { + lept_value v1, v2; + lept_init(&v1); + lept_init(&v2); + lept_set_string(&v1, "Hello", 5); + lept_set_string(&v2, "World!", 6); + lept_swap(&v1, &v2); + EXPECT_EQ_STRING("World!", lept_get_string(&v1), lept_get_string_length(&v1)); + EXPECT_EQ_STRING("Hello", lept_get_string(&v2), lept_get_string_length(&v2)); + lept_free(&v1); + lept_free(&v2); +} + +static void test_access_null() { + lept_value v; + lept_init(&v); + lept_set_string(&v, "a", 1); + lept_set_null(&v); + EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v)); + lept_free(&v); +} + +static void test_access_boolean() { + lept_value v; + lept_init(&v); + lept_set_string(&v, "a", 1); + lept_set_boolean(&v, 1); + EXPECT_TRUE(lept_get_boolean(&v)); + lept_set_boolean(&v, 0); + EXPECT_FALSE(lept_get_boolean(&v)); + lept_free(&v); +} + +static void test_access_number() { + lept_value v; + lept_init(&v); + lept_set_string(&v, "a", 1); + lept_set_number(&v, 1234.5); + EXPECT_EQ_DOUBLE(1234.5, lept_get_number(&v)); + lept_free(&v); +} + +static void test_access_string() { + lept_value v; + lept_init(&v); + lept_set_string(&v, "", 0); + EXPECT_EQ_STRING("", lept_get_string(&v), lept_get_string_length(&v)); + lept_set_string(&v, "Hello", 5); + EXPECT_EQ_STRING("Hello", lept_get_string(&v), lept_get_string_length(&v)); + lept_free(&v); +} + +static void test_access_array() { + lept_value a, e; + size_t i, j; + + lept_init(&a); + + for (j = 0; j <= 5; j += 5) { + lept_set_array(&a, j); + EXPECT_EQ_SIZE_T(0, lept_get_array_size(&a)); + EXPECT_EQ_SIZE_T(j, lept_get_array_capacity(&a)); + for (i = 0; i < 10; i++) { + lept_init(&e); + lept_set_number(&e, i); + lept_move(lept_pushback_array_element(&a), &e); + lept_free(&e); + } + + EXPECT_EQ_SIZE_T(10, lept_get_array_size(&a)); + for (i = 0; i < 10; i++) + EXPECT_EQ_DOUBLE((double)i, lept_get_number(lept_get_array_element(&a, i))); + } + + lept_popback_array_element(&a); + EXPECT_EQ_SIZE_T(9, lept_get_array_size(&a)); + for (i = 0; i < 9; i++) + EXPECT_EQ_DOUBLE((double)i, lept_get_number(lept_get_array_element(&a, i))); + + lept_erase_array_element(&a, 4, 0); + EXPECT_EQ_SIZE_T(9, lept_get_array_size(&a)); + for (i = 0; i < 9; i++) + EXPECT_EQ_DOUBLE((double)i, lept_get_number(lept_get_array_element(&a, i))); + + lept_erase_array_element(&a, 8, 1); + EXPECT_EQ_SIZE_T(8, lept_get_array_size(&a)); + for (i = 0; i < 8; i++) + EXPECT_EQ_DOUBLE((double)i, lept_get_number(lept_get_array_element(&a, i))); + + lept_erase_array_element(&a, 0, 2); + EXPECT_EQ_SIZE_T(6, lept_get_array_size(&a)); + for (i = 0; i < 6; i++) + EXPECT_EQ_DOUBLE((double)i + 2, lept_get_number(lept_get_array_element(&a, i))); + + for (i = 0; i < 2; i++) { + lept_init(&e); + lept_set_number(&e, i); + lept_move(lept_insert_array_element(&a, i), &e); + lept_free(&e); + } + + EXPECT_EQ_SIZE_T(8, lept_get_array_size(&a)); + for (i = 0; i < 8; i++) + EXPECT_EQ_DOUBLE((double)i, lept_get_number(lept_get_array_element(&a, i))); + + EXPECT_TRUE(lept_get_array_capacity(&a) > 8); + lept_shrink_array(&a); + EXPECT_EQ_SIZE_T(8, lept_get_array_capacity(&a)); + EXPECT_EQ_SIZE_T(8, lept_get_array_size(&a)); + for (i = 0; i < 8; i++) + EXPECT_EQ_DOUBLE((double)i, lept_get_number(lept_get_array_element(&a, i))); + + lept_set_string(&e, "Hello", 5); + lept_move(lept_pushback_array_element(&a), &e); /* Test if element is freed */ + lept_free(&e); + + i = lept_get_array_capacity(&a); + lept_clear_array(&a); + EXPECT_EQ_SIZE_T(0, lept_get_array_size(&a)); + EXPECT_EQ_SIZE_T(i, lept_get_array_capacity(&a)); /* capacity remains unchanged */ + lept_shrink_array(&a); + EXPECT_EQ_SIZE_T(0, lept_get_array_capacity(&a)); + + lept_free(&a); +} + +static void test_access_object() { +#if 0 + lept_value o, v, *pv; + size_t i, j, index; + + lept_init(&o); + + for (j = 0; j <= 5; j += 5) { + lept_set_object(&o, j); + EXPECT_EQ_SIZE_T(0, lept_get_object_size(&o)); + EXPECT_EQ_SIZE_T(j, lept_get_object_capacity(&o)); + for (i = 0; i < 10; i++) { + char key[2] = "a"; + key[0] += i; + lept_init(&v); + lept_set_number(&v, i); + lept_move(lept_set_object_value(&o, key, 1), &v); + lept_free(&v); + } + EXPECT_EQ_SIZE_T(10, lept_get_object_size(&o)); + for (i = 0; i < 10; i++) { + char key[] = "a"; + key[0] += i; + index = lept_find_object_index(&o, key, 1); + EXPECT_TRUE(index != LEPT_KEY_NOT_EXIST); + pv = lept_get_object_value(&o, index); + EXPECT_EQ_DOUBLE((double)i, lept_get_number(pv)); + } + } + + index = lept_find_object_index(&o, "j", 1); + EXPECT_TRUE(index != LEPT_KEY_NOT_EXIST); + lept_remove_object_value(&o, index); + index = lept_find_object_index(&o, "j", 1); + EXPECT_TRUE(index == LEPT_KEY_NOT_EXIST); + EXPECT_EQ_SIZE_T(9, lept_get_object_size(&o)); + + index = lept_find_object_index(&o, "a", 1); + EXPECT_TRUE(index != LEPT_KEY_NOT_EXIST); + lept_remove_object_value(&o, index); + index = lept_find_object_index(&o, "a", 1); + EXPECT_TRUE(index == LEPT_KEY_NOT_EXIST); + EXPECT_EQ_SIZE_T(8, lept_get_object_size(&o)); + + EXPECT_TRUE(lept_get_object_capacity(&o) > 8); + lept_shrink_object(&o); + EXPECT_EQ_SIZE_T(8, lept_get_object_capacity(&o)); + EXPECT_EQ_SIZE_T(8, lept_get_object_size(&o)); + for (i = 0; i < 8; i++) { + char key[] = "a"; + key[0] += i + 1; + EXPECT_EQ_DOUBLE((double)i + 1, lept_get_number(lept_get_object_value(&o, lept_find_object_index(&o, key, 1)))); + } + + lept_set_string(&v, "Hello", 5); + lept_move(lept_set_object_value(&o, "World", 5), &v); /* Test if element is freed */ + lept_free(&v); + + pv = lept_find_object_value(&o, "World", 5); + EXPECT_TRUE(pv != NULL); + EXPECT_EQ_STRING("Hello", lept_get_string(pv), lept_get_string_length(pv)); + + i = lept_get_object_capacity(&o); + lept_clear_object(&o); + EXPECT_EQ_SIZE_T(0, lept_get_object_size(&o)); + EXPECT_EQ_SIZE_T(i, lept_get_object_capacity(&o)); /* capacity remains unchanged */ + lept_shrink_object(&o); + EXPECT_EQ_SIZE_T(0, lept_get_object_capacity(&o)); + + lept_free(&o); +#endif +} + +static void test_access() { + test_access_null(); + test_access_boolean(); + test_access_number(); + test_access_string(); + test_access_array(); + test_access_object(); +} + +int main() { +#ifdef _WINDOWS + _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); +#endif + test_parse(); + test_stringify(); + test_equal(); + test_copy(); + test_move(); + test_swap(); + test_access(); + printf("%d/%d (%3.2f%%) passed\n", test_pass, test_count, test_pass * 100.0 / test_count); + return main_ret; +}