summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--plugins/pychrysalide/glibext/singleton.c435
-rw-r--r--src/glibext/singleton-int.h21
-rw-r--r--src/glibext/singleton.c352
-rw-r--r--src/glibext/singleton.h23
-rw-r--r--tests/glibext/singleton.py54
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 <assert.h>
#include <pygobject.h>
@@ -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)