/* Chrysalide - Outil d'analyse de fichiers binaires
* manager.c - collecte et gestion des instances partagées
*
* Copyright (C) 2016-2017 Cyrille Bagard
*
* This file is part of Chrysalide.
*
* Chrysalide 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; either version 3 of the License, or
* (at your option) any later version.
*
* Chrysalide 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 Foobar. If not, see .
*/
#include "manager.h"
#include
#include
#ifdef DEBUG_DUMP_STATS
# include
#endif
#include
#include "../../common/sort.h"
/* Gestionnaire d'instances de type identique partagées (instance) */
struct _GShareManager
{
GObject parent; /* A laisser en premier */
GSharedInstance **instances; /* Instances partagées */
size_t count; /* Quantité de ces instances */
GMutex access; /* Accès à la table */
GType managed; /* Type d'instances gérées */
};
/* Gestionnaire d'instances de type identique partagées (classe) */
struct _GShareManagerClass
{
GObjectClass parent; /* A laisser en premier */
};
/* Procède à l'initialisation d'une classe de suivi de largeurs. */
static void g_share_manager_class_init(GShareManagerClass *);
/* Procède à l'initialisation d'un suivi de largeurs de lignes. */
static void g_share_manager_init(GShareManager *);
/* Supprime toutes les références externes. */
static void g_share_manager_dispose(GShareManager *);
/* Procède à la libération totale de la mémoire. */
static void g_share_manager_finalize(GShareManager *);
/* Détermine le type du gestionnaire d'instances partagées. */
G_DEFINE_TYPE(GShareManager, g_share_manager, G_TYPE_OBJECT);
/******************************************************************************
* *
* Paramètres : class = classe de composant GTK à initialiser. *
* *
* Description : Procède à l'initialisation d'une classe de suivi de largeurs.*
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_share_manager_class_init(GShareManagerClass *class)
{
GObjectClass *object; /* Autre version de la classe */
object = G_OBJECT_CLASS(class);
object->dispose = (GObjectFinalizeFunc/* ! */)g_share_manager_dispose;
object->finalize = (GObjectFinalizeFunc)g_share_manager_finalize;
}
/******************************************************************************
* *
* Paramètres : manager = composant GLib à initialiser. *
* *
* Description : Procède à l'initialisation d'un suivi de largeurs de lignes. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_share_manager_init(GShareManager *manager)
{
manager->instances = NULL;
manager->count = 0;
g_mutex_init(&manager->access);
}
/******************************************************************************
* *
* Paramètres : manager = instance d'objet GLib à traiter. *
* *
* Description : Supprime toutes les références externes. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_share_manager_dispose(GShareManager *manager)
{
size_t i; /* Boucle de parcours */
for (i = 0; i < manager->count; i++)
g_object_unref(G_OBJECT(manager->instances[i]));
g_mutex_clear(&manager->access);
G_OBJECT_CLASS(g_share_manager_parent_class)->dispose(G_OBJECT(manager));
}
/******************************************************************************
* *
* Paramètres : manager = instance d'objet GLib à traiter. *
* *
* Description : Procède à la libération totale de la mémoire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_share_manager_finalize(GShareManager *manager)
{
if (manager->instances != NULL)
free(manager->instances);
G_OBJECT_CLASS(g_share_manager_parent_class)->finalize(G_OBJECT(manager));
}
/******************************************************************************
* *
* Paramètres : type = type d'instances encadrées ici. *
* *
* Description : Crée un nouveau gestionnaire d'instances partagées. *
* *
* Retour : Composant GLib créé. *
* *
* Remarques : - *
* *
******************************************************************************/
GShareManager *g_share_manager_new(GType type)
{
GShareManager *result; /* Gestionnaire à renvoyer */
result = g_object_new(G_TYPE_SHARE_MANAGER, NULL);
result->managed = type;
return result;
}
/******************************************************************************
* *
* Paramètres : manager = gestionnaire d'instance à consulter. *
* template = informations à retrouver intégralement. *
* *
* Description : Retrouve ou crée une instance partagée. *
* *
* Retour : Instance existante déjà partagée ou nouvellement créée. *
* *
* Remarques : - *
* *
******************************************************************************/
GSharedInstance *g_share_manager_get(GShareManager *manager, GSharedInstance *template)
{
GSharedInstance *result; /* Trouvaille à retourner */
size_t index; /* Indice d'une instance idéale*/
bool found; /* Existence de cette instance */
bool status; /* Conclusion d'initialisation */
g_mutex_lock(&manager->access);
found = bsearch_index(&template, manager->instances, manager->count, sizeof(GSharedInstance *),
(__compar_fn_t)g_shared_instance_quickly_compare, &index);
if (!found)
{
result = g_object_new(manager->managed, NULL);
status = g_shared_instance_init(result, template);
if (!status)
{
g_object_unref(result);
result = NULL;
}
else
{
g_shared_instance_inc_references(result);
manager->instances = _qinsert(manager->instances, &manager->count,
sizeof(GSharedInstance *), &result, index);
}
}
else
result = manager->instances[index];
if (result != NULL)
{
g_object_ref(G_OBJECT(result));
g_shared_instance_inc_references(result);
}
g_mutex_unlock(&manager->access);
return result;
}
/******************************************************************************
* *
* Paramètres : manager = gestionnaire d'instance à consulter. *
* old = ancienne instance partagée à faire évoluer. *
* template = informations à retrouver intégralement. *
* container = propriétaire de l'ancienne version à contacter. *
* *
* Description : Met à jour une instance partagée. *
* *
* Retour : Instance existante déjà partagée ou nouvellement créée. *
* *
* Remarques : - *
* *
******************************************************************************/
GSharedInstance *g_share_manager_update(GShareManager *manager, GSharedInstance *old, GSharedInstance *template, GShareContainer *container)
{
GSharedInstance *result; /* Nouvelle instance à renvoyer*/
bool replaced; /* Remplacement effectué ? */
result = g_share_manager_get(manager, template);
if (container != NULL)
replaced = g_share_container_replace(container, old, result);
else
replaced = false;
if (replaced)
g_share_manager_put(manager, old);
else
{
g_share_manager_put(manager, result);
result = old;
}
return result;
}
/******************************************************************************
* *
* Paramètres : manager = gestionnaire d'instance à consulter. *
* instance = instance partagée vouée à évoluer. *
* *
* Description : Prépare une modification légère et locale d'un élément. *
* *
* Retour : Indice de l'élément prêt à être modifié. *
* *
* Remarques : - *
* *
******************************************************************************/
size_t g_share_manager_prepare_light_update(GShareManager *manager, GSharedInstance *instance)
{
size_t result; /* Indice courant à retourner */
#ifndef NDEBUG
bool found; /* Validation de présence */
#endif
g_mutex_lock(&manager->access);
#ifndef NDEBUG
found = bsearch_index(&instance, manager->instances, manager->count,sizeof(GSharedInstance *),
(__compar_fn_t)g_shared_instance_quickly_compare, &result);
assert(found);
#else
bsearch_index(&instance, manager->instances, manager->count,sizeof(GSharedInstance *),
(__compar_fn_t)g_shared_instance_quickly_compare, &result);
#endif
return result;
}
/******************************************************************************
* *
* Paramètres : manager = gestionnaire d'instance à consulter. *
* instance = instance partagée vouée à évoluer. *
* index = indice de la place de l'élément avant modifcation.*
* *
* Description : Conclut une modification légère et locale d'un élément. *
* *
* Retour : Indice de l'élément prêt à être modifié. *
* *
* Remarques : - *
* *
******************************************************************************/
GSharedInstance *g_share_manager_complete_light_update(GShareManager *manager, GSharedInstance *instance, size_t index)
{
GSharedInstance *result; /* Nouvelle instance à renvoyer*/
manager->instances = _qdelete(manager->instances, &manager->count,
sizeof(GSharedInstance *), index);
g_mutex_unlock(&manager->access);
result = g_share_manager_get(manager, instance);
g_object_unref(G_OBJECT(instance));
return result;
}
/******************************************************************************
* *
* Paramètres : manager = gestionnaire d'instance à consulter. *
* shared = instance partagée à libérer. *
* *
* Description : Abandonne un usage d'une instance partagée. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_share_manager_put(GShareManager *manager, GSharedInstance *shared)
{
g_mutex_lock(&manager->access);
g_shared_instance_dec_references(shared);
if (g_shared_instance_get_references(shared) == 1)
{
manager->instances = qdelete(manager->instances, &manager->count,
sizeof(GSharedInstance *),
(__compar_fn_t)g_shared_instance_quickly_compare,
&shared);
g_object_unref(G_OBJECT(shared));
}
g_mutex_unlock(&manager->access);
}
/******************************************************************************
* *
* Paramètres : manager = gestionnaire d'instance à consulter. *
* *
* Description : Imprime des statistiques d'utilisation du gestionnaire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
#ifdef DEBUG_DUMP_STATS
void g_share_manager_dump_stats(GShareManager *manager)
{
unsigned int counter; /* Quantité nécessaire */
size_t i; /* Boucle de parcours */
GTypeQuery query; /* Informations sur un type */
counter = 0;
g_mutex_lock(&manager->access);
for (i = 0; i < manager->count; i++)
counter += g_shared_instance_get_references(manager->instances[i]);
g_mutex_unlock(&manager->access);
g_type_query(manager->managed, &query);
printf("%s: current = %zu / %zu - needed = %u / %u (size=%u, saved=%zu)\n",
query.type_name,
manager->count, manager->count * query.instance_size,
counter, counter * query.instance_size,
query.instance_size,
(counter - manager->count) * query.instance_size);
}
#endif