#ifndef wasm_support_json_h
#define wasm_support_json_h
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <functional>
#include <iomanip>
#include <iostream>
#include <limits>
#include <memory>
#include <ostream>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "emscripten-optimizer/istring.h"
#include "support/safe_integer.h"
namespace json {
typedef cashew::IString IString;
struct Value {
struct Ref : public std::shared_ptr<Value> {
Ref() = default;
Ref(Value* value) : std::shared_ptr<Value>(value) {}
Ref& operator[](size_t x) { return (*this->get())[x]; }
Ref& operator[](IString x) { return (*this->get())[x]; }
};
enum Type {
String = 0,
Number = 1,
Array = 2,
Null = 3,
Bool = 4,
Object = 5,
};
Type type = Null;
typedef std::vector<Ref> ArrayStorage;
typedef std::unordered_map<IString, Ref> ObjectStorage;
#ifdef _MSC_VER
IString str;
#endif
union { #ifndef _MSC_VER
IString str;
#endif
double num = 0;
ArrayStorage* arr; bool boo;
ObjectStorage* obj; Ref ref;
};
Value() {}
explicit Value(const char* s) : type(Null) { setString(s); }
explicit Value(double n) : type(Null) { setNumber(n); }
explicit Value(ArrayStorage& a) : type(Null) {
setArray();
*arr = a;
}
~Value() { free(); }
void free() {
if (type == Array) {
delete arr;
arr = nullptr;
} else if (type == Object) {
delete obj;
obj = nullptr;
}
type = Null;
num = 0;
}
Value& setString(const char* s) {
free();
type = String;
str.set(s);
return *this;
}
Value& setString(const IString& s) {
free();
type = String;
str.set(s);
return *this;
}
Value& setNumber(double n) {
free();
type = Number;
num = n;
return *this;
}
Value& setArray(ArrayStorage& a) {
free();
type = Array;
arr = new ArrayStorage;
*arr = a;
return *this;
}
Value& setArray(size_t size_hint = 0) {
free();
type = Array;
arr = new ArrayStorage;
arr->reserve(size_hint);
return *this;
}
Value& setNull() {
free();
type = Null;
return *this;
}
Value&
setBool(bool b) { free();
type = Bool;
boo = b;
return *this;
}
Value& setObject() {
free();
type = Object;
obj = new ObjectStorage();
return *this;
}
bool isString() { return type == String; }
bool isNumber() { return type == Number; }
bool isArray() { return type == Array; }
bool isNull() { return type == Null; }
bool isBool() { return type == Bool; }
bool isObject() { return type == Object; }
bool isBool(bool b) {
return type == Bool && b == boo;
}
const char* getCString() {
assert(isString());
return str.str;
}
IString& getIString() {
assert(isString());
return str;
}
double& getNumber() {
assert(isNumber());
return num;
}
ArrayStorage& getArray() {
assert(isArray());
return *arr;
}
bool& getBool() {
assert(isBool());
return boo;
}
int32_t getInteger() { assert(wasm::isInteger(getNumber()));
int32_t ret = getNumber();
assert(double(ret) == getNumber()); return ret;
}
Value& operator=(const Value& other) {
free();
switch (other.type) {
case String:
setString(other.str);
break;
case Number:
setNumber(other.num);
break;
case Array:
setArray(*other.arr);
break;
case Null:
setNull();
break;
case Bool:
setBool(other.boo);
break;
default:
abort(); }
return *this;
}
bool operator==(const Value& other) {
if (type != other.type) {
return false;
}
switch (other.type) {
case String:
return str == other.str;
case Number:
return num == other.num;
case Array:
return this == &other; case Null:
break;
case Bool:
return boo == other.boo;
case Object:
return this == &other; default:
abort();
}
return true;
}
char* parse(char* curr) {
#define is_json_space(x) \
(x == 32 || x == 9 || x == 10 || \
x == 13)
#define skip() \
{ \
while (*curr && is_json_space(*curr)) \
curr++; \
}
skip();
if (*curr == '"') {
curr++;
char* close = strchr(curr, '"');
assert(close);
*close = 0; setString(curr);
curr = close + 1;
} else if (*curr == '[') {
curr++;
skip();
setArray();
while (*curr != ']') {
Ref temp = Ref(new Value());
arr->push_back(temp);
curr = temp->parse(curr);
skip();
if (*curr == ']') {
break;
}
assert(*curr == ',');
curr++;
skip();
}
curr++;
} else if (*curr == 'n') {
assert(strncmp(curr, "null", 4) == 0);
setNull();
curr += 4;
} else if (*curr == 't') {
assert(strncmp(curr, "true", 4) == 0);
setBool(true);
curr += 4;
} else if (*curr == 'f') {
assert(strncmp(curr, "false", 5) == 0);
setBool(false);
curr += 5;
} else if (*curr == '{') {
curr++;
skip();
setObject();
while (*curr != '}') {
assert(*curr == '"');
curr++;
char* close = strchr(curr, '"');
assert(close);
*close = 0; IString key(curr);
curr = close + 1;
skip();
assert(*curr == ':');
curr++;
skip();
Ref value = Ref(new Value());
curr = value->parse(curr);
(*obj)[key] = value;
skip();
if (*curr == '}') {
break;
}
assert(*curr == ',');
curr++;
skip();
}
curr++;
} else {
char* after;
setNumber(strtod(curr, &after));
curr = after;
}
return curr;
}
void stringify(std::ostream& os, bool pretty = false);
size_t size() {
assert(isArray());
return arr->size();
}
void setSize(size_t size) {
assert(isArray());
auto old = arr->size();
if (old != size) {
arr->resize(size);
}
if (old < size) {
for (auto i = old; i < size; i++) {
(*arr)[i] = Ref(new Value());
}
}
}
Ref& operator[](unsigned x) {
assert(isArray());
return (*arr)[x];
}
Value& push_back(Ref r) {
assert(isArray());
arr->push_back(r);
return *this;
}
Ref pop_back() {
assert(isArray());
Ref ret = arr->back();
arr->pop_back();
return ret;
}
Ref back() {
assert(isArray());
if (arr->size() == 0) {
return nullptr;
}
return arr->back();
}
Ref& operator[](IString x) {
assert(isObject());
return (*obj)[x];
}
bool has(IString x) {
assert(isObject());
return obj->count(x) > 0;
}
};
typedef Value::Ref Ref;
}
#endif