#include "gason.h" #include #define JSON_ZONE_SIZE 4096 #define JSON_STACK_SIZE 32 const char *jsonStrError(int err) { switch (err) { #define XX(no, str) \ case JSON_##no: \ return str; JSON_ERRNO_MAP(XX) #undef XX default: return "unknown"; } } void *JsonAllocator::allocate(size_t size) { size = (size + 7) & ~7; if (head && head->used + size <= JSON_ZONE_SIZE) { char *p = (char *)head + head->used; head->used += size; return p; } size_t allocSize = sizeof(Zone) + size; Zone *zone = (Zone *)malloc(allocSize <= JSON_ZONE_SIZE ? JSON_ZONE_SIZE : allocSize); if (zone == nullptr) return nullptr; zone->used = allocSize; if (allocSize <= JSON_ZONE_SIZE || head == nullptr) { zone->next = head; head = zone; } else { zone->next = head->next; head->next = zone; } return (char *)zone + sizeof(Zone); } void JsonAllocator::deallocate() { while (head) { Zone *next = head->next; free(head); head = next; } } static inline bool isspace(char c) { return c == ' ' || (c >= '\t' && c <= '\r'); } static inline bool isdelim(char c) { return c == ',' || c == ':' || c == ']' || c == '}' || isspace(c) || !c; } static inline bool isdigit(char c) { return c >= '0' && c <= '9'; } static inline bool isxdigit(char c) { return (c >= '0' && c <= '9') || ((c & ~' ') >= 'A' && (c & ~' ') <= 'F'); } static inline int char2int(char c) { if (c <= '9') return c - '0'; return (c & ~' ') - 'A' + 10; } static double string2double(char *s, char **endptr) { char ch = *s; if (ch == '-') ++s; double result = 0; while (isdigit(*s)) result = (result * 10) + (*s++ - '0'); if (*s == '.') { ++s; double fraction = 1; while (isdigit(*s)) { fraction *= 0.1; result += (*s++ - '0') * fraction; } } if (*s == 'e' || *s == 'E') { ++s; double base = 10; if (*s == '+') ++s; else if (*s == '-') { ++s; base = 0.1; } unsigned int exponent = 0; while (isdigit(*s)) exponent = (exponent * 10) + (*s++ - '0'); double power = 1; for (; exponent; exponent >>= 1, base *= base) if (exponent & 1) power *= base; result *= power; } *endptr = s; return ch == '-' ? -result : result; } static inline JsonNode *insertAfter(JsonNode *tail, JsonNode *node) { if (!tail) return node->next = node; node->next = tail->next; tail->next = node; return node; } static inline JsonValue listToValue(JsonTag tag, JsonNode *tail) { if (tail) { auto head = tail->next; tail->next = nullptr; return JsonValue(tag, head); } return JsonValue(tag, nullptr); } int jsonParse(char *s, char **endptr, JsonValue *value, JsonAllocator &allocator) { JsonNode *tails[JSON_STACK_SIZE]; JsonTag tags[JSON_STACK_SIZE]; char *keys[JSON_STACK_SIZE]; JsonValue o; int pos = -1; bool separator = true; JsonNode *node; *endptr = s; while (*s) { while (isspace(*s)) { ++s; if (!*s) break; } *endptr = s++; switch (**endptr) { case '-': if (!isdigit(*s) && *s != '.') { *endptr = s; return JSON_BAD_NUMBER; } case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': o = JsonValue(string2double(*endptr, &s)); if (!isdelim(*s)) { *endptr = s; return JSON_BAD_NUMBER; } break; case '"': o = JsonValue(JSON_STRING, s); for (char *it = s; *s; ++it, ++s) { int c = *it = *s; if (c == '\\') { c = *++s; switch (c) { case '\\': case '"': case '/': *it = c; break; case 'b': *it = '\b'; break; case 'f': *it = '\f'; break; case 'n': *it = '\n'; break; case 'r': *it = '\r'; break; case 't': *it = '\t'; break; case 'u': c = 0; for (int i = 0; i < 4; ++i) { if (isxdigit(*++s)) { c = c * 16 + char2int(*s); } else { *endptr = s; return JSON_BAD_STRING; } } if (c < 0x80) { *it = c; } else if (c < 0x800) { *it++ = 0xC0 | (c >> 6); *it = 0x80 | (c & 0x3F); } else { *it++ = 0xE0 | (c >> 12); *it++ = 0x80 | ((c >> 6) & 0x3F); *it = 0x80 | (c & 0x3F); } break; default: *endptr = s; return JSON_BAD_STRING; } } else if ((unsigned int)c < ' ' || c == '\x7F') { *endptr = s; return JSON_BAD_STRING; } else if (c == '"') { *it = 0; ++s; break; } } if (!isdelim(*s)) { *endptr = s; return JSON_BAD_STRING; } break; case 't': if (!(s[0] == 'r' && s[1] == 'u' && s[2] == 'e' && isdelim(s[3]))) return JSON_BAD_IDENTIFIER; o = JsonValue(JSON_TRUE); s += 3; break; case 'f': if (!(s[0] == 'a' && s[1] == 'l' && s[2] == 's' && s[3] == 'e' && isdelim(s[4]))) return JSON_BAD_IDENTIFIER; o = JsonValue(JSON_FALSE); s += 4; break; case 'n': if (!(s[0] == 'u' && s[1] == 'l' && s[2] == 'l' && isdelim(s[3]))) return JSON_BAD_IDENTIFIER; o = JsonValue(JSON_NULL); s += 3; break; case ']': if (pos == -1) return JSON_STACK_UNDERFLOW; if (tags[pos] != JSON_ARRAY) return JSON_MISMATCH_BRACKET; o = listToValue(JSON_ARRAY, tails[pos--]); break; case '}': if (pos == -1) return JSON_STACK_UNDERFLOW; if (tags[pos] != JSON_OBJECT) return JSON_MISMATCH_BRACKET; if (keys[pos] != nullptr) return JSON_UNEXPECTED_CHARACTER; o = listToValue(JSON_OBJECT, tails[pos--]); break; case '[': if (++pos == JSON_STACK_SIZE) return JSON_STACK_OVERFLOW; tails[pos] = nullptr; tags[pos] = JSON_ARRAY; keys[pos] = nullptr; separator = true; continue; case '{': if (++pos == JSON_STACK_SIZE) return JSON_STACK_OVERFLOW; tails[pos] = nullptr; tags[pos] = JSON_OBJECT; keys[pos] = nullptr; separator = true; continue; case ':': if (separator || keys[pos] == nullptr) return JSON_UNEXPECTED_CHARACTER; separator = true; continue; case ',': if (separator || keys[pos] != nullptr) return JSON_UNEXPECTED_CHARACTER; separator = true; continue; case '\0': continue; default: return JSON_UNEXPECTED_CHARACTER; } separator = false; if (pos == -1) { *endptr = s; *value = o; return JSON_OK; } if (tags[pos] == JSON_OBJECT) { if (!keys[pos]) { if (o.getTag() != JSON_STRING) return JSON_UNQUOTED_KEY; keys[pos] = o.toString(); continue; } if ((node = (JsonNode *) allocator.allocate(sizeof(JsonNode))) == nullptr) return JSON_ALLOCATION_FAILURE; tails[pos] = insertAfter(tails[pos], node); tails[pos]->key = keys[pos]; keys[pos] = nullptr; } else { if ((node = (JsonNode *) allocator.allocate(sizeof(JsonNode) - sizeof(char *))) == nullptr) return JSON_ALLOCATION_FAILURE; tails[pos] = insertAfter(tails[pos], node); } tails[pos]->value = o; } return JSON_BREAKING_BAD; }