/* * Anope IRC Services * * Copyright (C) 2012-2017 Anope Team * * 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. * * 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 . */ #include "services.h" #include "anope.h" #include "serialize.h" #include "modules.h" #include "event.h" using namespace Serialize; static std::map> objects; std::vector Serialize::serializableFields; std::multimap Serialize::child_types; static ID curid; Object *Serialize::GetID(Serialize::TypeBase *type, ID id) { auto it = objects[type].find(id); if (it != objects[type].end()) return it->second; return nullptr; } void Serialize::GC() { for (auto it = objects.begin(); it != objects.end(); ++it) for (auto it2 = it->second.begin(); it2 != it->second.end();) { Object *o = it2->second; if (!o->CanGC()) { // Wipe internal storage to force refetch o->Wipe(); ++it2; continue; } Anope::Logger.Debug2("garbage collected object {0}", o->id); it2 = it->second.erase(it2); delete o; } } void Serialize::Unregister(Module *m) { for (TypeBase *s : ServiceManager::Get()->FindServices()) if (s->GetOwner() == m) s->Unregister(); for (FieldBase *field : serializableFields) if (field->GetOwner() == m) field->Unregister(); } void Serialize::Object::Wipe() { for (Serialize::FieldBase *base : s_type->GetFields()) { base->Uncache(this); } } std::vector Object::GetEdges(TypeBase *type) { std::vector refs; EventReturn result = EventManager::Get()->Dispatch(&Event::SerializeEvents::OnSerializeGetRefs, this, type, refs); if (result == EVENT_ALLOW) return refs; if (type == nullptr) { refs.clear(); for (const std::pair> &p : edges) { const std::vector &e = p.second; refs.insert(refs.end(), e.begin(), e.end()); } return refs; } auto it = edges.find(type); if (it != edges.end()) return it->second; return std::vector(); } Object::Object(TypeBase *type) { ID i; EventReturn result = EventManager::Get()->Dispatch(&Event::SerializeEvents::OnSerializableGetId, type, i); if (result != EVENT_ALLOW) { while (GetID(type, ++curid)); i = curid; } id = i; objects[type][id] = this; this->s_type = type; type->objects.insert(this); Anope::Logger.Debug2("Creating object id #{0} address {1} type {2}", id, static_cast(this), type->GetName()); EventManager::Get()->Dispatch(&Event::SerializeEvents::OnSerializableCreate, this); } Object::Object(TypeBase *type, ID i) { this->id = i; objects[type][id] = this; this->s_type = type; type->objects.insert(this); Anope::Logger.Debug2("Creating object from id #{0} address {1} type {2}", id, static_cast(this), type->GetName()); } Object::~Object() { Anope::Logger.Debug2("Destructing object id #{0} address {1} type {2}", id, static_cast(this), s_type->GetName()); /* Remove in memory edges */ std::map> copy = edges; for (const std::pair> &p : copy) for (const Edge &edge : p.second) { if (!edge.direction) { Anope::Logger.Debug2("Removing edge from object id #{0} type {1} on field {2}", edge.other->id, edge.other->GetSerializableType()->GetName(), edge.field->serialize_name); edge.other->RemoveEdge(this, edge.field); } else { Anope::Logger.Debug2("Removing edge to object id #{0} type {1} on field {2}", edge.other->id, edge.other->GetSerializableType()->GetName(), edge.field->serialize_name); this->RemoveEdge(edge.other, edge.field); } } objects[s_type].erase(id); s_type->objects.erase(this); } void Object::Delete() { Anope::Logger.Debug2("Deleting object id #{0} type {1}", id, s_type->GetName()); /* Delete dependant objects */ for (const Edge &edge : GetEdges(nullptr)) { Object *other = edge.other; FieldBase *field = edge.field; if (edge.direction) continue; if (field->depends) { Anope::Logger.Debug2("Deleting dependent object #{0} type {1} due to edge on {2}", other->id, other->GetSerializableType()->GetName(), field->serialize_name); other->Delete(); } else { Anope::Logger.Debug2("Unsetting field {0} on object #{1} type {2}", field->serialize_name, other->id, other->GetSerializableType()->GetName()); field->UnsetS(other); } } EventManager::Get()->Dispatch(&Event::SerializeEvents::OnSerializableDelete, this); delete this; } void Object::AddEdge(Object *other, FieldBase *field) { // field = the field on 'this' object this->edges[other->GetSerializableType()].emplace_back(other, field, true); // field = the field on the other object other->edges[this->GetSerializableType()].emplace_back(this, field, false); } void Object::RemoveEdge(Object *other, FieldBase *field) { std::vector &myedges = this->edges[other->GetSerializableType()]; auto it = std::find(myedges.begin(), myedges.end(), Edge(other, field, true)); if (it != myedges.end()) myedges.erase(it); else Anope::Logger.Debug2("Unable to locate edge for removal on #{0} type {1} -> #{2} type {3}", this->id, s_type->GetName(), other->id, other->GetSerializableType()->GetName()); if (myedges.empty()) this->edges.erase(other->GetSerializableType()); std::vector &theiredges = other->edges[this->GetSerializableType()]; it = std::find(theiredges.begin(), theiredges.end(), Edge(this, field, false)); if (it != theiredges.end()) theiredges.erase(it); else Anope::Logger.Debug2("Unable to locate edge for removal on #{0} type {1} <- #{2} type {3}", this->id, s_type->GetName(), other->id, other->GetSerializableType()->GetName()); if (theiredges.empty()) other->edges.erase(this->GetSerializableType()); } TypeBase::TypeBase(Module *o, const Anope::string &n) : Service(o, TypeBase::NAME, n), name(n), owner(o) { } void TypeBase::Unregister() { Anope::Logger.Debug2("Unregistering type {0}", this->GetName()); // Delete in memory objects std::unordered_map objs = ::objects[this]; for (auto &pair : objs) delete pair.second; ::objects.erase(this); for (FieldBase *field : serializableFields) { if (field->serialize_type == this->GetName()) { field->Unregister(); } } } Serialize::FieldBase *TypeBase::GetField(const Anope::string &fname) { /* is this too slow? */ for (FieldBase *fb : ServiceManager::Get()->FindServices()) if (fb->serialize_type == this->GetName() && fb->serialize_name == fname) return fb; Anope::Logger.Debug2("GetField() for unknown field {0} on {1}", fname, this->GetName()); return nullptr; } std::vector TypeBase::GetFields() { std::vector fields; for (FieldBase *fb : ServiceManager::Get()->FindServices()) if (fb->serialize_type == this->GetName()) fields.push_back(fb); return fields; } TypeBase *TypeBase::Find(const Anope::string &name) { return ServiceManager::Get()->FindService(name); } std::vector TypeBase::GetTypes() { return ServiceManager::Get()->FindServices(); } FieldBase::FieldBase(Module *c, const Anope::string &n, const Anope::string &t, bool d) : Service(c, FieldBase::NAME) , serialize_type(t) , serialize_name(n) , depends(d) { serializableFields.push_back(this); } FieldBase::~FieldBase() { auto it = std::find(serializableFields.begin(), serializableFields.end(), this); if (it != serializableFields.end()) serializableFields.erase(it); } void FieldBase::Unregister() { Anope::Logger.Debug2("Unregistering field {0} on {1}", serialize_name, serialize_type); /* find edges on this field */ for (Object *s : Serialize::GetObjects(serialize_type)) { for (const std::pair> &p : s->edges) for (const Edge &edge : p.second) if (edge.direction && edge.field == this) { Anope::Logger.Debug2("Removing edge on #{0} type {1} -> #{2} type {3}", s->id, s->GetSerializableType()->GetName(), edge.other->id, edge.other->GetSerializableType()->GetName()); s->RemoveEdge(edge.other, edge.field); goto cont; } cont:; } } void Serialize::SetParent(const Anope::string &child, const Anope::string &parent) { child_types.insert(std::make_pair(parent, child)); } std::vector Serialize::GetTypes(const Anope::string &name) { std::vector v; Serialize::TypeBase *t = Serialize::TypeBase::Find(name); if (t != nullptr) v.push_back(t); else Anope::Logger.Debug2("GetTypes for unknown type {0}", name); auto its = child_types.equal_range(name); for (; its.first != its.second; ++its.first) { t = Serialize::TypeBase::Find(its.first->second); if (t != nullptr) v.push_back(t); } return v; }