#pragma once #include #include "assert.h" #include "platform.h" // This file is the runtime representation of a config data structure. A config data structure // is a JSON-like object for representing generic data. This format is suitable when the // development cost of creating a custom binary format is big and the benefits are small. // (I.e. for any data that is not massive bulk data -- such as small configuration files.) // // The runtime format is essential (type, data) where type is a four-byte type identifier // and data is four bytes of data. We use four bytes for the type identifier for alignment // purposes. Note that the runtime format represent an entire config object as a single // memory block. Local pointers inside the memory block are used to build structure. // // Note that it is possible to optimize this format further to squeeze out a few more bytes. namespace stingray_plugin_foundation { struct ConstConfigArray; struct ConstConfigObject; namespace const_config { // Type used for offset parameters typedef unsigned offset_t; // The supported value types enum ValueType {NIL, BOOL, INTEGER, FLOAT, STRING, ARRAY, OBJECT}; // Generic representation of a value of one of the types. union Value { int b; int i; float f; offset_t s; // Offset to char * offset_t a; // Offset to ConstConfigArray * offset_t o; // Offset toConstConfigObject * }; } // Represents the root object (the first item in the memory block). struct ConstConfigRoot { int type; // ValueType const_config::Value value; }; // Represents an array. Note that the type is only stored once, all objects in an array // must have the same type. In the memory layout, the ConstConfigArray block is followed // by *size* data items (which are bool, int, float, etc) depending on type. struct ConstConfigArray { int type; // ValueType int size; const_config::Value first_item; // size data items follows in memory }; // Represents an entry in a ConstConfigObject. Stores the key (always a string), the // type of the entry and the value. struct ConstConfigObjectEntry { const_config::offset_t name; // Offset to name int type; // ValueType const_config::Value value; }; // Represents an object. In the memory layout, ConstConfigObject is followed by // *size* ConstConfigObjectEntry structures that contain the actual objects. struct ConstConfigObject { int size; ConstConfigObjectEntry first_entry; }; // Convenience class used to access config data. class ConstConfigItem { public: // Creates a nil config item. ConstConfigItem() : _base(nullptr), _type(const_config::NIL) {} // Creates a config item from the specified root. ConstConfigItem(const ConstConfigRoot &root) : _base((const char *)&root), _type(root.type), _value(root.value) {} // Creates a config item with the specified base, type and data. ConstConfigItem(const char *base, const_config::ValueType t, const_config::Value v) : _base(base), _type(t), _value(v) {} bool is_nil() const {return _type == const_config::NIL;} bool is_bool() const {return _type == const_config::BOOL;} bool is_false() const {return _type == const_config::BOOL && !_value.b;} bool is_true() const {return _type == const_config::BOOL && _value.b;} bool is_integer() const {return _type == const_config::INTEGER;} bool is_float() const {return _type == const_config::FLOAT;} bool is_number() const {return is_integer() || is_float();} bool is_string() const {return _type == const_config::STRING;} bool is_resource() const { return is_string() || is_object() && (*this)["$resource_name"].is_string(); } bool is_resource(const char *type) const { return is_resource() && (is_string() || (*this)["$resource_type"].is_string() && strcmp((*this)["$resource_type"].to_string(),type)==0); } bool is_array() const { return _type == const_config::ARRAY; } bool is_object() const {return _type == const_config::OBJECT;} bool to_bool() const {XENSURE(is_bool()); return _value.b != 0;} int to_integer() const {XENSURE(is_integer()); return _value.i;} float to_float() const {return is_float() ? _value.f : float(to_integer());} const char *to_string() const {XENSURE(is_string()); return raw_s();} const char *to_resource(const char *type) const { XENSURE(is_resource(type)); if (is_string()) { return to_string(); } return (*this)["$resource_name"].to_string(); } bool operator||(bool b) const {return is_bool() ? to_bool() : b;} int operator||(int i) const {return is_integer() ? _value.i : i;} unsigned operator||(unsigned i) const {return is_integer() ? unsigned(_value.i) : i;} float operator||(float f) const {return is_float() ? _value.f : (is_integer() ? _value.i : f);} const char *operator||(const char *s) const {return is_string() ? raw_s() : s;} ConstConfigItem operator||(ConstConfigItem o) {return is_nil() ? o : *this;} int size() const {return is_array() ? raw_a()->size : 0;} int n_items() const {return is_object() ? raw_o()->size : 0;} ConstConfigItem operator[](int i) const { if (!is_array()) return ConstConfigItem(); if (i<0 || i>=raw_a()->size) return ConstConfigItem(); const ConstConfigArray &arr = *raw_a(); return ConstConfigItem(_base, (const_config::ValueType)arr.type, *(&arr.first_item + i)); } ConstConfigItem operator[](const char *s) const { if (!is_object()) return ConstConfigItem(); const ConstConfigObject &obj = *raw_o(); for (int i=0; i=obj.size) return ConstConfigItem(); const ConstConfigObjectEntry &e = *(&obj.first_entry + i); if(s) *s = _base + e.name; return ConstConfigItem(_base, (const_config::ValueType)e.type, e.value); } private: const char *raw_s() const {return _base + _value.s;} const ConstConfigArray *raw_a() const {return (ConstConfigArray *)(_base + _value.a);} const ConstConfigObject *raw_o() const {return (ConstConfigObject *)(_base + _value.o);} const char *_base; // Base for offsets int _type; // ValueType const_config::Value _value; }; } // namespace stingray_plugin_foundation