|
|
|
#include "gason.h" |
|
#include <stdlib.h> |
|
|
|
#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; |
|
} |
|
|