From 73a09734a145722a3bd6199750fad62b46dd9339 Mon Sep 17 00:00:00 2001 From: Cyrille Bagard Date: Tue, 18 May 2021 01:12:59 +0200 Subject: Dead with singleton instances containing singleton instances. --- plugins/pychrysalide/glibext/singleton.c | 435 ++++++++++++++++++++++++++++++- src/glibext/singleton-int.h | 21 +- src/glibext/singleton.c | 352 +++++++++++++++++++++++-- src/glibext/singleton.h | 23 +- tests/glibext/singleton.py | 54 +++- 5 files changed, 832 insertions(+), 53 deletions(-) diff --git a/plugins/pychrysalide/glibext/singleton.c b/plugins/pychrysalide/glibext/singleton.c index 2be1105..c4592ac 100644 --- a/plugins/pychrysalide/glibext/singleton.c +++ b/plugins/pychrysalide/glibext/singleton.c @@ -25,6 +25,7 @@ #include "singleton.h" +#include #include @@ -43,15 +44,33 @@ /* Procède à l'initialisation de l'interface de candidature. */ static void py_singleton_candidate_interface_init(GSingletonCandidateIface *, gpointer *); +/* Fournit une liste de candidats embarqués par un candidat. */ +static GSingletonCandidate **py_singleton_candidate_list_inner_instances_wrapper(const GSingletonCandidate *, size_t *); + +/* Met à jour une liste de candidats embarqués par un candidat. */ +static void py_singleton_candidate_update_inner_instances_wrapper(GSingletonCandidate *, GSingletonCandidate **, size_t); + /* Fournit l'empreinte d'un candidat à une centralisation. */ static guint py_singleton_candidate___hash__wrapper(const GSingletonCandidate *); /* Détermine si deux candidats à l'unicité sont identiques. */ static gboolean py_singleton_candidate___eq__wrapper(const GSingletonCandidate *, const GSingletonCandidate *); +/* Marque un candidat comme figé. */ +static void py_singleton_candidate_set_ro_wrapper(GSingletonCandidate *); + +/* Indique si le candidat est figé. */ +static bool py_singleton_candidate_is_ro_wrapper(GSingletonCandidate *); + /* Fournit l'empreinte d'un candidat à une centralisation. */ static PyObject *py_singleton_candidate_hash(PyObject *, PyObject *); +/* Fournit une liste de candidats embarqués par un candidat. */ +static PyObject *py_singleton_candidate_get_inner_instances(PyObject *, void *); + +/* Indique si le candidat est figé. */ +static PyObject *py_singleton_candidate_get_read_only(PyObject *, void *); + /* Effectue une comparaison avec un objet 'SingletonCandidate'. */ static PyObject *py_singleton_candidate_richcompare(PyObject *, PyObject *, int); @@ -92,8 +111,9 @@ static PyObject *py_singleton_factory_get_instance(PyObject *, PyObject *); static void py_singleton_candidate_interface_init(GSingletonCandidateIface *iface, gpointer *unused) { #define SINGLETON_CANDIDATE_DOC \ - "The SingletonCandidate class is a required interface for types aiming" \ - " at becoming singleton instances.\n" \ + "The SingletonCandidate class is a required interface for objects" \ + " aiming at becoming singleton instances. All shared singletons are" \ + " registered within a pychrysalide.glibext.SingletonFactory object.\n" \ "\n" \ "The main implemantations come with types derived from" \ " pychrysalide.analysis.DataType.\n" \ @@ -104,12 +124,188 @@ static void py_singleton_candidate_interface_init(GSingletonCandidateIface *ifac " ...\n" \ "\n" \ "The following methods have to be defined for new implementations:\n" \ + "* pychrysalide.glibext.SingletonCandidate._list_inner_instances();\n" \ + "* pychrysalide.glibext.SingletonCandidate._update_inner_instances();\n"\ "* pychrysalide.glibext.SingletonCandidate.__hash__();\n" \ - "* pychrysalide.glibext.SingletonCandidate.__eq__()." + "* pychrysalide.glibext.SingletonCandidate.__eq__();\n" \ + "* pychrysalide.glibext.SingletonCandidate._set_read_only();\n" \ + "* pychrysalide.glibext.SingletonCandidate._is_read_only().\n" + + iface->update_inner = py_singleton_candidate_update_inner_instances_wrapper; + iface->list_inner = py_singleton_candidate_list_inner_instances_wrapper; iface->hash = py_singleton_candidate___hash__wrapper; iface->is_equal = py_singleton_candidate___eq__wrapper; + iface->set_ro = py_singleton_candidate_set_ro_wrapper; + iface->is_ro = py_singleton_candidate_is_ro_wrapper; + +} + + +/****************************************************************************** +* * +* Paramètres : candidate = objet dont l'instance se veut unique. * +* count = quantité d'instances à l'unicité internes. * +* * +* Description : Fournit une liste de candidats embarqués par un candidat. * +* * +* Retour : Liste de candidats internes ou NULL si aucun. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static GSingletonCandidate **py_singleton_candidate_list_inner_instances_wrapper(const GSingletonCandidate *candidate, size_t *count) +{ + GSingletonCandidate **result; /* Instances à retourner */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *pyinstances; /* Liste en version Python */ + int ret; /* Bilan d'un appel */ + Py_ssize_t size; /* Taille de la liste */ + Py_ssize_t i; /* Boucle de parcours #1 */ + PyObject *pyinstance; /* Instance interne */ + Py_ssize_t k; /* Boucle de parcours #2 */ + +#define SINGLETON_CANDIDATE_LIST_INNER_INSTANCES_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _list_inner_instances, "$self, /", \ + METH_NOARGS, \ + "Provide an internal access to the list of optional internal singleton" \ + " candidate instances.\n" \ + "\n" \ + "The result has to be a tuple containing zero or more" \ + " pychrysalide.glibext.SingletonCandidate instances." \ +) + + result = NULL; + *count = 0; + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(candidate)); + + if (has_python_method(pyobj, "_list_inner_instances")) + { + pyinstances = run_python_method(pyobj, "_list_inner_instances", NULL); + + if (pyinstances != NULL) + { + ret = PyTuple_Check(pyinstances); + if (!ret) + { + PyErr_SetString(PyExc_TypeError, "the _inner_instances attribute must be a tuple"); + goto done; + } + + size = PyTuple_GET_SIZE(pyinstances); + + result = calloc(size, sizeof(GSingletonCandidate *)); + + for (i = 0; i < size; i++) + { + pyinstance = PyTuple_GET_ITEM(pyinstances, i); + + ret = PyObject_IsInstance(pyinstance, (PyObject *)get_python_singleton_candidate_type()); + if (ret != 1) + { + PyErr_SetString(PyExc_TypeError, "the _inner_instances attribute must only contain pychrysalide.glibext.SingletonCandidate instances"); + + for (k = 0; k < i; k++) + g_object_unref(G_OBJECT(result[k])); + + free(result); + result = NULL; + + goto done; + + } + + result[i] = G_SINGLETON_CANDIDATE(pygobject_get(pyinstance)); + assert(result[i] != NULL); + + g_object_ref(G_OBJECT(result[i])); + + } + + *count = size; + + done: + + Py_DECREF(pyinstances); + + } + + } + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : candidate = objet dont l'instance se veut unique. * +* instances = liste de candidats internes devenus singletons. * +* count = quantité d'instances à l'unicité internes. * +* * +* Description : Met à jour une liste de candidats embarqués par un candidat. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void py_singleton_candidate_update_inner_instances_wrapper(GSingletonCandidate *candidate, GSingletonCandidate **instances, size_t count) +{ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *pyinstances; /* Liste d'instances converties*/ + size_t i; /* Boucle de parcours */ + PyObject *pyret; /* Bilan de consultation */ + +#define SINGLETON_CANDIDATE_UPDATE_INNER_INSTANCES_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _update_inner_instances, "$self, instances, /", \ + METH_VARARGS, \ + "Update the list of internal singleton candidate instances.\n" \ + "\n" \ + "The provided *instances* are a tuple of pychrysalide.glibext.SingletonCandidate" \ + " objets promoted as singletons." \ +) + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(candidate)); + + if (has_python_method(pyobj, "_update_inner_instances")) + { + args = PyTuple_New(1); + + pyinstances = PyTuple_New(count); + PyTuple_SetItem(args, 0, pyinstances); + + for (i = 0; i < count; i++) + PyTuple_SetItem(pyinstances, i, pygobject_new(G_OBJECT(instances[i]))); + + pyret = run_python_method(pyobj, "_update_inner_instances", args); + + Py_XDECREF(pyret); + Py_DECREF(args); + + } + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + } @@ -132,13 +328,18 @@ static guint py_singleton_candidate___hash__wrapper(const GSingletonCandidate *c PyObject *pyobj; /* Objet Python concerné */ PyObject *pyret; /* Bilan de consultation */ -#define SINGLETON_CANDIDATE_HASH_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - __hash__, "$self, /", \ - METH_NOARGS, \ - "Abstract method used to produce a hash of the object.\n" \ - "\n" \ - "The result must be an integer value up to 64 bits." \ +#define SINGLETON_CANDIDATE_HASH_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + __hash__, "$self, /", \ + METH_NOARGS, \ + "Abstract method used to produce a hash of the object.\n" \ + "\n" \ + "The result must be an integer value up to 64 bits." \ + "\n" \ + "Inner instances which are listed through the" \ + " pychrysalide.glibext.SingletonCandidate._list_inner_instances()" \ + " method do not need to get processed here as they are handled" \ + " automatically by the interface core." \ ) result = 0; @@ -239,6 +440,111 @@ static gboolean py_singleton_candidate___eq__wrapper(const GSingletonCandidate * /****************************************************************************** * * +* Paramètres : candidate = objet dont l'instance se veut unique. * +* * +* Description : Marque un candidat comme figé. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void py_singleton_candidate_set_ro_wrapper(GSingletonCandidate *candidate) +{ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *pyret; /* Bilan de consultation */ + +#define SINGLETON_CANDIDATE_SET_RO_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _set_read_only, "$self, /", \ + METH_NOARGS, \ + "Abstract method used to mark the content of a singleton" \ + " candidate as read-only.\n" \ + "\n" \ + "The read-only state is mandatory once the candidate is" \ + " registered inside a pychrysalide.glibext.SingletonFactory"\ + " instance as official singleton." \ +) + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(candidate)); + + if (has_python_method(pyobj, "_set_read_only")) + { + pyret = run_python_method(pyobj, "_set_read_only", NULL); + + Py_XDECREF(pyret); + + } + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + +} + + +/****************************************************************************** +* * +* Paramètres : candidate = objet dont l'instance se veut unique. * +* * +* Description : Indique si le candidat est figé. * +* * +* Retour : true si le contenu du candidat ne peut plus être modifié. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool py_singleton_candidate_is_ro_wrapper(GSingletonCandidate *candidate) +{ + bool result; /* Etat à retourner */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *pyret; /* Bilan de consultation */ + +#define SINGLETON_CANDIDATE_IS_RO_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _is_read_only, "$self, /", \ + METH_NOARGS, \ + "Abstract method used to retrieve the status of the data" \ + " contained by a singleton candidate.\n" \ + "\n" \ + "The retured value is *True* if the candidate is" \ + " registered inside a pychrysalide.glibext.SingletonFactory"\ + " instance as official singleton, *False* otherwise." \ +) + + result = false; + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(candidate)); + + if (has_python_method(pyobj, "_is_read_only")) + { + pyret = run_python_method(pyobj, "_is_read_only", NULL); + + result = (pyret == Py_True); + + Py_XDECREF(pyret); + + } + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + + return result; + +} + + +/****************************************************************************** +* * * Paramètres : self = objet dont l'instance se veut unique. * * args = adresse non utilisée ici. * * * @@ -262,6 +568,10 @@ static PyObject *py_singleton_candidate_hash(PyObject *self, PyObject *args) METH_NOARGS, py_singleton_candidate, \ "Compute the hash value of the singleton candidate.\n" \ "\n" \ + "The method relies on the interface core to include in the" \ + " process the optional embedded instances which may become" \ + " singletons.\n" \ + "\n" \ "The result is an integer value.\n" \ "\n" \ "Even if the Python *hash()* method, relying on the" \ @@ -284,6 +594,99 @@ static PyObject *py_singleton_candidate_hash(PyObject *self, PyObject *args) /****************************************************************************** * * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Fournit une liste de candidats embarqués par un candidat. * +* * +* Retour : Liste de candidats internes, vide si aucun. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_singleton_candidate_get_inner_instances(PyObject *self, void *closure) +{ + PyObject *result; /* Valeur à retourner */ + GSingletonCandidate *candidate; /* Mécanismes natifs */ + size_t count; /* Quantité d'objets internes */ + GSingletonCandidate **instances; /* Liste des embarqués */ + size_t i; /* Boucle de parcours */ + +#define SINGLETON_CANDIDATE_INNER_INSTANCES_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + inner_instances, py_singleton_candidate, \ + "List of optional internal singleton candidate instances.\n" \ + "\n" \ + "The result has to be a tuple containing zero or more" \ + " pychrysalide.glibext.SingletonCandidate instances." \ +) + + candidate = G_SINGLETON_CANDIDATE(pygobject_get(self)); + + instances = g_singleton_candidate_list_inner_instances(candidate, &count); + + result = PyTuple_New(count); + + for (i = 0; i < count; i++) + { + PyTuple_SetItem(result, i, pygobject_new(G_OBJECT(instances[i]))); + g_object_unref(G_OBJECT(instances[i])); + } + + if (instances != NULL) + free(instances); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * +* Description : Indique si le candidat est figé. * +* * +* Retour : true si le contenu du candidat ne peut plus être modifié. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_singleton_candidate_get_read_only(PyObject *self, void *closure) +{ + PyObject *result; /* Valeur à retourner */ + GSingletonCandidate *candidate; /* Mécanismes natifs */ + bool status; /* Etat de l'élément consulté */ + +#define SINGLETON_CANDIDATE_READ_ONLY_ATTRIB PYTHON_GET_DEF_FULL \ +( \ + read_only, py_singleton_candidate, \ + "State of the singleton candidate content.\n" \ + "\n" \ + "The result is a boolean: *True* if the object is registered" \ + " as singleton, *False* otherwise.\n" \ + "\n" \ + "Once a singleton, the object must not change its content as" \ + " it is a shared instance." \ +) + + candidate = G_SINGLETON_CANDIDATE(pygobject_get(self)); + + status = g_singleton_candidate_is_read_only(candidate); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * * Paramètres : a = premier object Python à consulter. * * b = second object Python à consulter. * * op = type de comparaison menée. * @@ -300,8 +703,8 @@ static PyObject *py_singleton_candidate_richcompare(PyObject *a, PyObject *b, in { PyObject *result; /* Bilan à retourner */ int ret; /* Bilan de lecture des args. */ - const GSingletonCandidate *cand_a; /* Premier élément à traiter */ - const GSingletonCandidate *cand_b; /* Second élément à traiter */ + GSingletonCandidate *cand_a; /* Premier élément à traiter */ + GSingletonCandidate *cand_b; /* Second élément à traiter */ gboolean status; /* Résultat d'une comparaison */ if (op != Py_EQ) @@ -348,13 +751,19 @@ static PyObject *py_singleton_candidate_richcompare(PyObject *a, PyObject *b, in PyTypeObject *get_python_singleton_candidate_type(void) { static PyMethodDef py_singleton_candidate_methods[] = { + SINGLETON_CANDIDATE_LIST_INNER_INSTANCES_WRAPPER, + SINGLETON_CANDIDATE_UPDATE_INNER_INSTANCES_WRAPPER, SINGLETON_CANDIDATE_HASH_WRAPPER, SINGLETON_CANDIDATE_EQ_WRAPPER, + SINGLETON_CANDIDATE_SET_RO_WRAPPER, + SINGLETON_CANDIDATE_IS_RO_WRAPPER, SINGLETON_CANDIDATE_HASH_METHOD, { NULL } }; static PyGetSetDef py_singleton_candidate_getseters[] = { + SINGLETON_CANDIDATE_INNER_INSTANCES_ATTRIB, + SINGLETON_CANDIDATE_READ_ONLY_ATTRIB, { NULL } }; @@ -689,7 +1098,7 @@ PyTypeObject *get_python_singleton_factory_type(void) bool ensure_python_singleton_factory_is_registered(void) { - PyTypeObject *type; /* Type Python 'ObjectSingleton' */ + PyTypeObject *type; /* Type 'SingletonFactory' */ PyObject *module; /* Module à recompléter */ PyObject *dict; /* Dictionnaire du module */ diff --git a/src/glibext/singleton-int.h b/src/glibext/singleton-int.h index ac31a32..49cfecd 100644 --- a/src/glibext/singleton-int.h +++ b/src/glibext/singleton-int.h @@ -29,17 +29,23 @@ +/* Fournit une liste de candidats embarqués par un candidat. */ +typedef GSingletonCandidate ** (* list_inner_instances_fc) (const GSingletonCandidate *, size_t *); + +/* Met à jour une liste de candidats embarqués par un candidat. */ +typedef void (* update_inner_instances_fc) (GSingletonCandidate *, GSingletonCandidate **, size_t); + /* Fournit l'empreinte d'un candidat à une centralisation. */ typedef guint (* hash_candidate_fc) (const GSingletonCandidate *); /* Détermine si deux candidats à l'unicité sont identiques. */ typedef gboolean (* is_candidate_equal_fc) (const GSingletonCandidate *, const GSingletonCandidate *); -/* Marque un candidat comme traité ou en cours de traitement. */ -typedef void (* mark_candidate_as_processed_fc) (GSingletonCandidate *, bool); +/* Marque un candidat comme figé. */ +typedef void (* set_candidate_ro_fc) (GSingletonCandidate *); -/* Indique si un objet marqué comme unique. */ -typedef bool (* is_candidate_processed_fc) (const GSingletonCandidate *, bool); +/* Indique si le candidat est figé. */ +typedef bool (* is_candidate_ro_fc) (GSingletonCandidate *); /* Instance d'objet visant à être unique (interface) */ @@ -47,11 +53,14 @@ struct _GSingletonCandidateIface { GTypeInterface base_iface; /* A laisser en premier */ + list_inner_instances_fc list_inner; /* Récupération d'internes */ + update_inner_instances_fc update_inner; /* Mise à jour des éléments */ + hash_candidate_fc hash; /* Prise d'empreinte */ is_candidate_equal_fc is_equal; /* Comparaison */ - mark_candidate_as_processed_fc mark; /* Définition de l'état */ - is_candidate_processed_fc is_processed; /* Consultation de l'état */ + set_candidate_ro_fc set_ro; /* Bascule en mode figé */ + is_candidate_ro_fc is_ro; /* Consultation de l'état */ }; diff --git a/src/glibext/singleton.c b/src/glibext/singleton.c index f0ce86f..bcd5580 100644 --- a/src/glibext/singleton.c +++ b/src/glibext/singleton.c @@ -36,6 +36,18 @@ /* Procède à l'initialisation de l'interface de rassemblement. */ static void g_singleton_candidate_default_init(GSingletonCandidateInterface *); +/* Met à jour une liste de candidats embarqués par un candidat. */ +static void g_singleton_candidate_update_inner_instances(GSingletonCandidate *, GSingletonCandidate **, size_t); + +/* Fournit l'empreinte d'un candidat à une centralisation. */ +static guint _g_singleton_candidate_hash(GSingletonCandidate *, GList **); + +/* Détermine si deux candidats à l'unicité sont identiques. */ +static gboolean _g_singleton_candidate_is_equal(GSingletonCandidate *, GSingletonCandidate *, GList **); + +/* Marque un candidat comme figé. */ +static void _g_singleton_candidate_set_read_only(GSingletonCandidate *, GList **); + /* ------------------------- COLLECTION D'INSTANCES UNIQUES ------------------------- */ @@ -103,6 +115,73 @@ static void g_singleton_candidate_default_init(GSingletonCandidateInterface *ifa /****************************************************************************** * * * Paramètres : candidate = objet dont l'instance se veut unique. * +* count = quantité d'instances à l'unicité internes. * +* * +* Description : Fournit une liste de candidats embarqués par un candidat. * +* * +* Retour : Liste de candidats internes ou NULL si aucun. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GSingletonCandidate **g_singleton_candidate_list_inner_instances(const GSingletonCandidate *candidate, size_t *count) +{ + GSingletonCandidate **result; /* Instances à retourner */ + GSingletonCandidateIface *iface; /* Interface utilisée */ + + iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate); + + if (iface->list_inner == NULL) + { + *count = 0; + result = NULL; + } + + else + result = iface->list_inner(candidate, count); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : candidate = objet dont l'instance se veut unique. * +* instances = liste de candidats internes devenus singletons. * +* count = quantité d'instances à l'unicité internes. * +* * +* Description : Met à jour une liste de candidats embarqués par un candidat. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_singleton_candidate_update_inner_instances(GSingletonCandidate *candidate, GSingletonCandidate **instances, size_t count) +{ + GSingletonCandidateIface *iface; /* Interface utilisée */ + + iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate); + + if (iface->update_inner == NULL) + assert(iface->list_inner == NULL); + + else + { + assert(iface->list_inner != NULL); + iface->update_inner(candidate, instances, count); + } + +} + + +/****************************************************************************** +* * +* Paramètres : candidate = objet dont l'instance se veut unique. * +* processed = liste de candidats déjà traités. * * * * Description : Fournit l'empreinte d'un candidat à une centralisation. * * * @@ -112,14 +191,70 @@ static void g_singleton_candidate_default_init(GSingletonCandidateInterface *ifa * * ******************************************************************************/ -guint g_singleton_candidate_hash(const GSingletonCandidate *candidate) +static guint _g_singleton_candidate_hash(GSingletonCandidate *candidate, GList **processed) { guint result; /* Valeur à retourner */ + GList *skip; /* Détection de boucle */ GSingletonCandidateIface *iface; /* Interface utilisée */ + GSingletonCandidate **children; /* Instances internes */ + size_t count; /* Quantité de ces instances */ + size_t i; /* Boucle de parcours */ - iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate); + skip = g_list_find(*processed, candidate); + + if (skip != NULL) + result = 0; + + else + { + iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate); + + result = iface->hash(candidate); + + *processed = g_list_append(*processed, candidate); + + children = g_singleton_candidate_list_inner_instances(candidate, &count); + + for (i = 0; i < count; i++) + { + result ^= _g_singleton_candidate_hash(children[i], processed); + g_object_unref(G_OBJECT(children[i])); + } + + if (children != NULL) + free(children); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : candidate = objet dont l'instance se veut unique. * +* * +* Description : Fournit l'empreinte d'un candidat à une centralisation. * +* * +* Retour : Empreinte de l'élément représenté. * +* * +* Remarques : - * +* * +******************************************************************************/ + +guint g_singleton_candidate_hash(GSingletonCandidate *candidate) +{ + guint result; /* Valeur à retourner */ + GList *processed; /* Suivi des traitements */ + + processed = NULL; + + result = _g_singleton_candidate_hash(candidate, &processed); - result = iface->hash(candidate); + assert(processed != NULL); + + g_list_free(processed); return result; @@ -130,6 +265,7 @@ guint g_singleton_candidate_hash(const GSingletonCandidate *candidate) * * * Paramètres : candidate = objet dont l'instance se veut unique. * * other = second élément à analyser. * +* processed = liste de candidats déjà traités. * * * * Description : Détermine si deux candidats à l'unicité sont identiques. * * * @@ -139,27 +275,117 @@ guint g_singleton_candidate_hash(const GSingletonCandidate *candidate) * * ******************************************************************************/ -gboolean g_singleton_candidate_is_equal(const GSingletonCandidate *candidate, const GSingletonCandidate *other) +static gboolean _g_singleton_candidate_is_equal(GSingletonCandidate *candidate, GSingletonCandidate *other, GList **processed) { gboolean result; /* Bilan à renvoyer */ + GList *skip; /* Détection de boucle */ GSingletonCandidateIface *iface; /* Interface utilisée */ + GSingletonCandidate **children[2]; /* Instances internes */ + size_t count[2]; /* Quantité de ces instances */ + size_t i; /* Boucle de parcours */ - iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate); + skip = g_list_find(processed[0], candidate); + + if (skip != NULL) + result = (g_list_find(processed[1], other) != NULL); + + else + { + iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate); + + result = iface->is_equal(candidate, other); + + processed[0] = g_list_append(processed[0], candidate); + processed[1] = g_list_append(processed[1], other); + + if (!result) + goto done; + + children[0] = g_singleton_candidate_list_inner_instances(candidate, &count[0]); + children[1] = g_singleton_candidate_list_inner_instances(other, &count[1]); + + if (count[0] != count[1]) + { + for (i = 0; i < count[0]; i++) + g_object_unref(G_OBJECT(children[0][i])); - result = iface->is_equal(candidate, other); + for (i = 0; i < count[1]; i++) + g_object_unref(G_OBJECT(children[1][i])); + + } + + else + { + for (i = 0; i < count[0] && result; i++) + { + result = _g_singleton_candidate_is_equal(children[0][i], children[1][i], processed); + g_object_unref(G_OBJECT(children[0][i])); + g_object_unref(G_OBJECT(children[1][i])); + } + + for (; i < count[0]; i++) + { + g_object_unref(G_OBJECT(children[0][i])); + g_object_unref(G_OBJECT(children[1][i])); + } + + if (children[0] != NULL) + free(children[0]); + + if (children[1] != NULL) + free(children[1]); + + } + + } + + done: return result; } +/****************************************************************************** +* * +* Paramètres : candidate = objet dont l'instance se veut unique. * +* other = second élément à analyser. * +* * +* Description : Détermine si deux candidats à l'unicité sont identiques. * +* * +* Retour : Bilan de la comparaison. * +* * +* Remarques : - * +* * +******************************************************************************/ + +gboolean g_singleton_candidate_is_equal(GSingletonCandidate *candidate, GSingletonCandidate *other) +{ + gboolean result; /* Bilan à renvoyer */ + GList *processed[2]; /* Suivi des traitements */ + + processed[0] = NULL; + processed[1] = NULL; + + result = _g_singleton_candidate_is_equal(candidate, other, processed); + + assert(processed[0] != NULL); + assert(processed[1] != NULL); + + g_list_free(processed[0]); + g_list_free(processed[1]); + + return result; + +} + /****************************************************************************** * * * Paramètres : candidate = objet dont l'instance se veut unique. * -* soon = indique un traitement démarré et en cours. * +* processed = liste de candidats déjà traités. * * * -* Description : Marque un candidat comme traité ou en cours de traitement. * +* Description : Marque un candidat comme figé. * * * * Retour : - * * * @@ -167,13 +393,36 @@ gboolean g_singleton_candidate_is_equal(const GSingletonCandidate *candidate, co * * ******************************************************************************/ -void g_singleton_candidate_mark_as_processed(GSingletonCandidate *candidate, bool soon) +static void _g_singleton_candidate_set_read_only(GSingletonCandidate *candidate, GList **processed) { + GList *skip; /* Détection de boucle */ GSingletonCandidateIface *iface; /* Interface utilisée */ + GSingletonCandidate **children; /* Instances internes */ + size_t count; /* Quantité de ces instances */ + size_t i; /* Boucle de parcours */ - iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate); + skip = g_list_find(*processed, candidate); + + if (skip == NULL) + { + iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate); + + iface->set_ro(candidate); + + *processed = g_list_append(*processed, candidate); + + children = g_singleton_candidate_list_inner_instances(candidate, &count); - iface->mark(candidate, soon); + for (i = 0; i < count; i++) + { + _g_singleton_candidate_set_read_only(candidate, processed); + g_object_unref(G_OBJECT(children[i])); + } + + if (children != NULL) + free(children); + + } } @@ -181,28 +430,53 @@ void g_singleton_candidate_mark_as_processed(GSingletonCandidate *candidate, boo /****************************************************************************** * * * Paramètres : candidate = objet dont l'instance se veut unique. * -* soon = indique un traitement démarré et en cours. * * * -* Description : Indique si un objet marqué comme unique. * +* Description : Marque un candidat comme figé. * * * -* Retour : true si l'objet est traité ou en phase de l'être, ou false. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -bool g_singleton_candidate_is_processed(const GSingletonCandidate *candidate, bool soon) +void g_singleton_candidate_set_read_only(GSingletonCandidate *candidate) { - bool result; /* Statut à retourner */ + GList *processed; /* Suivi des traitements */ + + processed = NULL; + + _g_singleton_candidate_set_read_only(candidate, &processed); + + assert(processed != NULL); + + g_list_free(processed); + +} + + +/****************************************************************************** +* * +* Paramètres : candidate = objet dont l'instance se veut unique. * +* * +* Description : Indique si le candidat est figé. * +* * +* Retour : true si le contenu du candidat ne peut plus être modifié. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_singleton_candidate_is_read_only(GSingletonCandidate *candidate) +{ + bool result; /* Etat à retourner */ GSingletonCandidateIface *iface; /* Interface utilisée */ iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate); - result = iface->is_processed(candidate, soon); + result = iface->is_ro(candidate); return result; - } @@ -348,10 +622,48 @@ GSingletonFactory *g_singleton_factory_new(void) GSingletonCandidate *g_singleton_factory_get_instance(GSingletonFactory *factory, GSingletonCandidate *candidate) { GSingletonCandidate *result; /* Instance unique à retourner */ + size_t count; /* Quantité d'objets internes */ + GSingletonCandidate **instances; /* Liste d'instances internes */ + GSingletonCandidate **updated; /* Nouvelle liste d'instances */ + bool need_update; /* Mise à jour nécessaire */ + size_t i; /* Boucle de parcours */ #ifndef NDEBUG - gboolean status; /* Validation d'une opération */ + gboolean status; /* Validation d'une opération */ #endif + /* Validation des objets internes éventuels */ + + instances = g_singleton_candidate_list_inner_instances(candidate, &count); + + if (count > 0) + { + updated = malloc(count * sizeof(GSingletonCandidate *)); + need_update = false; + + for (i = 0; i < count; i++) + { + updated[i] = g_singleton_factory_get_instance(factory, instances[i]); + need_update |= (instances[i] != updated[i]); + } + + if (need_update) + g_singleton_candidate_update_inner_instances(candidate, updated, count); + + for (i = 0; i < count; i++) + { + g_object_unref(G_OBJECT(updated[i])); + g_object_unref(G_OBJECT(instances[i])); + } + + free(updated); + + } + + if (instances != NULL) + free(instances); + + /* Récupération de l'instance principale */ + g_mutex_lock(&factory->access); if (g_hash_table_contains(factory->table, candidate)) @@ -376,6 +688,8 @@ GSingletonCandidate *g_singleton_factory_get_instance(GSingletonFactory *factory g_hash_table_add(factory->table, candidate); #endif + g_singleton_candidate_set_read_only(candidate); + result = candidate; } diff --git a/src/glibext/singleton.h b/src/glibext/singleton.h index 6de9f41..0561f80 100644 --- a/src/glibext/singleton.h +++ b/src/glibext/singleton.h @@ -30,6 +30,11 @@ +/* Définition d'un compacteur d'instances de types (instance) */ +typedef struct _GSingletonFactory GSingletonFactory; + + + /* ------------------ INTERFACE POUR CANDIDAT A UNE CENTRALISATION ------------------ */ @@ -51,17 +56,20 @@ typedef struct _GSingletonCandidateIface GSingletonCandidateIface; /* Détermine le type d'une interface pour la lecture de binaire. */ GType g_singleton_candidate_get_type(void) G_GNUC_CONST; +/* Fournit une liste de candidats embarqués par un candidat. */ +GSingletonCandidate **g_singleton_candidate_list_inner_instances(const GSingletonCandidate *, size_t *); + /* Fournit l'empreinte d'un candidat à une centralisation. */ -guint g_singleton_candidate_hash(const GSingletonCandidate *); +guint g_singleton_candidate_hash(GSingletonCandidate *); /* Détermine si deux candidats à l'unicité sont identiques. */ -gboolean g_singleton_candidate_is_equal(const GSingletonCandidate *, const GSingletonCandidate *); +gboolean g_singleton_candidate_is_equal(GSingletonCandidate *, GSingletonCandidate *); -/* Marque un candidat comme traité ou en cours de traitement. */ -void g_singleton_candidate_mark_as_processed(GSingletonCandidate *, bool); +/* Marque un candidat comme figé. */ +void g_singleton_candidate_set_read_only(GSingletonCandidate *); -/* Indique si un objet marqué comme unique. */ -bool g_singleton_candidate_is_processed(const GSingletonCandidate *, bool); +/* Indique si le candidat est figé. */ +bool g_singleton_candidate_is_read_only(GSingletonCandidate *); @@ -76,9 +84,6 @@ bool g_singleton_candidate_is_processed(const GSingletonCandidate *, bool); #define G_SINGLETON_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_SINGLETON_FACTORY, GSingletonFactoryClass)) -/* Définition d'un compacteur d'instances de types (instance) */ -typedef struct _GSingletonFactory GSingletonFactory; - /* Définition d'un compacteur d'instances de types (classe) */ typedef struct _GSingletonFactoryClass GSingletonFactoryClass; diff --git a/tests/glibext/singleton.py b/tests/glibext/singleton.py index b0608f0..4588ae5 100644 --- a/tests/glibext/singleton.py +++ b/tests/glibext/singleton.py @@ -47,12 +47,15 @@ class TestSingleton(ChrysalideTestCase): super().__init__() self._val = val - def __eq__(self, other): - return self._val == other._val + def _list_inner_instances(self): + return () def __hash__(self): return hash('common-key') + def __eq__(self, other): + return self._val == other._val + val_0 = IntegerCacheImplem(0) val_0_bis = IntegerCacheImplem(0) val_1 = IntegerCacheImplem(1) @@ -70,24 +73,31 @@ class TestSingleton(ChrysalideTestCase): def testSingletonFootprint(self): """Check for singleton memory footprint.""" + sf = SingletonFactory() + + class IntegerCacheImplem(GObject.Object, SingletonCandidate): def __init__(self, val): super().__init__() self._val = val - def __eq__(self, other): - return self._val == other._val + def _list_inner_instances(self): + return () def __hash__(self): return hash('common-key') + def __eq__(self, other): + return self._val == other._val + + def _set_read_only(self): + pass + val_0 = IntegerCacheImplem(0) val_0_bis = IntegerCacheImplem(0) val_1 = IntegerCacheImplem(1) - sf = SingletonFactory() - obj = sf.get_instance(val_0) self.assertTrue(obj is val_0) @@ -99,3 +109,35 @@ class TestSingleton(ChrysalideTestCase): obj = sf.get_instance(val_1) self.assertTrue(obj is val_1) + + self.assertEqual(len(obj.inner_instances), 0) + + + class MasterCacheImplem(GObject.Object, SingletonCandidate): + + def __init__(self, children): + super().__init__() + self._children = children + + def _list_inner_instances(self): + return self._children + + def _update_inner_instances(self, instances): + self._children = instances + + def __hash__(self): + return hash('master-key') + + def __eq__(self, other): + return False + + def _set_read_only(self): + pass + + master = MasterCacheImplem(( val_0_bis, val_1 )) + + obj = sf.get_instance(master) + + self.assertTrue(obj.inner_instances[0] is val_0) + + self.assertTrue(obj.inner_instances[1] is val_1) -- cgit v0.11.2-87-g4458