--- /dev/null
+// https://github.com/vivkin/gason - pulled January 10, 2016
+#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;
+}