summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/Json.c462
-rw-r--r--libs/Json.h83
2 files changed, 545 insertions, 0 deletions
diff --git a/libs/Json.c b/libs/Json.c
new file mode 100644
index 0000000..c260bb3
--- /dev/null
+++ b/libs/Json.c
@@ -0,0 +1,462 @@
+/*
+ Copyright (c) 2009, Dave Gamble
+ Copyright (c) 2013, Esoteric Software
+
+ Permission is hereby granted, dispose of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ */
+
+/* Json */
+/* JSON parser in C. */
+
+#ifndef _DEFAULT_SOURCE
+/* Bring strings.h definitions into string.h, where appropriate */
+#define _DEFAULT_SOURCE
+#endif
+
+#ifndef _BSD_SOURCE
+/* Bring strings.h definitions into string.h, where appropriate */
+#define _BSD_SOURCE
+#endif
+
+#include "Json.h"
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h> /* strtod (C89), strtof (C99) */
+#include <string.h> /* strcasecmp (4.4BSD - compatibility), _stricmp (_WIN32) */
+#include <spine/extension.h>
+
+#ifndef SPINE_JSON_DEBUG
+/* Define this to do extra NULL and expected-character checking */
+#define SPINE_JSON_DEBUG 0
+#endif
+
+static const char* ep;
+
+const char* Json_getError (void) {
+ return ep;
+}
+
+static int Json_strcasecmp (const char* s1, const char* s2) {
+ /* TODO we may be able to elide these NULL checks if we can prove
+ * the graph and input (only callsite is Json_getItem) should not have NULLs
+ */
+ if (s1 && s2) {
+#if defined(_WIN32)
+ return _stricmp(s1, s2);
+#else
+ return strcasecmp( s1, s2 );
+#endif
+ } else {
+ if (s1 < s2)
+ return -1; /* s1 is null, s2 is not */
+ else if (s1 == s2)
+ return 0; /* both are null */
+ else
+ return 1; /* s2 is nul s1 is not */
+ }
+}
+
+/* Internal constructor. */
+static Json *Json_new (void) {
+ return (Json*)CALLOC(Json, 1);
+}
+
+/* Delete a Json structure. */
+void Json_dispose (Json *c) {
+ Json *next;
+ while (c) {
+ next = c->next;
+ if (c->child) Json_dispose(c->child);
+ if (c->valueString) FREE(c->valueString);
+ if (c->name) FREE(c->name);
+ FREE(c);
+ c = next;
+ }
+}
+
+/* Parse the input text to generate a number, and populate the result into item. */
+static const char* parse_number (Json *item, const char* num) {
+ double result = 0.0;
+ int negative = 0;
+ char* ptr = (char*)num;
+
+ if (*ptr == '-') {
+ negative = -1;
+ ++ptr;
+ }
+
+ while (*ptr >= '0' && *ptr <= '9') {
+ result = result * 10.0 + (*ptr - '0');
+ ++ptr;
+ }
+
+ if (*ptr == '.') {
+ double fraction = 0.0;
+ int n = 0;
+ ++ptr;
+
+ while (*ptr >= '0' && *ptr <= '9') {
+ fraction = (fraction * 10.0) + (*ptr - '0');
+ ++ptr;
+ ++n;
+ }
+ result += fraction / POW(10.0, n);
+ }
+ if (negative) result = -result;
+
+ if (*ptr == 'e' || *ptr == 'E') {
+ double exponent = 0;
+ int expNegative = 0;
+ int n = 0;
+ ++ptr;
+
+ if (*ptr == '-') {
+ expNegative = -1;
+ ++ptr;
+ } else if (*ptr == '+') {
+ ++ptr;
+ }
+
+ while (*ptr >= '0' && *ptr <= '9') {
+ exponent = (exponent * 10.0) + (*ptr - '0');
+ ++ptr;
+ ++n;
+ }
+
+ if (expNegative)
+ result = result / POW(10, exponent);
+ else
+ result = result * POW(10, exponent);
+ }
+
+ if (ptr != num) {
+ /* Parse success, number found. */
+ item->valueFloat = result;
+ item->valueInt = (int)result;
+ item->type = Json_Number;
+ return ptr;
+ } else {
+ /* Parse failure, ep is set. */
+ ep = num;
+ return 0;
+ }
+}
+
+/* Parse the input text into an unescaped cstring, and populate item. */
+static const unsigned char firstByteMark[7] = {0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC};
+static const char* parse_string (Json *item, const char* str) {
+ const char* ptr = str + 1;
+ char* ptr2;
+ char* out;
+ int len = 0;
+ unsigned uc, uc2;
+ if (*str != '\"') { /* TODO: don't need this check when called from parse_value, but do need from parse_object */
+ ep = str;
+ return 0;
+ } /* not a string! */
+
+ while (*ptr != '\"' && *ptr && ++len)
+ if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */
+
+ out = MALLOC(char, len + 1); /* The length needed for the string, roughly. */
+ if (!out) return 0;
+
+ ptr = str + 1;
+ ptr2 = out;
+ while (*ptr != '\"' && *ptr) {
+ if (*ptr != '\\')
+ *ptr2++ = *ptr++;
+ else {
+ ptr++;
+ switch (*ptr) {
+ case 'b':
+ *ptr2++ = '\b';
+ break;
+ case 'f':
+ *ptr2++ = '\f';
+ break;
+ case 'n':
+ *ptr2++ = '\n';
+ break;
+ case 'r':
+ *ptr2++ = '\r';
+ break;
+ case 't':
+ *ptr2++ = '\t';
+ break;
+ case 'u': /* transcode utf16 to utf8. */
+ sscanf(ptr + 1, "%4x", &uc);
+ ptr += 4; /* get the unicode char. */
+
+ if ((uc >= 0xDC00 && uc <= 0xDFFF) || uc == 0) break; /* check for invalid. */
+
+ /* TODO provide an option to ignore surrogates, use unicode replacement character? */
+ if (uc >= 0xD800 && uc <= 0xDBFF) /* UTF16 surrogate pairs. */
+ {
+ if (ptr[1] != '\\' || ptr[2] != 'u') break; /* missing second-half of surrogate. */
+ sscanf(ptr + 3, "%4x", &uc2);
+ ptr += 6;
+ if (uc2 < 0xDC00 || uc2 > 0xDFFF) break; /* invalid second-half of surrogate. */
+ uc = 0x10000 + (((uc & 0x3FF) << 10) | (uc2 & 0x3FF));
+ }
+
+ len = 4;
+ if (uc < 0x80)
+ len = 1;
+ else if (uc < 0x800)
+ len = 2;
+ else if (uc < 0x10000) len = 3;
+ ptr2 += len;
+
+ switch (len) {
+ case 4:
+ *--ptr2 = ((uc | 0x80) & 0xBF);
+ uc >>= 6;
+ /* fallthrough */
+ case 3:
+ *--ptr2 = ((uc | 0x80) & 0xBF);
+ uc >>= 6;
+ /* fallthrough */
+ case 2:
+ *--ptr2 = ((uc | 0x80) & 0xBF);
+ uc >>= 6;
+ /* fallthrough */
+ case 1:
+ *--ptr2 = (uc | firstByteMark[len]);
+ }
+ ptr2 += len;
+ break;
+ default:
+ *ptr2++ = *ptr;
+ break;
+ }
+ ptr++;
+ }
+ }
+ *ptr2 = 0;
+ if (*ptr == '\"') ptr++; /* TODO error handling if not \" or \0 ? */
+ item->valueString = out;
+ item->type = Json_String;
+ return ptr;
+}
+
+/* Predeclare these prototypes. */
+static const char* parse_value (Json *item, const char* value);
+static const char* parse_array (Json *item, const char* value);
+static const char* parse_object (Json *item, const char* value);
+
+/* Utility to jump whitespace and cr/lf */
+static const char* skip (const char* in) {
+ if (!in) return 0; /* must propagate NULL since it's often called in skip(f(...)) form */
+ while (*in && (unsigned char)*in <= 32)
+ in++;
+ return in;
+}
+
+/* Parse an object - create a new root, and populate. */
+Json *Json_create (const char* value) {
+ Json *c;
+ ep = 0;
+ if (!value) return 0; /* only place we check for NULL other than skip() */
+ c = Json_new();
+ if (!c) return 0; /* memory fail */
+
+ value = parse_value(c, skip(value));
+ if (!value) {
+ Json_dispose(c);
+ return 0;
+ } /* parse failure. ep is set. */
+
+ return c;
+}
+
+/* Parser core - when encountering text, process appropriately. */
+static const char* parse_value (Json *item, const char* value) {
+ /* Referenced by Json_create(), parse_array(), and parse_object(). */
+ /* Always called with the result of skip(). */
+#if SPINE_JSON_DEBUG /* Checked at entry to graph, Json_create, and after every parse_ call. */
+ if (!value) return 0; /* Fail on null. */
+#endif
+
+ switch (*value) {
+ case 'n': {
+ if (!strncmp(value + 1, "ull", 3)) {
+ item->type = Json_NULL;
+ return value + 4;
+ }
+ break;
+ }
+ case 'f': {
+ if (!strncmp(value + 1, "alse", 4)) {
+ item->type = Json_False;
+ /* calloc prevents us needing item->type = Json_False or valueInt = 0 here */
+ return value + 5;
+ }
+ break;
+ }
+ case 't': {
+ if (!strncmp(value + 1, "rue", 3)) {
+ item->type = Json_True;
+ item->valueInt = 1;
+ return value + 4;
+ }
+ break;
+ }
+ case '\"':
+ return parse_string(item, value);
+ case '[':
+ return parse_array(item, value);
+ case '{':
+ return parse_object(item, value);
+ case '-': /* fallthrough */
+ case '0': /* fallthrough */
+ case '1': /* fallthrough */
+ case '2': /* fallthrough */
+ case '3': /* fallthrough */
+ case '4': /* fallthrough */
+ case '5': /* fallthrough */
+ case '6': /* fallthrough */
+ case '7': /* fallthrough */
+ case '8': /* fallthrough */
+ case '9':
+ return parse_number(item, value);
+ default:
+ break;
+ }
+
+ ep = value;
+ return 0; /* failure. */
+}
+
+/* Build an array from input text. */
+static const char* parse_array (Json *item, const char* value) {
+ Json *child;
+
+#if SPINE_JSON_DEBUG /* unnecessary, only callsite (parse_value) verifies this */
+ if (*value != '[') {
+ ep = value;
+ return 0;
+ } /* not an array! */
+#endif
+
+ item->type = Json_Array;
+ value = skip(value + 1);
+ if (*value == ']') return value + 1; /* empty array. */
+
+ item->child = child = Json_new();
+ if (!item->child) return 0; /* memory fail */
+ value = skip(parse_value(child, skip(value))); /* skip any spacing, get the value. */
+ if (!value) return 0;
+ item->size = 1;
+
+ while (*value == ',') {
+ Json *new_item = Json_new();
+ if (!new_item) return 0; /* memory fail */
+ child->next = new_item;
+#if SPINE_JSON_HAVE_PREV
+ new_item->prev = child;
+#endif
+ child = new_item;
+ value = skip(parse_value(child, skip(value + 1)));
+ if (!value) return 0; /* parse fail */
+ item->size++;
+ }
+
+ if (*value == ']') return value + 1; /* end of array */
+ ep = value;
+ return 0; /* malformed. */
+}
+
+/* Build an object from the text. */
+static const char* parse_object (Json *item, const char* value) {
+ Json *child;
+
+#if SPINE_JSON_DEBUG /* unnecessary, only callsite (parse_value) verifies this */
+ if (*value != '{') {
+ ep = value;
+ return 0;
+ } /* not an object! */
+#endif
+
+ item->type = Json_Object;
+ value = skip(value + 1);
+ if (*value == '}') return value + 1; /* empty array. */
+
+ item->child = child = Json_new();
+ if (!item->child) return 0;
+ value = skip(parse_string(child, skip(value)));
+ if (!value) return 0;
+ child->name = child->valueString;
+ child->valueString = 0;
+ if (*value != ':') {
+ ep = value;
+ return 0;
+ } /* fail! */
+ value = skip(parse_value(child, skip(value + 1))); /* skip any spacing, get the value. */
+ if (!value) return 0;
+ item->size = 1;
+
+ while (*value == ',') {
+ Json *new_item = Json_new();
+ if (!new_item) return 0; /* memory fail */
+ child->next = new_item;
+#if SPINE_JSON_HAVE_PREV
+ new_item->prev = child;
+#endif
+ child = new_item;
+ value = skip(parse_string(child, skip(value + 1)));
+ if (!value) return 0;
+ child->name = child->valueString;
+ child->valueString = 0;
+ if (*value != ':') {
+ ep = value;
+ return 0;
+ } /* fail! */
+ value = skip(parse_value(child, skip(value + 1))); /* skip any spacing, get the value. */
+ if (!value) return 0;
+ item->size++;
+ }
+
+ if (*value == '}') return value + 1; /* end of array */
+ ep = value;
+ return 0; /* malformed. */
+}
+
+Json *Json_getItem (Json *object, const char* string) {
+ Json *c = object->child;
+ while (c && Json_strcasecmp(c->name, string))
+ c = c->next;
+ return c;
+}
+
+const char* Json_getString (Json* object, const char* name, const char* defaultValue) {
+ object = Json_getItem(object, name);
+ if (object) return object->valueString;
+ return defaultValue;
+}
+
+float Json_getFloat (Json* value, const char* name, float defaultValue) {
+ value = Json_getItem(value, name);
+ return value ? value->valueFloat : defaultValue;
+}
+
+int Json_getInt (Json* value, const char* name, int defaultValue) {
+ value = Json_getItem(value, name);
+ return value ? value->valueInt : defaultValue;
+}
diff --git a/libs/Json.h b/libs/Json.h
new file mode 100644
index 0000000..078a5a7
--- /dev/null
+++ b/libs/Json.h
@@ -0,0 +1,83 @@
+/*
+ Copyright (c) 2009 Dave Gamble
+
+ Permission is hereby granted, dispose of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+ */
+
+/* Esoteric Software: Removed everything except parsing, shorter method names, more get methods, double to float, formatted. */
+
+#ifndef SPINE_JSON_H_
+#define SPINE_JSON_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Json Types: */
+#define Json_False 0
+#define Json_True 1
+#define Json_NULL 2
+#define Json_Number 3
+#define Json_String 4
+#define Json_Array 5
+#define Json_Object 6
+
+#ifndef SPINE_JSON_HAVE_PREV
+/* Spine doesn't use the "prev" link in the Json sibling lists. */
+#define SPINE_JSON_HAVE_PREV 0
+#endif
+
+/* The Json structure: */
+typedef struct Json {
+ struct Json* next;
+#if SPINE_JSON_HAVE_PREV
+ struct Json* prev; /* next/prev allow you to walk array/object chains. Alternatively, use getSize/getItem */
+#endif
+ struct Json* child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
+
+ int type; /* The type of the item, as above. */
+ int size; /* The number of children. */
+
+ const char* valueString; /* The item's string, if type==Json_String */
+ int valueInt; /* The item's number, if type==Json_Number */
+ float valueFloat; /* The item's number, if type==Json_Number */
+
+ const char* name; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
+} Json;
+
+/* Supply a block of JSON, and this returns a Json object you can interrogate. Call Json_dispose when finished. */
+Json* Json_create (const char* value);
+
+/* Delete a Json entity and all subentities. */
+void Json_dispose (Json* json);
+
+/* Get item "string" from object. Case insensitive. */
+Json* Json_getItem (Json* json, const char* string);
+const char* Json_getString (Json* json, const char* name, const char* defaultValue);
+float Json_getFloat (Json* json, const char* name, float defaultValue);
+int Json_getInt (Json* json, const char* name, int defaultValue);
+
+/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when Json_create() returns 0. 0 when Json_create() succeeds. */
+const char* Json_getError (void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SPINE_JSON_H_ */