591 lines
17 KiB
C++

#pragma once
#include <stdint.h>
#include <string>
#include <memory>
#include <iosfwd>
#include <vector>
#include <cstring>
#include <stdexcept>
#include <functional>
#include <unordered_map>
namespace util {
template<class T> class Buffer;
}
namespace dv {
using integer_t = int64_t;
using number_t = double;
using boolean_t = bool;
using byte_t = unsigned char;
using key_t = std::string;
enum class value_type : uint8_t {
none = 0,
number,
boolean,
integer,
object,
list,
bytes,
string
};
inline const std::string& type_name(value_type type) {
static std::string type_names[] = {
"none",
"number",
"boolean",
"integer",
"object",
"list",
"bytes",
"string"
};
return type_names[static_cast<int>(type)];
}
class value;
using list_t = std::vector<value>;
using map_t = std::unordered_map<key_t, value>;
using pair = std::pair<const key_t, value>;
using reference = value&;
using const_reference = const value&;
namespace objects {
using Object = std::unordered_map<key_t, value>;
using List = std::vector<value>;
using Bytes = util::Buffer<byte_t>;
}
/// @brief nullable value reference returned by value.at(...)
struct optionalvalue {
value* ptr;
optionalvalue(value* ptr) : ptr(ptr) {}
inline operator bool() const {
return ptr != nullptr;
}
inline value& operator*() {
return *ptr;
}
inline const value& operator*() const {
return *ptr;
}
bool get(std::string& dst) const;
bool get(bool& dst) const;
bool get(char& dst) const;
bool get(short& dst) const;
bool get(int& dst) const;
bool get(long& dst) const;
bool get(long long& dst) const;
bool get(unsigned char& dst) const;
bool get(unsigned short& dst) const;
bool get(unsigned int& dst) const;
bool get(unsigned long& dst) const;
bool get(unsigned long long& dst) const;
bool get(float& dst) const;
bool get(double& dst) const;
};
inline void throw_type_error(value_type got, value_type expected) {
// place breakpoint here to find cause
throw std::runtime_error(
"type error: expected " + type_name(expected) + ", got " +
type_name(got)
);
}
inline void check_type(value_type got, value_type expected) {
if (got != expected) {
throw_type_error(got, expected);
}
}
class value {
value_type type = value_type::none;
union value_u {
integer_t integer;
number_t number;
boolean_t boolean;
std::unique_ptr<std::string> string;
std::shared_ptr<objects::Object> object;
std::shared_ptr<objects::List> list;
std::shared_ptr<objects::Bytes> bytes;
value_u() {}
~value_u() {}
} val;
inline value& setBoolean(boolean_t v) {
this->~value();
type = value_type::boolean;
val.boolean = v;
return *this;
}
inline value& setInteger(integer_t v) {
this->~value();
type = value_type::integer;
val.integer = v;
return *this;
}
inline value& setNumber(number_t v) {
this->~value();
type = value_type::number;
val.number = v;
return *this;
}
inline value& setNone() {
this->~value();
type = value_type::none;
return *this;
}
inline value& setString(std::string v) {
this->~value();
new(&val.string)std::unique_ptr<std::string>(
std::make_unique<std::string>(std::move(v)));
type = value_type::string;
return *this;
}
inline value& setString(std::unique_ptr<std::string> v) {
this->~value();
new(&val.string)std::unique_ptr<std::string>(std::move(v));
type = value_type::string;
return *this;
}
inline value& setList(std::shared_ptr<objects::List> ptr) {
this->~value();
new(&val.list)std::shared_ptr<objects::List>(std::move(ptr));
type = value_type::list;
return *this;
}
inline value& setObject(std::shared_ptr<objects::Object> ptr) {
this->~value();
new(&val.object)std::shared_ptr<objects::Object>(std::move(ptr));
type = value_type::object;
return *this;
}
inline value& setBytes(std::shared_ptr<objects::Bytes> ptr) {
this->~value();
new(&val.bytes)std::shared_ptr<objects::Bytes>(std::move(ptr));
type = value_type::bytes;
return *this;
}
public:
value() : type(value_type::none) {}
value(value_type type);
template<class T>
value(T v) {
this->operator=(v);
}
value(const value& v) noexcept : type(value_type::none) {
this->operator=(v);
}
value(value&& v) noexcept {
this->operator=(std::move(v));
}
~value() {
switch (type) {
case value_type::object:
val.object.reset();
break;
case value_type::list:
val.list.reset();
break;
case value_type::bytes:
val.bytes.reset();
break;
case value_type::string:
val.string.reset();
break;
default:
break;
}
}
inline value& operator=(std::nullptr_t) {
return setNone();
}
template<typename T>
inline std::enable_if_t<std::is_integral<T>() && !std::is_same<T, bool>(), value&>
operator=(T v) {
return setInteger(v);
}
inline value& operator=(float v) {
return setNumber(v);
}
inline value& operator=(double v) {
return setNumber(v);
}
inline value& operator=(bool v) {
return setBoolean(v);
}
inline value& operator=(std::string_view v) {
return setString(std::string(v));
}
inline value& operator=(std::string v) {
return setString(std::move(v));
}
inline value& operator=(const char* v) {
return setString(v);
}
inline value& operator=(std::shared_ptr<objects::List> ptr) {
return setList(std::move(ptr));
}
inline value& operator=(std::shared_ptr<objects::Object> ptr) {
return setObject(std::move(ptr));
}
inline value& operator=(std::shared_ptr<objects::Bytes> ptr) {
return setBytes(std::move(ptr));
}
value& operator=(const objects::Bytes& bytes);
inline value& operator=(const value& v) {
switch (v.type) {
case value_type::object:
setObject(v.val.object);
break;
case value_type::list:
setList(v.val.list);
break;
case value_type::bytes:
setBytes(v.val.bytes);
break;
case value_type::string:
setString(*v.val.string);
break;
case value_type::boolean:
setBoolean(v.val.boolean);
break;
case value_type::integer:
setInteger(v.val.integer);
break;
case value_type::number:
setNumber(v.val.number);
break;
case value_type::none:
setNone();
break;
}
return *this;
}
inline value& operator=(value&& v) noexcept {
if (type < value_type::object) {
type = v.type;
switch (v.type) {
case value_type::none:
break;
case value_type::integer:
val.integer = v.val.integer;
break;
case value_type::number:
val.number = v.val.number;
break;
case value_type::boolean:
val.boolean = v.val.boolean;
break;
case value_type::string:
new(&val.string)std::unique_ptr<std::string>(
std::move(v.val.string));
break;
case value_type::object:
new(&val.object)std::shared_ptr<objects::Object>(
std::move(v.val.object));
break;
case value_type::list:
new(&val.list)std::shared_ptr<objects::List>(
std::move(v.val.list));
break;
case value_type::bytes:
new(&val.list)std::shared_ptr<objects::Bytes>(
std::move(v.val.bytes));
break;
}
} else {
switch (v.type) {
case value_type::object:
setObject(std::move(v.val.object));
break;
case value_type::list:
setList(std::move(v.val.list));
break;
case value_type::bytes:
setBytes(std::move(v.val.bytes));
break;
case value_type::string:
setString(std::move(v.val.string));
break;
case value_type::boolean:
setBoolean(v.val.boolean);
break;
case value_type::integer:
setInteger(v.val.integer);
break;
case value_type::number:
setNumber(v.val.number);
break;
case value_type::none:
setNone();
break;
}
}
return *this;
}
void add(value v);
template<class T>
inline void add(T v) {
return add(value(v));
}
void erase(const key_t& key);
void erase(size_t index);
value& operator[](const key_t& key);
const value& operator[](const key_t& key) const;
value& operator[](size_t index);
const value& operator[](size_t index) const;
bool operator!=(std::nullptr_t) const noexcept {
return type != value_type::none;
}
bool operator==(std::nullptr_t) const noexcept {
return type == value_type::none;
}
value& object(const key_t& key);
value& list(const key_t& key);
value& object();
value& list();
list_t::iterator begin();
list_t::iterator end();
list_t::const_iterator begin() const;
list_t::const_iterator end() const;
const std::string& asString() const;
integer_t asInteger() const;
number_t asNumber() const;
boolean_t asBoolean() const;
objects::Bytes& asBytes();
const objects::Bytes& asBytes() const;
const objects::Object& asObject() const;
inline value_type getType() const {
return type;
}
std::string asString(std::string def) const {
if (type != value_type::string) {
return def;
}
return *val.string;
}
std::string asString(const char* s) const {
return asString(std::string(s));
}
integer_t asBoolean(boolean_t def) const {
switch (type) {
case value_type::boolean:
return val.boolean;
default:
return def;
}
}
integer_t asInteger(integer_t def) const {
switch (type) {
case value_type::integer:
return val.integer;
case value_type::number:
return static_cast<integer_t>(val.number);
default:
return def;
}
}
integer_t asNumber(integer_t def) const {
switch (type) {
case value_type::integer:
return static_cast<number_t>(val.integer);
case value_type::number:
return val.number;
default:
return def;
}
}
optionalvalue at(const key_t& k) const {
check_type(type, value_type::object);
const auto& found = val.object->find(k);
if (found == val.object->end()) {
return optionalvalue(nullptr);
}
return optionalvalue(&found->second);
}
optionalvalue at(size_t index) {
check_type(type, value_type::list);
return optionalvalue(&val.list->at(index));
}
const optionalvalue at(size_t index) const {
check_type(type, value_type::list);
return optionalvalue(&val.list->at(index));
}
bool has(const key_t& k) const;
size_t size() const;
size_t length() const {
return size();
}
inline bool empty() const {
return size() == 0;
}
inline bool isString() const {
return type == value_type::string;
}
inline bool isObject() const {
return type == value_type::object;
}
inline bool isList() const {
return type == value_type::list;
}
inline bool isInteger() const {
return type == value_type::integer;
}
inline bool isNumber() const {
return type == value_type::number;
}
};
inline value none = value();
inline bool is_numeric(const value& val) {
return val.isInteger() && val.isNumber();
}
using to_string_func = std::function<std::string(const value&)>;
}
namespace dv {
inline const std::string& type_name(const value& value) {
return type_name(value.getType());
}
inline value object() {
return std::make_shared<objects::Object>();
}
inline value list() {
return std::make_shared<objects::List>();
}
inline value list(std::initializer_list<value> values) {
return std::make_shared<objects::List>(values);
}
template<typename T> inline bool get_to_int(value* ptr, T& dst) {
if (ptr) {
dst = ptr->asInteger();
return true;
}
return false;
}
template<typename T> inline bool get_to_num(value* ptr, T& dst) {
if (ptr) {
dst = ptr->asNumber();
return true;
}
return false;
}
inline bool optionalvalue::get(std::string& dst) const {
if (ptr) {
dst = ptr->asString();
return true;
}
return false;
}
inline bool optionalvalue::get(bool& dst) const {
if (ptr) {
dst = ptr->asBoolean();
return true;
}
return false;
}
inline bool optionalvalue::get(char& dst) const {
return get_to_int(ptr, dst);
}
inline bool optionalvalue::get(short& dst) const {
return get_to_int(ptr, dst);
}
inline bool optionalvalue::get(int& dst) const {
return get_to_int(ptr, dst);
}
inline bool optionalvalue::get(long& dst) const {
return get_to_int(ptr, dst);
}
inline bool optionalvalue::get(long long& dst) const {
return get_to_int(ptr, dst);
}
inline bool optionalvalue::get(unsigned char& dst) const {
return get_to_int(ptr, dst);
}
inline bool optionalvalue::get(unsigned short& dst) const {
return get_to_int(ptr, dst);
}
inline bool optionalvalue::get(unsigned int& dst) const {
return get_to_int(ptr, dst);
}
inline bool optionalvalue::get(unsigned long& dst) const {
return get_to_int(ptr, dst);
}
inline bool optionalvalue::get(unsigned long long& dst) const {
return get_to_int(ptr, dst);
}
inline bool optionalvalue::get(float& dst) const {
return get_to_num(ptr, dst);
}
inline bool optionalvalue::get(double& dst) const {
return get_to_num(ptr, dst);
}
}
std::ostream& operator<<(std::ostream& stream, const dv::value& value);