/* 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