diff options
Diffstat (limited to 'include/serialize.h')
-rw-r--r-- | include/serialize.h | 1051 |
1 files changed, 848 insertions, 203 deletions
diff --git a/include/serialize.h b/include/serialize.h index 5b62c126c..af10fe856 100644 --- a/include/serialize.h +++ b/include/serialize.h @@ -1,335 +1,980 @@ /* + * Anope IRC Services * - * (C) 2003-2017 Anope Team - * Contact us at team@anope.org + * Copyright (C) 2011-2017 Anope Team <team@anope.org> * - * Please read COPYING and README for further details. + * This file is part of Anope. Anope is free software; you can + * redistribute it and/or modify it under the terms of the GNU + * General Public License as published by the Free Software + * Foundation, version 2. * - * Based on the original code of Epona by Lara. - * Based on the original code of Services by Andy Church. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see see <http://www.gnu.org/licenses/>. */ -#ifndef SERIALIZE_H -#define SERIALIZE_H - -#include <sstream> +#pragma once #include "anope.h" #include "base.h" +#include "extensible.h" +#include "event.h" namespace Serialize { - class Data - { - public: - enum Type - { - DT_TEXT, - DT_INT - }; + class Object; + + class TypeBase; + + class FieldBase; + template<typename> class FieldTypeBase; + template<typename, typename> class CommonFieldBase; + template<typename, typename> class Field; + template<typename, typename> class ObjectField; + + template<typename> class Storage; + + template<typename T, typename> class Type; + template<typename T> class Reference; + + extern std::vector<FieldBase *> serializableFields; + + extern Object *GetID(Serialize::TypeBase *type, ID id); + + template<typename T> + inline T GetObject(); + + template<typename T> + inline std::vector<T> GetObjects_(TypeBase *); - virtual ~Data() { } + template<typename T> + inline std::vector<T> GetObjects(const Anope::string &name); - virtual std::iostream& operator[](const Anope::string &key) = 0; - virtual std::set<Anope::string> KeySet() const { throw CoreException("Not supported"); } - virtual size_t Hash() const { throw CoreException("Not supported"); } + template<typename T> + inline std::vector<T> GetObjects(); - virtual void SetType(const Anope::string &key, Type t) { } - virtual Type GetType(const Anope::string &key) const { return DT_TEXT; } + template<typename T> + inline T New(); + + extern void GC(); + + extern void Unregister(Module *); + + struct Edge + { + Object *other; + FieldBase *field; + bool direction; + + Edge(Object *o, FieldBase *f, bool d) : other(o), field(f), direction(d) { } + + bool operator==(const Edge &e) const + { + return other == e.other && field == e.field && direction == e.direction; + } }; - extern void RegisterTypes(); - extern void CheckTypes(); + extern std::multimap<Anope::string, Anope::string> child_types; - class Type; - template<typename T> class Checker; - template<typename T> class Reference; + extern void SetParent(const Anope::string &child, const Anope::string &parent); + + extern std::vector<Serialize::TypeBase *> GetTypes(const Anope::string &name); } -/** A serialziable object. Serializable objects can be serialized into - * abstract data types (Serialize::Data), and then reconstructed or - * updated later at any time. - */ -class CoreExport Serializable : public virtual Base +class CoreExport Serialize::Object : public Extensible, public virtual Base { private: - /* A list of every serializable item in Anope. - * Some of these are static and constructed at runtime, - * so this list must be on the heap, as it is not always - * constructed before other objects are if it isn't. - */ - static std::list<Serializable *> *SerializableItems; - friend class Serialize::Type; + friend class Serialize::FieldBase; + /* The type of item this object is */ - Serialize::Type *s_type; - /* Iterator into serializable_items */ - std::list<Serializable *>::iterator s_iter; - /* The hash of the last serialized form of this object committed to the database */ - size_t last_commit; - /* The last time this object was committed to the database */ - time_t last_commit_time; + TypeBase *s_type; - protected: - Serializable(const Anope::string &serialize_type); - Serializable(const Serializable &); + std::map<TypeBase *, std::vector<Edge>> edges; - Serializable &operator=(const Serializable &); + std::vector<Edge> GetEdges(TypeBase *); public: - virtual ~Serializable(); + static constexpr const char *NAME = "object"; + + Object(TypeBase *type); + Object(TypeBase *type, ID); + + virtual ~Object(); + + virtual void Delete(); - /* Unique ID (per type, not globally) for this object */ - uint64_t id; + /* Unique ID for this object */ + ID id; + + void AddEdge(Object *other, FieldBase *field); + + void RemoveEdge(Object *other, FieldBase *field); + + /** + * Get an object of type T that this object references. + */ + template<typename T> + T GetRef() + { + std::vector<T> t = GetRefs<T>(); + return !t.empty() ? t[0] : nullptr; + } + + /** + * Gets all objects of type T that this object references + */ + template<typename T> + std::vector<T> GetRefs(); + + std::vector<Object *> GetRefs(Serialize::TypeBase *); + + /** + * Get the value of a field on this object. + */ + template< + typename Type, + template<typename, typename> class Field, // Field type being read + typename TypeImpl, + typename T // type of the Extensible + > + T Get(Field<TypeImpl, T> Type::*field) + { + static_assert(std::is_base_of<Object, TypeImpl>::value, ""); + static_assert(std::is_base_of<Serialize::TypeBase, Type>::value, ""); + + Type *t = static_cast<Type *>(s_type); + Field<TypeImpl, T>& f = t->*field; + return f.GetField(f.Upcast(this)); + } + + /** + * Get the value of a field on this object. Allows specifying return + * type if the return type can't be inferred. + */ + template<typename Ret, typename Field, typename Type> + Ret Get(Field Type::*field) + { + static_assert(std::is_base_of<Serialize::TypeBase, Type>::value, ""); - /* Only used by redis, to ignore updates */ - unsigned short redis_ignore; + Type *t = static_cast<Type *>(s_type); + Field& f = t->*field; + return f.GetField(f.Upcast(this)); + } - /** Marks the object as potentially being updated "soon". + /** + * Set the value of a field on this object */ - void QueueUpdate(); + template< + typename Type, + template<typename, typename> class Field, + typename TypeImpl, + typename T + > + void Set(Field<TypeImpl, T> Type::*field, const T& value) + { + static_assert(std::is_base_of<Object, TypeImpl>::value, ""); + static_assert(std::is_base_of<Serialize::TypeBase, Type>::value, ""); - bool IsCached(Serialize::Data &); - void UpdateCache(Serialize::Data &); + Type *t = static_cast<Type *>(s_type); + Field<TypeImpl, T>& f = t->*field; + f.SetField(f.Upcast(this), value); + } - bool IsTSCached(); - void UpdateTS(); + /** + * Set the value of a field on this object + */ + template<typename Field, typename Type, typename T> + void Set(Field Type::*field, const T& value) + { + static_assert(std::is_base_of<Serialize::TypeBase, Type>::value, ""); + + Type *t = static_cast<Type *>(s_type); + Field& f = t->*field; + f.SetField(f.Upcast(this), value); + } /** Get the type of serializable object this is * @return The serializable object type */ - Serialize::Type* GetSerializableType() const { return this->s_type; } + TypeBase* GetSerializableType() const { return this->s_type; } - virtual void Serialize(Serialize::Data &data) const = 0; + /** Set the value of a field on this object, by field name + */ + template<typename T> + void SetS(const Anope::string &name, const T &what); - static const std::list<Serializable *> &GetItems(); -}; + /** Unset a field on this object, by field name + */ + template<typename T> + void UnsetS(const Anope::string &name); -/* A serializable type. There should be one of these classes for each type - * of class that inherits from Serialiable. Used for unserializing objects - * of this type, as it requires a function pointer to a static member function. - */ -class CoreExport Serialize::Type : public Base -{ - typedef Serializable* (*unserialize_func)(Serializable *obj, Serialize::Data &); + /** Test if a field is set. Only useful with extensible fields, + * which can unset (vs set to the default value) + */ + bool HasFieldS(const Anope::string &name); - static std::vector<Anope::string> TypeOrder; - static std::map<Anope::string, Serialize::Type *> Types; + /** Whether or not the object can be garbage collected. + * Should be true unless your object holds in-memory only + * state. + */ + virtual bool CanGC() { return true; } + + void Wipe(); +}; - /* The name of this type, should be a class name */ +class CoreExport Serialize::TypeBase : public Service +{ Anope::string name; - unserialize_func unserialize; - /* Owner of this type. Used for placing objects of this type in separate databases - * based on what module, if any, owns it. - */ - Module *owner; - /* The timesatmp for this type. All objects of this type are as up to date as - * this timestamp. if curtime == timestamp then we have the most up to date - * version of every object of this type. - */ - time_t timestamp; + Module *owner = nullptr; public: - /* Map of Serializable::id to Serializable objects */ - std::map<uint64_t, Serializable *> objects; + static constexpr const char *NAME = "typebase"; + + std::set<Object *> objects; - /** Creates a new serializable type - * @param n Type name - * @param f Func to unserialize objects - * @param owner Owner of this type. Leave NULL for the core. - */ - Type(const Anope::string &n, unserialize_func f, Module *owner = NULL); - ~Type(); + TypeBase(Module *owner, const Anope::string &n); + + void Unregister(); /** Gets the name for this type * @return The name, eg "NickAlias" */ const Anope::string &GetName() { return this->name; } - /** Unserialized an object. - * @param obj NULL if this object doesn't yet exist. If this isn't NULL, instead - * update the contents of this object. - * @param data The data to unserialize - * @return The unserialized object. If obj != NULL this should be obj. + /** Create a new object of this type. */ - Serializable *Unserialize(Serializable *obj, Serialize::Data &data); + virtual Object *Create() anope_abstract; - /** Check if this object type has any pending changes and update them. + /** Get or otherwise create an object of this type + * with the given ID. */ - void Check(); + virtual Object *Require(Serialize::ID) anope_abstract; - /** Gets the timestamp for the object type. That is, the time we know - * all objects of this type are updated at least to. + /** Find a field on this type */ - time_t GetTimestamp() const; + FieldBase *GetField(const Anope::string &name); - /** Bumps object type timestamp to current time + /** Get all fields of this type */ - void UpdateTimestamp(); + std::vector<FieldBase *> GetFields(); Module* GetOwner() const { return this->owner; } - static Serialize::Type *Find(const Anope::string &name); + static TypeBase *Find(const Anope::string &name); - static const std::vector<Anope::string> &GetTypeOrder(); + static std::vector<TypeBase *> GetTypes(); +}; + +template<typename T, typename Base = Serialize::TypeBase> +class Serialize::Type : public Base +{ + public: + Type(Module *module) : Base(module, T::NAME) { } + Type(Module *module, const Anope::string &name) : Base(module, name) { } - static const std::map<Anope::string, Serialize::Type *>& GetTypes(); + Object *Create() override + { + return new T(this); + } + + Object *Require(Serialize::ID id) override + { + return RequireID(id); + } + + T* RequireID(ID id) + { + Object *s = Serialize::GetID(this, id); + if (s == nullptr) + return new T(this, id); + + if (s->GetSerializableType() != this) + { + Anope::Logger.Debug("Mismatch for required id {0}, is of type {1} but wants {2}", id, s->GetSerializableType()->GetName(), this->GetName()); + return nullptr; + } + + return static_cast<T *>(s); + } }; -/** Should be used to hold lists and other objects of a specific type, - * but not a specific object. Used for ensuring that any access to - * this object type is always up to date. These are usually constructed - * at run time, before main is called, so no types are registered. This - * is why there are static Serialize::Type* variables in every function. +/** A reference to a serializable object. Serializable objects may not always + * exist in memory at all times, like if they exist in an external database, + * so you can't hold pointers to them. Instead, hold a Serialize::Reference + * which will properly fetch the object on demand from the underlying database + * system. */ template<typename T> -class Serialize::Checker +class Serialize::Reference { - Anope::string name; - T obj; - mutable ::Reference<Serialize::Type> type; + protected: + bool valid = false; + TypeBase *type; + /* ID of the object which we reference */ + ID id; - inline void Check() const + public: + Serialize::Reference<T>& operator=(T* obj) { - if (!type) - type = Serialize::Type::Find(this->name); - if (type) - type->Check(); + if (obj != nullptr) + { + type = obj->GetSerializableType(); + id = obj->id; + valid = true; + } + else + { + valid = false; + } + return *this; + } + + explicit operator bool() const + { + return Dereference() != nullptr; + } + + operator T*() const { return Dereference(); } + + T* operator*() const { return Dereference(); } + T* operator->() const { return Dereference(); } + + private: + T* Dereference() const + { + if (!valid) + return nullptr; + + Object *targ = GetID(type, id); + if (targ != nullptr && targ->GetSerializableType() == type) + return anope_dynamic_static_cast<T*>(targ); + + EventReturn result = EventManager::Get()->Dispatch(&Event::SerializeEvents::OnSerializeDeref, id, type); + if (result == EVENT_ALLOW) + return anope_dynamic_static_cast<T *>(type->Require(id)); + + return nullptr; } +}; +/** A field, associated with a type. + */ +class Serialize::FieldBase : public Service +{ public: - Checker(const Anope::string &n) : name(n), type(NULL) { } + static constexpr const char *NAME = "fieldbase"; + + Anope::string serialize_type; // type the field is on + Anope::string serialize_name; // field name + + /** For fields which reference other Objects. If true, when + * the object the field references gets deleted, this object + * gets deleted too. + */ + bool depends = false; + + bool is_object = false; - inline const T* operator->() const + FieldBase(Module *, const Anope::string &, const Anope::string &, bool); + virtual ~FieldBase(); + void Unregister(); + + /** Serialize value of this field on the given object to string form + */ + virtual Anope::string SerializeToString(Object *s) anope_abstract; + + /** Unserialize value of this field on the given object from string form + */ + virtual void UnserializeFromString(Object *s, const Anope::string &) anope_abstract; + + /** Test if the given object has the given field, only usefil for extensible fields + */ + virtual bool HasFieldS(Object *) anope_abstract; + + /** Unset this field on the given object + */ + virtual void UnsetS(Object *) anope_abstract; + + virtual void Uncache(Object *) anope_abstract; + + virtual Anope::string GetTypeName() { return ""; } +}; + +template<typename T> +class Serialize::FieldTypeBase : public FieldBase +{ + public: + using FieldBase::FieldBase; + + /** Set this field to the given value on the given object. + */ + virtual void SetFieldS(Object *, const T &) anope_abstract; +}; + +/** Base class for serializable fields and serializable object fields. + */ +template< + typename TypeImpl, // Serializable type + typename T // actual type of field +> +class Serialize::CommonFieldBase : public FieldTypeBase<T> +{ + static_assert(std::is_base_of<Object, TypeImpl>::value, ""); + + /** Extensible storage for value of fields. Only used if field + * isn't set. Note extensible fields can be "unset", where field + * pointers are never unset, but are T(). + */ + ExtensibleItem<T> *ext = nullptr; + + /** Storage pointer in the TypeImpl object for this field. + */ + Serialize::Storage<T> TypeImpl::*storage = nullptr; + + protected: + /** Set the value of a field in storage + */ + void Set_(TypeImpl *object, const T &value) + { + if (storage != nullptr) + { + Serialize::Storage<T> &s = object->*storage; + s.t = value; + s.cached = true; + } + else if (ext != nullptr) + ext->Set(object, value); + else + throw CoreException("No field or ext"); + } + + /* Get the value of a field from storage + */ + T* Get_(TypeImpl *object) + { + if (storage != nullptr) + return &(object->*storage).t; + else if (ext != nullptr) + return ext->Get(object); + else + throw CoreException("No field or ext"); + } + + /** Unset a field from storage + */ + void Unset_(TypeImpl *object) + { + if (storage != nullptr) + { + Serialize::Storage<T> &s = object->*storage; + s.t = T(); + s.cached = false; + } + else if (ext != nullptr) + ext->Unset(object); + else + throw CoreException("No field or ext"); + } + + /** Check is a field is set. Only useful for + * extensible storage. Returns true for field storage. + */ + bool HasField_(TypeImpl *object) { - this->Check(); - return &this->obj; + if (storage != nullptr) + return true; + else if (ext != nullptr) + return ext->HasExt(object); + else + throw CoreException("No field or ext"); } - inline T* operator->() + + bool Cached_(TypeImpl *object) { - this->Check(); - return &this->obj; + if (storage != nullptr) + return (object->*storage).cached; + else if (ext != nullptr) + return ext->HasExt(object); + else + throw CoreException("No field or ext"); } - inline const T& operator*() const + public: + CommonFieldBase(Module *creator, const Anope::string &n, bool d) + : FieldTypeBase<T>(creator, n, TypeImpl::NAME, d) { - this->Check(); - return this->obj; + ext = new ExtensibleItem<T>(creator, TypeImpl::NAME, n); } - inline T& operator*() + + CommonFieldBase(Serialize::TypeBase *t, + const Anope::string &n, + Serialize::Storage<T> TypeImpl::*s, + bool d) + : FieldTypeBase<T>(t->GetOwner(), n, t->GetName(), d) + , storage(s) + { + } + + ~CommonFieldBase() + { + delete ext; + } + + /** Get the value of this field on the given object + */ + virtual T GetField(TypeImpl *) anope_abstract; + + /** Unset this field on the given object + */ + virtual void UnsetField(TypeImpl *) anope_abstract; + + void UnsetS(Object *s) override + { + UnsetField(Upcast(s)); + } + + bool HasFieldS(Object *s) override + { + return HasField(Upcast(s)); + } + + /** Cast a serializable object of type Object to type TypeImpl, + * if appropriate + */ + TypeImpl* Upcast(Object *s) { - this->Check(); - return this->obj; + if (this->serialize_type != s->GetSerializableType()->GetName()) + { + return nullptr; + } + + return anope_dynamic_static_cast<TypeImpl *>(s); } - inline operator const T&() const + bool HasField(TypeImpl *s) { - this->Check(); - return this->obj; + EventReturn result = EventManager::Get()->Dispatch(&Event::SerializeEvents::OnSerializeHasField, s, this); + if (result != EVENT_CONTINUE) + return true; + + return this->HasField_(s); } - inline operator T&() + + void Uncache(Object *s) override { - this->Check(); - return this->obj; + Unset_(Upcast(s)); } }; -/** Used to hold references to serializable objects. Reference should always be - * used when holding references to serializable objects for extended periods of time - * to ensure that the object it refers to it always up to date. This also behaves like - * Reference in that it will invalidate itself if the object it refers to is - * destructed. +/** Class for all fields that aren't to other serializable objects */ -template<typename T> -class Serialize::Reference : public ReferenceBase +template<typename TypeImpl, typename T> +class Serialize::Field : public CommonFieldBase<TypeImpl, T> { - protected: - T *ref; - public: - Reference() : ref(NULL) + Field(Module *creator, const Anope::string &n) : CommonFieldBase<TypeImpl, T>(creator, n, false) + { + } + + Field(TypeBase *t, const Anope::string &n, Serialize::Storage<T> TypeImpl::*f) : CommonFieldBase<TypeImpl, T>(t, n, f, false) { } - Reference(T *obj) : ref(obj) + T GetField(TypeImpl *s) override { - if (obj) - obj->AddReference(this); + T* t = this->Get_(s); + + // If this field is cached + if (t && this->Cached_(s)) + return *t; + + // Query modules + Anope::string value; + EventReturn result = EventManager::Get()->Dispatch(&Event::SerializeEvents::OnSerializeGet, s, this, value); + if (result == EVENT_ALLOW) + { + // module returned us data, so we unserialize it + T t2 = this->Unserialize(value); + + // set & cache + OnSet(s, t2); + this->Set_(s, t2); + + return t2; + } + + return t ? *t : T(); } - Reference(const Reference<T> &other) : ReferenceBase(other), ref(other.ref) + void SetFieldS(Object *s, const T &value) override { - if (ref && !invalid) - this->ref->AddReference(this); + SetField(this->Upcast(s), value); } - ~Reference() + /** + * Override to hook to changes in the internally + * cached value + */ + virtual void OnSet(TypeImpl *s, const T &value) { } + + void SetField(TypeImpl *s, const T &value) { - if (ref && !invalid) - this->ref->DelReference(this); + Anope::string strvalue = this->Serialize(value); + EventManager::Get()->Dispatch(&Event::SerializeEvents::OnSerializeSet, s, this, strvalue); + + OnSet(s, value); + this->Set_(s, value); } - inline Reference<T>& operator=(const Reference<T> &other) + void UnsetField(TypeImpl *s) override { - if (this != &other) + EventManager::Get()->Dispatch(&Event::SerializeEvents::OnSerializeUnset, s, this); + + this->Unset_(s); + } + + Anope::string Serialize(const T& t) + { + try + { + return stringify(t); + } + catch (const ConvertException &) { - if (ref && !invalid) - this->ref->DelReference(this); + return ""; + } + } - this->ref = other.ref; - this->invalid = other.invalid; + T Unserialize(const Anope::string &str) + { + if (str.empty()) + return T(); - if (ref && !invalid) - this->ref->AddReference(this); + try + { + return convertTo<T>(str); + } + catch (const ConvertException &) + { + return T(); } - return *this; } - inline operator bool() const + Anope::string SerializeToString(Object *s) override { - if (!this->invalid) - return this->ref != NULL; - return false; + T t = GetField(this->Upcast(s)); + return this->Serialize(t); + } + + void UnserializeFromString(Object *s, const Anope::string &v) override + { + T t = this->Unserialize(v); + SetField(this->Upcast(s), t); + } + + void Set(Extensible *obj, const T &value) + { + TypeImpl *s = anope_dynamic_static_cast<TypeImpl *>(obj); + SetField(s, value); + } + + void Unset(Extensible *obj) + { + TypeImpl *s = anope_dynamic_static_cast<TypeImpl *>(obj); + this->UnsetField(s); + } + + T Get(Extensible *obj) + { + TypeImpl *s = anope_dynamic_static_cast<TypeImpl *>(obj); + return this->GetField(s); + } + + bool HasExt(Extensible *obj) + { + TypeImpl *s = anope_dynamic_static_cast<TypeImpl *>(obj); + return this->HasField(s); + } +}; + +/** Class for all fields that contain a reference to another serializable object. + */ +template<typename TypeImpl, typename T> +class Serialize::ObjectField : public CommonFieldBase<TypeImpl, T> +{ + public: + ObjectField(Module *creator, const Anope::string &n) : CommonFieldBase<TypeImpl, T>(creator, n, false) + { + this->is_object = true; } - inline operator T*() const + ObjectField(TypeBase *t, const Anope::string &n, Serialize::Storage<T> TypeImpl::*field, bool d = false) : CommonFieldBase<TypeImpl, T>(t, n, field, d) { - if (!this->invalid) + this->is_object = true; + } + + T GetField(TypeImpl *s) override + { + T *t = this->Get_(s); + + if (t && this->Cached_(s)) + return *t; + + Anope::string type; + ID sid; + EventReturn result = EventManager::Get()->Dispatch(&Event::SerializeEvents::OnSerializeGetSerializable, s, this, type, sid); + if (result != EVENT_CONTINUE) { - if (this->ref) - // This can invalidate me - this->ref->QueueUpdate(); - if (!this->invalid) - return this->ref; + Serialize::TypeBase *base = Serialize::TypeBase::Find(type); + if (base == nullptr) + { + Anope::Logger.Debug2("OnSerializeGetSerializable returned unknown type {0}", type); + return nullptr; + } + + T t2 = result == EVENT_ALLOW ? static_cast<T>(base->Require(sid)) : nullptr; + + OnSet(s, t2); + this->Set_(s, t2); + + return t2; } - return NULL; + + return t ? *t : T(); + } + + void SetFieldS(Object *s, const T &value) override + { + SetField(this->Upcast(s), value); } - inline T* operator*() const + virtual void OnSet(TypeImpl *s, T value) { } + + void SetField(TypeImpl *s, T value) { - if (!this->invalid) + EventManager::Get()->Dispatch(&Event::SerializeEvents::OnSerializeSetSerializable, s, this, value); + + T *old = this->Get_(s); + if (old != nullptr && *old != nullptr) + s->RemoveEdge(*old, this); + + OnSet(s, value); + this->Set_(s, value); + + if (value != nullptr) + s->AddEdge(value, this); + } + + void UnsetField(TypeImpl *s) override + { + EventManager::Get()->Dispatch(&Event::SerializeEvents::OnSerializeUnsetSerializable, s, this); + + this->Unset_(s); + } + + Anope::string SerializeToString(Object *s) override + { + T t = GetField(this->Upcast(s)); + return this->Serialize(t); + } + + void UnserializeFromString(Object *s, const Anope::string &v) override + { + T t = this->Unserialize(v); + SetField(this->Upcast(s), t); + } + + Anope::string Serialize(const T& t) + { + return t ? t->GetSerializableType()->GetName() + ":" + stringify(t->id) : ""; + } + + T Unserialize(const Anope::string &str) + { + size_t c = str.rfind(':'); + if (c == Anope::string::npos) + return nullptr; + + Anope::string type = str.substr(0, c); + ID id; + try { - if (this->ref) - // This can invalidate me - this->ref->QueueUpdate(); - if (!this->invalid) - return this->ref; + id = convertTo<ID>(str.substr(c + 1)); } - return NULL; + catch (const ConvertException &) + { + return nullptr; + } + + TypeBase *t = TypeBase::Find(type); + if (!t) + { + return nullptr; + } + + return anope_dynamic_static_cast<T>(t->Require(id)); } - inline T* operator->() const + Anope::string GetTypeName() override { - if (!this->invalid) + const char* const name = std::remove_pointer<T>::type::NAME; + return name; + } +}; + +template<typename T> +class Serialize::Storage +{ + public: + T t = T(); + bool cached = false; +}; + +template<typename T> +T Serialize::GetObject() +{ + std::vector<T> v = GetObjects<T>(); + return v.empty() ? nullptr : v[0]; +} + +template<typename T> +std::vector<T> Serialize::GetObjects_(TypeBase *type) +{ + std::vector<T> o; + std::vector<ID> ids; + EventReturn result = EventManager::Get()->Dispatch(&Event::SerializeEvents::OnSerializeList, type, ids); + if (result == EVENT_ALLOW) + { + for (ID id : ids) { - if (this->ref) - // This can invalidate me - this->ref->QueueUpdate(); - if (!this->invalid) - return this->ref; + Object *s = type->Require(id); + if (s) + o.push_back(anope_dynamic_static_cast<T>(s)); } - return NULL; + return o; } -}; -#endif // SERIALIZE_H + std::transform(type->objects.begin(), type->objects.end(), std::back_inserter(o), [](Object *e) { return anope_dynamic_static_cast<T>(e); }); + return o; +} + +template<typename T> +std::vector<T> Serialize::GetObjects(const Anope::string &name) +{ + std::vector<T> objs; + + for (TypeBase *t : GetTypes(name)) + for (T obj : GetObjects_<T>(t)) + objs.push_back(obj); + + return objs; +} + +template<typename T> +std::vector<T> Serialize::GetObjects() +{ + const char* const name = std::remove_pointer<T>::type::NAME; + return GetObjects<T>(name); +} + +template<typename T> +T Serialize::New() +{ + const char* const name = std::remove_pointer<T>::type::NAME; + Serialize::TypeBase *type = TypeBase::Find(name); + + if (type == nullptr) + { + Anope::Logger.Debug("Serialize::New with unknown type {0}", name); + return nullptr; + } + + return static_cast<T>(type->Create()); +} + +inline std::vector<Serialize::Object *> Serialize::Object::GetRefs(Serialize::TypeBase *type) +{ + std::vector<Serialize::TypeBase *> types = GetTypes(type->GetName()); + std::vector<Object *> objs; + + if (types.empty()) + { + Anope::Logger.Debug("GetRefs for unknown type on #{0} type {1} named {2}", this->id, s_type->GetName(), type->GetName()); + return objs; + } + + for (Serialize::TypeBase *t : types) + for (const Serialize::Edge &edge : GetEdges(t)) + if (!edge.direction) + objs.push_back(edge.other); + + return objs; +} + +template<typename T> +std::vector<T> Serialize::Object::GetRefs() +{ + const char* const name = std::remove_pointer<T>::type::NAME; + std::vector<Serialize::TypeBase *> types = GetTypes(name); + std::vector<T> objs; + + if (types.empty()) + { + Anope::Logger.Debug("GetRefs for unknown type on #{0} type {1} named {2}", this->id, s_type->GetName(), name); + return objs; + } + + for (Serialize::TypeBase *t : types) + for (const Serialize::Edge &edge : GetEdges(t)) + if (!edge.direction) + objs.push_back(anope_dynamic_static_cast<T>(edge.other)); + + return objs; +} + +template<typename T> +void Serialize::Object::SetS(const Anope::string &name, const T &what) +{ + FieldBase *field = s_type->GetField(name); + if (field == nullptr) + { + Anope::Logger.Debug("Set for unknown field {0} on {1}", name, s_type->GetName()); + return; + } + + FieldTypeBase<T> *fieldt = static_cast<FieldTypeBase<T> *>(field); + fieldt->SetFieldS(this, what); +} + +template<typename T> +void Serialize::Object::UnsetS(const Anope::string &name) +{ + FieldBase *field = s_type->GetField(name); + if (field == nullptr) + { + Anope::Logger.Debug("Unset for unknown field {0} on {1}", name, s_type->GetName()); + return; + } + + FieldTypeBase<T> *fieldt = static_cast<FieldTypeBase<T> *>(field); + fieldt->UnsetS(this); +} + +inline bool Serialize::Object::HasFieldS(const Anope::string &name) +{ + FieldBase *field = s_type->GetField(name); + if (field == nullptr) + { + Anope::Logger.Debug("HasField for unknown field {0} on {1}", name, s_type->GetName()); + return false; + } + + FieldTypeBase<void *> *fieldt = static_cast<FieldTypeBase<void *> *>(field); + return fieldt->HasFieldS(this); +} + |