mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-23 13:30:02 +00:00
335 lines
9.3 KiB
C++
335 lines
9.3 KiB
C++
#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;
|
|
}
|