From f2b1817eccc581c6cced14624dca0c681ffaf595 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 15 Apr 2018 11:24:10 -0700 Subject: [PATCH] json: Switch to gason instead of vjson. From the same author. Most importantly, reads numbers as doubles rather than as signed ints and floats. This allows us to actually read 32 bit unsigned int parameters. Moved all the native customization to a separate json_reader.cpp. --- CMakeLists.txt | 12 +- Core/Config.cpp | 6 +- UI/RemoteISOScreen.cpp | 19 +- UI/Store.cpp | 39 +- UI/Store.h | 4 +- UWP/NativeUWP/NativeUWP.vcxproj | 8 +- UWP/NativeUWP/NativeUWP.vcxproj.filters | 15 - ext/native/Android.mk | 4 +- ext/native/ext/gason/LICENSE | 20 + ext/native/ext/gason/gason.cpp | 334 ++++++++++++ ext/native/ext/gason/gason.h | 135 +++++ ext/native/ext/vjson/CMakeLists.txt | 10 - ext/native/ext/vjson/block_allocator.cpp | 52 -- ext/native/ext/vjson/block_allocator.h | 35 -- ext/native/ext/vjson/json.cpp | 643 ----------------------- ext/native/ext/vjson/json.h | 113 ---- ext/native/ext/vjson/main.cpp | 118 ----- ext/native/json/json_reader.cpp | 119 +++++ ext/native/json/json_reader.h | 85 +++ ext/native/json/json_writer.cpp | 72 +-- ext/native/json/json_writer.h | 4 +- ext/native/native.vcxproj | 8 +- ext/native/native.vcxproj.filters | 30 +- libretro/Makefile.common | 6 +- 24 files changed, 799 insertions(+), 1092 deletions(-) create mode 100644 ext/native/ext/gason/LICENSE create mode 100644 ext/native/ext/gason/gason.cpp create mode 100644 ext/native/ext/gason/gason.h delete mode 100644 ext/native/ext/vjson/CMakeLists.txt delete mode 100644 ext/native/ext/vjson/block_allocator.cpp delete mode 100644 ext/native/ext/vjson/block_allocator.h delete mode 100644 ext/native/ext/vjson/json.cpp delete mode 100644 ext/native/ext/vjson/json.h delete mode 100644 ext/native/ext/vjson/main.cpp create mode 100644 ext/native/json/json_reader.cpp create mode 100644 ext/native/json/json_reader.h diff --git a/CMakeLists.txt b/CMakeLists.txt index bc52b1b7a1f1..9d1b5200c441 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -458,11 +458,9 @@ if(USING_GLES2) find_package(X11) endif() -add_library(vjson STATIC - ext/native/ext/vjson/json.cpp - ext/native/ext/vjson/json.h - ext/native/ext/vjson/block_allocator.cpp - ext/native/ext/vjson/block_allocator.h +add_library(gason STATIC + ext/native/ext/gason/gason.cpp + ext/native/ext/gason/gason.h ) add_library(rg_etc1 STATIC @@ -972,6 +970,8 @@ add_library(native STATIC ext/native/input/keycodes.h ext/native/input/input_state.h ext/native/input/input_state.cpp + ext/native/json/json_reader.h + ext/native/json/json_reader.cpp ext/native/json/json_writer.h ext/native/json/json_writer.cpp ext/native/math/fast/fast_math.c @@ -1060,7 +1060,7 @@ if(ANDROID) SET(ATOMIC_LIB atomic) endif() -target_link_libraries(native ${LIBZIP_LIBRARY} ${PNG_LIBRARY} ${ZLIB_LIBRARY} rg_etc1 vjson udis86 ${RT_LIB} ${nativeExtraLibs} ${ATOMIC_LIB}) +target_link_libraries(native ${LIBZIP_LIBRARY} ${PNG_LIBRARY} ${ZLIB_LIBRARY} rg_etc1 gason udis86 ${RT_LIB} ${nativeExtraLibs} ${ATOMIC_LIB}) if(TARGET Ext::GLEW) target_link_libraries(native Ext::GLEW) endif() diff --git a/Core/Config.cpp b/Core/Config.cpp index e2bde296d9fc..b5b775b8da42 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -22,9 +22,9 @@ #include "base/display.h" #include "base/NativeApp.h" -#include "ext/vjson/json.h" #include "file/ini_file.h" #include "i18n/i18n.h" +#include "json/json_reader.h" #include "gfx_es2/gpu_features.h" #include "net/http_client.h" #include "util/text/parsers.h" @@ -1177,13 +1177,13 @@ void Config::DownloadCompletedCallback(http::Download &download) { } JsonReader reader(data.c_str(), data.size()); - const json_value *root = reader.root(); + const JsonGet root = reader.root(); if (!root) { ERROR_LOG(LOADER, "Failed to parse json"); return; } - std::string version = root->getString("version", ""); + std::string version = root.getString("version", ""); const char *gitVer = PPSSPP_GIT_VERSION; Version installed(gitVer); diff --git a/UI/RemoteISOScreen.cpp b/UI/RemoteISOScreen.cpp index 2767e72761f6..c9d71c6ab80a 100644 --- a/UI/RemoteISOScreen.cpp +++ b/UI/RemoteISOScreen.cpp @@ -21,9 +21,9 @@ #include #include "base/timeutil.h" -#include "ext/vjson/json.h" #include "file/fd_util.h" #include "i18n/i18n.h" +#include "json/json_reader.h" #include "net/http_client.h" #include "net/http_server.h" #include "net/resolve.h" @@ -252,16 +252,19 @@ static bool FindServer(std::string &resultHost, int &resultPort) { return false; } - const json_value *entries = reader.root(); - if (!entries) { + const JsonValue entries = reader.rootArray(); + if (entries.getTag() != JSON_ARRAY) { return false; } std::vector servers; - const json_value *entry = entries->first_child; - while (entry && !scanCancelled) { - const char *host = entry->getString("ip", ""); - int port = entry->getInt("p", 0); + for (const auto pentry : entries) { + JsonGet entry = pentry->value; + if (scanCancelled) + return false; + + const char *host = entry.getString("ip", ""); + int port = entry.getInt("p", 0); char url[1024] = {}; snprintf(url, sizeof(url), "http://%s:%d", host, port); @@ -270,8 +273,6 @@ static bool FindServer(std::string &resultHost, int &resultPort) { if (TryServer(host, port)) { return true; } - - entry = entry->next_sibling; } // None of the local IPs were reachable. diff --git a/UI/Store.cpp b/UI/Store.cpp index 99c79d2213d2..da2d2f1a4f17 100644 --- a/UI/Store.cpp +++ b/UI/Store.cpp @@ -18,7 +18,7 @@ #include #include "base/basictypes.h" -#include "ext/vjson/json.h" +#include "json/json_reader.h" #include "i18n/i18n.h" #include "ui/screen.h" @@ -392,33 +392,32 @@ void StoreScreen::update() { void StoreScreen::ParseListing(std::string json) { JsonReader reader(json.c_str(), json.size()); - if (!reader.ok()) { + if (!reader.ok() || !reader.root()) { ELOG("Error parsing JSON from store"); connectionError_ = true; RecreateViews(); return; } - json_value *root = reader.root(); - const json_value *entries = root->getArray("entries"); + const JsonGet root = reader.root(); + const JsonNode *entries = root.getArray("entries"); if (entries) { entries_.clear(); - const json_value *game = entries->first_child; - while (game) { + for (const JsonNode *pgame : entries->value) { + JsonGet game = pgame->value; StoreEntry e; e.type = ENTRY_PBPZIP; e.name = GetTranslatedString(game, "name"); e.description = GetTranslatedString(game, "description", ""); - e.author = game->getString("author", "?"); - e.size = game->getInt("size"); - e.downloadURL = game->getString("download-url", ""); - e.iconURL = game->getString("icon-url", ""); - e.hidden = game->getBool("hidden", false); - const char *file = game->getString("file", 0); + e.author = game.getString("author", "?"); + e.size = game.getInt("size"); + e.downloadURL = game.getString("download-url", ""); + e.iconURL = game.getString("icon-url", ""); + e.hidden = game.getBool("hidden", false); + const char *file = game.getString("file", nullptr); if (!file) continue; e.file = file; entries_.push_back(e); - game = game->next_sibling; } } } @@ -541,16 +540,16 @@ std::string StoreScreen::GetStoreJsonURL(std::string storePath) const { return path; } -std::string StoreScreen::GetTranslatedString(const json_value *json, std::string key, const char *fallback) const { - const json_value *dict = json->getDict("en_US"); - if (dict && json->hasChild(lang_.c_str(), JSON_OBJECT)) { - if (json->getDict(lang_.c_str())->hasChild(key.c_str(), JSON_STRING)) { - dict = json->getDict(lang_.c_str()); +std::string StoreScreen::GetTranslatedString(const JsonGet json, std::string key, const char *fallback) const { + JsonGet dict = json.getDict("en_US"); + if (dict && json.hasChild(lang_.c_str(), JSON_OBJECT)) { + if (json.getDict(lang_.c_str()).hasChild(key.c_str(), JSON_STRING)) { + dict = json.getDict(lang_.c_str()); } } - const char *str = 0; + const char *str = nullptr; if (dict) { - str = dict->getString(key.c_str(), 0); + str = dict.getString(key.c_str(), nullptr); } if (str) { return std::string(str); diff --git a/UI/Store.h b/UI/Store.h index 1fff85a4c924..e7786c7c9cff 100644 --- a/UI/Store.h +++ b/UI/Store.h @@ -29,7 +29,7 @@ // set game specific settings, etc. // Uses GameInfoCache heavily to implement the functionality. -struct json_value; +struct JsonGet; class ProductItemView; enum EntryType { @@ -79,7 +79,7 @@ class StoreScreen : public UIDialogScreenWithBackground { std::vector FilterEntries(); std::string GetStoreJsonURL(std::string storePath) const; - std::string GetTranslatedString(const json_value *json, std::string key, const char *fallback = 0) const; + std::string GetTranslatedString(const JsonGet json, std::string key, const char *fallback = nullptr) const; std::shared_ptr listing_; std::shared_ptr image_; diff --git a/UWP/NativeUWP/NativeUWP.vcxproj b/UWP/NativeUWP/NativeUWP.vcxproj index e6e8d6a89fbd..da5942bbfbac 100644 --- a/UWP/NativeUWP/NativeUWP.vcxproj +++ b/UWP/NativeUWP/NativeUWP.vcxproj @@ -328,8 +328,6 @@ - - @@ -338,6 +336,8 @@ + + @@ -1160,8 +1160,6 @@ NotUsing NotUsing - - @@ -1169,6 +1167,8 @@ + + diff --git a/UWP/NativeUWP/NativeUWP.vcxproj.filters b/UWP/NativeUWP/NativeUWP.vcxproj.filters index 6b58b4d5c17c..ce9603f3322a 100644 --- a/UWP/NativeUWP/NativeUWP.vcxproj.filters +++ b/UWP/NativeUWP/NativeUWP.vcxproj.filters @@ -37,9 +37,6 @@ {2be24387-0b6a-4253-97fa-b8b6f75a8a4c} - - {7fdd3320-a8e0-42ed-b08b-2d86ba2ff414} - {1a486fc4-bac0-4b33-9139-262272690c74} @@ -214,12 +211,6 @@ i18n - - ext\vjson - - - ext\vjson - ext\libzip @@ -656,12 +647,6 @@ i18n - - ext\vjson - - - ext\vjson - ext\libzip diff --git a/ext/native/Android.mk b/ext/native/Android.mk index 78a75db9d434..bcef6cec25cd 100644 --- a/ext/native/Android.mk +++ b/ext/native/Android.mk @@ -35,8 +35,7 @@ LOCAL_SRC_FILES :=\ ext/jpge/jpgd.cpp \ ext/jpge/jpge.cpp \ ext/sha1/sha1.cpp \ - ext/vjson/json.cpp \ - ext/vjson/block_allocator.cpp \ + ext/gason/gason.cpp \ file/fd_util.cpp \ file/chunk_file.cpp \ file/file_util.cpp \ @@ -44,6 +43,7 @@ LOCAL_SRC_FILES :=\ file/path.cpp \ file/ini_file.cpp \ file/zip_read.cpp \ + json/json_reader.cpp \ json/json_writer.cpp \ i18n/i18n.cpp \ input/gesture_detector.cpp \ diff --git a/ext/native/ext/gason/LICENSE b/ext/native/ext/gason/LICENSE new file mode 100644 index 000000000000..0c4133a47ed3 --- /dev/null +++ b/ext/native/ext/gason/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2013-2015 Ivan Vashchaev + +Permission is hereby granted, free 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. diff --git a/ext/native/ext/gason/gason.cpp b/ext/native/ext/gason/gason.cpp new file mode 100644 index 000000000000..c3ce4efec7ea --- /dev/null +++ b/ext/native/ext/gason/gason.cpp @@ -0,0 +1,334 @@ +#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; +} diff --git a/ext/native/ext/gason/gason.h b/ext/native/ext/gason/gason.h new file mode 100644 index 000000000000..7c3eed96f63f --- /dev/null +++ b/ext/native/ext/gason/gason.h @@ -0,0 +1,135 @@ +#pragma once + +#include +#include +#include + +enum JsonTag { + JSON_NUMBER = 0, + JSON_STRING, + JSON_ARRAY, + JSON_OBJECT, + JSON_TRUE, + JSON_FALSE, + JSON_NULL = 0xF +}; + +struct JsonNode; + +#define JSON_VALUE_PAYLOAD_MASK 0x00007FFFFFFFFFFFULL +#define JSON_VALUE_NAN_MASK 0x7FF8000000000000ULL +#define JSON_VALUE_TAG_MASK 0xF +#define JSON_VALUE_TAG_SHIFT 47 + +union JsonValue { + uint64_t ival; + double fval; + + JsonValue(double x) + : fval(x) { + } + JsonValue(JsonTag tag = JSON_NULL, void *payload = nullptr) { + assert((uintptr_t)payload <= JSON_VALUE_PAYLOAD_MASK); + ival = JSON_VALUE_NAN_MASK | ((uint64_t)tag << JSON_VALUE_TAG_SHIFT) | (uintptr_t)payload; + } + bool isDouble() const { + return (int64_t)ival <= (int64_t)JSON_VALUE_NAN_MASK; + } + JsonTag getTag() const { + return isDouble() ? JSON_NUMBER : JsonTag((ival >> JSON_VALUE_TAG_SHIFT) & JSON_VALUE_TAG_MASK); + } + uint64_t getPayload() const { + assert(!isDouble()); + return ival & JSON_VALUE_PAYLOAD_MASK; + } + double toNumber() const { + assert(getTag() == JSON_NUMBER); + return fval; + } + char *toString() const { + assert(getTag() == JSON_STRING); + return (char *)getPayload(); + } + JsonNode *toNode() const { + assert(getTag() == JSON_ARRAY || getTag() == JSON_OBJECT); + return (JsonNode *)getPayload(); + } +}; + +struct JsonNode { + JsonValue value; + JsonNode *next; + char *key; +}; + +struct JsonIterator { + JsonNode *p; + + void operator++() { + p = p->next; + } + bool operator!=(const JsonIterator &x) const { + return p != x.p; + } + JsonNode *operator*() const { + return p; + } + JsonNode *operator->() const { + return p; + } +}; + +inline JsonIterator begin(JsonValue o) { + return JsonIterator{o.toNode()}; +} +inline JsonIterator end(JsonValue) { + return JsonIterator{nullptr}; +} + +#define JSON_ERRNO_MAP(XX) \ + XX(OK, "ok") \ + XX(BAD_NUMBER, "bad number") \ + XX(BAD_STRING, "bad string") \ + XX(BAD_IDENTIFIER, "bad identifier") \ + XX(STACK_OVERFLOW, "stack overflow") \ + XX(STACK_UNDERFLOW, "stack underflow") \ + XX(MISMATCH_BRACKET, "mismatch bracket") \ + XX(UNEXPECTED_CHARACTER, "unexpected character") \ + XX(UNQUOTED_KEY, "unquoted key") \ + XX(BREAKING_BAD, "breaking bad") \ + XX(ALLOCATION_FAILURE, "allocation failure") + +enum JsonErrno { +#define XX(no, str) JSON_##no, + JSON_ERRNO_MAP(XX) +#undef XX +}; + +const char *jsonStrError(int err); + +class JsonAllocator { + struct Zone { + Zone *next; + size_t used; + } *head; + +public: + JsonAllocator() : head(nullptr) {}; + JsonAllocator(const JsonAllocator &) = delete; + JsonAllocator &operator=(const JsonAllocator &) = delete; + JsonAllocator(JsonAllocator &&x) : head(x.head) { + x.head = nullptr; + } + JsonAllocator &operator=(JsonAllocator &&x) { + head = x.head; + x.head = nullptr; + return *this; + } + ~JsonAllocator() { + deallocate(); + } + void *allocate(size_t size); + void deallocate(); +}; + +int jsonParse(char *str, char **endptr, JsonValue *value, JsonAllocator &allocator); diff --git a/ext/native/ext/vjson/CMakeLists.txt b/ext/native/ext/vjson/CMakeLists.txt deleted file mode 100644 index b8d19dc34f15..000000000000 --- a/ext/native/ext/vjson/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -cmake_minimum_required(VERSION 2.6) - -#if(UNIX) -add_definitions(-fPIC) -add_definitions(-g) -add_definitions(-O2) -add_definitions(-Wall) -#endif(UNIX) - -add_library(vjson json.cpp block_allocator.cpp) diff --git a/ext/native/ext/vjson/block_allocator.cpp b/ext/native/ext/vjson/block_allocator.cpp deleted file mode 100644 index f3a573c6531b..000000000000 --- a/ext/native/ext/vjson/block_allocator.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include -#include -#include -#include "block_allocator.h" - -block_allocator::block_allocator(size_t blocksize) - : m_head(0), m_blocksize(blocksize) -{ -} - -block_allocator::~block_allocator() -{ - while (m_head) - { - block *temp = m_head->next; - ::free(m_head); - m_head = temp; - } -} - -void block_allocator::swap(block_allocator &rhs) -{ - std::swap(m_blocksize, rhs.m_blocksize); - std::swap(m_head, rhs.m_head); -} - -void *block_allocator::Malloc(size_t size) -{ - if ((m_head && m_head->used + size > m_head->size) || !m_head) - { - // calc needed size for allocation - size_t alloc_size = std::max(sizeof(block) + size, m_blocksize); - - // create new block - char *buffer = (char *)::malloc(alloc_size); - block *b = reinterpret_cast(buffer); - b->size = alloc_size; - b->used = sizeof(block); - b->buffer = buffer; - b->next = m_head; - m_head = b; - } - - void *ptr = m_head->buffer + m_head->used; - m_head->used += size; - return ptr; -} - -void block_allocator::Free() -{ - block_allocator(0).swap(*this); -} diff --git a/ext/native/ext/vjson/block_allocator.h b/ext/native/ext/vjson/block_allocator.h deleted file mode 100644 index 9fff7efc5074..000000000000 --- a/ext/native/ext/vjson/block_allocator.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef BLOCK_ALLOCATOR_H -#define BLOCK_ALLOCATOR_H - -class block_allocator -{ -public: - block_allocator(size_t blocksize); - ~block_allocator(); - - // exchange contents with rhs - void swap(block_allocator &rhs); - - // allocate memory - void *Malloc(size_t size); - - // free all allocated blocks - void Free(); - -private: - struct block - { - size_t size; - size_t used; - char *buffer; - block *next; - }; - - block *m_head; - size_t m_blocksize; - - block_allocator(const block_allocator &); - block_allocator &operator=(block_allocator &); -}; - -#endif diff --git a/ext/native/ext/vjson/json.cpp b/ext/native/ext/vjson/json.cpp deleted file mode 100644 index 7203cb577b30..000000000000 --- a/ext/native/ext/vjson/json.cpp +++ /dev/null @@ -1,643 +0,0 @@ -#include -#include -#include -#include "json.h" -#include "file/zip_read.h" -#include "file/vfs.h" - -// true if character represent a digit -#define IS_DIGIT(c) (c >= '0' && c <= '9') - -int json_value::numChildren() const { - int count = 0; - const json_value *c = first_child; - while (c) { - count++; - c = c->next_sibling; - } - return count; -} - -// only works right for first child. includes itself in count. -int json_value::numSiblings() const { - const json_value *s = next_sibling; - int count = 1; - while (s) { - count++; - s = s->next_sibling; - } - return count; -} - -const json_value *json_value::get(const char *child_name) const { - if (!child_name) { - FLOG("JSON: Cannot get from null child name"); - return 0; - } - const json_value *c = first_child; - while (c) { - if (!strcmp(c->name, child_name)) { - return c; - } - c = c->next_sibling; - } - return 0; -} - -const json_value *json_value::get(const char *child_name, json_type type) const { - const json_value *v = get(child_name); - if (v && type == v->type) - return v; - else - return 0; -} - -const char *json_value::getStringOrDie(const char *child_name) const { - const json_value *val = get(child_name, JSON_STRING); - if (val) - return val->string_value; - else - FLOG("String '%s' missing from node '%s'", child_name, this->name); - return 0; -} - -const char *json_value::getString(const char *child_name, const char *default_value) const { - const json_value *val = get(child_name, JSON_STRING); - if (!val) - return default_value; - return val->string_value; -} - -bool json_value::getStringVector(std::vector *vec) const { - vec->clear(); - if (type == JSON_ARRAY) { - json_value *val = first_child; - while (val) { - if (val->type == JSON_STRING) { - vec->push_back(val->string_value); - } - } - return true; - } else { - return false; - } -} - -float json_value::getFloat(const char *child_name) const { - return get(child_name, JSON_FLOAT)->float_value; -} - -float json_value::getFloat(const char *child_name, float default_value) const { - const json_value *val = get(child_name, JSON_FLOAT); - if (!val) { - // Let's try int. - val = get(child_name, JSON_INT); - if (!val) - return default_value; - return val->int_value; - } - return val->float_value; -} - -int json_value::getInt(const char *child_name) const { - return get(child_name, JSON_INT)->int_value; -} - -int json_value::getInt(const char *child_name, int default_value) const { - const json_value *val = get(child_name, JSON_INT); - if (!val) - return default_value; - return val->int_value; -} - -bool json_value::getBool(const char *child_name) const { - return get(child_name, JSON_BOOL)->int_value != 0 ? true : false; -} - -bool json_value::getBool(const char *child_name, bool default_value) const { - const json_value *val = get(child_name, JSON_BOOL); - if (!val) - return default_value; - return val->int_value != 0 ? true : false; -} - - -// convert string to integer -char *atoi(char *first, char *last, int *out) -{ - int sign = 1; - if (first != last) - { - if (*first == '-') - { - sign = -1; - ++first; - } - else if (*first == '+') - { - ++first; - } - } - - int result = 0; - for (; first != last && IS_DIGIT(*first); ++first) - { - result = 10 * result + (*first - '0'); - } - *out = result * sign; - - return first; -} - -// convert hexadecimal string to unsigned integer -char *hatoui(char *first, char *last, unsigned int *out) -{ - unsigned int result = 0; - for (; first != last; ++first) - { - int digit; - if (IS_DIGIT(*first)) - { - digit = *first - '0'; - } - else if (*first >= 'a' && *first <= 'f') - { - digit = *first - 'a' + 10; - } - else if (*first >= 'A' && *first <= 'F') - { - digit = *first - 'A' + 10; - } - else - { - break; - } - result = 16 * result + digit; - } - *out = result; - - return first; -} - -// convert string to floating point -char *atof(char *first, char *last, float *out) -{ - // sign - float sign = 1; - if (first != last) - { - if (*first == '-') - { - sign = -1; - ++first; - } - else if (*first == '+') - { - ++first; - } - } - - // integer part - float result = 0; - for (; first != last && IS_DIGIT(*first); ++first) - { - result = 10 * result + (*first - '0'); - } - - // fraction part - if (first != last && *first == '.') - { - ++first; - - float inv_base = 0.1f; - for (; first != last && IS_DIGIT(*first); ++first) - { - result += (*first - '0') * inv_base; - inv_base *= 0.1f; - } - } - - // result w\o exponent - result *= sign; - - // exponent - bool exponent_negative = false; - int exponent = 0; - if (first != last && (*first == 'e' || *first == 'E')) - { - ++first; - - if (*first == '-') - { - exponent_negative = true; - ++first; - } - else if (*first == '+') - { - ++first; - } - - for (; first != last && IS_DIGIT(*first); ++first) - { - exponent = 10 * exponent + (*first - '0'); - } - } - - if (exponent) - { - float power_of_ten = 10; - for (; exponent > 1; exponent--) - { - power_of_ten *= 10; - } - - if (exponent_negative) - { - result /= power_of_ten; - } - else - { - result *= power_of_ten; - } - } - - *out = result; - - return first; -} - -json_value *json_alloc(block_allocator *allocator) -{ - json_value *value = (json_value *)allocator->Malloc(sizeof(json_value)); - memset(value, 0, sizeof(json_value)); - return value; -} - -void json_append(json_value *lhs, json_value *rhs) -{ - rhs->parent = lhs; - if (lhs->last_child) - { - lhs->last_child = lhs->last_child->next_sibling = rhs; - } - else - { - lhs->first_child = lhs->last_child = rhs; - } -} - -#undef ERROR - -#define ERROR(it, desc)\ - *error_pos = it;\ - *error_desc = (char *)desc;\ - *error_line = 1 - escaped_newlines;\ - for (char *c = it; c != source; --c)\ - if (*c == '\n') ++*error_line;\ - return 0 - -#define CHECK_TOP() if (!top) {ERROR(it, "Unexpected character");} - -json_value *json_parse(char *source, char **error_pos, char **error_desc, int *error_line, block_allocator *allocator) -{ - json_value *root = 0; - json_value *top = 0; - - char *name = 0; - char *it = source; - - int escaped_newlines = 0; - - while (*it) - { - switch (*it) - { - case '{': - case '[': - { - // create new value - json_value *object = json_alloc(allocator); - - // name - object->name = name; - name = 0; - - // type - object->type = (*it == '{') ? JSON_OBJECT : JSON_ARRAY; - - // skip open character - ++it; - - // set top and root - if (top) - { - json_append(top, object); - } - else if (!root) - { - root = object; - } - else - { - ERROR(it, "Second root. Only one root allowed"); - } - top = object; - } - break; - - case '}': - case ']': - { - if (!top || top->type != ((*it == '}') ? JSON_OBJECT : JSON_ARRAY)) - { - ERROR(it, "Mismatch closing brace/bracket"); - } - - // skip close character - ++it; - - // set top - top = top->parent; - } - break; - - case ':': - if (!top || top->type != JSON_OBJECT) - { - ERROR(it, "Unexpected character (no top level)"); - } - ++it; - break; - - case ',': - CHECK_TOP(); - ++it; - break; - - case '"': - { - CHECK_TOP(); - - // skip '"' character - ++it; - - char *first = it; - char *last = it; - while (*it) - { - if ((unsigned char)*it < '\x20') - { - ERROR(first, "Control characters not allowed in strings"); - } - else if (*it == '\\') - { - switch (it[1]) - { - case '"': - *last = '"'; - break; - case '\\': - *last = '\\'; - break; - case '/': - *last = '/'; - break; - case 'b': - *last = '\b'; - break; - case 'f': - *last = '\f'; - break; - case 'n': - *last = '\n'; - ++escaped_newlines; - break; - case 'r': - *last = '\r'; - break; - case 't': - *last = '\t'; - break; - case 'u': - { - unsigned int codepoint; - if (hatoui(it + 2, it + 6, &codepoint) != it + 6) - { - ERROR(it, "Bad unicode codepoint"); - } - - if (codepoint <= 0x7F) - { - *last = (char)codepoint; - } - else if (codepoint <= 0x7FF) - { - *last++ = (char)(0xC0 | (codepoint >> 6)); - *last = (char)(0x80 | (codepoint & 0x3F)); - } - else if (codepoint <= 0xFFFF) - { - *last++ = (char)(0xE0 | (codepoint >> 12)); - *last++ = (char)(0x80 | ((codepoint >> 6) & 0x3F)); - *last = (char)(0x80 | (codepoint & 0x3F)); - } - } - it += 4; - break; - default: - ERROR(first, "Unrecognized escape sequence"); - } - - ++last; - it += 2; - } - else if (*it == '"') - { - *last = 0; - ++it; - break; - } - else - { - *last++ = *it++; - } - } - - if (!name && top->type == JSON_OBJECT) - { - // field name in object - name = first; - } - else - { - // new string value - json_value *object = json_alloc(allocator); - - object->name = name; - name = 0; - - object->type = JSON_STRING; - object->string_value = first; - - json_append(top, object); - } - } - break; - - case 'n': - case 't': - case 'f': - { - CHECK_TOP(); - - // new null/bool value - json_value *object = json_alloc(allocator); - - object->name = name; - name = 0; - - // null - if (it[0] == 'n' && it[1] == 'u' && it[2] == 'l' && it[3] == 'l') - { - object->type = JSON_NULL; - it += 4; - } - // true - else if (it[0] == 't' && it[1] == 'r' && it[2] == 'u' && it[3] == 'e') - { - object->type = JSON_BOOL; - object->int_value = 1; - it += 4; - } - // false - else if (it[0] == 'f' && it[1] == 'a' && it[2] == 'l' && it[3] == 's' && it[4] == 'e') - { - object->type = JSON_BOOL; - object->int_value = 0; - it += 5; - } - else - { - ERROR(it, "Unknown identifier"); - } - - json_append(top, object); - } - break; - - case '-': - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - { - CHECK_TOP(); - - // new number value - json_value *object = json_alloc(allocator); - - object->name = name; - name = 0; - - object->type = JSON_INT; - - char *first = it; - while (*it != '\x20' && *it != '\x9' && *it != '\xD' && *it != '\xA' && *it != ',' && *it != ']' && *it != '}') - { - if (*it == '.' || *it == 'e' || *it == 'E') - { - object->type = JSON_FLOAT; - } - ++it; - } - - if (object->type == JSON_INT && atoi(first, it, &object->int_value) != it) - { - ERROR(first, "Bad integer number"); - } - - if (object->type == JSON_FLOAT && atof(first, it, &object->float_value) != it) - { - ERROR(first, "Bad float number"); - } - - json_append(top, object); - } - break; - - default: - ERROR(it, "Unexpected character (unknown)"); - } - - // skip white space - while (*it == '\x20' || *it == '\x9' || *it == '\xD' || *it == '\xA') - { - ++it; - } - } - - if (top) - { - ERROR(it, "Not all objects/arrays have been properly closed"); - } - - return root; -} - -#define IDENT(n) for (int i = 0; i < n; ++i) printf(" ") - -void json_print(json_value *value, int ident) -{ - IDENT(ident); - if (value->name) printf("\"%s\" = ", value->name); - switch(value->type) - { - case JSON_NULL: - printf("null\n"); - break; - case JSON_OBJECT: - case JSON_ARRAY: - printf(value->type == JSON_OBJECT ? "{\n" : "[\n"); - for (json_value *it = value->first_child; it; it = it->next_sibling) - { - json_print(it, ident + 1); - } - IDENT(ident); - printf(value->type == JSON_OBJECT ? "}\n" : "]\n"); - break; - case JSON_STRING: - printf("\"%s\"\n", value->string_value); - break; - case JSON_INT: - printf("%d\n", value->int_value); - break; - case JSON_FLOAT: - printf("%f\n", value->float_value); - break; - case JSON_BOOL: - printf(value->int_value ? "true\n" : "false\n"); - break; - } -} - -JsonReader::JsonReader(const std::string &filename) : alloc_(1 << 12), root_(0) { - size_t buf_size; - buffer_ = (char *)VFSReadFile(filename.c_str(), &buf_size); - if (buffer_) { - parse(); - } else { - // Okay, try to read on the local file system - buffer_ = (char *)ReadLocalFile(filename.c_str(), &buf_size); - if (buffer_) { - parse(); - } else { - ELOG("Failed to read json %s", filename.c_str()); - } - } -} diff --git a/ext/native/ext/vjson/json.h b/ext/native/ext/vjson/json.h deleted file mode 100644 index 792ab1cba414..000000000000 --- a/ext/native/ext/vjson/json.h +++ /dev/null @@ -1,113 +0,0 @@ -#pragma once - -#include -#include -#include -#include "base/logging.h" -#include "block_allocator.h" -#include "file/vfs.h" - -enum json_type { - JSON_NULL, - JSON_OBJECT, - JSON_ARRAY, - JSON_STRING, - JSON_INT, - JSON_FLOAT, - JSON_BOOL, -}; - -struct json_value { - json_value() {} - - json_value *parent; - json_value *next_sibling; - json_value *first_child; - json_value *last_child; - - char *name; - union - { - char *string_value; - int int_value; - float float_value; - }; - - json_type type; - - int numChildren() const; - int numSiblings() const; // num siblings *after* this one only - const json_value *get(const char *child_name) const; - const json_value *get(const char *child_name, json_type type) const; - const json_value *getArray(const char *child_name) const { - return get(child_name, JSON_ARRAY); - } - const json_value *getDict(const char *child_name) const { - return get(child_name, JSON_OBJECT); - } - const char *getStringOrDie(const char *child_name) const; - const char *getString(const char *child_name, const char *default_value) const; - bool getStringVector(std::vector *vec) const; - float getFloat(const char *child_name) const; - float getFloat(const char *child_name, float default_value) const; - int getInt(const char *child_name) const; - int getInt(const char *child_name, int default_value) const; - bool getBool(const char *child_name) const; - bool getBool(const char *child_name, bool default_value) const; - - bool hasChild(const char *child_name, json_type child_type) const { - return get(child_name, child_type) != 0; - } - -private: - DISALLOW_COPY_AND_ASSIGN(json_value); -}; - -// low level interface -json_value *json_parse(char *source, char **error_pos, char **error_desc, int *error_line, block_allocator *allocator); -void json_print(json_value *value, int ident = 0); - -// Easy-wrapper -class JsonReader { -public: - JsonReader(const std::string &filename); - JsonReader(const char *data, size_t size) : alloc_(1 << 12) { - buffer_ = (char *)malloc(size + 1); - memcpy(buffer_, data, size); - buffer_[size] = 0; - parse(); - } - - ~JsonReader() { - if (buffer_) - free(buffer_); - } - - bool ok() const { return root_ != 0; } - - json_value *root() { return root_; } - const json_value *root() const { return root_; } - - void print() { - json_print(root_); - } - -private: - bool parse() { - char *error_pos; - char *error_desc; - int error_line; - root_ = json_parse((char *)buffer_, &error_pos, &error_desc, &error_line, &alloc_); - if (!root_) { - ELOG("Error at (%i): %s\n%s\n\n", error_line, error_desc, error_pos); - return false; - } - return true; - } - - char *buffer_; - block_allocator alloc_; - json_value *root_; - - DISALLOW_COPY_AND_ASSIGN(JsonReader); -}; diff --git a/ext/native/ext/vjson/main.cpp b/ext/native/ext/vjson/main.cpp deleted file mode 100644 index 81f72e5c286f..000000000000 --- a/ext/native/ext/vjson/main.cpp +++ /dev/null @@ -1,118 +0,0 @@ -#include -#include -#include "json.h" - -void populate_sources(const char *filter, std::vector > &sources) -{ - char filename[256]; - for (int i = 1; i < 64; ++i) - { - sprintf(filename, filter, i); - FILE *fp = fopen(filename, "rb"); - if (fp) - { - fseek(fp, 0, SEEK_END); - int size = ftell(fp); - fseek(fp, 0, SEEK_SET); - std::vector buffer(size + 1); - fread (&buffer[0], 1, size, fp); - fclose(fp); - sources.push_back(buffer); - } - else - { - break; - } - } - - printf("Loaded %d json files\n", (int)sources.size()); -} - -#define IDENT(n) for (int i = 0; i < n; ++i) printf(" ") - -void print(json_value *value, int ident = 0) -{ - IDENT(ident); - if (value->name) printf("\"%s\" = ", value->name); - switch(value->type) - { - case JSON_NULL: - printf("null\n"); - break; - case JSON_OBJECT: - case JSON_ARRAY: - printf(value->type == JSON_OBJECT ? "{\n" : "[\n"); - for (json_value *it = value->first_child; it; it = it->next_sibling) - { - print(it, ident + 1); - } - IDENT(ident); - printf(value->type == JSON_OBJECT ? "}\n" : "]\n"); - break; - case JSON_STRING: - printf("\"%s\"\n", value->string_value); - break; - case JSON_INT: - printf("%d\n", value->int_value); - break; - case JSON_FLOAT: - printf("%f\n", value->float_value); - break; - case JSON_BOOL: - printf(value->int_value ? "true\n" : "false\n"); - break; - } -} - -bool parse(char *source) -{ - char *errorPos = 0; - char *errorDesc = 0; - int errorLine = 0; - block_allocator allocator(1 << 10); - - json_value *root = json_parse(source, &errorPos, &errorDesc, &errorLine, &allocator); - if (root) - { - print(root); - return true; - } - - printf("Error at line %d: %s\n%s\n\n", errorLine, errorDesc, errorPos); - return false; -} - -int main(int argc, char **argv) -{ - // Fail - printf("===FAIL===\n\n"); - std::vector > sources; - populate_sources("test/fail%d.json", sources); - int passed = 0; - for (size_t i = 0; i < sources.size(); ++i) - { - printf("Parsing %d\n", (int)i + 1); - if (parse(&sources[i][0])) - { - ++passed; - } - } - printf("Passed %d from %d tests\n", passed, (int)sources.size()); - - // Pass - sources.clear(); - printf("\n===PASS===\n\n"); - populate_sources("test/pass%d.json", sources); - passed = 0; - for (size_t i = 0; i < sources.size(); ++i) - { - printf("Parsing %d\n", (int)i + 1); - if (parse(&sources[i][0])) - { - ++passed; - } - } - printf("Passed %d from %d tests\n", passed, (int)sources.size()); - - return 0; -} diff --git a/ext/native/json/json_reader.cpp b/ext/native/json/json_reader.cpp new file mode 100644 index 000000000000..83fbe94fa463 --- /dev/null +++ b/ext/native/json/json_reader.cpp @@ -0,0 +1,119 @@ +#include "file/zip_read.h" +#include "file/vfs.h" +#include "json/json_reader.h" + +JsonReader::JsonReader(const std::string &filename) { + size_t buf_size; + buffer_ = (char *)VFSReadFile(filename.c_str(), &buf_size); + if (buffer_) { + parse(); + } else { + // Okay, try to read on the local file system + buffer_ = (char *)ReadLocalFile(filename.c_str(), &buf_size); + if (buffer_) { + parse(); + } else { + ELOG("Failed to read json %s", filename.c_str()); + } + } +} + +int JsonGet::numChildren() const { + int count = 0; + if (value_.getTag() == JSON_OBJECT || value_.getTag() == JSON_ARRAY) { + for (auto it : value_) { + count++; + } + } + return count; +} + +const JsonNode *JsonGet::get(const char *child_name) const { + if (!child_name) { + FLOG("JSON: Cannot get from null child name"); + return nullptr; + } + if (value_.getTag() != JSON_OBJECT) { + return nullptr; + } + for (auto it : value_) { + if (!strcmp(it->key, child_name)) { + return it; + } + } + return nullptr; +} + +const JsonNode *JsonGet::get(const char *child_name, JsonTag type) const { + const JsonNode *v = get(child_name); + if (v && type == v->value.getTag()) + return v; + return nullptr; +} + +const char *JsonGet::getStringOrDie(const char *child_name) const { + const JsonNode *val = get(child_name, JSON_STRING); + if (val) + return val->value.toString(); + FLOG("String '%s' missing from node", child_name); + return nullptr; +} + +const char *JsonGet::getString(const char *child_name, const char *default_value) const { + const JsonNode *val = get(child_name, JSON_STRING); + if (!val) + return default_value; + return val->value.toString(); +} + +bool JsonGet::getStringVector(std::vector *vec) const { + vec->clear(); + if (value_.getTag() == JSON_ARRAY) { + for (auto it : value_) { + if (it->value.getTag() == JSON_STRING) { + vec->push_back(it->value.toString()); + } + } + return true; + } else { + return false; + } +} + +double JsonGet::getFloat(const char *child_name) const { + return get(child_name, JSON_NUMBER)->value.toNumber(); +} + +double JsonGet::getFloat(const char *child_name, double default_value) const { + const JsonNode *val = get(child_name, JSON_NUMBER); + if (!val) + return default_value; + return val->value.toNumber(); +} + +int JsonGet::getInt(const char *child_name) const { + return (int)get(child_name, JSON_NUMBER)->value.toNumber(); +} + +int JsonGet::getInt(const char *child_name, int default_value) const { + const JsonNode *val = get(child_name, JSON_NUMBER); + if (!val) + return default_value; + return (int)val->value.toNumber(); +} + +bool JsonGet::getBool(const char *child_name) const { + return get(child_name)->value.getTag() == JSON_TRUE; +} + +bool JsonGet::getBool(const char *child_name, bool default_value) const { + const JsonNode *val = get(child_name); + if (val) { + JsonTag tag = val->value.getTag(); + if (tag == JSON_TRUE) + return true; + if (tag == JSON_FALSE) + return false; + } + return default_value; +} diff --git a/ext/native/json/json_reader.h b/ext/native/json/json_reader.h new file mode 100644 index 000000000000..b9e8de5e5398 --- /dev/null +++ b/ext/native/json/json_reader.h @@ -0,0 +1,85 @@ +#include +#include +#include "base/basictypes.h" +#include "base/logging.h" +#include "ext/gason/gason.h" + +struct JsonGet { + JsonGet(const JsonValue &value) : value_(value) { + } + + int numChildren() const; + const JsonNode *get(const char *child_name) const; + const JsonNode *get(const char *child_name, JsonTag type) const; + const JsonNode *getArray(const char *child_name) const { + return get(child_name, JSON_ARRAY); + } + const JsonGet getDict(const char *child_name) const { + return JsonGet(get(child_name, JSON_OBJECT)->value); + } + const char *getStringOrDie(const char *child_name) const; + const char *getString(const char *child_name, const char *default_value) const; + bool getStringVector(std::vector *vec) const; + double getFloat(const char *child_name) const; + double getFloat(const char *child_name, double default_value) const; + int getInt(const char *child_name) const; + int getInt(const char *child_name, int default_value) const; + bool getBool(const char *child_name) const; + bool getBool(const char *child_name, bool default_value) const; + + bool hasChild(const char *child_name, JsonTag child_type) const { + return get(child_name, child_type) != nullptr; + } + + operator bool() const { + return value_.getTag() != JSON_NULL; + } + + JsonValue value_; +}; + +// Easy-wrapper +class JsonReader { +public: + JsonReader(const std::string &filename); + JsonReader(const void *data, size_t size) { + buffer_ = (char *)malloc(size + 1); + memcpy(buffer_, data, size); + buffer_[size] = 0; + parse(); + } + JsonReader(const JsonNode *node) { + ok_ = true; + } + + ~JsonReader() { + if (buffer_) + free(buffer_); + } + + bool ok() const { return ok_; } + + JsonGet root() { return root_.getTag() == JSON_OBJECT ? JsonGet(root_) : JsonGet(JSON_NULL); } + const JsonValue rootArray() const { return root_.getTag() == JSON_ARRAY ? root_ : JSON_NULL; } + + const JsonValue rootValue() const { return root_; } + +private: + bool parse() { + char *error_pos; + int status = jsonParse(buffer_, &error_pos, &root_, alloc_); + if (status != JSON_OK) { + ELOG("Error at (%i): %s\n%s\n\n", (int)(error_pos - buffer_), jsonStrError(status), error_pos); + return false; + } + ok_ = true; + return true; + } + + char *buffer_ = nullptr; + JsonAllocator alloc_; + JsonValue root_; + bool ok_ = false; + + DISALLOW_COPY_AND_ASSIGN(JsonReader); +}; diff --git a/ext/native/json/json_writer.cpp b/ext/native/json/json_writer.cpp index 6d4c6905e187..3cc625e13d24 100644 --- a/ext/native/json/json_writer.cpp +++ b/ext/native/json/json_writer.cpp @@ -1,7 +1,7 @@ #include #include #include -#include "ext/vjson/json.h" +#include "json/json_reader.h" #include "json/json_writer.h" JsonWriter::JsonWriter(int flags) { @@ -234,33 +234,33 @@ void JsonWriter::writeEscapedString(const char *str) { } } -static void json_stringify_object(JsonWriter &writer, const json_value *value); -static void json_stringify_array(JsonWriter &writer, const json_value *value); +static void json_stringify_object(JsonWriter &writer, const JsonNode *node); +static void json_stringify_array(JsonWriter &writer, const JsonNode *node); -std::string json_stringify(const json_value *value) { +std::string json_stringify(const JsonNode *node) { JsonWriter writer; // Handle direct values too, not just objects. - switch (value->type) { + switch (node->value.getTag()) { case JSON_NULL: case JSON_STRING: - case JSON_INT: - case JSON_FLOAT: - case JSON_BOOL: + case JSON_NUMBER: + case JSON_TRUE: + case JSON_FALSE: writer.beginRaw(); // It's the same as a one entry array without brackets, so reuse. - json_stringify_array(writer, value); + json_stringify_array(writer, node); break; case JSON_OBJECT: writer.begin(); - for (const json_value *it = value->first_child; it; it = it->next_sibling) { + for (const JsonNode *it : node->value) { json_stringify_object(writer, it); } break; case JSON_ARRAY: writer.beginArray(); - for (const json_value *it = value->first_child; it; it = it->next_sibling) { + for (const JsonNode *it : node->value) { json_stringify_array(writer, it); } break; @@ -270,34 +270,34 @@ std::string json_stringify(const json_value *value) { return writer.str(); } -static void json_stringify_object(JsonWriter &writer, const json_value *value) { - switch (value->type) { +static void json_stringify_object(JsonWriter &writer, const JsonNode *node) { + switch (node->value.getTag()) { case JSON_NULL: - writer.writeRaw(value->name, "null"); + writer.writeRaw(node->key, "null"); break; case JSON_STRING: - writer.writeString(value->name, value->string_value); + writer.writeString(node->key, node->value.toString()); break; - case JSON_INT: - writer.writeInt(value->name, value->int_value); + case JSON_NUMBER: + writer.writeFloat(node->key, node->value.toNumber()); break; - case JSON_FLOAT: - writer.writeFloat(value->name, value->float_value); + case JSON_TRUE: + writer.writeBool(node->key, true); break; - case JSON_BOOL: - writer.writeBool(value->name, value->int_value != 0); + case JSON_FALSE: + writer.writeBool(node->key, false); break; case JSON_OBJECT: - writer.pushDict(value->name); - for (const json_value *it = value->first_child; it; it = it->next_sibling) { + writer.pushDict(node->key); + for (const JsonNode *it : node->value) { json_stringify_object(writer, it); } writer.pop(); break; case JSON_ARRAY: - writer.pushArray(value->name); - for (const json_value *it = value->first_child; it; it = it->next_sibling) { + writer.pushArray(node->key); + for (const JsonNode *it : node->value) { json_stringify_array(writer, it); } writer.pop(); @@ -305,34 +305,34 @@ static void json_stringify_object(JsonWriter &writer, const json_value *value) { } } -static void json_stringify_array(JsonWriter &writer, const json_value *value) { - switch (value->type) { +static void json_stringify_array(JsonWriter &writer, const JsonNode *node) { + switch (node->value.getTag()) { case JSON_NULL: writer.writeRaw("null"); break; case JSON_STRING: - writer.writeString(value->string_value); + writer.writeString(node->value.toString()); break; - case JSON_INT: - writer.writeInt(value->int_value); + case JSON_NUMBER: + writer.writeFloat(node->value.toNumber()); break; - case JSON_FLOAT: - writer.writeFloat(value->float_value); + case JSON_TRUE: + writer.writeBool(true); break; - case JSON_BOOL: - writer.writeBool(value->int_value != 0); + case JSON_FALSE: + writer.writeBool(false); break; case JSON_OBJECT: writer.pushDict(); - for (const json_value *it = value->first_child; it; it = it->next_sibling) { + for (const JsonNode *it : node->value) { json_stringify_object(writer, it); } writer.pop(); break; case JSON_ARRAY: writer.pushArray(); - for (const json_value *it = value->first_child; it; it = it->next_sibling) { + for (const JsonNode *it : node->value) { json_stringify_array(writer, it); } writer.pop(); diff --git a/ext/native/json/json_writer.h b/ext/native/json/json_writer.h index b2f4f30742bb..06afb66bc7a0 100644 --- a/ext/native/json/json_writer.h +++ b/ext/native/json/json_writer.h @@ -87,5 +87,5 @@ class JsonWriter { bool pretty_; }; -struct json_value; -std::string json_stringify(const json_value *json); +struct JsonNode; +std::string json_stringify(const JsonNode *json); diff --git a/ext/native/native.vcxproj b/ext/native/native.vcxproj index 8d43e4329d75..556c328d4fd9 100644 --- a/ext/native/native.vcxproj +++ b/ext/native/native.vcxproj @@ -206,6 +206,7 @@ + @@ -220,8 +221,6 @@ - - @@ -238,6 +237,7 @@ + @@ -340,6 +340,7 @@ Default Default + @@ -683,8 +684,6 @@ - - @@ -699,6 +698,7 @@ + diff --git a/ext/native/native.vcxproj.filters b/ext/native/native.vcxproj.filters index aecf22367478..999519c3bd0b 100644 --- a/ext/native/native.vcxproj.filters +++ b/ext/native/native.vcxproj.filters @@ -242,12 +242,6 @@ ext\jpge - - ext\vjson - - - ext\vjson - gfx @@ -341,6 +335,12 @@ thin3d + + ext\gason + + + json + @@ -712,12 +712,6 @@ ext\jpge - - ext\vjson - - - ext\vjson - gfx @@ -814,6 +808,12 @@ thin3d + + ext\gason + + + json + @@ -876,14 +876,14 @@ {3721adbc-1a4a-4017-bc1d-893c7e86bc22} - - {c6910d82-73d3-4d97-ac9c-bcc03265b1bc} - {5ce64c0e-98e4-4411-86cc-aacaf2f60b21} {06c6305a-a646-485b-85b9-645a24dd6553} + + {5b740c6f-8dbc-4529-9114-6564b37b3548} + \ No newline at end of file diff --git a/libretro/Makefile.common b/libretro/Makefile.common index c732d9a1e45c..d905f0a57afb 100644 --- a/libretro/Makefile.common +++ b/libretro/Makefile.common @@ -125,8 +125,7 @@ SOURCES_CXX += \ SOURCES_CXX += $(EXTDIR)/xbrz/xbrz.cpp SOURCES_CXX += \ - $(NATIVEDIR)/ext/vjson/json.cpp \ - $(NATIVEDIR)/ext/vjson/block_allocator.cpp + $(NATIVEDIR)/ext/gason/gason.cpp SOURCES_CXX += $(NATIVEDIR)/ext/cityhash/city.cpp @@ -203,10 +202,11 @@ SOURCES_CXX += \ $(EXTDIR)/native/base/buffer.cpp \ $(EXTDIR)/native/base/colorutil.cpp \ $(EXTDIR)/native/base/display.cpp \ - $(EXTDIR)/native/base/logging.cpp \ + $(EXTDIR)/native/base/logging.cpp \ $(EXTDIR)/native/base/stringutil.cpp \ $(EXTDIR)/native/base/timeutil.cpp \ $(EXTDIR)/native/data/compression.cpp \ + $(EXTDIR)/native/json/json_reader.cpp \ $(EXTDIR)/glslang/OGLCompilersDLL/InitializeDll.cpp \ $(EXTDIR)/glslang/glslang/GenericCodeGen/CodeGen.cpp \ $(EXTDIR)/glslang/glslang/GenericCodeGen/Link.cpp \