mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-23 05:19:56 +00:00
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.
This commit is contained in:
parent
b56249eec1
commit
082ddf5120
@ -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()
|
||||
|
@ -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"
|
||||
@ -1181,13 +1181,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);
|
||||
|
@ -21,9 +21,9 @@
|
||||
#include <condition_variable>
|
||||
|
||||
#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<std::string> 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.
|
||||
|
39
UI/Store.cpp
39
UI/Store.cpp
@ -18,7 +18,7 @@
|
||||
#include <functional>
|
||||
|
||||
#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);
|
||||
|
@ -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 @@ private:
|
||||
std::vector<StoreEntry> 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<http::Download> listing_;
|
||||
std::shared_ptr<http::Download> image_;
|
||||
|
@ -328,8 +328,6 @@
|
||||
<ClInclude Include="..\..\ext\native\ext\libzip\config.h" />
|
||||
<ClInclude Include="..\..\ext\native\ext\libzip\zip.h" />
|
||||
<ClInclude Include="..\..\ext\native\ext\libzip\zipint.h" />
|
||||
<ClInclude Include="..\..\ext\native\ext\vjson\block_allocator.h" />
|
||||
<ClInclude Include="..\..\ext\native\ext\vjson\json.h" />
|
||||
<ClInclude Include="..\..\ext\native\file\chunk_file.h" />
|
||||
<ClInclude Include="..\..\ext\native\file\fd_util.h" />
|
||||
<ClInclude Include="..\..\ext\native\file\file_util.h" />
|
||||
@ -338,6 +336,8 @@
|
||||
<ClInclude Include="..\..\ext\native\file\path.h" />
|
||||
<ClInclude Include="..\..\ext\native\file\vfs.h" />
|
||||
<ClInclude Include="..\..\ext\native\file\zip_read.h" />
|
||||
<ClInclude Include="..\..\ext\native\json\json_reader.h" />
|
||||
<ClInclude Include="..\..\ext\native\json\json_writer.h" />
|
||||
<ClInclude Include="..\..\ext\native\gfx\texture_atlas.h" />
|
||||
<ClInclude Include="..\..\ext\native\gfx_es2\draw_buffer.h" />
|
||||
<ClInclude Include="..\..\ext\native\gfx_es2\draw_text.h" />
|
||||
@ -1160,8 +1160,6 @@
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">NotUsing</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='UWP Gold|x64'">NotUsing</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\ext\native\ext\vjson\block_allocator.cpp" />
|
||||
<ClCompile Include="..\..\ext\native\ext\vjson\json.cpp" />
|
||||
<ClCompile Include="..\..\ext\native\file\chunk_file.cpp" />
|
||||
<ClCompile Include="..\..\ext\native\file\fd_util.cpp" />
|
||||
<ClCompile Include="..\..\ext\native\file\file_util.cpp" />
|
||||
@ -1169,6 +1167,8 @@
|
||||
<ClCompile Include="..\..\ext\native\file\ini_file.cpp" />
|
||||
<ClCompile Include="..\..\ext\native\file\path.cpp" />
|
||||
<ClCompile Include="..\..\ext\native\file\zip_read.cpp" />
|
||||
<ClCompile Include="..\..\ext\native\json\json_reader.cpp" />
|
||||
<ClCompile Include="..\..\ext\native\json\json_writer.cpp" />
|
||||
<ClCompile Include="..\..\ext\native\gfx\texture_atlas.cpp" />
|
||||
<ClCompile Include="..\..\ext\native\gfx_es2\draw_buffer.cpp" />
|
||||
<ClCompile Include="..\..\ext\native\gfx_es2\draw_text.cpp" />
|
||||
|
@ -37,9 +37,6 @@
|
||||
<Filter Include="ext">
|
||||
<UniqueIdentifier>{2be24387-0b6a-4253-97fa-b8b6f75a8a4c}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="ext\vjson">
|
||||
<UniqueIdentifier>{7fdd3320-a8e0-42ed-b08b-2d86ba2ff414}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="ext\libzip">
|
||||
<UniqueIdentifier>{1a486fc4-bac0-4b33-9139-262272690c74}</UniqueIdentifier>
|
||||
</Filter>
|
||||
@ -214,12 +211,6 @@
|
||||
<ClCompile Include="..\..\ext\native\i18n\i18n.cpp">
|
||||
<Filter>i18n</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\ext\native\ext\vjson\block_allocator.cpp">
|
||||
<Filter>ext\vjson</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\ext\native\ext\vjson\json.cpp">
|
||||
<Filter>ext\vjson</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\ext\native\ext\libzip\mkstemp.c">
|
||||
<Filter>ext\libzip</Filter>
|
||||
</ClCompile>
|
||||
@ -656,12 +647,6 @@
|
||||
<ClInclude Include="..\..\ext\native\i18n\i18n.h">
|
||||
<Filter>i18n</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\ext\native\ext\vjson\block_allocator.h">
|
||||
<Filter>ext\vjson</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\ext\native\ext\vjson\json.h">
|
||||
<Filter>ext\vjson</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\ext\native\ext\libzip\config.h">
|
||||
<Filter>ext\libzip</Filter>
|
||||
</ClInclude>
|
||||
|
@ -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 \
|
||||
|
20
ext/native/ext/gason/LICENSE
Normal file
20
ext/native/ext/gason/LICENSE
Normal file
@ -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.
|
334
ext/native/ext/gason/gason.cpp
Normal file
334
ext/native/ext/gason/gason.cpp
Normal file
@ -0,0 +1,334 @@
|
||||
#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;
|
||||
}
|
135
ext/native/ext/gason/gason.h
Normal file
135
ext/native/ext/gason/gason.h
Normal file
@ -0,0 +1,135 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <assert.h>
|
||||
|
||||
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);
|
@ -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)
|
@ -1,52 +0,0 @@
|
||||
#include <memory.h>
|
||||
#include <algorithm>
|
||||
#include <stdlib.h>
|
||||
#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<block *>(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);
|
||||
}
|
@ -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
|
@ -1,643 +0,0 @@
|
||||
#include <memory.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#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<std::string> *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());
|
||||
}
|
||||
}
|
||||
}
|
@ -1,113 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#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<std::string> *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);
|
||||
};
|
@ -1,118 +0,0 @@
|
||||
#include <vector>
|
||||
#include <stdio.h>
|
||||
#include "json.h"
|
||||
|
||||
void populate_sources(const char *filter, std::vector<std::vector<char> > &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<char> 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<std::vector<char> > 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;
|
||||
}
|
119
ext/native/json/json_reader.cpp
Normal file
119
ext/native/json/json_reader.cpp
Normal file
@ -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<std::string> *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;
|
||||
}
|
85
ext/native/json/json_reader.h
Normal file
85
ext/native/json/json_reader.h
Normal file
@ -0,0 +1,85 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#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<std::string> *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);
|
||||
};
|
@ -1,7 +1,7 @@
|
||||
#include <iomanip>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#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();
|
||||
|
@ -87,5 +87,5 @@ private:
|
||||
bool pretty_;
|
||||
};
|
||||
|
||||
struct json_value;
|
||||
std::string json_stringify(const json_value *json);
|
||||
struct JsonNode;
|
||||
std::string json_stringify(const JsonNode *json);
|
||||
|
@ -206,6 +206,7 @@
|
||||
<ClInclude Include="data\listable.h" />
|
||||
<ClInclude Include="ext\cityhash\city.h" />
|
||||
<ClInclude Include="ext\cityhash\citycrc.h" />
|
||||
<ClInclude Include="ext\gason\gason.h" />
|
||||
<ClInclude Include="ext\jpge\jpgd.h" />
|
||||
<ClInclude Include="ext\jpge\jpge.h" />
|
||||
<ClInclude Include="ext\libpng17\png.h" />
|
||||
@ -220,8 +221,6 @@
|
||||
<ClInclude Include="ext\libzip\zipint.h" />
|
||||
<ClInclude Include="ext\rg_etc1\rg_etc1.h" />
|
||||
<ClInclude Include="ext\sha1\sha1.h" />
|
||||
<ClInclude Include="ext\vjson\block_allocator.h" />
|
||||
<ClInclude Include="ext\vjson\json.h" />
|
||||
<ClInclude Include="file\chunk_file.h" />
|
||||
<ClInclude Include="file\fd_util.h" />
|
||||
<ClInclude Include="file\file_util.h" />
|
||||
@ -238,6 +237,7 @@
|
||||
<ClInclude Include="gfx_es2\draw_text_android.h" />
|
||||
<ClInclude Include="gfx_es2\draw_text_qt.h" />
|
||||
<ClInclude Include="gfx_es2\draw_text_win.h" />
|
||||
<ClInclude Include="json\json_reader.h" />
|
||||
<ClInclude Include="thin3d\d3d11_loader.h" />
|
||||
<ClInclude Include="thin3d\DataFormat.h" />
|
||||
<ClInclude Include="thin3d\DataFormatGL.h" />
|
||||
@ -340,6 +340,7 @@
|
||||
<BasicRuntimeChecks Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Default</BasicRuntimeChecks>
|
||||
<BasicRuntimeChecks Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Default</BasicRuntimeChecks>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ext\gason\gason.cpp" />
|
||||
<ClCompile Include="ext\jpge\jpgd.cpp" />
|
||||
<ClCompile Include="ext\jpge\jpge.cpp" />
|
||||
<ClCompile Include="ext\libpng17\png.c" />
|
||||
@ -683,8 +684,6 @@
|
||||
</ClCompile>
|
||||
<ClCompile Include="ext\rg_etc1\rg_etc1.cpp" />
|
||||
<ClCompile Include="ext\sha1\sha1.cpp" />
|
||||
<ClCompile Include="ext\vjson\block_allocator.cpp" />
|
||||
<ClCompile Include="ext\vjson\json.cpp" />
|
||||
<ClCompile Include="file\chunk_file.cpp" />
|
||||
<ClCompile Include="file\fd_util.cpp" />
|
||||
<ClCompile Include="file\file_util.cpp" />
|
||||
@ -699,6 +698,7 @@
|
||||
<ClCompile Include="gfx_es2\draw_text_android.cpp" />
|
||||
<ClCompile Include="gfx_es2\draw_text_qt.cpp" />
|
||||
<ClCompile Include="gfx_es2\draw_text_win.cpp" />
|
||||
<ClCompile Include="json\json_reader.cpp" />
|
||||
<ClCompile Include="math\dataconv.cpp" />
|
||||
<ClCompile Include="thin3d\d3d11_loader.cpp" />
|
||||
<ClCompile Include="thin3d\DataFormatGL.cpp" />
|
||||
|
@ -242,12 +242,6 @@
|
||||
<ClInclude Include="ext\jpge\jpge.h">
|
||||
<Filter>ext\jpge</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ext\vjson\block_allocator.h">
|
||||
<Filter>ext\vjson</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ext\vjson\json.h">
|
||||
<Filter>ext\vjson</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="gfx_es2\gl3stub.h">
|
||||
<Filter>gfx</Filter>
|
||||
</ClInclude>
|
||||
@ -341,6 +335,12 @@
|
||||
<ClInclude Include="thin3d\thin3d_create.h">
|
||||
<Filter>thin3d</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ext\gason\gason.h">
|
||||
<Filter>ext\gason</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="json\json_reader.h">
|
||||
<Filter>json</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="gfx\gl_debug_log.cpp">
|
||||
@ -712,12 +712,6 @@
|
||||
<ClCompile Include="ext\jpge\jpge.cpp">
|
||||
<Filter>ext\jpge</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ext\vjson\block_allocator.cpp">
|
||||
<Filter>ext\vjson</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ext\vjson\json.cpp">
|
||||
<Filter>ext\vjson</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="gfx_es2\gl3stub.c">
|
||||
<Filter>gfx</Filter>
|
||||
</ClCompile>
|
||||
@ -814,6 +808,12 @@
|
||||
<ClCompile Include="thin3d\DataFormatGL.cpp">
|
||||
<Filter>thin3d</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ext\gason\gason.cpp">
|
||||
<Filter>ext\gason</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="json\json_reader.cpp">
|
||||
<Filter>json</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="gfx">
|
||||
@ -876,14 +876,14 @@
|
||||
<Filter Include="ext\jpge">
|
||||
<UniqueIdentifier>{3721adbc-1a4a-4017-bc1d-893c7e86bc22}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="ext\vjson">
|
||||
<UniqueIdentifier>{c6910d82-73d3-4d97-ac9c-bcc03265b1bc}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="math\fast">
|
||||
<UniqueIdentifier>{5ce64c0e-98e4-4411-86cc-aacaf2f60b21}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="thin3d">
|
||||
<UniqueIdentifier>{06c6305a-a646-485b-85b9-645a24dd6553}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="ext\gason">
|
||||
<UniqueIdentifier>{5b740c6f-8dbc-4529-9114-6564b37b3548}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -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 \
|
||||
|
Loading…
Reference in New Issue
Block a user