BizHawk/ppsspp/native/ext/vjson/json.cpp

639 lines
12 KiB
C++

#include <memory.h>
#include <stdio.h>
#include <string.h>
#include "json.h"
#include "file/easy_file.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 {
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::getString(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;
}
}
#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");
}
++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");
}
// 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());
}
}
}