/* * * (C) 2003-2022 Anope Team * Contact us at team@anope.org * * Please read COPYING and README for further details. * * Based on the original code of Epona by Lara. * Based on the original code of Services by Andy Church. */ #ifndef SERIALIZE_H #define SERIALIZE_H #include #include "anope.h" #include "base.h" namespace Serialize { class Data { public: enum Type { DT_TEXT, DT_INT }; virtual ~Data() { } virtual std::iostream& operator[](const Anope::string &key) = 0; virtual std::set KeySet() const { throw CoreException("Not supported"); } virtual size_t Hash() const { throw CoreException("Not supported"); } virtual void SetType(const Anope::string &key, Type t) { } virtual Type GetType(const Anope::string &key) const { return DT_TEXT; } }; extern void RegisterTypes(); extern void CheckTypes(); class Type; template class Checker; template class Reference; } /** A serializable 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 { 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 *SerializableItems; friend class Serialize::Type; /* The type of item this object is */ Serialize::Type *s_type; /* Iterator into serializable_items */ std::list::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; protected: Serializable(const Anope::string &serialize_type); Serializable(const Serializable &); Serializable &operator=(const Serializable &); public: virtual ~Serializable(); /* Unique ID (per type, not globally) for this object */ uint64_t id; /* Only used by redis, to ignore updates */ unsigned short redis_ignore; /** Marks the object as potentially being updated "soon". */ void QueueUpdate(); bool IsCached(Serialize::Data &); void UpdateCache(Serialize::Data &); bool IsTSCached(); void UpdateTS(); /** Get the type of serializable object this is * @return The serializable object type */ Serialize::Type* GetSerializableType() const { return this->s_type; } virtual void Serialize(Serialize::Data &data) const = 0; static const std::list &GetItems(); }; /* A serializable type. There should be one of these classes for each type * of class that inherits from Serializable. 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 &); static std::vector TypeOrder; static std::map Types; /* The name of this type, should be a class name */ 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 timestamp 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; public: /* Map of Serializable::id to Serializable objects */ std::map 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(); /** 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. */ Serializable *Unserialize(Serializable *obj, Serialize::Data &data); /** Check if this object type has any pending changes and update them. */ void Check(); /** Gets the timestamp for the object type. That is, the time we know * all objects of this type are updated at least to. */ time_t GetTimestamp() const; /** Bumps object type timestamp to current time */ void UpdateTimestamp(); Module* GetOwner() const { return this->owner; } static Serialize::Type *Find(const Anope::string &name); static const std::vector &GetTypeOrder(); static const std::map& GetTypes(); }; /** 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. */ template class Serialize::Checker { Anope::string name; T obj; mutable ::Reference type; inline void Check() const { if (!type) type = Serialize::Type::Find(this->name); if (type) type->Check(); } public: Checker(const Anope::string &n) : name(n), type(NULL) { } inline const T* operator->() const { this->Check(); return &this->obj; } inline T* operator->() { this->Check(); return &this->obj; } inline const T& operator*() const { this->Check(); return this->obj; } inline T& operator*() { this->Check(); return this->obj; } inline operator const T&() const { this->Check(); return this->obj; } inline operator T&() { this->Check(); return this->obj; } }; /** 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. */ template class Serialize::Reference : public ReferenceBase { protected: T *ref; public: Reference() : ref(NULL) { } Reference(T *obj) : ref(obj) { if (obj) obj->AddReference(this); } Reference(const Reference &other) : ReferenceBase(other), ref(other.ref) { if (ref && !invalid) this->ref->AddReference(this); } ~Reference() { if (ref && !invalid) this->ref->DelReference(this); } inline Reference& operator=(const Reference &other) { if (this != &other) { if (ref && !invalid) this->ref->DelReference(this); this->ref = other.ref; this->invalid = other.invalid; if (ref && !invalid) this->ref->AddReference(this); } return *this; } inline operator bool() const { if (!this->invalid) return this->ref != NULL; return false; } inline operator T*() const { if (!this->invalid) { if (this->ref) // This can invalidate me this->ref->QueueUpdate(); if (!this->invalid) return this->ref; } return NULL; } inline T* operator*() const { if (!this->invalid) { if (this->ref) // This can invalidate me this->ref->QueueUpdate(); if (!this->invalid) return this->ref; } return NULL; } inline T* operator->() const { if (!this->invalid) { if (this->ref) // This can invalidate me this->ref->QueueUpdate(); if (!this->invalid) return this->ref; } return NULL; } }; #endif // SERIALIZE_H