summaryrefslogtreecommitdiff
path: root/include/serialize.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/serialize.h')
-rw-r--r--include/serialize.h1051
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);
+}
+