/* Chrysalide - Outil d'analyse de fichiers binaires * ranks.c - calcul de l'ordonnée des différents classements * * Copyright (C) 2013 Cyrille Bagard * * This file is part of Chrysalide. * * OpenIDA 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. * * OpenIDA 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 "ranks.h" #include #include #include "params.h" /* ---------------------- MANIPULATION DES PORTES HORIZONTALES ---------------------- */ /* Etendue horizontale d'un lien */ typedef struct _reserved_hspan { gint x1; /* Départ d'une ligne */ gint x2; /* Arrivée de cette même ligne */ } reserved_hspan; /* Lignes de liens d'un même niveau */ typedef struct _links_spans { reserved_hspan *hspans; /* Lignes sans chevauchement */ size_t count; /* Nombre de ces lignes */ } links_spans; /* Initialise les futures portées d'un même niveau. */ static void init_links_spans(links_spans *); /* Regarde si des portées horizontales se chevauchent ou non. */ static bool is_intersection_with_spans(const links_spans *, const reserved_hspan *); /* Ajoute une réservation à un ensemble de portées horizontales. */ static void extend_links_spans(links_spans *, const reserved_hspan *); /* ------------------------ GESTION D'UN ETAGE DE CLASSEMENT ------------------------ */ /* Information relatives à un rang de classement */ typedef struct _rank_props { links_spans *top_spans; /* Niveaux de lignes supérieurs*/ hspan_slot_t top_count; /* Nbre des niveaux de lignes */ links_spans *bottom_spans; /* Niveaux de lignes inférieurs*/ hspan_slot_t bottom_count; /* Nbre des niveaux de lignes */ gint height; /* Hauteur minimale */ gint y; /* Ordonnée finale */ } rank_props; /* Réserve une portion de hauteur pour un lien. */ static hspan_slot_t reserve_span_in_rank_props(rank_props *, gint, gint, bool); /* Calcule la position verticale du rang de classement. */ static gint compute_rank_props_y_pos(rank_props *, gint *, gint); /* Fournit la position verticale obtenue par réservation. */ static gint get_y_for_rank_props_hspan_slot(const rank_props *, hspan_slot_t, bool); /* ------------------------- CLASSEMENT VERTICAL DES NOEUDS ------------------------- */ /* Calcul de l'ordonnée des différents classements (instance) */ struct _GGraphRanks { GObject parent; /* A laisser en premier */ rank_props *props; /* Propriétés des rangs */ unsigned int count; /* Nombre de rangs présents */ gint height; /* Hauteur totale du graphique */ }; /* Calcul de l'ordonnée des différents classements (classe) */ struct _GGraphRanksClass { GObjectClass parent; /* A laisser en premier */ }; /* Initialise la classe de calcul de l'ordonnée des classements. */ static void g_graph_ranks_class_init(GGraphRanksClass *); /* Initialise un calcul de l'ordonnée des classements. */ static void g_graph_ranks_init(GGraphRanks *); /* Supprime toutes les références externes. */ static void g_graph_ranks_dispose(GGraphRanks *); /* Procède à la libération totale de la mémoire. */ static void g_graph_ranks_finalize(GGraphRanks *); /* ---------------------------------------------------------------------------------- */ /* MANIPULATION DES PORTES HORIZONTALES */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : spans = liste à initialiser. * * * * Description : Initialise les futures portées d'un même niveau. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void init_links_spans(links_spans *spans) { spans->hspans = NULL; spans->count = 0; } /****************************************************************************** * * * Paramètres : spans = liste de portées déjà en place. * * hspan = nouvelle portée à insérer quelque part. * * * * Description : Regarde si des portées horizontales se chevauchent ou non. * * * * Retour : true si les portées peuvent cohabiter, false sinon. * * * * Remarques : - * * * ******************************************************************************/ static bool is_intersection_with_spans(const links_spans *spans, const reserved_hspan *hspan) { bool result; /* Bilan d'analyse à retourner */ size_t i; /* Boucle de parcours */ result = false; for (i = 0; i < spans->count && !result; i++) { if (hspan->x1 < spans->hspans[i].x1) result = hspan->x2 >= spans->hspans[i].x1; else if (hspan->x1 > spans->hspans[i].x2) result = hspan->x2 <= spans->hspans[i].x2; else { result = spans->hspans[i].x1 < hspan->x1 && hspan->x1 < spans->hspans[i].x2; result |= spans->hspans[i].x1 < hspan->x2 && hspan->x2 < spans->hspans[i].x2; } } return result; } /****************************************************************************** * * * Paramètres : spans = liste de portées déjà en place. * * hspan = nouvelle portée à insérer quelque part. * * * * Description : Ajoute une réservation à un ensemble de portées horizontales.* * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void extend_links_spans(links_spans *spans, const reserved_hspan *new) { spans->hspans = (reserved_hspan *)realloc(spans->hspans, ++spans->count * sizeof(reserved_hspan)); if (new->x1 <= new->x2) spans->hspans[spans->count - 1] = *new; else { spans->hspans[spans->count - 1].x1 = new->x2; spans->hspans[spans->count - 1].x2 = new->x1; } } /* ---------------------------------------------------------------------------------- */ /* GESTION D'UN ETAGE DE CLASSEMENT */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : props = propriétés de rang à mettre à jour. * * * * Description : Supprime les portions de hauteur pour un lien. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void reset_spans_in_rank_props(rank_props *props) { hspan_slot_t i; /* Boucle de parcours */ for (i = 0; i < props->top_count; i++) if (props->top_spans[i].hspans != NULL) free(props->top_spans[i].hspans); if (props->top_spans != NULL) { free(props->top_spans); props->top_spans = NULL; props->top_count = 0; } for (i = 0; i < props->bottom_count; i++) if (props->bottom_spans[i].hspans != NULL) free(props->bottom_spans[i].hspans); if (props->bottom_spans != NULL) { free(props->bottom_spans); props->bottom_spans = NULL; props->bottom_count = 0; } props->y = 0; props->height = 0; } /****************************************************************************** * * * Paramètres : props = propriétés de rang à mettre à jour. * * x1 = début de la portée à réserver. * * x2 = fin de la portée à réserver. * * top = désignation de la zone à traiter. * * * * Description : Réserve une portion de hauteur pour un lien. * * * * Retour : Indice de la hauteur adaptée et réservée. * * * * Remarques : - * * * ******************************************************************************/ static hspan_slot_t reserve_span_in_rank_props(rank_props *props, gint x1, gint x2, bool top) { hspan_slot_t result; /* Indice à retourner */ reserved_hspan hspan; /* Portée à constituer */ links_spans **list; /* Liste à parcourir */ hspan_slot_t *count; /* Taille de cette liste */ hspan.x1 = x1; hspan.x2 = x2; if (top) { list = &props->top_spans; count = &props->top_count; } else { list = &props->bottom_spans; count = &props->bottom_count; } for (result = 0; result < *count; result++) if (!is_intersection_with_spans(&(*list)[result], &hspan)) break; if (result == *count) { *list = (links_spans *)realloc(*list, ++(*count) * sizeof(links_spans)); init_links_spans(&(*list)[result]); } extend_links_spans(&(*list)[result], &hspan); return result; } /****************************************************************************** * * * Paramètres : props = propriétés de rang à mettre à jour. * * y = ordonnée courante à faire évoluer. * * last_bottom = espace inférieur requis du niveau précédent. * * * * Description : Calcule la position verticale du rang de classement. * * * * Retour : Espace inférieur nécessaire au niveau de classement. * * * * Remarques : - * * * ******************************************************************************/ static gint compute_rank_props_y_pos(rank_props *props, gint *y, gint last_bottom) { gint result; /* Espace Sud à retourner */ gint top_margin; /* Espace Nord nécessaire */ /* Espace supérieur pour les liens */ top_margin = last_bottom; if (props->top_count > 0) { top_margin += props->top_count * EDGE_VERTICAL_MIN_SEP; top_margin += EDGE_SLOT_VERT_MARGIN; } /** * L'espace vertical entre les noeuds n'a lieu d'être qu'en présence de noeuds ! */ top_margin = MAX(top_margin, (*y > 0 && props->height > 0 ? NODE_VERTICAL_MARGIN : 0)); /* Position des noeuds */ props->y = *y + top_margin; *y = props->y + props->height; /* Espace inférieur pour les liens */ /** * Fournit une marge minimum : si des liens autres que boucles partent de ce rang, * il ne sont pas enregistrés à ce niveau, mais ont besoin d'espace quand même. * On prend néanmoins garde aux rangs terminaux, potentiellement vides. */ result = (props->height > 0 ? EDGE_SLOT_VERT_MARGIN : 0); if (props->bottom_count > 0) result += props->bottom_count * EDGE_VERTICAL_MIN_SEP; return result; } /****************************************************************************** * * * Paramètres : props = propriétés de rang à consulter. * * slot = numérod de la réservation. * * top = désignation de la zone à traiter. * * * * Description : Fournit la position verticale obtenue par réservation. * * * * Retour : Ordonnée à utiliser. * * * * Remarques : - * * * ******************************************************************************/ static gint get_y_for_rank_props_hspan_slot(const rank_props *props, hspan_slot_t slot, bool top) { gint result; /* Ordonnée à retourner */ result = EDGE_SLOT_VERT_MARGIN + slot * EDGE_VERTICAL_MIN_SEP; if (top) result = props->y - result; else result += props->y + props->height; return result; } /* ---------------------------------------------------------------------------------- */ /* CLASSEMENT VERTICAL DES NOEUDS */ /* ---------------------------------------------------------------------------------- */ /* Indique le type définit par la GLib pour le calcul de l'ordonnée des classements. */ G_DEFINE_TYPE(GGraphRanks, g_graph_ranks, G_TYPE_OBJECT); /****************************************************************************** * * * Paramètres : klass = classe à initialiser. * * * * Description : Initialise la classe de calcul de l'ordonnée des classements.* * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_graph_ranks_class_init(GGraphRanksClass *klass) { GObjectClass *object; /* Autre version de la classe */ object = G_OBJECT_CLASS(klass); object->dispose = (GObjectFinalizeFunc/* ! */)g_graph_ranks_dispose; object->finalize = (GObjectFinalizeFunc)g_graph_ranks_finalize; } /****************************************************************************** * * * Paramètres : ranks = instance à initialiser. * * * * Description : Initialise un calcul de l'ordonnée des classements. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_graph_ranks_init(GGraphRanks *ranks) { } /****************************************************************************** * * * Paramètres : node = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_graph_ranks_dispose(GGraphRanks *ranks) { G_OBJECT_CLASS(g_graph_ranks_parent_class)->dispose(G_OBJECT(ranks)); } /****************************************************************************** * * * Paramètres : ranks = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_graph_ranks_finalize(GGraphRanks *ranks) { if (ranks->props != NULL) free(ranks->props); G_OBJECT_CLASS(g_graph_ranks_parent_class)->finalize(G_OBJECT(ranks)); } /****************************************************************************** * * * Paramètres : count = nombre de rangs dans le classement au total. * * * * Description : Prépare de quoi se définir au mieux les ordonnées des noeuds.* * * * Retour : Adresse de la structure mise en place. * * * * Remarques : - * * * ******************************************************************************/ GGraphRanks *g_graph_ranks_new(unsigned int count) { GGraphRanks *result; /* Structure à retourner */ result = g_object_new(G_TYPE_GRAPH_RANKS, NULL); result->count = count; result->props = (rank_props *)calloc(count, sizeof(rank_props)); return result; } /****************************************************************************** * * * Paramètres : ranks = ensemble des rangs de classement à manipuler. * * index = indice du rang visé. * * height = hauteur minimale requise pour le rang donné. * * * * Description : Note une hauteur minimale requise pour un rang du classement.* * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_graph_ranks_set_min_height(GGraphRanks *ranks, unsigned int index, gint height) { /*BUG_ON(index >= ranks->count) */ ranks->props[index].height = MAX(ranks->props[index].height, height); } /****************************************************************************** * * * Paramètres : ranks = ensemble des rangs de classement à consulter. * * * * Description : Fournit la hauteur requise pour l'affichage des rangs. * * * * Retour : Hauteur nécessaire en pixel. * * * * Remarques : - * * * ******************************************************************************/ gint g_graph_ranks_get_height(const GGraphRanks *ranks) { return ranks->height; } /****************************************************************************** * * * Paramètres : ranks = ensemble des rangs à mettre à jour. * * * * Description : Supprime toutes les réservations faites pour les liens. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_graph_ranks_reset_reservations(GGraphRanks *ranks) { unsigned int i; /* Boucle de parcours */ for (i = 0; i < ranks->count; i++) reset_spans_in_rank_props(&ranks->props[i]); } /****************************************************************************** * * * Paramètres : ranks = ensemble des rangs à mettre à jour. * * index = indice du rang visé. * * x1 = début de la portée à réserver. * * x2 = fin de la portée à réserver. * * top = désignation de la zone à traiter. * * * * Description : Réserve une portion de hauteur pour un lien. * * * * Retour : Indice de la hauteur adaptée et réservée. * * * * Remarques : - * * * ******************************************************************************/ hspan_slot_t g_graph_ranks_reserve_span(GGraphRanks *ranks, unsigned int index, gint x1, gint x2, bool top) { /*BUG_ON(index >= ranks->count) */ return reserve_span_in_rank_props(&ranks->props[index], x1, x2, top); } /****************************************************************************** * * * Paramètres : ranks = ensemble des rangs de classement à manipuler. * * * * Description : Calcule l'ordonnée finale de chaque rang du classement. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_graph_ranks_compute_y_line(GGraphRanks *ranks) { gint last_bottom; /* Espace minimal à transmettre*/ gint y; /* Ordonnée courante à proposer*/ size_t i; /* Boucle de parcours */ last_bottom = GRAPH_VERTICAL_MARGIN; y = 0; for (i = 0; i < ranks->count; i++) last_bottom = compute_rank_props_y_pos(&ranks->props[i], &y, last_bottom); ranks->height = y + last_bottom + GRAPH_VERTICAL_MARGIN; } /****************************************************************************** * * * Paramètres : ranks = ensemble des rangs de classement à consulter. * * index = indice du rang visé. * * * * Description : Fournit la position verticale correspondant à un rang donné. * * * * Retour : Ordonnée à utiliser. * * * * Remarques : - * * * ******************************************************************************/ gint g_graph_ranks_get_y_for_rank(const GGraphRanks *ranks, unsigned int index) { /*BUG_ON(index >= ranks->count) */ return ranks->props[index].y; } /****************************************************************************** * * * Paramètres : ranks = ensemble des rangs de classement à consulter. * * index = indice du rang visé. * * slot = numérod de la réservation. * * top = désignation de la zone à traiter. * * * * Description : Fournit la position verticale obtenue par réservation. * * * * Retour : Ordonnée à utiliser. * * * * Remarques : - * * * ******************************************************************************/ gint g_graph_ranks_get_y_for_hspan_slot(const GGraphRanks *ranks, unsigned int index, hspan_slot_t slot, bool top) { /*BUG_ON(index >= ranks->count) */ return get_y_for_rank_props_hspan_slot(&ranks->props[index], slot, top); }