/* Chrysalide - Outil d'analyse de fichiers binaires * gbuffercache.c - affichage à la demande d'un ensemble de lignes * * 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 Chrysalide. If not, see . */ #include "gbuffercache.h" #include #include #include #include "chrysamarshal.h" /* --------------------- FONCTIONS AUXILIAIRES DE MANIPULATIONS --------------------- */ /* Informations rattachées à la génération d'une ligne */ typedef struct _generator_link { GLineGenerator *instance; /* Fournisseur de contenu */ size_t repeat; /* Compteur de successions */ } generator_link; /* Suivi interne de l'état d'une ligne */ typedef struct _cache_info { union { generator_link generator; /* Générateur unique */ generator_link *generators; /* Liste de générateurs */ }; size_t count; /* Taille de cette liste */ GBufferLine *line; /* Ligne en place ou NULL */ BufferLineFlags extra_flags; /* Propriétés supplémentaires */ } cache_info; /* Gros verrou global pour alléger les structures... */ G_LOCK_DEFINE_STATIC(_line_update); /* Met en place un nouvel ensemble d'information sur une ligne. */ static void init_cache_info(cache_info *, GLineGenerator *, size_t, BufferLineFlags); /* Libère la mémoire occupée par des informations sur une ligne. */ static void release_cache_info(cache_info *); /* Ajoute un générateur aux informations sur une ligne. */ static void extend_cache_info(cache_info *, GLineGenerator *, BufferLineFlags); /* Retire un générateur aux informations d'une ligne. */ static void remove_from_cache_info(cache_info *, GLineGenerator *); /* Retrouve l'emplacement correspondant à une position de ligne. */ static void get_cache_info_cursor(const cache_info *, size_t, gint, GLineCursor **); /* Suivit les variations du compteur de références d'une ligne. */ static void on_line_ref_toggle(cache_info *, GBufferLine *, gboolean); /* Fournit la ligne de tampon correspondant aux générateurs. */ static GBufferLine *get_cache_info_line(cache_info *, size_t, const GBinContent *); /* Force la réinitialisation d'une éventuelle ligne cachée. */ static void _reset_cache_info_line_unlocked(cache_info *); /* Force la réinitialisation d'une éventuelle ligne cachée. */ static void reset_cache_info_line(cache_info *); /* -------------------------- TAMPON POUR CODE DESASSEMBLE -------------------------- */ /* Tampon pour gestion de lignes optimisée (instance) */ struct _GBufferCache { GObject parent; /* A laisser en premier */ const GBinContent *content; /* Contenu binaire global */ cache_info *lines; /* Liste des lignes intégrées */ size_t count; /* Quantité en cache */ size_t used; /* Quantité utilisée */ GWidthTracker *tracker; /* Suivi des largeurs */ }; /* Tampon pour gestion de lignes optimisée (classe) */ struct _GBufferCacheClass { GObjectClass parent; /* A laisser en premier */ gint line_height; /* Hauteur maximale des lignes */ gint left_margin; /* Marge gauche + espace */ gint text_pos; /* Début d'impression du code */ /* Signaux */ void (* size_changed) (GBufferCache *, bool, size_t, size_t); }; /* Taille des allocations de masse */ #define LINE_ALLOC_BULK 1000 /* Procède à l'initialisation d'une classe de tampon de lignes. */ static void g_buffer_cache_class_init(GBufferCacheClass *); /* Procède à l'initialisation d'un tampon de gestion de lignes. */ static void g_buffer_cache_init(GBufferCache *); /* Supprime toutes les références externes. */ static void g_buffer_cache_dispose(GBufferCache *); /* Procède à la libération totale de la mémoire. */ static void g_buffer_cache_finalize(GBufferCache *); /* Calcule l'indice d'apparition d'un générateur dans le tampon. */ static size_t g_buffer_cache_compute_repetition(GBufferCache *, size_t, GLineGenerator *); /* ---------------------------------------------------------------------------------- */ /* FONCTIONS AUXILIAIRES DE MANIPULATIONS */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : info = informations concernant une ligne à constituer. * * generator = générateur à associer à toutes les lignes. * * repeat = compteur de répétition entre les lignes. * * flags = propriétés supplémentaires à associer à la ligne.* * * * Description : Met en place un nouvel ensemble d'information sur une ligne. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void init_cache_info(cache_info *info, GLineGenerator *generator, size_t repeat, BufferLineFlags flags) { info->generator.instance = generator; info->generator.repeat = repeat; g_object_ref(G_OBJECT(generator)); info->count = 1; info->line = NULL; info->extra_flags = flags; } /****************************************************************************** * * * Paramètres : info = informations concernant une ligne à constituer. * * * * Description : Libère la mémoire occupée par des informations sur une ligne.* * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void release_cache_info(cache_info *info) { size_t i; /* Boucle de parcours */ if (info->count == 1) g_object_unref(G_OBJECT(info->generator.instance)); else for (i = 0; i < info->count; i++) g_object_unref(G_OBJECT(info->generators[i].instance)); reset_cache_info_line(info); } /****************************************************************************** * * * Paramètres : info = informations concernant une ligne à actualiser. * * generator = générateur à associer à toutes les lignes. * * flags = propriétés supplémentaires à associer à la ligne.* * * * Description : Ajoute un générateur aux informations sur une ligne. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void extend_cache_info(cache_info *info, GLineGenerator *generator, BufferLineFlags flags) { generator_link first; /* Générateur déjà en place */ generator_link *new; /* Nouveau générateur placé */ if (info->count == 1) { first = info->generator; info->generators = (generator_link *)calloc(2, sizeof(generator_link)); info->generators[0] = first; info->count = 2; new = &info->generators[1]; } else { info->generators = (generator_link *)realloc(info->generators, ++info->count * sizeof(generator_link)); new = &info->generators[info->count - 1]; } new->instance = generator; new->repeat = 0; g_object_ref(G_OBJECT(generator)); reset_cache_info_line(info); /** * On peut rajouter des indications, mais, en cas de retrait d'un générateur, * on ne saura pas forcément lesquelles retirer puisque qu'on ne trace pas * leur origine. * * On considère donc que seul le premier générateur (le principal) a le * droit de poser des fanions. */ assert(flags == BLF_NONE); } /****************************************************************************** * * * Paramètres : info = informations concernant une ligne à actualiser. * * generator = générateur à dissocier de toutes les lignes. * * * * Description : Retire un générateur aux informations d'une ligne. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void remove_from_cache_info(cache_info *info, GLineGenerator *generator) { generator_link *link; /* Accès simplifié */ size_t i; /* Boucle de parcours */ generator_link *old; /* Mémorisation avant opérat° */ if (info->count == 1) { link = &info->generator; assert(link->instance == generator); g_object_unref(G_OBJECT(generator)); info->count = 0; } else { for (i = 0; i < info->count; i++) { link = &info->generators[i]; if (link->instance == generator) { if ((i + 1) < info->count) memmove(&info->generators[i], &info->generators[i + 1], (info->count - i - 1) * sizeof(generator_link)); if (info->count == 2) { old = info->generators; info->count = 1; info->generator = info->generators[0]; free(old); } else info->generators = (generator_link *)realloc(info->generators, --info->count * sizeof(generator_link)); g_object_unref(G_OBJECT(generator)); break; } } #ifndef NDEBUG /** * Attention : si l'élément était en dernière position, * l'indice de parcours est désormais égal au nombre de générateurs présents ! */ assert(i <= info->count); for ( ; i < info->count; i++) { link = &info->generators[i]; assert(link->instance != generator); } #endif } reset_cache_info_line(info); } /****************************************************************************** * * * Paramètres : info = informations sur une ligne à venir consulter. * * index = indice de la ligne visée par la consultation. * * x = position géographique sur la ligne concernée. * * cursor = emplacement à constituer. [OUT] * * * * Description : Retrouve l'emplacement correspondant à une position de ligne.* * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void get_cache_info_cursor(const cache_info *info, size_t index, gint x, GLineCursor **cursor) { const generator_link *generator; /* Générateur retenu */ if (info->count == 1) generator = &info->generator; else generator = &info->generators[0]; g_line_generator_compute_cursor(generator->instance, x, index, generator->repeat, cursor); } /****************************************************************************** * * * Paramètres : info = informations sur une ligne à venir manipuler. * * line = tampon de lignes à venir supprimer au besoin. * * last = indication sur la valeur du compteur de références. * * * * Description : Suivit les variations du compteur de références d'une ligne. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void on_line_ref_toggle(cache_info *info, GBufferLine *line, gboolean last) { if (last) { G_LOCK(_line_update); assert(info->line != NULL); _reset_cache_info_line_unlocked(info); G_UNLOCK(_line_update); } } /****************************************************************************** * * * Paramètres : info = informations sur une ligne à venir manipuler. * * index = indice de la ligne à constituer. * * content = éventuel contenu binaire brut à imprimer. * * * * Description : Fournit la ligne de tampon correspondant aux générateurs. * * * * Retour : Ligne déjà en place ou créée pour le besoin. * * * * Remarques : - * * * ******************************************************************************/ static GBufferLine *get_cache_info_line(cache_info *info, size_t index, const GBinContent *content) { GBufferLine *result; /* Construction à retourner */ size_t i; /* Boucle de parcours */ G_LOCK(_line_update); result = info->line; if (result == NULL) { result = g_buffer_line_new((mrange_t []){ { { 0 }, 0 } }, 0/* !! */); g_object_add_toggle_ref(G_OBJECT(result), (GToggleNotify)on_line_ref_toggle, info); if (info->count == 1) g_line_generator_print(info->generator.instance, result, index, info->generator.repeat, content); else for (i = 0; i < info->count; i++) g_line_generator_print(info->generators[i].instance, result, index, info->generators[i].repeat, content); info->line = result; } else g_object_ref(G_OBJECT(result)); G_UNLOCK(_line_update); return result; } /****************************************************************************** * * * Paramètres : info = informations sur une ligne à venir manipuler. * * * * Description : Force la réinitialisation d'une éventuelle ligne cachée. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void _reset_cache_info_line_unlocked(cache_info *info) { if (info->line != NULL) { g_object_remove_toggle_ref(G_OBJECT(info->line), (GToggleNotify)on_line_ref_toggle, info); info->line = NULL; } } /****************************************************************************** * * * Paramètres : info = informations sur une ligne à venir manipuler. * * * * Description : Force la réinitialisation d'une éventuelle ligne cachée. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void reset_cache_info_line(cache_info *info) { G_LOCK(_line_update); _reset_cache_info_line_unlocked(info); G_UNLOCK(_line_update); } /* ---------------------------------------------------------------------------------- */ /* TAMPON POUR CODE DESASSEMBLE */ /* ---------------------------------------------------------------------------------- */ /* Détermine le type du composant de tampon pour gestion de lignes optimisée. */ G_DEFINE_TYPE(GBufferCache, g_buffer_cache, G_TYPE_OBJECT); /****************************************************************************** * * * Paramètres : class = classe de composant GLib à initialiser. * * * * Description : Procède à l'initialisation d'une classe de tampon de lignes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_buffer_cache_class_init(GBufferCacheClass *class) { GObjectClass *object; /* Autre version de la classe */ object = G_OBJECT_CLASS(class); object->dispose = (GObjectFinalizeFunc/* ! */)g_buffer_cache_dispose; object->finalize = (GObjectFinalizeFunc)g_buffer_cache_finalize; class->line_height = 17; class->left_margin = 2 * class->line_height; class->text_pos = 2.5 * class->line_height; /* Signaux */ g_signal_new("size-changed", G_TYPE_BUFFER_CACHE, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET(GBufferCacheClass, size_changed), NULL, NULL, g_cclosure_user_marshal_VOID__BOOLEAN_ULONG_ULONG, G_TYPE_NONE, 3, G_TYPE_BOOLEAN, G_TYPE_ULONG, G_TYPE_ULONG); } /****************************************************************************** * * * Paramètres : cache = composant GLib à initialiser. * * * * Description : Procède à l'initialisation d'un tampon de gestion de lignes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_buffer_cache_init(GBufferCache *cache) { cache->tracker = g_width_tracker_new(cache); } /****************************************************************************** * * * Paramètres : cache = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_buffer_cache_dispose(GBufferCache *cache) { size_t i; /* Boucle de parcours #1 */ cache_info *info; /* Accès direct à une ligne */ size_t j; /* Boucle de parcours #2 */ if (cache->content != NULL) g_object_unref(G_OBJECT(cache->content)); for (i = 0; i < cache->used; i++) { info = &cache->lines[i]; if (info->count == 1) g_object_unref(G_OBJECT(info->generator.instance)); else for (j = 0; j < info->count; j++) g_object_unref(G_OBJECT(info->generators[j].instance)); if (info->line) g_object_unref(G_OBJECT(info->line)); } g_object_unref(G_OBJECT(cache->tracker)); G_OBJECT_CLASS(g_buffer_cache_parent_class)->dispose(G_OBJECT(cache)); } /****************************************************************************** * * * Paramètres : cache = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_buffer_cache_finalize(GBufferCache *cache) { size_t i; /* Boucle de parcours */ cache_info *info; /* Accès direct à une ligne */ for (i = 0; i < cache->used; i++) { info = &cache->lines[i]; if (info->count > 1) free(info->generators); } if (cache->lines != NULL) free(cache->lines); G_OBJECT_CLASS(g_buffer_cache_parent_class)->finalize(G_OBJECT(cache)); } /****************************************************************************** * * * Paramètres : content = éventuel contenu binaire brut à référencer. * * * * Description : Crée un nouveau composant de tampon pour code désassemblé. * * * * Retour : Composant GLib créé. * * * * Remarques : - * * * ******************************************************************************/ GBufferCache *g_buffer_cache_new(const GBinContent *content) { GBufferCache *result; /* Composant à retourner */ result = g_object_new(G_TYPE_BUFFER_CACHE, NULL); if (content != NULL) { result->content = content; g_object_ref(G_OBJECT(content)); } return result; } /****************************************************************************** * * * Paramètres : cache = tampon de lignes à consulter. * * * * Description : Fournit la hauteur d'impression d'une ligne visualisée. * * * * Retour : Hauteur de ligne en pixels. * * * * Remarques : - * * * ******************************************************************************/ gint g_buffer_cache_get_line_height(const GBufferCache *cache) { GBufferCacheClass *class; /* Classe des tampons */ class = G_BUFFER_CACHE_GET_CLASS(cache); return class->line_height; } /****************************************************************************** * * * Paramètres : cache = tampon de lignes à consulter. * * * * Description : Fournit la taille réservée pour la marge gauche. * * * * Retour : Largeur en pixels. * * * * Remarques : - * * * ******************************************************************************/ gint g_buffer_cache_get_left_margin(const GBufferCache *cache) { GBufferCacheClass *class; /* Classe des tampons */ class = G_BUFFER_CACHE_GET_CLASS(cache); return class->left_margin; } /****************************************************************************** * * * Paramètres : cache = tampon de lignes à consulter. * * * * Description : Fournit la position de départ pour l'impression de texte. * * * * Retour : Position en pixels. * * * * Remarques : - * * * ******************************************************************************/ gint g_buffer_cache_get_text_position(const GBufferCache *cache) { GBufferCacheClass *class; /* Classe des tampons */ class = G_BUFFER_CACHE_GET_CLASS(cache); return class->text_pos; } /****************************************************************************** * * * Paramètres : cache = instance GLib à consulter. * * * * Description : Compte le nombre de lignes rassemblées dans un tampon. * * * * Retour : Nombre de lignes constituant le tampon. * * * * Remarques : - * * * ******************************************************************************/ size_t g_buffer_cache_count_lines(const GBufferCache *cache) { return cache->used; } /****************************************************************************** * * * Paramètres : cache = composant GLib à consulter. * * * * Description : Fournit un lien vers la structure de suivi de largeurs. * * * * Retour : Gestionnaire de largeurs de lignes. * * * * Remarques : - * * * ******************************************************************************/ GWidthTracker *g_buffer_cache_get_width_tracker(const GBufferCache *cache) { GWidthTracker *result; /* Instance à retourner * */ result = cache->tracker; g_object_ref(G_OBJECT(result)); return result; } /****************************************************************************** * * * Paramètres : cache = instance GLib à consulter. * * index = indice de la ligne où se trouve le générateur. * * generator = générateur associé à au moins une ligne. * * * * Description : Calcule l'indice d'apparition d'un générateur dans le tampon.* * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static size_t g_buffer_cache_compute_repetition(GBufferCache *cache, size_t index, GLineGenerator *generator) { size_t result; /* Compteur à retourner */ cache_info *info; /* Accès direct à une ligne */ size_t i; /* Boucle de parcours */ result = 0; if (index > 0) { info = &cache->lines[index - 1]; if (info->count == 1) { if (info->generator.instance == generator) result = info->generator.repeat + 1; } else for (i = 0; i < info->count; i++) if (info->generators[i].instance == generator) { result = info->generators[i].repeat + 1; break; } } return result; } /****************************************************************************** * * * Paramètres : cache = instance GLib à modifier. * * index = point d'insertion, puis de sauvegarde. * * generator = générateur à insérer dans les lignes. * * flags = propriétés supplémentaires à associer à la ligne.* * before = précise l'emplacement final des nouvelles lignes.* * after = précise l'emplacement final des nouvelles lignes.* * * * Description : Insère un générateur dans des lignes à une position donnée. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_buffer_cache_insert_at(GBufferCache *cache, size_t index, GLineGenerator *generator, BufferLineFlags flags, bool before, bool after) { #ifndef NDEBUG GLineCursor *gen_cursor; /* Position du générateur */ GLineCursor *line_cursor; /* Position de la ligne */ int ret; /* Bilan de comparaison */ #endif size_t needed; /* Emplacements nécessaires */ size_t i; /* Boucle de parcours */ assert(index < cache->used); assert(!(before && after)); #ifndef NDEBUG if (!before && !after) { g_line_generator_compute_cursor(generator, 0, index, 0, &gen_cursor); get_cache_info_cursor(&cache->lines[index], index, 0, &line_cursor); ret = g_line_cursor_compare(gen_cursor, line_cursor); g_object_unref(G_OBJECT(gen_cursor)); g_object_unref(G_OBJECT(line_cursor)); assert(ret == 0); } #endif /* Cas particulier d'ajout en fin de cache... */ if (after && (index + 1) == cache->used) { g_buffer_cache_append(cache, generator, flags); goto gbcia_done; } /* Adaptation de l'espace */ needed = g_line_generator_count_lines(generator); if (before || after) { if ((cache->used + needed) >= cache->count) { cache->count += needed + LINE_ALLOC_BULK; cache->lines = (cache_info *)realloc(cache->lines, cache->count * sizeof(cache_info)); } } else if (needed > 1) { if ((cache->used + needed - 1) >= cache->count) { cache->count += needed - 1 + LINE_ALLOC_BULK; cache->lines = (cache_info *)realloc(cache->lines, cache->count * sizeof(cache_info)); } } /* Insertion du générateur */ if (after) index++; if (before || after) { memmove(&cache->lines[index + needed], &cache->lines[index], (cache->used - index) * sizeof(cache_info)); for (i = 0; i < needed; i++) init_cache_info(&cache->lines[index + i], generator, i, flags); cache->used += needed; g_width_tracker_update_added(cache->tracker, index, needed); g_signal_emit_by_name(cache, "size-changed", true, index, needed); } else { extend_cache_info(&cache->lines[index], generator, flags); g_width_tracker_update(cache->tracker, index); if (needed > 1) { /* On déborde sur les lignes suivantes, donc on crée de l'espace ! */ memmove(&cache->lines[index + 1], &cache->lines[index + 1 + needed - 1], (cache->used - index - 1) * sizeof(cache_info)); for (i = 1; i < needed; i++) init_cache_info(&cache->lines[index + i], generator, i, BLF_NONE); cache->used += needed - 1; g_width_tracker_update_added(cache->tracker, index + 1, needed - 1); } g_signal_emit_by_name(cache, "size-changed", true, index, needed - 1); } gbcia_done: ; } /****************************************************************************** * * * Paramètres : cache = instance GLib à modifier. * * index = point de suppression. * * * * Description : Retire une ligne du tampon. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_buffer_cache_delete_at(GBufferCache *cache, size_t index) { cache_info *info; /* Accès direct à une ligne */ assert(index < cache->used); info = &cache->lines[index]; release_cache_info(info); if ((index + 1) < cache->used) memmove(&cache->lines[index], &cache->lines[index + 1], (cache->used - index - 1) * sizeof(cache_info)); cache->used--; g_width_tracker_update_deleted(cache->tracker, index, index); g_signal_emit_by_name(cache, "size-changed", false, index, 1); } /****************************************************************************** * * * Paramètres : cache = instance GLib à modifier. * * index = point d'insertion, puis de sauvegarde. * * type = type de générateurs à retirer des lignes visées. * * before = précise l'emplacement final de l'élément visé. * * after = précise l'emplacement final de l'élément visé. * * * * Description : Retire un type de générateur de lignes. * * * * Retour : Générateur éventuellement trouvé ou NULL si aucun. * * * * Remarques : - * * * ******************************************************************************/ GLineGenerator *g_buffer_cache_delete_type_at(GBufferCache *cache, size_t index, GType type, bool before, bool after) { GLineGenerator *result; /* Prédécesseur à retourner */ cache_info *info; /* Accès direct à une ligne */ generator_link *link; /* Accès simplifié */ size_t i; /* Boucle de parcours */ size_t count; /* Emplacements occupés */ size_t delete; /* Indice de suppression */ assert(index < cache->used); assert(!(before && after)); result = NULL; /* Recherche d'un générateur correspondant */ if (before) info = &cache->lines[index - 1]; else if (after) info = &cache->lines[index + 1]; else info = &cache->lines[index]; if (info->count == 1) { link = &info->generator; if (G_OBJECT_TYPE(link->instance) == type) result = link->instance; } else for (i = 0; i < info->count && result == NULL; i++) { link = &info->generators[i]; if (G_OBJECT_TYPE(link->instance) == type) result = link->instance; } /* Retrait de l'instance trouvée */ if (result != NULL) { count = g_line_generator_count_lines(result); #ifndef NDEBUG if (!before && !after) assert(count == 1); #endif g_object_ref(G_OBJECT(result)); /* Suppression de l'élément */ for (i = 0; i < count; i++) { if (before) info = &cache->lines[index - 1 - i]; else if (after) info = &cache->lines[index + 1 + i]; else info = &cache->lines[index]; remove_from_cache_info(info, result); } /* Suppression des lignes associées */ for (i = 0; i < count; i++) { if (before) delete = index - 1; else if (after) delete = index + 1; else delete = index; info = &cache->lines[delete]; if (info->count == 0) { release_cache_info(info); if ((delete + 1) < cache->used) memmove(&cache->lines[delete], &cache->lines[delete + 1], (cache->used - delete - 1) * sizeof(cache_info)); cache->used--; g_width_tracker_update_deleted(cache->tracker, delete, delete); g_signal_emit_by_name(cache, "size-changed", false, delete, 1); } } } return result; } /****************************************************************************** * * * Paramètres : cache = instance GLib à modifier. * * generator = générateur à associer à toutes les lignes. * * flags = propriétés supplémentaires à associer à la ligne.* * * * Description : Ajoute en fin de tampon un générateur de lignes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_buffer_cache_append(GBufferCache *cache, GLineGenerator *generator, BufferLineFlags flags) { size_t count; /* Nombre de lignes générées */ size_t index; /* Point d'insertion */ size_t i; /* Boucle de parcours */ cache_info *info; /* Accès direct à une ligne */ count = g_line_generator_count_lines(generator); assert(count > 0); assert((flags != BLF_NONE && count == 1) || flags == BLF_NONE); if ((cache->used + count) > cache->count) { cache->count += count + LINE_ALLOC_BULK; cache->lines = (cache_info *)realloc(cache->lines, cache->count * sizeof(cache_info)); } index = cache->used; for (i = 0; i < count; i++) { info = &cache->lines[index + i]; info->generator.instance = generator; info->generator.repeat = g_buffer_cache_compute_repetition(cache, index + i, generator); g_object_ref(G_OBJECT(generator)); info->count = 1; info->line = NULL; info->extra_flags = flags; } cache->used += count; g_width_tracker_update_added(cache->tracker, index, count); g_signal_emit_by_name(cache, "size-changed", true, index, count); } /****************************************************************************** * * * Paramètres : cache = instance GLib à modifier. * * count = quantité totale de lignes à avoir à disposition. * * generator = générateur à associer à toutes les lignes. * * * * Description : Etend un tampon avec un générateur de lignes unique. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_buffer_cache_extend_with(GBufferCache *cache, size_t count, GLineGenerator *generator) { size_t index; /* Point d'insertion */ size_t i; /* Boucle de parcours */ cache_info *info; /* Accès direct à une ligne */ size_t added; /* Nombre d'ajouts effectués */ assert(count >= cache->used); if (count > cache->count) { cache->lines = (cache_info *)realloc(cache->lines, count * sizeof(cache_info)); cache->count = count; } index = cache->used; for (i = index; i < count; i++) { info = &cache->lines[i]; info->generator.instance = generator; info->generator.repeat = g_buffer_cache_compute_repetition(cache, i, generator); g_object_ref(G_OBJECT(generator)); info->count = 1; info->line = NULL; } added = count - cache->used; cache->used = count; g_object_unref(G_OBJECT(generator)); if (added > 0) { g_width_tracker_update_added(cache->tracker, index, added); g_signal_emit_by_name(cache, "size-changed", true, index, added); } } /****************************************************************************** * * * Paramètres : cache = instance GLib à modifier. * * max = nombre maximal de lignes à conserver. * * * * Description : Réduit le tampon à une quantité de lignes précise. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_buffer_cache_truncate(GBufferCache *cache, size_t max) { size_t i; /* Boucle de parcours #1 */ cache_info *info; /* Accès direct à une ligne */ size_t j; /* Boucle de parcours #2 */ size_t removed; /* Nombre de retraits effectués*/ assert(max <= cache->used); for (i = max; i < cache->used; i++) { info = &cache->lines[i]; if (info->count == 1) g_object_unref(G_OBJECT(info->generator.instance)); else { for (j = 0; j < info->count; j++) g_object_unref(G_OBJECT(info->generators[j].instance)); free(info->generators); } reset_cache_info_line(info); } removed = cache->used - max; cache->used = max - 1; if (removed > 0) { g_width_tracker_update_deleted(cache->tracker, max, max + removed - 1); g_signal_emit_by_name(cache, "size-changed", false, max, removed); } } /****************************************************************************** * * * Paramètres : cache = tampon de lignes à venir consulter. * * index = indice de la ligne visée par la consultation. * * x = position géographique sur la ligne concernée. * * cursor = emplacement à constituer. [OUT] * * * * Description : Retrouve l'emplacement correspondant à une position de ligne.* * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_buffer_cache_get_line_cursor(const GBufferCache *cache, size_t index, gint x, GLineCursor **cursor) { assert(index < cache->used); get_cache_info_cursor(&cache->lines[index], index, x, cursor); } /****************************************************************************** * * * Paramètres : cache = tampon de lignes à venir consulter. * * index = indice de la ligne visée par la consultation. * * * * Description : Détermine l'ensemble des propriétés attachées à une ligne. * * * * Retour : Somme de toutes les propriétés enregistrées. * * * * Remarques : - * * * ******************************************************************************/ BufferLineFlags g_buffer_cache_get_line_flags(const GBufferCache *cache, size_t index) { BufferLineFlags result; /* Somme à renvoyer */ cache_info *info; /* Accès direct à une ligne */ const generator_link *generator; /* Générateur retenu */ size_t i; /* Boucle de parcours */ assert(index < cache->used); info = &cache->lines[index]; result = info->extra_flags; if (info->count == 1) { generator = &info->generator; result |= g_line_generator_get_flags(generator->instance, index, generator->repeat); } else for (i = 0; i < info->count; i++) { generator = &info->generators[i]; result |= g_line_generator_get_flags(generator->instance, index, generator->repeat); } return result; } /****************************************************************************** * * * Paramètres : cache = tampon de lignes à consulter. * * index = indice de la ligne recherchée. * * * * Description : Retrouve une ligne au sein d'un tampon avec un indice. * * * * Retour : Line retrouvée ou NULL en cas d'échec. * * * * Remarques : - * * * ******************************************************************************/ GBufferLine *g_buffer_cache_find_line_by_index(const GBufferCache *cache, size_t index) { GBufferLine *result; /* Ligne trouvée à retourner */ if (index < cache->used) result = get_cache_info_line(&cache->lines[index], index, cache->content); else result = NULL; return result; } /****************************************************************************** * * * Paramètres : cache = tampon de lignes à venir consulter. * * index = indice de la ligne à mesurer. * * summary = largeurs maximales à faire évoluer. * * * * Description : Fait remonter les largeurs requises par une ligne donnée. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_buffer_cache_collect_widths(GBufferCache *cache, size_t index, line_width_summary *summary) { GBufferLine *line; /* Ligne éphémère à mesurer */ line = get_cache_info_line(&cache->lines[index], index, cache->content); g_buffer_line_collect_widths(line, summary); g_object_unref(G_OBJECT(line)); } /****************************************************************************** * * * Paramètres : cache = visualisation à représenter. * * cr = contexte graphique dédié à la procédure. * * first = première ligne à dessiner. * * last = dernière ligne à dessiner. * * area = position et surface à traiter. * * options = règles d'affichage des colonnes modulables. * * offsets = décalages supplémentaires à appliquer. * * selected = ordonnée d'une ligne sélectionnée ou NULL. * * list = liste de contenus à mettre en évidence. * * * * Description : Imprime une partie choisie du tampon contenant des lignes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_buffer_cache_draw(const GBufferCache *cache, cairo_t *cr, size_t first, size_t last, const cairo_rectangle_int_t *area, const GDisplayOptions *options, const line_width_summary *offsets, const gint *selected, const segcnt_list *list) { GBufferCacheClass *class; /* Classe des tampons */ gint y; /* Point de départ en ordonnée */ bool wait_selection; /* Sélection déjà passée ? */ size_t i; /* Boucle de parcours */ cache_info *info; /* Accès direct à une ligne */ line_width_summary summary; /* Résumé concis des largeurs */ GBufferLine *line; /* Ligne à venir dessiner */ class = G_BUFFER_CACHE_GET_CLASS(cache); y = 0; wait_selection = true; if (cache->used > 0) for (i = first; i <= last; i++) { /* Si sélection, on sousligne la ligne concernée */ if (wait_selection && selected != NULL && *selected == y) { cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 0.05); cairo_rectangle(cr, area->x, y, area->width, class->line_height); cairo_fill(cr); wait_selection = false; } info = &cache->lines[i]; if (i == first || (g_buffer_cache_get_line_flags(cache, i) & BLF_WIDTH_MANAGER)) g_width_tracker_get_local_width_summary(cache->tracker, i, &summary); line = get_cache_info_line(info, i, cache->content); g_buffer_line_draw(line, cr, &summary, class->text_pos, y, options, offsets, list); g_object_unref(G_OBJECT(line)); y += class->line_height; } } /****************************************************************************** * * * Paramètres : cache = tampon de lignes à consulter. * * cursor = emplacement à retrouver dans le tampon. * * first = indique si on l'arrête à la première ou la dernière.* * start = borne inférieure des recherches (incluse). * * end = borne supérieure des recherches (incluse). * * * * Description : Indique l'indice correspondant à une adresse donnée. * * * * Retour : Indice des infos à l'adresse demandée, ou nombre de lignes. * * * * Remarques : - * * * ******************************************************************************/ size_t _g_buffer_cache_find_index_by_cursor(const GBufferCache *cache, const GLineCursor *cursor, bool first, size_t start, size_t end) { size_t result; /* Indice à retourner */ cache_info *found; /* Eventuel élément trouvé */ int find_containing_generator(const GLineCursor *c, const cache_info *i) { const generator_link *generator; /* Générateur retenu */ if (i->count == 1) generator = &i->generator; else generator = &i->generators[0]; return g_line_generator_contains_cursor(generator->instance, i - cache->lines, generator->repeat, c); } found = (cache_info *)bsearch(cursor, &cache->lines[start], end - start + 1, sizeof(cache_info), (__compar_fn_t)find_containing_generator); if (found == NULL) result = cache->used; else { result = (found - cache->lines); assert(start <= result && result <= end); /* On s'assure d'un arrêt sur la bonne ligne */ if (first) for (; result > start; result--) { found = &cache->lines[result - 1]; if (find_containing_generator(cursor, found) != 0) break; } else for (; result < end; result++) { found = &cache->lines[result + 1]; if (find_containing_generator(cursor, found) != 0) break; } } return result; } /****************************************************************************** * * * Paramètres : cache = tampon de lignes à consulter. * * cursor = emplacement à retrouver dans le tampon. * * first = indique si on l'arrête à la première ou la dernière.* * * * Description : Indique l'indice correspondant à une adresse donnée. * * * * Retour : Indice des infos à l'adresse demandée, ou nombre de lignes. * * * * Remarques : - * * * ******************************************************************************/ size_t g_buffer_cache_find_index_by_cursor(const GBufferCache *cache, const GLineCursor *cursor, bool first) { size_t result; /* Indice à retourner */ if (cache->used == 0) result = 0; else result = _g_buffer_cache_find_index_by_cursor(cache, cursor, first, 0, cache->used - 1); return result; } /****************************************************************************** * * * Paramètres : cache = tampon de lignes à consulter. * * start = point de départ du parcours. * * flag = propriétés à retrouver si possible. * * * * Description : Avance autant que possible vers une ligne idéale. * * * * Retour : Indice de la ligne recherchée, si elle existe. * * * * Remarques : - * * * ******************************************************************************/ size_t g_buffer_cache_look_for_flag(const GBufferCache *cache, size_t start, BufferLineFlags flag) { size_t result; /* Indice de ligne à retourner */ GLineCursor *init; /* Localisation de départ */ size_t i; /* Boucle de parcours */ GLineCursor *next; /* Localisation suivante */ int ret; /* Bilan de comparaison */ assert(start < cache->used); result = start; get_cache_info_cursor(&cache->lines[start], start, 0, &init); for (i = start + 1; i < cache->used; i++) { get_cache_info_cursor(&cache->lines[i], i, 0, &next); ret = g_line_cursor_compare(init, next); g_object_unref(G_OBJECT(next)); if (ret != 0) break; if ((g_buffer_cache_get_line_flags(cache, i) & flag) != 0) { result = i; break; } } g_object_unref(G_OBJECT(init)); return result; } /****************************************************************************** * * * Paramètres : cache = tampon de lignes à consulter. * * cursor = emplacement à présenter à l'écran. * * first = borne inférieure des recherches (incluse). * * last = borne supérieure des recherches (incluse). * * code = s'arrête si possible à une ligne avec code. * * x = position horizontale au sein du composant. [OUT] * * y = position verticale au sein du composant. [OUT] * * * * Description : Indique la position d'affichage d'une adresse donnée. * * * * Retour : true si l'adresse fait partie du composant, false sinon. * * * * Remarques : - * * * ******************************************************************************/ bool g_buffer_cache_get_cursor_coordinates(const GBufferCache *cache, const GLineCursor *cursor, size_t first, size_t last, bool code, gint *x, gint *y) { bool result; /* Bilan à retourner */ size_t index; /* Indice de correspondance */ gint lheight; /* Hauteur d'une ligne */ const cache_info *info; /* Infos sur une ligne donnée */ const generator_link *generator; /* Générateur retenu */ index = _g_buffer_cache_find_index_by_cursor(cache, cursor, true, first, last); result = (index < cache->used); if (result) { lheight = G_BUFFER_CACHE_GET_CLASS(cache)->line_height; *x = 0; *y = (index - first) * G_BUFFER_CACHE_GET_CLASS(cache)->line_height; for (; code && index <= last; index++) { if (g_buffer_cache_get_line_flags(cache, index) & BLF_HAS_CODE) break; if (index == last) break; info = &cache->lines[index + 1]; if (info->count == 1) generator = &info->generator; else generator = &info->generators[0]; if (!g_line_generator_contains_cursor(generator->instance, index + 1, generator->repeat, cursor)) break; *y += lheight; } } return result; }