diff options
Diffstat (limited to 'src/glibext')
57 files changed, 6580 insertions, 4248 deletions
diff --git a/src/glibext/Makefile.am b/src/glibext/Makefile.am index b5ea0f0..b0a7c31 100644 --- a/src/glibext/Makefile.am +++ b/src/glibext/Makefile.am @@ -1,50 +1,74 @@ BUILT_SOURCES = chrysamarshal.h chrysamarshal.c resources.h resources.c -noinst_LTLIBRARIES = libglibext4.la libglibextui.la # libglibext.la - -libglibext_la_SOURCES = \ - comparison-int.h \ - comparison.h comparison.c \ - configuration-int.h \ - configuration.h configuration.c \ - gbinarycursor.h gbinarycursor.c \ - gbinportion-int.h \ - gbinportion.h gbinportion.c \ - glinecursor-int.h \ - glinecursor.h glinecursor.c \ - gnhash.h gnhash.c \ - notifier.h \ - objhole.h \ - proto.h \ - seq.h seq.c \ - _signal.h signal.c \ - singleton.h singleton.c \ - linetoken.h linetoken.c \ - umemslice-int.h \ - umemslice.h umemslice.c - -if BUILD_GTK_SUPPORT - -libglibext_la_SOURCES += \ - gloadedpanel-int.h \ - gloadedpanel.h gloadedpanel.c \ - named-int.h \ - named.h named.c - -endif - -libglibext_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) +noinst_LTLIBRARIES = libglibext.la libglibextui.la + +# libglibext_la_SOURCES = \ +# configuration-int.h \ +# configuration.h configuration.c \ +# gbinarycursor.h gbinarycursor.c \ +# glinecursor-int.h \ +# glinecursor.h glinecursor.c \ +# gnhash.h gnhash.c \ +# notifier.h \ +# \ +# proto.h \ +# seq.h seq.c \ +# singleton.h singleton.c \ +# linetoken.h linetoken.c \ +# umemslice-int.h \ +# umemslice.h umemslice.c + +# if BUILD_GTK_SUPPORT + +# libglibext_la_SOURCES += \ +# gloadedpanel-int.h \ +# gloadedpanel.h gloadedpanel.c \ +# named-int.h \ +# named.h named.c + +# endif + +# libglibext_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBXML_CFLAGS) #libglibext_la_LIBADD = \ # generators/libglibextgenerators.la -libglibext4_la_SOURCES = \ +libglibext_la_SOURCES = \ chrysamarshal.h chrysamarshal.c \ - helpers.h - -libglibext4_la_CFLAGS = $(TOOLKIT_CFLAGS) + comparable-int.h \ + comparable.h comparable.c \ + hashable-int.h \ + hashable.h hashable.c \ + helpers.h \ + log-int.h \ + log.h log.c \ + objhole-int.h \ + objhole.h objhole.c \ + portion-int.h \ + portion.h portion.c \ + secstorage-int.h \ + secstorage.h secstorage.c \ + serialize-int.h \ + serialize.h serialize.c \ + sigredir.h sigredir.c \ + singleton-int.h \ + singleton.h singleton.c \ + storage-int.h \ + storage.h storage.c \ + strbuilder-int.h \ + strbuilder.h strbuilder.c \ + tpmem-int.h \ + tpmem.h tpmem.c \ + work-int.h \ + work.h work.c \ + workgroup-int.h \ + workgroup.h workgroup.c \ + workqueue-int.h \ + workqueue.h workqueue.c + +libglibext_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBZIP_CFLAGS) RES_FILES = \ @@ -78,7 +102,7 @@ libglibextui_la_LIBADD = \ devdir = $(includedir)/chrysalide/$(subdir:src/%=core/%) -dev_HEADERS = $(libglibext_la_SOURCES:%c=) +dev_HEADERS = $(libglibext_la_SOURCES:%c=) $(libglibextui_la_SOURCES:%c=) SUBDIRS = generators options diff --git a/src/glibext/bufferline.c b/src/glibext/bufferline.c index 4862e9f..e3fb27b 100644 --- a/src/glibext/bufferline.c +++ b/src/glibext/bufferline.c @@ -24,14 +24,16 @@ #include "bufferline.h" +#include <assert.h> +#include <malloc.h> + + #include "bufferline-int.h" #if 0 -#include <assert.h> -#include <malloc.h> #include <string.h> @@ -273,158 +275,12 @@ bool g_buffer_line_create(GBufferLine *line, size_t col_count) } - - - - - - - -/****************************************************************************** -* * -* Paramètres : line = ligne à venir compléter. * -* column = colonne de la ligne visée par l'insertion. * -* tag = type de décorateur à utiliser. * -* text = texte à insérer dans l'existant. * -* length = taille du texte à traiter. * -* style = gestionnaire de paramètres de rendu à consulter. * -* creator = instance GLib quelconque à associer. * -* * -* Description : Ajoute du texte à formater dans une ligne donnée. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_buffer_line_append_text(GBufferLine *line, size_t column, TokenRenderingTag tag, const char *text, size_t length, const GTokenStyle *style, GObject *creator) -{ - size_t index; /* Indice d'insertion */ - //content_origin *origin; /* Définition d'une origine */ - - assert(column < line->col_count); - assert(length > 0); - - index = append_text_to_line_column(&line->columns[column], tag, text, length, style); - - /* - if (creator != NULL) - { - line->origins = realloc(line->origins, ++line->ocount * sizeof(content_origin)); - - origin = &line->origins[line->ocount - 1]; - - origin->coord.column = column; - origin->coord.index = index; - - origin->creator = creator; - g_object_ref(G_OBJECT(creator)); - - } - */ - -} - - - - - - - - - - - -/****************************************************************************** -* * -* Paramètres : line = ligne de texte à manipuler. * -* cr = contexte graphique dédié à la procédure. * -* column = (première) colonne à traiter. * -* y = ordonnée du point d'impression. * -* style = style de rendu pour les bribes de texte. * -* * -* Description : Imprime la ligne de texte représentée. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_buffer_line_draw(const GBufferLine *line, cairo_t *cr, size_t column, int y, const GTokenStyle *style) -{ -#if 0 - GBufferLineClass *class; /* Stockage de briques de base */ - bool has_src_surface; /* Note une présence définie */ -#endif - size_t max_column; /* Borne de fin des colonnes */ - int x; /* Point de départ d'impression*/ - size_t i; /* Boucle de parcours */ - - /* - if (line->flags != BLF_NONE) - { - class = G_BUFFER_LINE_GET_CLASS(line); - - if (line->flags & BLF_ENTRYPOINT) - { - cairo_set_source_surface(cairo, class->entrypoint_img, 5, y); - has_src_surface = true; - } - else if (line->flags & BLF_BOOKMARK) - { - cairo_set_source_surface(cairo, class->bookmark_img, 5, y); - has_src_surface = true; - } - else - has_src_surface = false; - - if (has_src_surface) - cairo_paint(cairo); - - } - */ - - - /* Détermination de l'éventail des colonnes à traiter */ - - if (column == line->merge_start) - max_column = line->col_count; - - else if (column > line->merge_start) - max_column = 0; - - else - max_column = column + 1; - - /* Dessin du contenu de ces colonnes */ - - x = 0; - - for (i = column; i < max_column; i++) - draw_line_column(&line->columns[i], cr, &x, y, style); - -} - - - - - - - - - - -#if 0 - - /****************************************************************************** * * -* Paramètres : line = ligne à venir compléter. * -* col = indice de la colonne à constituer. * -* size = taille souhaitée de l'impression des positions. * -* addr = localisation physique à venir représenter. * +* Paramètres : line = ligne à venir compléter. * +* column = indice de la colonne visée par l'insertion. * +* size = taille souhaitée de l'impression des positions. * +* addr = localisation physique à venir représenter. * * * * Description : Construit le tronc commun d'une ligne autour de sa position. * * * @@ -434,7 +290,7 @@ void g_buffer_line_draw(const GBufferLine *line, cairo_t *cr, size_t column, int * * ******************************************************************************/ -void g_buffer_line_fill_phys(GBufferLine *line, size_t col, MemoryDataSize size, const vmpa2t *addr) +void g_buffer_line_fill_physical(GBufferLine *line, size_t column, MemoryDataSize size, const vmpa2t *addr) { VMPA_BUFFER(position); /* Emplacement au format texte */ size_t len; /* Taille de l'élément inséré */ @@ -448,20 +304,22 @@ void g_buffer_line_fill_phys(GBufferLine *line, size_t col, MemoryDataSize size, if (i == len) i = len - 1; + assert(column < line->col_count); + if (i > 0) - g_buffer_line_append_text(line, col, position, i, RTT_PHYS_ADDR_PAD, NULL); + g_buffer_line_append_text(line, column, TRT_PHYS_ADDR_PAD, position, i, NULL, NULL); - g_buffer_line_append_text(line, col, &position[i], len - i, RTT_PHYS_ADDR, NULL); + g_buffer_line_append_text(line, column, TRT_PHYS_ADDR, &position[i], len - i, NULL, NULL); } /****************************************************************************** * * -* Paramètres : line = ligne à venir compléter. * -* col = indice de la colonne à constituer. * -* size = taille souhaitée de l'impression des positions. * -* addr = localisation virtuelle à venir représenter. * +* Paramètres : line = ligne à venir compléter. * +* column = indice de la colonne visée par l'insertion. * +* size = taille souhaitée de l'impression des positions. * +* addr = localisation virtuelle à venir représenter. * * * * Description : Construit le tronc commun d'une ligne autour de sa position. * * * @@ -471,7 +329,7 @@ void g_buffer_line_fill_phys(GBufferLine *line, size_t col, MemoryDataSize size, * * ******************************************************************************/ -void g_buffer_line_fill_virt(GBufferLine *line, size_t col, MemoryDataSize size, const vmpa2t *addr) +void g_buffer_line_fill_virtual(GBufferLine *line, size_t column, MemoryDataSize size, const vmpa2t *addr) { VMPA_BUFFER(position); /* Emplacement au format texte */ size_t len; /* Taille de l'élément inséré */ @@ -479,6 +337,8 @@ void g_buffer_line_fill_virt(GBufferLine *line, size_t col, MemoryDataSize size, vmpa2_virt_to_string(addr, size, position, &len); + assert(column < line->col_count); + if (has_virt_addr(addr)) { for (i = 2; i < len; i++) @@ -488,14 +348,14 @@ void g_buffer_line_fill_virt(GBufferLine *line, size_t col, MemoryDataSize size, i = len - 1; if (i > 0) - g_buffer_line_append_text(line, col, position, i, RTT_VIRT_ADDR_PAD, NULL); + g_buffer_line_append_text(line, column, TRT_VIRT_ADDR_PAD, position, i, NULL, NULL); - g_buffer_line_append_text(line, col, &position[i], len - i, RTT_VIRT_ADDR, NULL); + g_buffer_line_append_text(line, column, TRT_VIRT_ADDR, &position[i], len - i, NULL, NULL); } else - g_buffer_line_append_text(line, col, position, len, RTT_VIRT_ADDR_PAD, NULL); + g_buffer_line_append_text(line, column, TRT_VIRT_ADDR_PAD, position, len, NULL, NULL); } @@ -503,7 +363,7 @@ void g_buffer_line_fill_virt(GBufferLine *line, size_t col, MemoryDataSize size, /****************************************************************************** * * * Paramètres : line = ligne à venir compléter. * -* col = indice de la colonne à constituer. * +* column = indice de la colonne visée par l'insertion. * * content = contenu binaire global à venir lire. * * range = localisation des données à venir lire et présenter.* * max = taille maximale de la portion binaire en octets. * @@ -516,7 +376,7 @@ void g_buffer_line_fill_virt(GBufferLine *line, size_t col, MemoryDataSize size, * * ******************************************************************************/ -void g_buffer_line_fill_content(GBufferLine *line, size_t col, const GBinContent *content, const mrange_t *range, phys_t max) +void g_buffer_line_fill_content(GBufferLine *line, size_t column, const GBinContent *content, const mrange_t *range, phys_t max) { phys_t length; /* Taille de la couverture */ bool truncated; /* Indique si le code est coupé*/ @@ -548,7 +408,7 @@ void g_buffer_line_fill_content(GBufferLine *line, size_t col, const GBinContent if (required <= sizeof(static_buffer)) bin_code = static_buffer; else - bin_code = (char *)calloc(required, sizeof(char)); + bin_code = calloc(required, sizeof(char)); /* Code brut */ @@ -589,7 +449,7 @@ void g_buffer_line_fill_content(GBufferLine *line, size_t col, const GBinContent /* Conclusion */ - g_buffer_line_append_text(line, col, bin_code, iter - bin_code, RTT_RAW_CODE, NULL); + g_buffer_line_append_text(line, column, TRT_RAW_CODE, bin_code, iter - bin_code, NULL, NULL); if (bin_code != static_buffer) free(bin_code); @@ -599,6 +459,146 @@ void g_buffer_line_fill_content(GBufferLine *line, size_t col, const GBinContent /****************************************************************************** * * +* Paramètres : line = ligne à venir compléter. * +* column = colonne de la ligne visée par l'insertion. * +* tag = type de décorateur à utiliser. * +* text = texte à insérer dans l'existant. * +* length = taille du texte à traiter. * +* style = gestionnaire de paramètres de rendu à consulter. * +* creator = instance GLib quelconque à associer. * +* * +* Description : Ajoute du texte à formater dans une ligne donnée. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_buffer_line_append_text(GBufferLine *line, size_t column, TokenRenderingTag tag, const char *text, size_t length, const GTokenStyle *style, GObject *creator) +{ + size_t index; /* Indice d'insertion */ + //content_origin *origin; /* Définition d'une origine */ + + assert(column < line->col_count); + assert(length > 0); + + index = append_text_to_line_column(&line->columns[column], tag, text, length, style); + + /* + if (creator != NULL) + { + line->origins = realloc(line->origins, ++line->ocount * sizeof(content_origin)); + + origin = &line->origins[line->ocount - 1]; + + origin->coord.column = column; + origin->coord.index = index; + + origin->creator = creator; + g_object_ref(G_OBJECT(creator)); + + } + */ + +} + + + + + + + + + + + +/****************************************************************************** +* * +* Paramètres : line = ligne de texte à manipuler. * +* cr = contexte graphique dédié à la procédure. * +* column = (première) colonne à traiter. * +* y = ordonnée du point d'impression. * +* style = style de rendu pour les bribes de texte. * +* * +* Description : Imprime la ligne de texte représentée. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_buffer_line_draw(const GBufferLine *line, cairo_t *cr, size_t column, int y, const GTokenStyle *style) +{ +#if 0 + GBufferLineClass *class; /* Stockage de briques de base */ + bool has_src_surface; /* Note une présence définie */ +#endif + size_t max_column; /* Borne de fin des colonnes */ + int x; /* Point de départ d'impression*/ + size_t i; /* Boucle de parcours */ + + /* + if (line->flags != BLF_NONE) + { + class = G_BUFFER_LINE_GET_CLASS(line); + + if (line->flags & BLF_ENTRYPOINT) + { + cairo_set_source_surface(cairo, class->entrypoint_img, 5, y); + has_src_surface = true; + } + else if (line->flags & BLF_BOOKMARK) + { + cairo_set_source_surface(cairo, class->bookmark_img, 5, y); + has_src_surface = true; + } + else + has_src_surface = false; + + if (has_src_surface) + cairo_paint(cairo); + + } + */ + + + /* Détermination de l'éventail des colonnes à traiter */ + + if (column == line->merge_start) + max_column = line->col_count; + + else if (column > line->merge_start) + max_column = 0; + + else + max_column = column + 1; + + /* Dessin du contenu de ces colonnes */ + + x = 0; + + for (i = column; i < max_column; i++) + draw_line_column(&line->columns[i], cr, &x, y, style); + +} + + + + + + + + + + +#if 0 + + + +/****************************************************************************** +* * * Paramètres : line = ligne à venir consulter. * * column = indice de la colonne visée par les recherches. * * * diff --git a/src/glibext/bufferline.h b/src/glibext/bufferline.h index d6a2e2d..e95ee2b 100644 --- a/src/glibext/bufferline.h +++ b/src/glibext/bufferline.h @@ -30,6 +30,8 @@ #include "helpers.h" #include "tokenstyle.h" +#include "../arch/vmpa.h" +#include "../analysis/content.h" @@ -45,8 +47,6 @@ #ifdef INCLUDE_GTK_SUPPORT # include "widthtracker.h" #endif -#include "../analysis/content.h" -#include "../arch/vmpa.h" @@ -75,6 +75,9 @@ typedef struct _GBufferLineClass GBufferLineClass; + + + #define G_TYPE_BUFFER_LINE (g_buffer_line_get_type()) DECLARE_GTYPE(GBufferLine, g_buffer_line, G, BUFFER_LINE); @@ -98,7 +101,14 @@ typedef enum _BufferLineFlags /* Crée une nouvelle représentation de fragments de texte. */ GBufferLine *g_buffer_line_new(size_t); +/* Construit le tronc commun d'une ligne autour de sa position. */ +void g_buffer_line_fill_physical(GBufferLine *, size_t, MemoryDataSize, const vmpa2t *); + +/* Construit le tronc commun d'une ligne autour de sa position. */ +void g_buffer_line_fill_virtual(GBufferLine *, size_t, MemoryDataSize, const vmpa2t *); +/* Construit le tronc commun d'une ligne autour de son contenu. */ +void g_buffer_line_fill_content(GBufferLine *, size_t, const GBinContent *, const mrange_t *, phys_t); /* Ajoute du texte à formater dans une ligne donnée. */ void g_buffer_line_append_text(GBufferLine *, size_t, TokenRenderingTag, const char *, size_t, const GTokenStyle *, GObject *); diff --git a/src/glibext/comparable-int.h b/src/glibext/comparable-int.h new file mode 100644 index 0000000..18972c2 --- /dev/null +++ b/src/glibext/comparable-int.h @@ -0,0 +1,47 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * comparable-int.h - définitions internes propres aux opérations de comparaison d'objets + * + * Copyright (C) 2022-2025 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_COMPARABLE_INT_H +#define _GLIBEXT_COMPARABLE_INT_H + + +#include "comparable.h" + + + +/* Réalise une comparaison étendue entre objets. */ +typedef int (* compare_object_fc) (const GComparableObject *, const GComparableObject *); + + +/* Instance d'objet comparable (interface) */ +struct _GComparableObjectInterface +{ + GTypeInterface base_iface; /* A laisser en premier */ + + compare_object_fc compare; /* Comparaison étendue */ + +}; + + + +#endif /* _GLIBEXT_COMPARABLE_INT_H */ diff --git a/src/glibext/comparable.c b/src/glibext/comparable.c new file mode 100644 index 0000000..40fd110 --- /dev/null +++ b/src/glibext/comparable.c @@ -0,0 +1,124 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * comparable.c - opérations de comparaison d'objets + * + * Copyright (C) 2022-2025 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 <http://www.gnu.org/licenses/>. + */ + + +#include "comparable.h" + + +#include "comparable-int.h" +#include "../common/sort.h" + + + +/* Procède à l'initialisation de l'interface de comparaison. */ +static void g_comparable_object_default_init(GComparableObjectInterface *); + + + +/* Détermine le type d'une interface pour un objet comparable. */ +G_DEFINE_INTERFACE(GComparableObject, g_comparable_object, G_TYPE_OBJECT) + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* * +* Description : Procède à l'initialisation de l'interface de comparaison. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_comparable_object_default_init(GComparableObjectInterface *iface) +{ + iface->compare = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : object = premier objet à consulter pour une comparaison. * +* other = second objet à consulter pour une comparaison. * +* * +* Description : Réalise une comparaison étendue entre objets. * +* * +* Retour : Bilan de la comparaison. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int g_comparable_object_compare(const GComparableObject *object, const GComparableObject *other) +{ + int result; /* Bilan à retourner */ + GType type_a; /* Type de l'object A */ + GType type_b; /* Type de l'object B */ + GComparableObjectInterface *iface; /* Interface utilisée */ + + type_a = G_OBJECT_TYPE(G_OBJECT(object)); + type_b = G_OBJECT_TYPE(G_OBJECT(other)); + + assert(sizeof(GType) <= sizeof(unsigned long)); + + result = sort_unsigned_long(type_a, type_b); + + if (result == 0) + { + iface = G_COMPARABLE_OBJECT_GET_IFACE(object); + + result = iface->compare(object, other); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : object = premier objet à consulter pour une comparaison. * +* other = second objet à consulter pour une comparaison. * +* * +* Description : Détermine si deux objets sont fonctionnellement identiques. * +* * +* Retour : Bilan de la comparaison. * +* * +* Remarques : - * +* * +******************************************************************************/ + +gboolean g_comparable_object_is_equal(const GComparableObject *object, const GComparableObject *other) +{ + gboolean result; /* Bilan à renvoyer */ + int ret; /* Bilan d'une comparaison */ + + ret = g_comparable_object_compare(object, other); + + result = (ret == 0); + + return result; + +} diff --git a/src/glibext/comparable.h b/src/glibext/comparable.h new file mode 100644 index 0000000..7a652ff --- /dev/null +++ b/src/glibext/comparable.h @@ -0,0 +1,46 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * comparable.h - prototypes pour les opérations de comparaison d'objets + * + * Copyright (C) 2022-2025 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_COMPARABLE_H +#define _GLIBEXT_COMPARABLE_H + + +#include "helpers.h" + + + +#define G_TYPE_COMPARABLE_OBJECT (g_comparable_object_get_type()) + +DECLARE_INTERFACE(GComparableObject, g_comparable_object, G, COMPARABLE_OBJECT); + + + +/* Réalise une comparaison étendue entre objets. */ +int g_comparable_object_compare(const GComparableObject *, const GComparableObject *); + +/* Détermine si deux objets sont fonctionnellement identiques. */ +gboolean g_comparable_object_is_equal(const GComparableObject *, const GComparableObject *); + + + +#endif /* _GLIBEXT_COMPARABLE_H */ diff --git a/src/glibext/comparison-int.h b/src/glibext/comparison-int.h deleted file mode 100644 index 446f25d..0000000 --- a/src/glibext/comparison-int.h +++ /dev/null @@ -1,58 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * comparison-int.h - définitions internes propres aux opérations de comparaison d'objets - * - * Copyright (C) 2022 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 <http://www.gnu.org/licenses/>. - */ - - -#ifndef _GLIBEXT_COMPARISON_INT_H -#define _GLIBEXT_COMPARISON_INT_H - - -#include "comparison.h" - - - -/* Réalise une comparaison entre objets selon un critère précis. */ -typedef bool (* compare_rich_fc) (const GComparableItem *, const GComparableItem *, RichCmpOperation, bool *); - - -/* Instance d'élément comparable (interface) */ -struct _GComparableItemIface -{ - GTypeInterface base_iface; /* A laisser en premier */ - - compare_rich_fc cmp_rich; /* Comparaison de façon précise*/ - -}; - - -/* Redéfinition */ -typedef GComparableItemIface GComparableItemInterface; - - -/* Réalise une comparaison riche entre valeurs entière. */ -bool compare_rich_integer_values_signed(long long, long long, RichCmpOperation); - -/* Réalise une comparaison riche entre valeurs entière. */ -bool compare_rich_integer_values_unsigned(unsigned long long, unsigned long long, RichCmpOperation); - - - -#endif /* _GLIBEXT_COMPARISON_INT_H */ diff --git a/src/glibext/comparison.c b/src/glibext/comparison.c deleted file mode 100644 index 8ce6941..0000000 --- a/src/glibext/comparison.c +++ /dev/null @@ -1,199 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * comparison.c - opérations de comparaison d'objets - * - * Copyright (C) 2022 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 <http://www.gnu.org/licenses/>. - */ - - -#include "comparison.h" - - -#include <assert.h> - - -#include "comparison-int.h" - - - -/* Procède à l'initialisation de l'interface de comparaison. */ -static void g_comparable_item_default_init(GComparableItemInterface *); - - - -/* Détermine le type d'une interface pour un objet comparable. */ -G_DEFINE_INTERFACE(GComparableItem, g_comparable_item, G_TYPE_OBJECT) - - -/****************************************************************************** -* * -* Paramètres : iface = interface GLib à initialiser. * -* * -* Description : Procède à l'initialisation de l'interface de comparaison. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_comparable_item_default_init(GComparableItemInterface *iface) -{ - -} - - -/****************************************************************************** -* * -* Paramètres : item = premier objet à consulter pour une comparaison. * -* other = second objet à consulter pour une comparaison. * -* op = opération de comparaison à réaliser. * -* status = bilan des opérations de comparaison. [OUT] * -* * -* Description : Réalise une comparaison entre objets selon un critère précis.* -* * -* Retour : true si la comparaison a pu être effectuée, false sinon. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool g_comparable_item_compare_rich(const GComparableItem *item, const GComparableItem *other, RichCmpOperation op, bool *status) -{ - bool result; /* Etat à retourner */ - GComparableItemIface *iface; /* Interface utilisée */ - - iface = G_COMPARABLE_ITEM_GET_IFACE(item); - - result = iface->cmp_rich(item, other, op, status); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : a = premier élément à consulter pour une comparaison. * -* b = second objet à consulter pour une comparaison. * -* op = opération de comparaison à réaliser. * -* * -* Description : Réalise une comparaison riche entre valeurs entière. * -* * -* Retour : Bilan des opérations de comparaison. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool compare_rich_integer_values_signed(long long a, long long b, RichCmpOperation op) -{ - bool result; /* Bilan à retourner */ - - switch (op) - { - case RCO_LT: - result = (a < b); - break; - - case RCO_LE: - result = (a <= b); - break; - - case RCO_EQ: - result = (a == b); - break; - - case RCO_NE: - result = (a != b); - break; - - case RCO_GT: - result = (a > b); - break; - - case RCO_GE: - result = (a >= b); - break; - - default: - assert(false); - result = false; - break; - - } - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : a = premier élément à consulter pour une comparaison. * -* b = second objet à consulter pour une comparaison. * -* op = opération de comparaison à réaliser. * -* * -* Description : Réalise une comparaison riche entre valeurs entière. * -* * -* Retour : Bilan des opérations de comparaison. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool compare_rich_integer_values_unsigned(unsigned long long a, unsigned long long b, RichCmpOperation op) -{ - bool result; /* Bilan à retourner */ - - switch (op) - { - case RCO_LT: - result = (a < b); - break; - - case RCO_LE: - result = (a <= b); - break; - - case RCO_EQ: - result = (a == b); - break; - - case RCO_NE: - result = (a != b); - break; - - case RCO_GT: - result = (a > b); - break; - - case RCO_GE: - result = (a >= b); - break; - - default: - assert(false); - result = false; - break; - - } - - return result; - -} diff --git a/src/glibext/comparison.h b/src/glibext/comparison.h deleted file mode 100644 index 8d43210..0000000 --- a/src/glibext/comparison.h +++ /dev/null @@ -1,80 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * comparison.h - prototypes pour les opérations de comparaison d'objets - * - * Copyright (C) 2022 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 <http://www.gnu.org/licenses/>. - */ - - -#ifndef _GLIBEXT_COMPARISON_H -#define _GLIBEXT_COMPARISON_H - - -#include <glib-object.h> -#include <stdbool.h> - - - -#define G_TYPE_COMPARABLE_ITEM (g_comparable_item_get_type()) -#define G_COMPARABLE_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_COMPARABLE_ITEM, GComparableItem)) -#define G_COMPARABLE_ITEM_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST((vtable), G_TYPE_COMPARABLE_ITEM, GComparableItemIface)) -#define G_IS_COMPARABLE_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_COMPARABLE_ITEM)) -#define G_IS_COMPARABLE_ITEM_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE((vtable), G_TYPE_COMPARABLE_ITEM)) -#define G_COMPARABLE_ITEM_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE((inst), G_TYPE_COMPARABLE_ITEM, GComparableItemIface)) - - -/* Instance d'élément comparable (coquille vide) */ -typedef struct _GComparableItem GComparableItem; - -/* Instance d'élément comparable (interface) */ -typedef struct _GComparableItemIface GComparableItemIface; - - -/* Modes de comparaison */ -typedef enum _RichCmpOperation -{ - RCO_LT, /* Equivalent de '<' */ - RCO_LE, /* Equivalent de '<=' */ - RCO_EQ, /* Equivalent de '==' */ - RCO_NE, /* Equivalent de '!=' */ - RCO_GT, /* Equivalent de '>' */ - RCO_GE, /* Equivalent de '>°' */ - -} RichCmpOperation; - -/* Détermination d'un besoin de comparaison supplémentaire */ -#define STATUS_NOT_EQUAL(_s, _o) \ - ({ \ - bool __result; \ - if (_o == RCO_LE || _o == RCO_EQ || _o == RCO_GE) \ - __result = !_s; \ - else \ - __result = _s; \ - __result; \ - }) - - -/* Détermine le type d'une interface pour un objet comparable. */ -GType g_comparable_item_get_type(void) G_GNUC_CONST; - -/* Réalise une comparaison entre objets selon un critère précis. */ -bool g_comparable_item_compare_rich(const GComparableItem *, const GComparableItem *, RichCmpOperation, bool *); - - - -#endif /* _GLIBEXT_COMPARISON_H */ diff --git a/src/glibext/hashable-int.h b/src/glibext/hashable-int.h new file mode 100644 index 0000000..f8a85e1 --- /dev/null +++ b/src/glibext/hashable-int.h @@ -0,0 +1,47 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * hashable-int.h - définitions internes propres aux calculs de l'empreinte d'un objet + * + * Copyright (C) 2025 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_HASHABLE_INT_H +#define _GLIBEXT_HASHABLE_INT_H + + +#include "hashable.h" + + + +/* Calcule l'empreinte sur 32 bits d'un objet. */ +typedef guint (* hash_object_fc) (const GHashableObject *); + + +/* Instance d'objet visant à être unique (interface) */ +struct _GHashableObjectInterface +{ + GTypeInterface base_iface; /* A laisser en premier */ + + hash_object_fc hash; /* Réduction en valeur */ + +}; + + + +#endif /* _GLIBEXT_HASHABLE_INT_H */ diff --git a/src/glibext/hashable.c b/src/glibext/hashable.c new file mode 100644 index 0000000..f988a90 --- /dev/null +++ b/src/glibext/hashable.c @@ -0,0 +1,82 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * hashable.c - calculs de l'empreinte d'un objet + * + * Copyright (C) 2025 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 <http://www.gnu.org/licenses/>. + */ + + +#include "hashable.h" + + +#include "hashable-int.h" + + + +/* Procède à l'initialisation de l'interface de détermination. */ +static void g_hashable_object_default_init(GHashableObjectInterface *); + + + +/* Détermine le type d'une interface pour la réduction d'un objet à une valeur. */ +G_DEFINE_INTERFACE(GHashableObject, g_hashable_object, G_TYPE_OBJECT) + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* * +* Description : Procède à l'initialisation de l'interface de détermination. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_hashable_object_default_init(GHashableObjectInterface *iface) +{ + iface->hash = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : object = objet dont l'instance est à consulter. * +* * +* Description : Calcule l'empreinte sur 32 bits d'un objet. * +* * +* Retour : Valeur de représentation, unique pour l'objet ou non. * +* * +* Remarques : - * +* * +******************************************************************************/ + +guint g_hashable_object_hash(const GHashableObject *object) +{ + guint result; /* Valeur à retourner */ + GHashableObjectInterface *iface; /* Interface utilisée */ + + iface = G_HASHABLE_OBJECT_GET_IFACE(object); + + result = iface->hash(object); + + return result; + +} diff --git a/src/glibext/hashable.h b/src/glibext/hashable.h new file mode 100644 index 0000000..165c744 --- /dev/null +++ b/src/glibext/hashable.h @@ -0,0 +1,42 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * hashable.h - prototypes pour les calculs de l'empreinte d'un objet + * + * Copyright (C) 2025 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_HASHABLE_H +#define _GLIBEXT_HASHABLE_H + + +#include "helpers.h" + + + +#define G_TYPE_HASHABLE_OBJECT (g_hashable_object_get_type()) + +DECLARE_INTERFACE(GHashableObject, g_hashable_object, G, HASHABLE_OBJECT); + + +/* Calcule l'empreinte sur 32 bits d'un objet. */ +guint g_hashable_object_hash(const GHashableObject *); + + + +#endif /* _GLIBEXT_HASHABLE_H */ diff --git a/src/glibext/helpers.h b/src/glibext/helpers.h index 04e51ad..6176245 100644 --- a/src/glibext/helpers.h +++ b/src/glibext/helpers.h @@ -29,6 +29,9 @@ #include <glib-object.h> +#include "../common/compiler.h" + + /** * Les définitions issues de <glib-2.80>/gobject/gtype.h fournissent des macros @@ -118,6 +121,31 @@ } +/** + * Définition sous condition d'une inclusion d'interface. Cette inclusion se réalise + * lorsque la fonction d'initialisation renseignée est définie. + * + * Cette version étendue de la macro G_IMPLEMENT_INTERFACE d'origine est principalement + * pour les raffinements d'objets en forme graphique. + */ + +#define G_IMPLEMENT_INTERFACE_IF_SYM(iface_tp_getter, iface_init) \ + do \ + { \ + extern GType iface_tp_getter(void) __weak; \ + extern void iface_init(GTypeInterface *, gpointer) __weak; \ + if (&iface_tp_getter != NULL && &iface_init != NULL) \ + { \ + GType iface_type = iface_tp_getter(); \ + const GInterfaceInfo implementation_info = { \ + (GInterfaceInitFunc)(void (*)(void))iface_init, NULL, NULL \ + }; \ + g_type_add_interface_static(g_define_type_id, iface_type, &implementation_info);\ + } \ + } \ + while (0); + + /** * Les principales fonctions incrémentant ou réduisant le nombre de références @@ -193,4 +221,5 @@ #endif + #endif /* _GLIBEXT_HELPERS_H */ diff --git a/src/glibext/log-int.h b/src/glibext/log-int.h new file mode 100644 index 0000000..3b3208a --- /dev/null +++ b/src/glibext/log-int.h @@ -0,0 +1,58 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * log-int.h - prototypes internes pour la conservation à destination graphique des éléments de journalisation + * + * Copyright (C) 2025 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_LOG_INT_H +#define _GLIBEXT_LOG_INT_H + + +#include <stdbool.h> + + +#include "log.h" + + + +/* Définition d'une conservation d'objets construits (instance) */ +struct _GLogEntry +{ + GObject parent; /* A laisser en premier */ + + LogMessageType type; /* Type de message porté */ + char *msg; /* Contenu du message diffusé */ + +}; + +/* Définition d'une conservation d'objets construits (classe) */ +struct _GLogEntryClass +{ + GObjectClass parent; /* A laisser en premier */ + +}; + + +/* Met en place une conservation pour élément de journalisation. */ +bool g_log_entry_create(GLogEntry *, LogMessageType, const char *); + + + +#endif /* _GLIBEXT_LOG_INT_H */ diff --git a/src/glibext/log.c b/src/glibext/log.c new file mode 100644 index 0000000..039172c --- /dev/null +++ b/src/glibext/log.c @@ -0,0 +1,306 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * log.c - conservation hors mémoire d'objets choisis + * + * Copyright (C) 2020-2025 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 <http://www.gnu.org/licenses/>. + */ + + +#include "log.h" + + +#include <malloc.h> +#include <string.h> + + +#include "log-int.h" + + + +/* ---------------------------- ENTREE DE JOURNALISATION ---------------------------- */ + + +/* Liste des propriétés */ + +typedef enum _LogEntryProperty { + + PROP_0, /* Réservé */ + + PROP_ICON_NAME, /* Nom d'image de représentat° */ + PROP_MESSAGE, /* Contenu du message diffusé */ + + N_PROPERTIES + +} LogEntryProperty; + +static GParamSpec *_log_entry_properties[N_PROPERTIES] = { NULL, }; + + +/* Initialise la classe des conservations d'objets en place. */ +static void g_log_entry_class_init(GLogEntryClass *); + +/* Initialise une instance de conservation d'objets en place. */ +static void g_log_entry_init(GLogEntry *); + +/* Supprime toutes les références externes. */ +static void g_log_entry_dispose(GObject *); + +/* Procède à la libération totale de la mémoire. */ +static void g_log_entry_finalize(GObject *); + + + +/* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ + + +/* Fournit la valeur d'une propriété d'instance GObject. */ +static void gtk_log_entry_get_property(GObject *, guint, GValue *, GParamSpec *); + + + +/* ---------------------------------------------------------------------------------- */ +/* ENTREE DE JOURNALISATION */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini pour une conservation d'objets construits. */ +G_DEFINE_TYPE(GLogEntry, g_log_entry, G_TYPE_OBJECT); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des conservations d'objets en place. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_log_entry_class_init(GLogEntryClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = g_log_entry_dispose; + object->finalize = g_log_entry_finalize; + object->get_property = gtk_log_entry_get_property; + + _log_entry_properties[PROP_ICON_NAME] = + g_param_spec_string("icon-name", NULL, NULL, + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + _log_entry_properties[PROP_MESSAGE] = + g_param_spec_string("message", NULL, NULL, + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); + + g_object_class_install_properties(object, N_PROPERTIES, _log_entry_properties); + +} + + +/****************************************************************************** +* * +* Paramètres : entry = instance à initialiser. * +* * +* Description : Initialise une instance de conservation d'objets en place. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_log_entry_init(GLogEntry *entry) +{ + entry->type = LMT_COUNT; + entry->msg = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_log_entry_dispose(GObject *object) +{ + G_OBJECT_CLASS(g_log_entry_parent_class)->dispose(object); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_log_entry_finalize(GObject *object) +{ + GLogEntry *entry; /* Version spécialisée */ + + entry = G_LOG_ENTRY(object); + + if (entry->msg != NULL) + free(entry->msg); + + G_OBJECT_CLASS(g_log_entry_parent_class)->finalize(object); + +} + + +/****************************************************************************** +* * +* Paramètres : type = espèce du message à représenter. * +* msg = message à faire apparaître à l'écran. * +* * +* Description : Crée une conservation pour un élément de journalisation. * +* * +* Retour : Conservation mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GLogEntry *g_log_entry_new(LogMessageType type, const char *msg) +{ + GLogEntry *result; /* Structure à retourner */ + + result = g_object_new(G_TYPE_LOG_ENTRY, NULL); + + if (!g_log_entry_create(result, type, msg)) + g_clear_object(&result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : entry = instance de conservation à initialiser pleinement. * +* type = espèce du message à représenter. * +* msg = message à faire apparaître à l'écran. * +* * +* Description : Met en place une conservation pour élément de journalisation.* +* * +* Retour : Conservation mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_log_entry_create(GLogEntry *entry, LogMessageType type, const char *msg) +{ + bool result; /* Bilan à retourner */ + + result = true; + + entry->type = type; + entry->msg = strdup(msg); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* IMPLEMENTATION DES FONCTIONS DE CLASSE */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à mamnipuler. * +* prop_id = identifiant de la propriété visée. * +* value = valeur à transmettre. [OUT] * +* pspec = définition de la propriété. * +* * +* Description : Fournit la valeur d'une propriété d'instance GObject. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void gtk_log_entry_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + GLogEntry *entry; /* Version spécialisée */ + + entry = G_LOG_ENTRY(object); + + switch (prop_id) + { + case PROP_ICON_NAME: + switch (entry->type) + { + case LMT_INFO: + g_value_set_string(value, "dialog-information-symbolic"); + break; + + case LMT_PROCESS: + g_value_set_string(value, "system-run-symbolic"); + break; + + case LMT_WARNING: + case LMT_BAD_BINARY: + g_value_set_string(value, "dialog-warning-symbolic"); + break; + + case LMT_ERROR: + case LMT_EXT_ERROR: + g_value_set_string(value, "computer-fail-symbolic"); + break; + + + case LMT_COUNT: + g_value_set_string(value, "dialog-question-symbolic"); + break; + + } + break; + + case PROP_MESSAGE: + g_value_set_string(value, entry->msg); + break; + + } + +} diff --git a/src/glibext/log.h b/src/glibext/log.h new file mode 100644 index 0000000..472773c --- /dev/null +++ b/src/glibext/log.h @@ -0,0 +1,43 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * log.h - prototypes pour la conservation à destination graphique des éléments de journalisation + * + * Copyright (C) 2025 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_LOG_H +#define _GLIBEXT_LOG_H + + +#include "helpers.h" +#include "../core/logs.h" + + + +#define G_TYPE_LOG_ENTRY (g_log_entry_get_type()) + +DECLARE_GTYPE(GLogEntry, g_log_entry, G, LOG_ENTRY); + + +/* Crée une conservation pour un élément de journalisation. */ +GLogEntry *g_log_entry_new(LogMessageType, const char *); + + + +#endif /* _GLIBEXT_LOG_H */ diff --git a/src/glibext/objhole-int.h b/src/glibext/objhole-int.h new file mode 100644 index 0000000..dbfb412 --- /dev/null +++ b/src/glibext/objhole-int.h @@ -0,0 +1,169 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * objhole.h - définitions internes pour ll'utilisation d'un espace inutilisé dans la structure GObject + * + * Copyright (C) 2024 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_OBJHOLE_INT_H +#define _GLIBEXT_OBJHOLE_INT_H + + +#include "objhole.h" + + +#include "../common/cpp.h" + + + +/** + * Une structure GObject a la définition suivante : + * + * struct _GObject + * { + * GTypeInstance g_type_instance; + * volatile guint ref_count; + * GData *qdata; + * }; + * + * Chaque objet GLib alloué comporte ainsi 4 octets inutilisés : + * + * (gdb) pt /o GObject + * type = struct _GObject { + * / 0 | 8 / GTypeInstance g_type_instance; + * / 8 | 4 / guint ref_count; + * / XXX 4-byte hole / + * / 16 | 8 / GData *qdata; + * + * / total size (bytes): 24 / + * } + * + * La situation n'a pas échappé aux développeurs GLib, avec la définition réelle + * de la structure (cf. https://github.com/GNOME/glib/blob/main/gobject/gobject.c) : + * + * #if SIZEOF_INT == 4 && GLIB_SIZEOF_VOID_P == 8 + * #define HAVE_OPTIONAL_FLAGS + * #endif + * + * typedef struct + * { + * GTypeInstance g_type_instance; + * guint ref_count; + * #ifdef HAVE_OPTIONAL_FLAGS + * guint optional_flags; + * #endif + * GData *qdata; + * } GObjectReal; + * + * G_STATIC_ASSERT(sizeof(GObject) == sizeof(GObjectReal)); + * G_STATIC_ASSERT(G_STRUCT_OFFSET(GObject, ref_count) == G_STRUCT_OFFSET(GObjectReal, ref_count)); + * G_STATIC_ASSERT(G_STRUCT_OFFSET(GObject, qdata) == G_STRUCT_OFFSET(GObjectReal, qdata)); + * + * L'espace entre les deux derniers champs ne peut donc être pleinement exploité deux fois. + */ + +/** + * Les bits effectivements utilisés par GLib s'identifie ainsi (02/12/24) : + * + * $ wget -qO /dev/stdout https://raw.githubusercontent.com/GNOME/glib/refs/heads/main/gobject/gobject.c \ + * | grep ' * #define OPTIONAL_FLAG_' + * #define OPTIONAL_FLAG_IN_CONSTRUCTION (1 << 0) + * #define OPTIONAL_FLAG_HAS_SIGNAL_HANDLER (1 << 1) + * #define OPTIONAL_FLAG_HAS_NOTIFY_HANDLER (1 << 2) + * #define OPTIONAL_FLAG_LOCK (1 << 3) + * #define OPTIONAL_FLAG_EVER_HAD_WEAK_REF (1 << 4) + * + * Les n premiers bits doivent ainsi être préservés, même s'il est possible de + * partager le bit de verrouilage OPTIONAL_FLAG_LOCK. + */ + + +#ifndef HAVE_OPTIONAL_FLAGS_IN_GOBJECT + +# if SIZEOF_INT == 4 && GLIB_SIZEOF_VOID_P >= 8 +# define HAVE_OPTIONAL_FLAGS_IN_GOBJECT 1 +# else +# define HAVE_OPTIONAL_FLAGS_IN_GOBJECT 0 +# endif + +#endif + + +#define GOBJECT_LOCK_BIT 3 + + +/* Nouvelle version dense des objets (instance) */ +typedef struct _GThickObject +{ + /** + * (cf. structure GObjectReal officielle). + */ + + GTypeInstance g_type_instance; /* Type d'objet */ + guint ref_count; /* Décompte des références */ + +#ifdef HAVE_OPTIONAL_FLAGS_IN_GOBJECT + guint extra; /* Zone partagée avec GLib */ +#endif + + GData *qdata; /* Données complémentaires ? */ + +#ifndef HAVE_OPTIONAL_FLAGS_IN_GOBJECT + guint extra; /* Zone supplémentaire propre */ +#endif + +} GThickObject; + +/* Nouvelle version dense des objets (classe) */ +struct _GThickObjectClass +{ + GObjectClass parent; /* A laisser en premier */ + +}; + + +/** + * Définition du périmètre et des moyens d'accès. + */ + +/* GLib 2.83.0 - cfa36f5e9 */ +#define GOBJECT_RESERVED_EXTRA_BITS 5 + +#define GET_GOBJECT_EXTRA(obj, tp) \ + ({ \ + BUILD_BUG_ON(sizeof(tp) > sizeof(guint)); \ + tp ___result; \ + guint __val; \ + __val = g_thick_object_get_extra(G_THICK_OBJECT(obj)); \ + ___result = *(tp *)(guint []){ __val }; \ + ___result; \ + }) + +#define SET_GOBJECT_EXTRA(obj, tp, data) \ + ({ \ + BUILD_BUG_ON(sizeof(tp) > sizeof(guint)); \ + BUILD_BUG_ON(sizeof(data) > sizeof(guint *)); \ + guint __val; \ + __val = *(guint *)data; \ + g_thick_object_set_extra(G_THICK_OBJECT(obj), __val); \ + }) + + + +#endif /* _GLIBEXT_OBJHOLE_INT_H */ diff --git a/src/glibext/objhole.c b/src/glibext/objhole.c new file mode 100644 index 0000000..fd6fbc9 --- /dev/null +++ b/src/glibext/objhole.c @@ -0,0 +1,256 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * objhole.c - utilisation d'un espace inutilisé dans la structure GObject + * + * Copyright (C) 2024 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 <http://www.gnu.org/licenses/>. + */ + + +#include "objhole.h" + + +#include <assert.h> + + +#include "objhole-int.h" + + + +/* Initialise la classe des objets à la structure dense. */ +static void g_thick_object_class_init(GThickObjectClass *); + +/* Initialise une instance d'objet à la structure dense. */ +static void g_thick_object_init(GThickObject *); + +/* Supprime toutes les références externes. */ +static void g_thick_object_dispose(GObject *); + +/* Procède à la libération totale de la mémoire. */ +static void g_thick_object_finalize(GObject *); + + + +G_DEFINE_TYPE(GThickObject, g_thick_object, G_TYPE_OBJECT); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des objets à la structure dense. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_thick_object_class_init(GThickObjectClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = g_thick_object_dispose; + object->finalize = g_thick_object_finalize; + +} + + +/****************************************************************************** +* * +* Paramètres : obj = instance à initialiser. * +* * +* Description : Initialise une instance d'objet à la structure dense. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_thick_object_init(GThickObject *obj) +{ + obj->extra = 0; + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_thick_object_dispose(GObject *object) +{ + G_OBJECT_CLASS(g_thick_object_parent_class)->dispose(object); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_thick_object_finalize(GObject *object) +{ + G_OBJECT_CLASS(g_thick_object_parent_class)->finalize(object); + +} + + +/****************************************************************************** +* * +* Paramètres : obj = instance d'objet GLib à manipuler. * +* * +* Description : Pose un verrou à l'aide du bit dédié de GObject. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_thick_object_lock(GThickObject *obj) +{ + g_bit_lock((gint *)&obj->extra, GOBJECT_LOCK_BIT); + +} + + +/****************************************************************************** +* * +* Paramètres : obj = instance d'objet GLib à manipuler. * +* * +* Description : Retire un verrou via le bit dédié de GObject. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_thick_object_unlock(GThickObject *obj) +{ + g_bit_unlock((gint *)&obj->extra, GOBJECT_LOCK_BIT); + +} + + +/****************************************************************************** +* * +* Paramètres : obj = instance d'objet GLib à manipuler. * +* * +* Description : Vérifie qu'un verrou est appliqué à l'aide du bit de GObject.* +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ +#ifndef NDEBUG +bool g_thick_object_check_lock(GThickObject *obj) +{ + bool result; /* Bilan à retourner */ + gboolean status; /* Bilan d'une tentative */ + + status = g_bit_trylock((gint *)&obj->extra, GOBJECT_LOCK_BIT); + + result = (status == FALSE); + + return result; + +} +#endif + + +/****************************************************************************** +* * +* Paramètres : obj = instance d'objet GLib à consulter. * +* * +* Description : Fournit la valeur courante de la zone de stockage d'un objet.* +* * +* Retour : Valeur de 32 lues et expurgées des bits GLib. * +* * +* Remarques : - * +* * +******************************************************************************/ + +guint g_thick_object_get_extra(const GThickObject *obj) +{ + guint result; /* Valeur à retourner */ + guint mask; /* Masque à appliquer */ + + result = g_atomic_int_get(&obj->extra); + + assert(GOBJECT_RESERVED_EXTRA_BITS < 30); + + mask = (1 << GOBJECT_RESERVED_EXTRA_BITS) - 1; + + result &= ~mask; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : obj = instance d'objet GLib à consulter. * +* val = valeur de 32 à conserver. * +* * +* Description : Définit la valeur courante de la zone de stockage d'un objet.* +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_thick_object_set_extra(GThickObject *obj, guint val) +{ + guint mask; /* Masque à appliquer */ + + assert(GOBJECT_RESERVED_EXTRA_BITS < 30); + + mask = (1 << GOBJECT_RESERVED_EXTRA_BITS) - 1; + + assert((val & mask) == 0); + + val &= ~mask; + + g_atomic_int_and(&obj->extra, val | mask); + + g_atomic_int_or(&obj->extra, val); + +} diff --git a/src/glibext/objhole.h b/src/glibext/objhole.h index 38f4bd5..461c37a 100644 --- a/src/glibext/objhole.h +++ b/src/glibext/objhole.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * objhole.h - prototypes pour l'utilisation d'un espace inutilisé dans la structure GObject * - * Copyright (C) 2020 Cyrille Bagard + * Copyright (C) 2020-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -25,118 +25,36 @@ #define _GLIBEXT_OBJHOLE_H -#include <glib.h> -#include <glib-object.h> - - -#include "../common/cpp.h" - - - -/** - * Une structure GObject a la définition suivante : - * - * struct _GObject - * { - * GTypeInstance g_type_instance; - * volatile guint ref_count; - * GData *qdata; - * }; - * - * En revanche, le fichier "glib/gobject/gobject.c" précise la définition - * réelle de la structure selon l'environnement : - * - * #if SIZEOF_INT == 4 && GLIB_SIZEOF_VOID_P == 8 - * #define HAVE_OPTIONAL_FLAGS - * #endif - * - * typedef struct - * { - * GTypeInstance g_type_instance; - * guint ref_count; - * #ifdef HAVE_OPTIONAL_FLAGS - * guint optional_flags; - * #endif - * GData *qdata; - * } GObjectReal; - * - * G_STATIC_ASSERT(sizeof(GObject) == sizeof(GObjectReal)); - * G_STATIC_ASSERT(G_STRUCT_OFFSET(GObject, ref_count) == G_STRUCT_OFFSET(GObjectReal, ref_count)); - * G_STATIC_ASSERT(G_STRUCT_OFFSET(GObject, qdata) == G_STRUCT_OFFSET(GObjectReal, qdata)); - * - * L'espace entre les deux derniers champs ne peut donc être pleinement exploité deux fois. - */ - -#if 0 - -# define GET_GOBJECT_EXTRA(obj, tp) \ - ({ \ - BUILD_BUG_ON(sizeof(tp) > sizeof(guint)); \ - tp *___result; \ - ___result = (tp *)(((guint *)&obj->ref_count) + 1); \ - BUILD_BUG_ON((___result + 1) == (tp *)&obj->qdata); \ - ___result; \ - }) - +#ifndef NDEBUG +# include <stdbool.h> #endif -/** - * Choix du bit de verrou pour le champ "lock". - * - * Dans la structure exploitant le mot utilisé ici, ce verrou est généralement - * placé dans le bit de poids fort pour les objets qui l'utilisent. - */ +#include "../glibext/helpers.h" + -#if __BYTE_ORDER == __LITTLE_ENDIAN -# define HOLE_LOCK_BIT 31 +#define G_TYPE_THICK_OBJECT (g_thick_object_get_type()) -#elif __BYTE_ORDER == __BIG_ENDIAN +DECLARE_GTYPE(GThickObject, g_thick_object, G, THICK_OBJECT); -# define HOLE_LOCK_BIT 0 -#else +/* Pose un verrou à l'aide du bit dédié de GObject. */ +void g_thick_object_lock(GThickObject *); -# error "Unknown byte order" +/* Retire un verrou via le bit dédié de GObject. */ +void g_thick_object_unlock(GThickObject *); +/* Vérifie qu'un verrou est appliqué à l'aide du bit de GObject. */ +#ifndef NDEBUG +bool g_thick_object_check_lock(GThickObject *obj); #endif +/* Fournit la valeur courante de la zone de stockage d'un objet. */ +guint g_thick_object_get_extra(const GThickObject *); -/* Verrou d'accès pour une encapsulation */ -typedef struct _lockable_obj_extra_t -{ - gint lock; /* Gestion d'accès aux fanions */ - -} lockable_obj_extra_t; - - -#define INIT_GOBJECT_EXTRA_LOCK(xtr) \ - do \ - { \ - lockable_obj_extra_t *__lockable; \ - __lockable = (lockable_obj_extra_t *)xtr; \ - __lockable->lock = 0; \ - } \ - while (0) - -#define LOCK_GOBJECT_EXTRA(xtr) \ - do \ - { \ - lockable_obj_extra_t *__lockable; \ - __lockable = (lockable_obj_extra_t *)xtr; \ - g_bit_lock(&__lockable->lock, HOLE_LOCK_BIT); \ - } \ - while (0) - -#define UNLOCK_GOBJECT_EXTRA(xtr) \ - do \ - { \ - lockable_obj_extra_t *__lockable; \ - __lockable = (lockable_obj_extra_t *)xtr; \ - g_bit_unlock(&__lockable->lock, HOLE_LOCK_BIT); \ - } \ - while (0) +/* Définit la valeur courante de la zone de stockage d'un objet. */ +void g_thick_object_set_extra(GThickObject *, guint); diff --git a/src/glibext/options/Makefile.am b/src/glibext/options/Makefile.am index 448de7b..3f400c6 100644 --- a/src/glibext/options/Makefile.am +++ b/src/glibext/options/Makefile.am @@ -2,7 +2,8 @@ noinst_LTLIBRARIES = libglibextoptions.la -libglibextoptions_la_SOURCES = \ +libglibextoptions_la_SOURCES = \ + asm.h \ hex.h hex.c libglibextoptions_la_CFLAGS = $(TOOLKIT_CFLAGS) diff --git a/src/glibext/options/asm.h b/src/glibext/options/asm.h new file mode 100644 index 0000000..d7a2c86 --- /dev/null +++ b/src/glibext/options/asm.h @@ -0,0 +1,65 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * asm.h - prototypes pour les options de rendus de code désassemblé + * + * Copyright (C) 2025 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_OPTIONS_ASM_H +#define _GLIBEXT_OPTIONS_ASM_H + + +#include "../helpers.h" + + + +/* Liste des colonnes en options */ +typedef enum _DisassColumnOptions +{ + ACO_PHYSICAL, /* Position physique */ + ACO_VIRTUAL, /* Adresse virtuelle */ + ACO_BINARY, /* Contenu sous forme binaire */ + + ACO_COUNT + +} DisassColumnOptions; + + +#define ACO_ASSEMBLY_LABEL (ACO_COUNT + 0) /* Etiquette dans les données */ +#define ACO_ASSEMBLY_HEAD (ACO_COUNT + 1) /* Instruction pour assembleur */ +#define ACO_ASSEMBLY (ACO_COUNT + 2) /* Code pour assembleur */ +#define ACO_COMMENTS (ACO_COUNT + 3) /* Commentaires éventuels */ + + + + + + +#if 0 +#define G_TYPE_HEX_OPTIONS (g_hex_options_get_type()) + +DECLARE_GTYPE(GHexOptions, g_hex_options, G, HEX_OPTIONS); + + +/* Crée un groupe d'options pour le rendu d'hexadécimal. */ +GHexOptions *g_hex_options_new(void); +#endif + + +#endif /* _GLIBEXT_OPTIONS_HEX_H */ diff --git a/src/glibext/gbinportion-int.h b/src/glibext/portion-int-ui.h index a29f53c..a29f53c 100644 --- a/src/glibext/gbinportion-int.h +++ b/src/glibext/portion-int-ui.h diff --git a/src/glibext/portion-int.h b/src/glibext/portion-int.h new file mode 100644 index 0000000..c044206 --- /dev/null +++ b/src/glibext/portion-int.h @@ -0,0 +1,69 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * portion-int.h - prototypes internes pour la définition interne des portions de binaire + * + * Copyright (C) 2019-2024 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef GLIBEXT_PORTION_INT_H +#define GLIBEXT_PORTION_INT_H + + +#include "portion.h" + + + +/* Portion de données binaires quelconques (instance) */ +struct _GBinaryPortion +{ + GObject parent; /* A laisser en premier */ + + char *desc; /* Désignation humaine */ + + mrange_t range; /* Emplacement dans le code */ + bool continued; /* Suite d'une découpe ? */ + + PortionAccessRights rights; /* Droits d'accès */ + + GBinaryPortion **subs; /* Portions incluses */ + size_t count; /* Quantité d'inclusions */ + +}; + +/* Portion de données binaires quelconques (classe) */ +struct _GBinaryPortionClass +{ + GObjectClass parent; /* A laisser en premier */ + +}; + + +/* Met en place une description de partie de code vierge. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_portion_create(GBinaryPortion *, const vmpa2t *, phys_t); + + + +#endif /* GLIBEXT_PORTION_INT_H */ diff --git a/src/glibext/gbinportion.c b/src/glibext/portion-ui.c index 12e12fb..12e12fb 100644 --- a/src/glibext/gbinportion.c +++ b/src/glibext/portion-ui.c diff --git a/src/glibext/gbinportion.h b/src/glibext/portion-ui.h index ea4b4aa..ea4b4aa 100644 --- a/src/glibext/gbinportion.h +++ b/src/glibext/portion-ui.h diff --git a/src/glibext/portion.c b/src/glibext/portion.c new file mode 100644 index 0000000..096a39d --- /dev/null +++ b/src/glibext/portion.c @@ -0,0 +1,974 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * portion.c - représentation graphique de portions de binaire + * + * Copyright (C) 2013-2024 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 <http://www.gnu.org/licenses/>. + */ + + +#include "portion.h" + + +#include <assert.h> +#include <malloc.h> +//#include <stdio.h> +//#include <stdlib.h> +#include <string.h> + + +#include <i18n.h> + + +#include "portion-int.h" +//#include "../analysis/human/asm/lang.h" +#include "../common/extstr.h" +#include "../common/sort.h" +//#include "../core/columns.h" +//#include "../glibext/gbinarycursor.h" +//#include "../glibext/linegen-int.h" + + + +/* ------------------------------- PORTION DE BINAIRE ------------------------------- */ + + +/* Initialise la classe des portions de données binaires. */ +static void g_binary_portion_class_init(GBinaryPortionClass *); + +/* Initialise une instance de portion de données binaires. */ +static void g_binary_portion_init(GBinaryPortion *); + +/* Supprime toutes les références externes. */ +static void g_binary_portion_dispose(GBinaryPortion *); + +/* Procède à la libération totale de la mémoire. */ +static void g_binary_portion_finalize(GBinaryPortion *); + + + +/* ------------------------ PARCOURS D'ENSEMBLES DE PORTIONS ------------------------ */ + + +/* Détermine si une portion contient une adresse donnée. */ +static bool g_binary_portion_contains_vmpa(const GBinaryPortion *, const vmpa2t *); + + + +/* ---------------------------------------------------------------------------------- */ +/* PORTION DE BINAIRE */ +/* ---------------------------------------------------------------------------------- */ + + +/* Indique le type défini par la GLib pour les portions de données binaires. */ +G_DEFINE_TYPE(GBinaryPortion, g_binary_portion, G_TYPE_OBJECT); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des portions de données binaires. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_binary_portion_class_init(GBinaryPortionClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = (GObjectFinalizeFunc/* ! */)g_binary_portion_dispose; + object->finalize = (GObjectFinalizeFunc)g_binary_portion_finalize; + + +} + + +/****************************************************************************** +* * +* Paramètres : portion = instance à initialiser. * +* * +* Description : Initialise une instance de portion de données binaires. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_binary_portion_init(GBinaryPortion *portion) +{ + vmpa2t dummy; /* Coquille presque vide */ + + portion->desc = NULL; + + init_vmpa(&dummy, VMPA_NO_PHYSICAL, VMPA_NO_VIRTUAL); + init_mrange(&portion->range, &dummy, VMPA_NO_VIRTUAL); + + portion->continued = false; + + portion->rights = PAC_NONE; + + portion->subs = NULL; + portion->count = 0; + +} + + +/****************************************************************************** +* * +* Paramètres : portion = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_binary_portion_dispose(GBinaryPortion *portion) +{ + size_t i; /* Boucle de parcours */ + + for (i = 0; i < portion->count; i++) + g_clear_object(&portion->subs[i]); + + G_OBJECT_CLASS(g_binary_portion_parent_class)->dispose(G_OBJECT(portion)); + +} + + +/****************************************************************************** +* * +* Paramètres : portion = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_binary_portion_finalize(GBinaryPortion *portion) +{ + if (portion->desc != NULL) + free(portion->desc); + + if (portion->subs != NULL) + free(portion->subs); + + G_OBJECT_CLASS(g_binary_portion_parent_class)->finalize(G_OBJECT(portion)); + +} + + +/****************************************************************************** +* * +* Paramètres : addr = emplacement de la section à conserver. * +* size = taille de la section à conserver. * +* * +* Description : Crée une description de partie de code vierge. * +* * +* Retour : Instance mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GBinaryPortion *g_binary_portion_new(const vmpa2t *addr, phys_t size) +{ + GBinaryPortion *result; /* Structure à retourner */ + + result = g_object_new(G_TYPE_BINARY_PORTION, NULL); + + if (!g_binary_portion_create(result, addr, size)) + g_clear_object(&result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : portion = instance à initialiser pleinement. * +* addr = emplacement de la section à conserver. * +* size = taille de la section à conserver. * +* * +* Description : Met en place une description de partie de code vierge. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_portion_create(GBinaryPortion *portion, const vmpa2t *addr, phys_t size) +{ + bool result; /* Bilan à retourner */ + + result = true; + + init_mrange(&portion->range, addr, size); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : a = premières informations à consulter. * +* b = secondes informations à consulter. * +* * +* Description : Etablit la comparaison ascendante entre deux portions. * +* * +* Retour : Bilan : -1 (a < b), 0 (a == b) ou 1 (a > b). * +* * +* Remarques : - * +* * +******************************************************************************/ + +int g_binary_portion_compare(const GBinaryPortion **a, const GBinaryPortion **b) +{ + int result; /* Bilan à retourner */ + const vmpa2t *addr_a; /* Adresse de la portion 'a' */ + const vmpa2t *addr_b; /* Adresse de la portion 'b' */ + + addr_a = get_mrange_addr(&(*a)->range); + addr_b = get_mrange_addr(&(*b)->range); + + result = cmp_vmpa(addr_a, addr_b); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : portion = description de partie à mettre à jour. * +* desc = nom à donner à la partie. * +* * +* Description : Attribue une description humaine à une partie de code. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_binary_portion_set_desc(GBinaryPortion *portion, const char *desc) +{ + if (portion->desc != NULL) + free(portion->desc); + + if (desc == NULL) + portion->desc = NULL; + + else + { + portion->desc = strdup(desc); + + if (portion->continued) + portion->desc = stradd(portion->desc, _(" (continued)")); + + } + +} + + +/****************************************************************************** +* * +* Paramètres : portion = description de partie à consulter. * +* * +* Description : Fournit la description attribuée à une partie de code. * +* * +* Retour : Nom donné à la partie. * +* * +* Remarques : - * +* * +******************************************************************************/ + +const char *g_binary_portion_get_desc(const GBinaryPortion *portion) +{ + return portion->desc; + +} + + +/****************************************************************************** +* * +* Paramètres : portion = description de partie à mettre à jour. * +* * +* Description : Fournit l'emplacement d'une partie de code binaire. * +* * +* Retour : Espace de couverture associé à la portion. * +* * +* Remarques : - * +* * +******************************************************************************/ + +const mrange_t *g_binary_portion_get_range(const GBinaryPortion *portion) +{ + return &portion->range; + +} + +/****************************************************************************** +* * +* Paramètres : portion = description de partie à mettre à jour. * +* max = taille maximale accordée à la portion. * +* * +* Description : Assure qu'une portion ne dépasse pas une position donnée. * +* * +* Retour : true si la portion a été modifiée, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_portion_limit_range(GBinaryPortion *portion, phys_t max) +{ + bool result; /* Bilan à retourner */ + phys_t current; /* Taille courante */ + + current = get_mrange_length(&portion->range); + + result = (current > max); + + if (result) + set_mrange_length(&portion->range, max); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : portion = portion dont la définition est à metre à jour. * +* * +* Description : Définit la nature de la portion en terme d'originalité. * +* * +* Retour : - * +* * +* Remarques : L'action ne modifie aucunement la description courante. * +* C'est le changement de description qui s'appuie sur la * +* notée ici. * +* * +******************************************************************************/ + +void g_binary_portion_mark_as_continued(GBinaryPortion *portion, bool continued) +{ + portion->continued = continued; + +} + + +/****************************************************************************** +* * +* Paramètres : portion = portion dont la définition est à consulter. * +* * +* Description : Indique la nature de la portion en terme d'originalité. * +* * +* Retour : true si la portion est la suite d'une portion découpée. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_portion_is_continuation(const GBinaryPortion *portion) +{ + return portion->continued; + +} + + +/****************************************************************************** +* * +* Paramètres : portion = description de partie à mettre à jour. * +* rights = droits d'accès de la partie. * +* * +* Description : Définit les droits associés à une partie de code. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_binary_portion_set_rights(GBinaryPortion *portion, PortionAccessRights rights) +{ + portion->rights = rights; + +} + + +/****************************************************************************** +* * +* Paramètres : portion = description de partie à consulter. * +* * +* Description : Fournit les droits associés à une partie de code. * +* * +* Retour : Droits d'accès de la partie. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PortionAccessRights g_binary_portion_get_rights(const GBinaryPortion *portion) +{ + return portion->rights; + +} + + +/****************************************************************************** +* * +* Paramètres : portion = portion principale à compléter. * +* sub = portion à inclure dans la définition courante. * +* * +* Description : Procède à l'inclusion d'une portion dans une autre. * +* * +* Retour : Bilan de l'opération : true si inclusion, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_portion_include(GBinaryPortion *portion, GBinaryPortion *sub) +{ + bool result; /* Bilan à retourner */ + bool found; /* Zone d'accueil trouvée ? */ + size_t best; /* Meilleur point d'insertion */ + size_t missed; /* Indice de zone à déplacer */ + const mrange_t *brange; /* Raccourci de confort d'usage*/ + vmpa2t end; /* Fin de la zone commune */ + phys_t overlapping; /* Taille de la zone commune */ + bool continued; /* Suite d'une découpe ? */ + GBinaryPortion *left_part; /* Partie intégrable */ + vmpa2t start; /* Départ de la seconde partie */ + GBinaryPortion *right_part; /* Partie restante */ + + int g_binary_portion_is_included(const GBinaryPortion **a, const GBinaryPortion **b) + { + int result; /* Bilan à retourner */ + + result = mrange_includes_mrange(&(*b)->range, &(*a)->range); + + return result; + + } + + found = bsearch_index(&sub, portion->subs, portion->count, sizeof(GBinaryPortion *), + (__compar_fn_t)g_binary_portion_is_included, &best); + + if (!found) + { + /** + * On se prépare à réaliser une insertion au niveau courant. Mais des + * portions précédentes sont peut-être à déplacer dans la nouvelle zone : + * + * EXIDX 0x001178 0x00009178 0x00009178 0x00008 0x00008 R 0x4 + * PHDR 0x000034 0x00008034 0x00008034 0x00120 0x00120 R E 0x4 + * INTERP 0x000154 0x00008154 0x00008154 0x00019 0x00019 R 0x1 + * LOAD 0x000000 0x00008000 0x00008000 0x01184 0x01184 R E 0x8000 + * + * On refait donc une passe sur toutes les sous-portions du niveau. + * + * Cette approche a le mérite de traiter également et naturellement les + * sections définies dans le désordre : + * + * [21] .bss NOBITS 00088240 07823c 0018c8 00 WA 0 0 8 + * [22] __libc_freeres_ptrs NOBITS 00089b08 07823c 000018 00 WA 0 0 4 + * [23] .comment PROGBITS 00000000 07823c 000022 01 MS 0 0 1 + * + * Quant aux cas de figure où les portions sont identiques, l'ordre d'appel + * induit l'ordre d'inclusion. + * + * Cela concerne par exemple les zones de données : + * + * En-têtes de section: + * [Nr] Nom Type Adr Décala.Taille ES Fan LN Inf Al + * ... + * [ 2] .data PROGBITS 00010098 000098 00000c 00 WA 0 0 1 + * + * En-têtes de programme: + * Type Décalage Adr. vir. Adr.phys. T.Fich. T.Mém. Fan Alignement + * ... + * LOAD 0x000098 0x00010098 0x00010098 0x0000c 0x0000c RW 0x8000 + * + */ + + int g_binary_portion_track_missed_inclusion(const GBinaryPortion **a, const GBinaryPortion **b) + { + int result; /* Bilan à retourner */ + + result = mrange_includes_mrange(&(*a)->range, &(*b)->range); + + return result; + + } + + do + { + found = bsearch_index(&sub, portion->subs, portion->count, sizeof(GBinaryPortion *), + (__compar_fn_t)g_binary_portion_track_missed_inclusion, &missed); + + if (found) + { + result = g_binary_portion_include(sub, portion->subs[missed]); + assert(result); + + portion->subs = _qdelete(portion->subs, &portion->count, sizeof(GBinaryPortion *), missed); + + } + + } + while (found); + + /** + * Il peut arriver que certaines portions débordent de leur zone d'inclusion : + * + * [24] .bss NOBITS 00012088 002084 000044 00 WA 0 0 8 + * [25] .ARM.attributes ARM_ATTRIBUTES 00000000 002084 000037 00 0 0 1 + * [26] .shstrtab STRTAB 00000000 0020bb 0000ed 00 0 0 1 + * + * Afin de respecter une certaine cohérence dans l'arbre des portions, on choisit + * de découper la portion qui déborde. + */ + + int g_binary_portion_track_partial_inclusion(const GBinaryPortion **a, const GBinaryPortion **b) + { + int result; /* Bilan à retourner */ + + result = cmp_mrange_with_vmpa(&(*b)->range, get_mrange_addr(&(*a)->range)); + + return result; + + } + + found = bsearch_index(&sub, portion->subs, portion->count, sizeof(GBinaryPortion *), + (__compar_fn_t)g_binary_portion_track_partial_inclusion, &best); + + if (found) + { + brange = &portion->subs[best]->range; + + compute_mrange_end_addr(brange, &end); + overlapping = compute_vmpa_diff(get_mrange_addr(&sub->range), &end); + + continued = g_binary_portion_is_continuation(sub); + + /* Partie contenue */ + + left_part = g_binary_portion_new(get_mrange_addr(&sub->range), overlapping); + + g_binary_portion_set_desc(left_part, sub->desc); + g_binary_portion_mark_as_continued(left_part, continued); + g_binary_portion_set_rights(left_part, sub->rights); + + /* Partie qui déborde... */ + + /** + * Comme la portion incluante peut avoir une définition d'adresse + * virtuelle différente de celle de la portion incluse, on recalcule + * la position de départ de la seconde partie de la portion découpée + * à partir des données d'origine. + */ + + copy_vmpa(&start, get_mrange_addr(&sub->range)); + advance_vmpa(&start, overlapping); + + right_part = g_binary_portion_new(&start, get_mrange_length(&sub->range) - overlapping); + + if (!continued) + g_binary_portion_mark_as_continued(right_part, true); + + g_binary_portion_set_desc(right_part, sub->desc); + + if (continued) + g_binary_portion_mark_as_continued(right_part, true); + + g_binary_portion_set_rights(right_part, sub->rights); + + /* Inclusions des parties */ + + result = g_binary_portion_include(portion, left_part); + + if (result) + result = g_binary_portion_include(portion, right_part); + + unref_object(left_part); + unref_object(right_part); + + } + + else + { + ref_object(sub); + + portion->subs = qinsert(portion->subs, &portion->count, sizeof(GBinaryPortion *), + (__compar_fn_t)g_binary_portion_compare, &sub); + + result = true; + + } + + } + + /* Poursuite de l'inclusion dans la sous-portion adaptée... */ + else + result = g_binary_portion_include(portion->subs[best], sub); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : portion = première couche amorçant la visite. * +* visitor = fonction à appeler à chaque étape de la descente. * +* data = adresse pointant vers des données de l'utilisateur.* +* * +* Description : Parcourt un ensemble de portions binaires. * +* * +* Retour : true si la visite a été jusqu'à son terme, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_portion_visit(GBinaryPortion *portion, visit_portion_fc visitor, void *data) +{ + bool result; /* Etat à retourner */ + + bool visit_portion(GBinaryPortion *p, GBinaryPortion *pp) + { + bool ret; /* Etat à retourner */ + size_t i; /* Boucle de parcours */ + + if (p->count == 0) + ret = visitor(p, pp, BPV_SHOW, data); + + else + { + ret = visitor(p, pp, BPV_ENTER, data); + + for (i = 0; i < p->count && ret; i++) + ret = visit_portion(p->subs[i], p); + + if (ret) + ret = visitor(p, pp, BPV_EXIT, data); + + } + + return ret; + + } + + result = visit_portion(portion, NULL); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* PARCOURS D'ENSEMBLES DE PORTIONS */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : portion = portion mère à consulter. * +* addr = adresse du point de recherche. * +* * +* Description : Détermine si une portion contient une adresse donnée. * +* * +* Retour : true ou false selon le résultat. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_binary_portion_contains_vmpa(const GBinaryPortion *portion, const vmpa2t *addr) +{ + bool result; /* Bilan à retourner */ + const mrange_t *range; /* Emplacement de portion */ + + result = false; + + range = g_binary_portion_get_range(portion); + + /* Portion non allouée en mémoire : on écarte */ + if (!has_virt_addr(get_mrange_addr(range))) + goto not_found; + + result = mrange_contains_addr(range, addr); + + not_found: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : portion = couche de portions à parcourir pour les recherches.* +* addr = adresse du point de recherche. * +* * +* Description : Recherche la portion présente à une adresse donnée. * +* * +* Retour : Portion trouvée à l'endroit indiqué. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GBinaryPortion *g_binary_portion_find_at_addr(GBinaryPortion *portion, const vmpa2t *addr) +{ + GBinaryPortion *result; /* Portion à retourner */ + size_t i; /* Boucle de parcours */ + GBinaryPortion *sub; /* Portion incluse à traiter */ + + result = NULL; + + if (!g_binary_portion_contains_vmpa(portion, addr)) + goto done; + + for (i = 0; i < portion->count && result == NULL; i++) + { + sub = portion->subs[i]; + + if (!g_binary_portion_contains_vmpa(sub, addr)) + continue; + + result = g_binary_portion_find_at_addr(sub, addr); + + } + + if (result == NULL) + { + result = portion; + ref_object(result); + } + + done: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : portion = portion mère à consulter. * +* off = position physique du point de recherche. * +* * +* Description : Détermine si une portion contient une position donnée. * +* * +* Retour : true ou false selon le résultat. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_binary_portion_contains_physical(const GBinaryPortion *portion, phys_t off) +{ + bool result; /* Bilan à retourner */ + const mrange_t *range; /* Emplacement de portion */ + const vmpa2t *addr; /* Départ de la portion */ + + range = g_binary_portion_get_range(portion); + addr = get_mrange_addr(range); + + if (!has_phys_addr(addr)) + result = false; + + else + result = (addr->physical <= off && off < (addr->physical + range->length)); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : portion = couche de portions à parcourir pour les recherches.* +* off = position physique à retrouver. * +* pos = position correspondante. [OUT] * +* * +* Description : Fournit l'emplacement correspondant à une position physique. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_portion_translate_offset_into_vmpa(const GBinaryPortion *portion, phys_t off, vmpa2t *pos) +{ + bool result; /* Bilan à retourner */ + size_t i; /* Boucle de parcours #1 */ + GBinaryPortion *sub; /* Portion incluse à traiter */ + const mrange_t *range; /* Emplacement de portion */ + const vmpa2t *addr; /* Départ de la portion */ + + result = false; + + for (i = 0; i < portion->count; i++) + { + sub = portion->subs[i]; + + if (!g_binary_portion_contains_physical(sub, off)) + continue; + + result = g_binary_portion_translate_offset_into_vmpa(sub, off, pos); + + break; + + } + + if (i == portion->count) + { + result = g_binary_portion_contains_physical(portion, off); + + if (result) + { + range = g_binary_portion_get_range(portion); + addr = get_mrange_addr(range); + + if (has_virt_addr(get_mrange_addr(range))) + init_vmpa(pos, off, addr->virtual + off - addr->physical); + + else + init_vmpa(pos, off, VMPA_NO_VIRTUAL); + + } + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : portion = portion mère à consulter. * +* virt = adresse virtuelle du point de recherche. * +* * +* Description : Détermine si une portion contient une adresse donnée. * +* * +* Retour : true ou false selon le résultat. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_binary_portion_contains_virtual(const GBinaryPortion *portion, virt_t virt) +{ + bool result; /* Bilan à retourner */ + const mrange_t *range; /* Emplacement de portion */ + const vmpa2t *addr; /* Départ de la portion */ + + range = g_binary_portion_get_range(portion); + addr = get_mrange_addr(range); + + if (!has_virt_addr(addr)) + result = false; + + else + result = (addr->virtual <= virt && virt < (addr->virtual + range->length)); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : portion = couche de portions à parcourir pour les recherches.* +* virt = adresse virtuelle à retrouver. * +* pos = position correspondante. [OUT] * +* * +* Description : Fournit l'emplacement correspondant à une adresse virtuelle. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_binary_portion_translate_address_into_vmpa(const GBinaryPortion *portion, virt_t virt, vmpa2t *pos) +{ + bool result; /* Bilan à retourner */ + size_t i; /* Boucle de parcours #1 */ + GBinaryPortion *sub; /* Portion incluse à traiter */ + const mrange_t *range; /* Emplacement de portion */ + const vmpa2t *addr; /* Départ de la portion */ + + result = false; + + for (i = 0; i < portion->count; i++) + { + sub = portion->subs[i]; + + if (!g_binary_portion_contains_virtual(sub, virt)) + continue; + + result = g_binary_portion_translate_address_into_vmpa(sub, virt, pos); + + break; + + } + + if (i == portion->count) + { + result = g_binary_portion_contains_virtual(portion, virt); + + if (result) + { + range = g_binary_portion_get_range(portion); + addr = get_mrange_addr(range); + + if (has_phys_addr(addr) && has_virt_addr(addr)) + init_vmpa(pos, addr->physical + virt - addr->virtual, virt); + + else + init_vmpa(pos, VMPA_NO_PHYSICAL, virt); + + } + + } + + return result; + +} diff --git a/src/glibext/portion.h b/src/glibext/portion.h new file mode 100644 index 0000000..88d69b6 --- /dev/null +++ b/src/glibext/portion.h @@ -0,0 +1,121 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * portion.h - prototypes pour la représentation graphique de portions de binaire + * + * Copyright (C) 2013-2024 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_PORTION_H +#define _GLIBEXT_PORTION_H + + +#include <stdbool.h> + + +#include "helpers.h" +#include "../arch/vmpa.h" + + + +/* ------------------------------- PORTION DE BINAIRE ------------------------------- */ + + +#define G_TYPE_BINARY_PORTION (g_binary_portion_get_type()) + +DECLARE_GTYPE(GBinaryPortion, g_binary_portion, G, BINARY_PORTION); + + +/* Crée une description de partie de code vierge. */ +GBinaryPortion *g_binary_portion_new(const vmpa2t *, phys_t); + +/* Etablit la comparaison ascendante entre deux portions. */ +int g_binary_portion_compare(const GBinaryPortion **, const GBinaryPortion **); + +/* Attribue une description humaine à une partie de code. */ +void g_binary_portion_set_desc(GBinaryPortion *, const char *); + +/* Fournit la description attribuée à une partie de code. */ +const char *g_binary_portion_get_desc(const GBinaryPortion *); + +/* Fournit l'emplacement d'une partie de code binaire. */ +const mrange_t *g_binary_portion_get_range(const GBinaryPortion *); + +/* Assure qu'une portion ne dépasse pas une position donnée. */ +bool g_binary_portion_limit_range(GBinaryPortion *, phys_t); + +/* Définit la nature de la portion en terme d'originalité. */ +void g_binary_portion_mark_as_continued(GBinaryPortion *, bool); + +/* Indique la nature de la portion en terme d'originalité. */ +bool g_binary_portion_is_continuation(const GBinaryPortion *); + +/* Droits d'accès à une portion */ +typedef enum _PortionAccessRights +{ + PAC_NONE = (0 << 0), /* Aucun */ + PAC_READ = (1 << 0), /* Lecture */ + PAC_WRITE = (1 << 1), /* Ecriture */ + PAC_EXEC = (1 << 2) /* Exécution */ + +} PortionAccessRights; + +#define PAC_ALL ((PortionAccessRights)(PAC_READ | PAC_WRITE | PAC_EXEC)) + +/* Définit les droits associés à une partie de code. */ +void g_binary_portion_set_rights(GBinaryPortion *, PortionAccessRights); + +/* Fournit les droits associés à une partie de code. */ +PortionAccessRights g_binary_portion_get_rights(const GBinaryPortion *); + +/* Procède à l'inclusion d'une portion dans une autre. */ +bool g_binary_portion_include(GBinaryPortion *, GBinaryPortion *); + +/* Sens des visites */ +typedef enum _BinaryPortionVisit +{ + BPV_ENTER, /* Arrivée sur une branche */ + BPV_SHOW, /* Visite d'une feuille */ + BPV_EXIT /* Départ d'une branche */ + +} BinaryPortionVisit; + + +/* Fonction appelée à chaque visite de portion.*/ +typedef bool (* visit_portion_fc) (GBinaryPortion *, GBinaryPortion *, BinaryPortionVisit, void *); + +/* Parcourt un ensemble de portions binaires. */ +bool g_binary_portion_visit(GBinaryPortion *, visit_portion_fc, void *); + + + +/* ------------------------ PARCOURS D'ENSEMBLES DE PORTIONS ------------------------ */ + + +/* Recherche la portion présente à une adresse donnée. */ +GBinaryPortion *g_binary_portion_find_at_addr(GBinaryPortion *, const vmpa2t *); + +/* Fournit l'emplacement correspondant à une position physique. */ +bool g_binary_portion_translate_offset_into_vmpa(const GBinaryPortion *, phys_t, vmpa2t *); + +/* Fournit l'emplacement correspondant à une adresse virtuelle. */ +bool g_binary_portion_translate_address_into_vmpa(const GBinaryPortion *, virt_t, vmpa2t *); + + + +#endif /* _GLIBEXT_BINPORTION_H */ diff --git a/src/glibext/secstorage-int.h b/src/glibext/secstorage-int.h new file mode 100644 index 0000000..bbac133 --- /dev/null +++ b/src/glibext/secstorage-int.h @@ -0,0 +1,60 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * secstorage-int.h - définitions internes pour la conservation sécurisée d'éléments de configuration + * + * Copyright (C) 2025 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_SECSTORAGE_INT_H +#define _GLIBEXT_SECSTORAGE_INT_H + + +#include "secstorage.h" + + + +/* Gardien des secrets avec support des stockages (instance) */ +struct _GSecretStorage +{ + GObject parent; /* A laisser en premier */ + + GSettings *settings; /* Configuration sollicitée */ + + void *master_key; /* Clef déverrouillée */ + +}; + +/* Gardien des secrets avec support des stockages (classe) */ +struct _GSecretStorageClass +{ + GObjectClass parent; /* A laisser en premier */ + + /* Signaux */ + + void (* lock_update) (GSecretStorage *); + +}; + + +/* Met en place un gardien des secrets avec support de stockage. */ +bool g_secret_storage_create(GSecretStorage *, GSettings *); + + + +#endif /* _GLIBEXT_SECSTORAGE_INT_H */ diff --git a/src/glibext/secstorage.c b/src/glibext/secstorage.c new file mode 100644 index 0000000..161214c --- /dev/null +++ b/src/glibext/secstorage.c @@ -0,0 +1,950 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * secstorage.c - conservation sécurisée d'éléments de configuration + * + * Copyright (C) 2025 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 <http://www.gnu.org/licenses/>. + */ + + +#include "secstorage.h" + + +#include <assert.h> +#include <string.h> +#include <openssl/evp.h> +#include <openssl/rand.h> + + +#include "secstorage-int.h" +#include "../core/logs.h" + + + +/** + * Les mécanismes de hachage de mot de passe doivent être utilisés avec un sel, + * et la longueur du sel doit être d’au moins 128 bits. + * + * Cette note concerne le hachage de mots de passe et non la dérivation de secrets + * cependant. + * + * Source : https://cyber.gouv.fr/sites/default/files/2021/03/anssi-guide-selection_crypto-1.0.pdf + */ + +#define SECRET_STORAGE_SALT_SIZE (256 / 8) + + +/** + * Nombre d'itérations pour PBKDF2 : en 2023, OWASP recommande 600000 itérations + * pour PBKDF2-HMAC-SHA256 (et 210000 pour PBKDF2-HMAC-SHA512). + * + * Source : https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2 + */ + +#define PBKDF2_HMAC_SHA256_ITERATIONS (2 << 20) + + +/** + * AES 256 : clef de 256 bits, IV de 128 bits + */ + +#define SECRET_STORAGE_KEK_SIZE (256 / 8) + +#define SECRET_STORAGE_KEY_SIZE (256 / 8) + +#define SECRET_STORAGE_BLOCK_SIZE (128 / 8) + +#define SECRET_STORAGE_IV_SIZE SECRET_STORAGE_BLOCK_SIZE + + + +/* Initialise la classe des stockages de secrets. */ +static void g_secret_storage_class_init(GSecretStorageClass *); + +/* Initialise une instance de stockage de secrets. */ +static void g_secret_storage_init(GSecretStorage *); + +/* Supprime toutes les références externes. */ +static void g_secret_storage_dispose(GObject *); + +/* Procède à la libération totale de la mémoire. */ +static void g_secret_storage_finalize(GObject *); + +/* Teste un mot de passe par Déverrouillage de clef maître. */ +static bool g_secret_storage_check_primary_password(GSecretStorage *, const char *, void **); + + + +/* Indique le type défini pour un gardien des secrets avec support des stockages. */ +G_DEFINE_TYPE(GSecretStorage, g_secret_storage, G_TYPE_OBJECT); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des stockages de secrets. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_secret_storage_class_init(GSecretStorageClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = g_secret_storage_dispose; + object->finalize = g_secret_storage_finalize; + + g_signal_new("lock-update", + G_TYPE_SECRET_STORAGE, + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(GSecretStorageClass, lock_update), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + +} + + +/****************************************************************************** +* * +* Paramètres : storage = instance à initialiser. * +* * +* Description : Initialise une instance de stockage de secrets. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_secret_storage_init(GSecretStorage *storage) +{ + storage->settings = NULL; + + storage->master_key = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_secret_storage_dispose(GObject *object) +{ + GSecretStorage *storage; /* Version spécialisée */ + + storage = G_SECRET_STORAGE(object); + + g_clear_object(&storage->settings); + + G_OBJECT_CLASS(g_secret_storage_parent_class)->dispose(object); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_secret_storage_finalize(GObject *object) +{ + GSecretStorage *storage; /* Version spécialisée */ + + storage = G_SECRET_STORAGE(object); + + if (storage->master_key != NULL) + free(storage->master_key); + + G_OBJECT_CLASS(g_secret_storage_parent_class)->finalize(object); + +} + + +/****************************************************************************** +* * +* Paramètres : settings = éventuel espace de configuration à utiliser. * +* * +* Description : Créé un nouveau gardien des secrets avec support de stockage.* +* * +* Retour : Gestionnaire de stockage sécurisé mis en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GSecretStorage *g_secret_storage_new(GSettings *settings) +{ + GSecretStorage *result; /* Instance à retourner */ + + result = g_object_new(G_TYPE_SECRET_STORAGE, NULL); + + if (!g_secret_storage_create(result, settings)) + g_clear_object(&result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = stockage sécurisé à initialiser. * +* settings = éventuel espace de configuration à utiliser. * +* * +* Description : Met en place un gardien des secrets avec support de stockage.* +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_secret_storage_create(GSecretStorage *storage, GSettings *settings) +{ + bool result; /* Bilan à retourner */ + + result = true; + + if (settings != NULL) + { + ref_object(settings); + storage->settings = settings; + } + else + storage->settings = g_settings_new("re.chrysalide.framework.secstorage"); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = espace de stockage sécurisé à consulter. * +* * +* Description : Détermine si une clef de chiffrement protégée est en place. * +* * +* Retour : Bilan de l'analyse. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_secret_storage_has_key(const GSecretStorage *storage) +{ + bool result; /* Bilan à retourner */ + GVariant *value; /* Valeur de configuration */ + gsize length; /* Taille d'une valeur donnée */ + + result = false; + + value = g_settings_get_value(storage->settings, "master"); + + g_variant_get_fixed_array(value, &length, 1); + + result = (length > SECRET_STORAGE_IV_SIZE); + + g_variant_unref(value); + + return result;; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = espace de stockage sécurisé à consulter. * +* password = mot de passe principal à appliquer. * +* * +* Description : Définit un mot de passe pour protéger une clef maître. * +* * +* Retour : Bilan de la mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_secret_storage_set_password(GSecretStorage *storage, const char *passwd) +{ + bool result; /* Bilan à retourner */ + unsigned char salt[SECRET_STORAGE_SALT_SIZE]; /* Sel pour la dérivation*/ + int ret; /* Bilan à d'un appel */ + GVariant *value; /* Valeur de configuration */ + unsigned char kek[SECRET_STORAGE_KEK_SIZE]; /* Clef de protection */ + unsigned char key[SECRET_STORAGE_KEY_SIZE]; /* Clef maître */ + unsigned char iv[SECRET_STORAGE_IV_SIZE]; /* IV associé */ + EVP_CIPHER_CTX *ctx; /* Contexte pour le chiffrement*/ + unsigned char encrypted[64]; /* Partie chiffrée à conserver */ + unsigned char *iter; /* Tête d'écriture */ + int outlen; /* Taille des données utiles */ + + result = false; + + if (g_secret_storage_has_key(storage)) + goto exit; + + /* Création d'un sel pour la dérivation du mot de passe */ + + ret = RAND_bytes(salt, sizeof(salt)); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit; + } + + /** + * La fonction g_variant_new_fixed_array() retourne un variant + * avec un décompte de référence flottant. + */ + + value = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, + salt, SECRET_STORAGE_SALT_SIZE, sizeof(unsigned char)); + + /** + * Comme le variant à une référence flottante, la fonction + * g_settings_set_value() consomme cette référence. + * + * Il n'y a donc pas lieu d'appeler g_variant_unref(). + */ + + g_settings_set_value(storage->settings, "salt", value); + + /* Dérivation du mot de passe */ + + ret = PKCS5_PBKDF2_HMAC(passwd, strlen(passwd), + salt, SECRET_STORAGE_SALT_SIZE, + PBKDF2_HMAC_SHA256_ITERATIONS, EVP_sha256(), + SECRET_STORAGE_KEK_SIZE, kek); + + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit; + } + + /* Définition de la clef maître et de son IV de chiffrement */ + + ret = RAND_bytes(key, sizeof(key)); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit; + } + + ret = RAND_bytes(iv, sizeof(iv)); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit; + } + + /* Chiffrement de la clef maître */ + + ctx = EVP_CIPHER_CTX_new(); + + if (ctx == NULL) + { + LOG_ERROR_OPENSSL; + goto exit; + } + + EVP_CIPHER_CTX_set_flags(ctx, EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); + + ret = EVP_EncryptInit_ex2(ctx, EVP_aes_256_wrap_pad(), kek, iv, NULL); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + memcpy(encrypted, iv, SECRET_STORAGE_IV_SIZE); + + iter = encrypted + SECRET_STORAGE_IV_SIZE; + + ret = EVP_EncryptUpdate(ctx, iter, &outlen, key, SECRET_STORAGE_KEY_SIZE); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + iter += outlen; + + ret = EVP_EncryptFinal_ex(ctx, iter, &outlen); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + iter += outlen; + + assert((iter - encrypted) < 64); + + /* Conservation de la clef protégée */ + + value = g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, + encrypted, iter - encrypted, sizeof(unsigned char)); + + g_settings_set_value(storage->settings, "master", value); + + g_signal_emit_by_name(storage, "lock-update"); + + result = true; + + exit_with_ctx: + + EVP_CIPHER_CTX_free(ctx); + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = espace de stockage sécurisé à consulter. * +* old = ancien mot de passe principal à vérifier. * +* new = nouveau mot de passe principal à appliquer. * +* * +* Description : Modifie le mot de passe protégeant une clef maître. * +* * +* Retour : Bilan de la mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_secret_storage_change_password(GSecretStorage *storage, const char *old, const char *new) +{ + bool result; /* Bilan à retourner */ + + result = false; + + if (!g_secret_storage_has_key(storage)) + goto exit; + + if (!g_secret_storage_check_primary_password(storage, old, NULL)) + goto exit; + + if (!g_secret_storage_is_locked(storage)) + g_secret_storage_lock(storage); + + g_settings_reset(storage->settings, "salt"); + g_settings_reset(storage->settings, "master"); + + result = g_secret_storage_set_password(storage, new); + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = espace de stockage sécurisé à consulter. * +* password = mot de passe principal à appliquer. * +* * +* Description : Supprime le mot de passe protégeant une clef maître. * +* * +* Retour : Bilan de la mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_secret_storage_remove_password(GSecretStorage *storage, const char *passwd) +{ + bool result; /* Bilan à retourner */ + + result = false; + + if (!g_secret_storage_has_key(storage)) + goto exit; + + if (!g_secret_storage_check_primary_password(storage, passwd, NULL)) + goto exit; + + if (!g_secret_storage_is_locked(storage)) + g_secret_storage_lock(storage); + + g_settings_reset(storage->settings, "salt"); + g_settings_reset(storage->settings, "master"); + + g_signal_emit_by_name(storage, "lock-update"); + + result = true; + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = espace de stockage sécurisé à consulter. * +* * +* Description : Détermine si la clef de chiffrement maître est vérouillée. * +* * +* Retour : Bilan de la détermination. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_secret_storage_is_locked(const GSecretStorage *storage) +{ + bool result; /* Bilan à retourner */ + + result = (storage->master_key == NULL); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = espace de stockage sécurisé à manipuler. * +* password = mot de passe principal à utiliser. * +* master = éventuelle adresse pour un stockage de clef. [OUT]* +* * +* Description : Teste un mot de passe par Déverrouillage de clef maître. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_secret_storage_check_primary_password(GSecretStorage *storage, const char *passwd, void **master) +{ + bool result; /* Bilan à retourner */ + GVariant *salt_value; /* Valeur du sel configuré */ + gsize salt_length; /* Taille du sel conservé */ + gconstpointer salt; /* Données associées #1 */ + unsigned char kek[SECRET_STORAGE_KEK_SIZE]; /* Clef de protection */ + int ret; /* Bilan à d'un appel */ + GVariant *enc_value; /* Paramètres de chiffrement */ + gsize enc_length; /* Taille de ces paramètrs */ + gconstpointer encrypted; /* Données associées #2 */ + EVP_CIPHER_CTX *ctx; /* Contexte de déchiffrement */ + unsigned char iv[SECRET_STORAGE_IV_SIZE]; /* IV associé */ + unsigned char key[SECRET_STORAGE_KEY_SIZE]; /* Clef maître */ + unsigned char *iter; /* Tête d'écriture */ + int outlen; /* Taille des données utiles */ + + result = false; + + if (!g_secret_storage_is_locked(storage)) + { + result = true; + goto quick_exit; + } + + /* Récupération du sel mis en place */ + + salt_value = g_settings_get_value(storage->settings, "salt"); + + salt = g_variant_get_fixed_array(salt_value, &salt_length, sizeof(bin_t)); + + if (salt_length != SECRET_STORAGE_SALT_SIZE) + goto exit_with_salt; + + /* Dérivation du mot de passe */ + + ret = PKCS5_PBKDF2_HMAC(passwd, strlen(passwd), + salt, SECRET_STORAGE_SALT_SIZE, + PBKDF2_HMAC_SHA256_ITERATIONS, EVP_sha256(), + SECRET_STORAGE_KEK_SIZE, kek); + + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_salt; + } + + /* Récupération des paramètres chiffrés */ + + enc_value = g_settings_get_value(storage->settings, "master"); + + encrypted = g_variant_get_fixed_array(enc_value, &enc_length, sizeof(bin_t)); + + if (enc_length <= SECRET_STORAGE_IV_SIZE) + goto exit_with_enc; + + /* Déhiffrement de la clef maître */ + + ctx = EVP_CIPHER_CTX_new(); + + if (ctx == NULL) + { + LOG_ERROR_OPENSSL; + goto exit_with_enc; + } + + EVP_CIPHER_CTX_set_flags(ctx, EVP_CIPHER_CTX_FLAG_WRAP_ALLOW); + + memcpy(iv, encrypted, SECRET_STORAGE_IV_SIZE); + + ret = EVP_DecryptInit_ex2(ctx, EVP_aes_256_wrap_pad(), kek, iv, NULL); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + iter = key; + + ret = EVP_DecryptUpdate(ctx, iter, &outlen, + ((unsigned char *)encrypted) + SECRET_STORAGE_IV_SIZE, + enc_length - SECRET_STORAGE_IV_SIZE); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + iter += outlen; + + ret = EVP_DecryptFinal_ex(ctx, iter, &outlen); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + assert((iter - key) == SECRET_STORAGE_KEY_SIZE); + + /* Stockage de la clef maître en mémoire */ + + if (master != NULL) + { + *master = malloc(SECRET_STORAGE_KEY_SIZE); + memcpy(*master, key, SECRET_STORAGE_KEY_SIZE); + } + + result = true; + + /* Sortie */ + + exit_with_ctx: + + EVP_CIPHER_CTX_free(ctx); + + exit_with_enc: + + g_variant_unref(enc_value); + + exit_with_salt: + + g_variant_unref(salt_value); + + quick_exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = espace de stockage sécurisé à manipuler. * +* password = mot de passe principal à utiliser. * +* * +* Description : Déverrouille la clef de chiffrement maître. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_secret_storage_unlock(GSecretStorage *storage, const char *passwd) +{ + bool result; /* Bilan à retourner */ + + result = g_secret_storage_check_primary_password(storage, passwd, &storage->master_key); + + if (result) + g_signal_emit_by_name(storage, "lock-update"); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = espace de stockage sécurisé à manipuler. * +* * +* Description : Verrouille la clef de chiffrement maître. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_secret_storage_lock(GSecretStorage *storage) +{ + if (storage->master_key != NULL) + { + free(storage->master_key); + storage->master_key = NULL; + + g_signal_emit_by_name(storage, "lock-update"); + + } + +} + + +/****************************************************************************** +* * +* Paramètres : storage = espace de stockage sécurisé à consulter. * +* in = séquence d'octets à traiter. * +* out = séquence d'octets résultantes. [OUT] * +* * +* Description : Chiffre des données avec la clef de chiffrement maître. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_secret_storage_encrypt_data(const GSecretStorage *storage, const sized_binary_t *in, sized_binary_t *out) +{ + bool result; /* Bilan à retourner */ + unsigned char iv[SECRET_STORAGE_IV_SIZE]; /* IV associé */ + int ret; /* Bilan à d'un appel */ + EVP_CIPHER_CTX *ctx; /* Contexte pour le chiffrement*/ + size_t needed; /* Taille de la sortie */ + unsigned char *iter; /* Tête d'écriture */ + int outlen; /* Taille des données utiles */ + + result = false; + + if (g_secret_storage_is_locked(storage)) + goto quick_exit; + + /* Récupération de la clef maître et d'un IV de chiffrement */ + + ret = RAND_bytes(iv, sizeof(iv)); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit; + } + + /* Préparation de la zone de réception */ + + needed = SECRET_STORAGE_IV_SIZE + ((in->size / SECRET_STORAGE_BLOCK_SIZE) + 1) * SECRET_STORAGE_BLOCK_SIZE; + + setup_sized_binary(out, needed); + + /* Chiffrement des données */ + + ctx = EVP_CIPHER_CTX_new(); + + if (ctx == NULL) + { + LOG_ERROR_OPENSSL; + goto exit; + } + + ret = EVP_EncryptInit_ex2(ctx, EVP_aes_256_cbc(), storage->master_key, iv, NULL); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + memcpy(out->data, iv, SECRET_STORAGE_IV_SIZE); + + iter = out->bin_data + SECRET_STORAGE_IV_SIZE; + + ret = EVP_EncryptUpdate(ctx, iter, &outlen, in->bin_data, in->size); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + iter += outlen; + + ret = EVP_EncryptFinal_ex(ctx, iter, &outlen); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + iter += outlen; + + assert((iter - out->bin_data) == out->size); + + result = true; + + /* Sortie */ + + exit_with_ctx: + + EVP_CIPHER_CTX_free(ctx); + + if (!result) + exit_sized_binary(out); + + exit: + quick_exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = espace de stockage sécurisé à consulter. * +* in = séquence d'octets à traiter. * +* out = séquence d'octets résultantes. [OUT] * +* * +* Description : Déchiffre des données avec la clef de chiffrement maître. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_secret_storage_decrypt_data(const GSecretStorage *storage, const sized_binary_t *in, sized_binary_t *out) +{ + bool result; /* Bilan à retourner */ + unsigned char iv[SECRET_STORAGE_IV_SIZE]; /* IV associé */ + int ret; /* Bilan à d'un appel */ + EVP_CIPHER_CTX *ctx; /* Contexte pour le chiffrement*/ + size_t needed; /* Taille de la sortie */ + unsigned char *iter; /* Tête d'écriture */ + int outlen; /* Taille des données utiles */ + + result = false; + + if (g_secret_storage_is_locked(storage)) + goto quick_exit; + + /* Récupération d'un IV de déchiffrement */ + + if (in->size < SECRET_STORAGE_IV_SIZE) + goto exit; + + memcpy(iv, in->data, SECRET_STORAGE_IV_SIZE); + + /* Préparation de la zone de réception */ + + needed = in->size - SECRET_STORAGE_IV_SIZE; + + setup_sized_binary(out, needed); + + /* Chiffrement des données */ + + ctx = EVP_CIPHER_CTX_new(); + + if (ctx == NULL) + { + LOG_ERROR_OPENSSL; + goto exit; + } + + ret = EVP_DecryptInit_ex2(ctx, EVP_aes_256_cbc(), storage->master_key, iv, NULL); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + iter = out->bin_data; + + ret = EVP_DecryptUpdate(ctx, iter, &outlen, + in->bin_data + SECRET_STORAGE_IV_SIZE, in->size - SECRET_STORAGE_IV_SIZE); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + iter += outlen; + + ret = EVP_DecryptFinal_ex(ctx, iter, &outlen); + if (ret != 1) + { + LOG_ERROR_OPENSSL; + goto exit_with_ctx; + } + + iter += outlen; + + assert((iter - out->bin_data) <= out->size); + + resize_sized_binary(out, iter - out->bin_data); + + result = true; + + /* Sortie */ + + exit_with_ctx: + + EVP_CIPHER_CTX_free(ctx); + + if (!result) + exit_sized_binary(out); + + exit: + quick_exit: + + return result; + +} diff --git a/src/glibext/secstorage.h b/src/glibext/secstorage.h new file mode 100644 index 0000000..ed3f79c --- /dev/null +++ b/src/glibext/secstorage.h @@ -0,0 +1,74 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * secstorage.h - prototypes pour la conservation sécurisée d'éléments de configuration + * + * Copyright (C) 2025 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_SECSTORAGE_H +#define _GLIBEXT_SECSTORAGE_H + + +#include <stdbool.h> +#include <gio/gio.h> + + +#include "helpers.h" +#include "../common/szbin.h" + + + +#define G_TYPE_SECRET_STORAGE (g_secret_storage_get_type()) + +DECLARE_GTYPE(GSecretStorage, g_secret_storage, G, SECRET_STORAGE); + + +/* Créé un nouveau gardien des secrets avec support de stockage. */ +GSecretStorage *g_secret_storage_new(GSettings *); + +/* Détermine si une clef de chiffrement protégée est en place. */ +bool g_secret_storage_has_key(const GSecretStorage *); + +/* Définit un mot de passe pour protéger une clef maître. */ +bool g_secret_storage_set_password(GSecretStorage *, const char *); + +/* Modifie le mot de passe protégeant une clef maître. */ +bool g_secret_storage_change_password(GSecretStorage *, const char *, const char *); + +/* Supprime le mot de passe protégeant une clef maître. */ +bool g_secret_storage_remove_password(GSecretStorage *, const char *); + +/* Détermine si la clef de chiffrement maître est vérouillée. */ +bool g_secret_storage_is_locked(const GSecretStorage *); + +/* Déverrouille la clef de chiffrement maître. */ +bool g_secret_storage_unlock(GSecretStorage *, const char *); + +/* Verrouille la clef de chiffrement maître. */ +void g_secret_storage_lock(GSecretStorage *); + +/* Chiffre des données avec la clef de chiffrement maître. */ +bool g_secret_storage_encrypt_data(const GSecretStorage *, const sized_binary_t *, sized_binary_t *); + +/* Déchiffre des données avec la clef de chiffrement maître. */ +bool g_secret_storage_decrypt_data(const GSecretStorage *, const sized_binary_t *, sized_binary_t *); + + + +#endif /* _GLIBEXT_SECSTORAGE_H */ diff --git a/src/glibext/serialize-int.h b/src/glibext/serialize-int.h new file mode 100644 index 0000000..df9c597 --- /dev/null +++ b/src/glibext/serialize-int.h @@ -0,0 +1,54 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * serialize-int.h - définitions internes propres aux objets entreposables dans un cache + * + * Copyright (C) 2020-2025 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_SERIALIZE_INT_H +#define _GLIBEXT_SERIALIZE_INT_H + + +#include "serialize.h" + + +#include "storage.h" + + + +/* Charge un objet depuis une mémoire tampon. */ +typedef bool (* load_serializable_object_cb) (GSerializableObject *, GObjectStorage *, int); + +/* Sauvegarde un objet dans une mémoire tampon. */ +typedef bool (* store_serializable_object_cb) (const GSerializableObject *, GObjectStorage *, int); + + +/* Intermédiaire pour la mise en cache d'objet (interface) */ +struct _GSerializableObjectInterface +{ + GTypeInterface base_iface; /* A laisser en premier */ + + load_serializable_object_cb load; /* Chargement */ + store_serializable_object_cb store; /* Enregistrement */ + +}; + + + +#endif /* _GLIBEXT_SERIALIZE_INT_H */ diff --git a/src/glibext/serialize.c b/src/glibext/serialize.c new file mode 100644 index 0000000..b43f0c2 --- /dev/null +++ b/src/glibext/serialize.c @@ -0,0 +1,113 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * serialize.h - objets entreposables dans un cache + * + * Copyright (C) 2020-2025 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 <http://www.gnu.org/licenses/>. + */ + + +#include "serialize.h" + + +#include "serialize-int.h" + + + +/* Procède à l'initialisation de l'interface de mise en cache. */ +static void g_serializable_object_default_init(GSerializableObjectInterface *); + + + +/* Détermine le type d'une interface pour la mise en cache d'objet. */ +G_DEFINE_INTERFACE(GSerializableObject, g_serializable_object, G_TYPE_OBJECT) + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* * +* Description : Procède à l'initialisation de l'interface de mise en cache. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_serializable_object_default_init(GSerializableObjectInterface *iface) +{ + iface->load = NULL; + iface->store = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : object = élément GLib à constuire. * +* storage = conservateur de données à manipuler. * +* fd = flux ouvert en lecture. * +* * +* Description : Charge un objet depuis un flux de données. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_serializable_object_load(GSerializableObject *object, GObjectStorage *storage, int fd) +{ + bool result; /* Bilan à retourner */ + GSerializableObjectInterface *iface; /* Interface utilisée */ + + iface = G_SERIALIZABLE_OBJECT_GET_IFACE(object); + + result = iface->load(object, storage, fd); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : object = élément GLib à consulter. * +* storage = conservateur de données à manipuler. * +* fd = flux ouvert en écriture. * +* * +* Description : Sauvegarde un objet dans un flux de données. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_serializable_object_store(const GSerializableObject *object, GObjectStorage *storage, int fd) +{ + bool result; /* Bilan à retourner */ + GSerializableObjectInterface *iface; /* Interface utilisée */ + + iface = G_SERIALIZABLE_OBJECT_GET_IFACE(object); + + result = iface->store(object, storage, fd); + + return result; + +} diff --git a/src/glibext/serialize.h b/src/glibext/serialize.h new file mode 100644 index 0000000..c95ac30 --- /dev/null +++ b/src/glibext/serialize.h @@ -0,0 +1,52 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * serialize.h - prototypes pour les objets entreposables dans un cache + * + * Copyright (C) 2020-2025 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_SERIALIZE_H +#define _GLIBEXT_SERIALIZE_H + + +#include <stdbool.h> + + +#include "helpers.h" + + + +#define G_TYPE_SERIALIZABLE_OBJECT (g_serializable_object_get_type()) + +DECLARE_INTERFACE(GSerializableObject, g_serializable_object, G, SERIALIZABLE_OBJECT); + + +/* storage.h : définition d'une conservation d'objets construits */ +typedef struct _GObjectStorage GObjectStorage; + + +/* Charge un objet depuis un flux de données. */ +bool g_serializable_object_load(GSerializableObject *, GObjectStorage *, int); + +/* Sauvegarde un objet dans un flux de données. */ +bool g_serializable_object_store(const GSerializableObject *, GObjectStorage *, int); + + + +#endif /* _GLIBEXT_SERIALIZE_H */ diff --git a/src/glibext/signal.c b/src/glibext/sigredir.c index 33290fb..67e8563 100644 --- a/src/glibext/signal.c +++ b/src/glibext/sigredir.c @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * signal.c - encadrement des signaux supplémentaire par rapport à celui de la GLib + * sigredir.c - encadrement des signaux supplémentaire par rapport à celui de la GLib * - * Copyright (C) 2014-2018 Cyrille Bagard + * Copyright (C) 2014-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,49 +21,55 @@ */ -#include "signal.h" +#include "sigredir.h" #include <assert.h> #include <malloc.h> #include <stdarg.h> #include <stdbool.h> +#include <gobject/gclosure.h> #include <gobject/gvaluecollector.h> -/* Prototype pour le transfert d'exécution. */ -typedef void (* GSignalCallback) (gpointer, ...); +/* Informations utiles à l'appel final */ +typedef struct _gsignal_wrapper_params_t +{ + GClosure *closure; /* Glue pour les appels */ + + guint n_params; /* Nombre de paramètres */ + GValue instance_and_params[0]; /* Instance & paramètres */ + +} gsignal_wrapper_params_t; + +/* Transmet un signal dans le contexte principal. */ +static gboolean to_main_wrapper(gpointer); +/* Supprime de la mémoire le transporteur d'informations. */ +static void destroy_wrapper_params(gpointer); /* Informations concernant une diffusion de signal */ -typedef struct _gsignal_wrapper_info +typedef struct _gsignal_wrapper_info_t { gpointer instance; /* Instance GLib initiatrice */ + gulong id; /* Identifiant de connexion */ GClosure *closure; /* Glue pour les appels */ - GType return_type; /* Type de la valeur retournée */ guint n_params; /* Nombre de paramètres */ - const GType *param_types; /* Type des paramètres associés*/ + GType param_types[0]; /* Type des paramètres associés*/ - GValue return_value; /* Valeur de retour */ - GValue instance_and_params[0]; /* Instance & paramètres */ - -} gsignal_wrapper_info; - - -/* Transmet un signal dans le contexte principal. */ -static gboolean to_main_wrapper(gsignal_wrapper_info *); +} gsignal_wrapper_info_t; /* Réceptionne un signal et redirige son exécution. */ -static void carry_signal_to_main_thread(gsignal_wrapper_info *, ...); +static void carry_signal_to_main_thread(gsignal_wrapper_info_t *, ...); /****************************************************************************** * * -* Paramètres : info = collecteur d'informations sur la diffusion. * +* Paramètres : data = collecteur d'informations sur la diffusion. * * * * Description : Transmet un signal dans le contexte principal. * * * @@ -73,11 +79,17 @@ static void carry_signal_to_main_thread(gsignal_wrapper_info *, ...); * * ******************************************************************************/ -static gboolean to_main_wrapper(gsignal_wrapper_info *info) +static gboolean to_main_wrapper(gpointer data) { - g_closure_invoke(info->closure, NULL/*&info->return_value*/, - info->n_params + 1, info->instance_and_params, - NULL); + gsignal_wrapper_params_t *params; /* Informations d'appel */ + + params = (gsignal_wrapper_params_t *)data; + + g_closure_invoke(params->closure, + NULL /* return_value */, + params->n_params + 1, + params->instance_and_params, + NULL /* invocation_hint */); return G_SOURCE_REMOVE; @@ -86,6 +98,35 @@ static gboolean to_main_wrapper(gsignal_wrapper_info *info) /****************************************************************************** * * +* Paramètres : data = collecteur d'informations à supprimer. * +* * +* Description : Supprime de la mémoire le transporteur d'informations. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void destroy_wrapper_params(gpointer data) +{ + gsignal_wrapper_params_t *params; /* Informations d'appel */ + guint i; /* Boucle de parcours */ + + params = (gsignal_wrapper_params_t *)data; + + g_closure_unref(params->closure); + + for (i = 0; i < (params->n_params + 1); i++) + g_value_unset(params->instance_and_params + i); + + free(params); + +} + + +/****************************************************************************** +* * * Paramètres : info = collecteur d'informations sur la diffusion. * * ... = arguments poussés par la GLib sur la pile. * * * @@ -97,23 +138,26 @@ static gboolean to_main_wrapper(gsignal_wrapper_info *info) * * ******************************************************************************/ -static void carry_signal_to_main_thread(gsignal_wrapper_info *info, ...) +static void carry_signal_to_main_thread(gsignal_wrapper_info_t *info, ...) { + gsignal_wrapper_params_t *params; /* Informations d'appel */ GValue *param_values; /* Paramètres d'appel */ va_list ap; /* Liste d'arguments sur pile */ guint i; /* Boucle de parcours */ bool static_scope; /* Portée des arguments */ gchar *error; /* Eventuelle erreur inattendue*/ - //g_value_init(&info->return_value, info->return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE); + params = calloc(1, sizeof(gsignal_wrapper_params_t) + sizeof(GValue) * (info->n_params + 1)); + + params->closure = info->closure; + g_closure_ref(info->closure); - if (G_IS_VALUE(info->instance_and_params)) - g_value_unset(info->instance_and_params); + params->n_params = info->n_params; - g_value_init(info->instance_and_params, G_TYPE_FROM_INSTANCE(info->instance)); - g_value_set_instance(info->instance_and_params, info->instance); + g_value_init(params->instance_and_params, G_TYPE_FROM_INSTANCE(info->instance)); + g_value_set_instance(params->instance_and_params, info->instance); - param_values = info->instance_and_params + 1; + param_values = params->instance_and_params + 1; va_start(ap, info); @@ -121,9 +165,6 @@ static void carry_signal_to_main_thread(gsignal_wrapper_info *info, ...) for (i = 0; i < info->n_params; i++) { - if (G_IS_VALUE(param_values + i)) - g_value_unset(param_values + i); - static_scope = info->param_types[i] & G_SIGNAL_TYPE_STATIC_SCOPE; G_VALUE_COLLECT_INIT(param_values + i, @@ -143,7 +184,28 @@ static void carry_signal_to_main_thread(gsignal_wrapper_info *info, ...) va_end(ap); if (error == NULL) - g_idle_add_full(G_PRIORITY_HIGH_IDLE, (GSourceFunc)to_main_wrapper, info, NULL); + g_idle_add_full(G_PRIORITY_HIGH_IDLE, to_main_wrapper, params, destroy_wrapper_params); + +} + + +/****************************************************************************** +* * +* Paramètres : info = collecteur d'informations à supprimer. * +* * +* Description : Déconnecte un signal redirigé vers le contexte principal. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +void g_signal_disconnect_from_main(gsignal_wrapper_info_t *info) +{ + g_signal_handler_disconnect(info->instance, info->id); + + free(info); } @@ -165,41 +227,44 @@ static void carry_signal_to_main_thread(gsignal_wrapper_info *info, ...) * * ******************************************************************************/ -gulong _g_signal_connect_to_main(gpointer instance, const gchar *signal, GCallback handler, gpointer data, GClosureMarshal marshal, GConnectFlags flags) +gsignal_wrapper_info_t *_g_signal_connect_to_main(gpointer instance, const gchar *signal, GCallback handler, gpointer data, GClosureMarshal marshal, GConnectFlags flags) { + gsignal_wrapper_info_t *result; /* Structure à renvoyer */ guint signal_id; /* Identifiant du signal visé */ GSignalQuery query; /* Information sur le signal */ - gsignal_wrapper_info *info; /* Encapsulation des données */ /* Collection d'informations */ signal_id = g_signal_lookup(signal, G_TYPE_FROM_INSTANCE(instance)); g_signal_query(signal_id, &query); + assert(query.signal_id != 0); + assert(query.return_type == G_TYPE_NONE); /* Allocation adaptée */ - info = calloc(1, sizeof(gsignal_wrapper_info) + sizeof(GValue) * (query.n_params + 1)); + result = malloc(sizeof(gsignal_wrapper_info_t) + sizeof(GType) * query.n_params); - info->instance = instance; + result->instance = instance; if (flags & G_CONNECT_SWAPPED) - info->closure = g_cclosure_new_swap(handler, data, NULL); + result->closure = g_cclosure_new_swap(handler, data, NULL); else - info->closure = g_cclosure_new(handler, data, NULL); + result->closure = g_cclosure_new(handler, data, NULL); - g_closure_ref(info->closure); - g_closure_sink(info->closure); + g_closure_ref(result->closure); + g_closure_sink(result->closure); - g_closure_set_marshal(info->closure, marshal); + g_closure_set_marshal(result->closure, marshal); - info->return_type = query.return_type; - info->n_params = query.n_params; - info->param_types = query.param_types; + result->n_params = query.n_params; + memcpy(result->param_types, query.param_types, sizeof(GType) * query.n_params); - assert(query.return_type == G_TYPE_NONE); + /* Connexion au signal */ + + result->id = g_signal_connect_swapped(instance, signal, G_CALLBACK(carry_signal_to_main_thread), result); - return g_signal_connect_swapped(instance, signal, G_CALLBACK(carry_signal_to_main_thread), info); + return result; } diff --git a/src/glibext/_signal.h b/src/glibext/sigredir.h index 4f0ab4b..f394d77 100644 --- a/src/glibext/_signal.h +++ b/src/glibext/sigredir.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * signal.h - prototypes pour un encadrement des signaux supplémentaire par rapport à celui de la GLib + * sigredir.h - prototypes pour un encadrement des signaux supplémentaire par rapport à celui de la GLib * - * Copyright (C) 2014-2018 Cyrille Bagard + * Copyright (C) 2014-2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,20 +21,23 @@ */ -#ifndef _GLIBEXT_SIGNAL_H -#define _GLIBEXT_SIGNAL_H +#ifndef _GLIBEXT_SIGREDIR_H +#define _GLIBEXT_SIGREDIR_H #include <glib-object.h> -#include <gobject/gclosure.h> -#include <glib/gdataset.h> -#include <glib/glist.h> #include <gobject/gsignal.h> +/* Informations concernant une diffusion de signal */ +typedef struct _gsignal_wrapper_info_t gsignal_wrapper_info_t; + /* Reproduit le comportement de la fonction g_signal_connect(). */ -gulong _g_signal_connect_to_main(gpointer, const gchar *, GCallback, gpointer, GClosureMarshal, GConnectFlags); +gsignal_wrapper_info_t *_g_signal_connect_to_main(gpointer, const gchar *, GCallback, gpointer, GClosureMarshal, GConnectFlags); + +/* Déconnecte un signal redirigé vers le contexte principal. */ +void g_signal_disconnect_from_main(gsignal_wrapper_info_t *); #define g_signal_connect_to_main(instance, signal, handler, data, marshal) \ @@ -45,4 +48,4 @@ gulong _g_signal_connect_to_main(gpointer, const gchar *, GCallback, gpointer, G -#endif /* _GLIBEXT_SIGNAL_H */ +#endif /* _GLIBEXT_SIGREDIR_H */ diff --git a/src/glibext/singleton-int.h b/src/glibext/singleton-int.h index 3db17f9..747e64a 100644 --- a/src/glibext/singleton-int.h +++ b/src/glibext/singleton-int.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * singleton-int.h - définitions internes propres aux réductions du nombre d'instances d'un même type * - * Copyright (C) 2021 Cyrille Bagard + * Copyright (C) 2021-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -29,44 +29,61 @@ +/* ------------------ INTERFACE POUR CANDIDAT A UNE CENTRALISATION ------------------ */ + + /* 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 figé. */ -typedef void (* set_candidate_ro_fc) (GSingletonCandidate *); +typedef void (* mark_candidate_as_ro_fc) (GSingletonCandidate *); /* Indique si le candidat est figé. */ typedef bool (* is_candidate_ro_fc) (const GSingletonCandidate *); +/* Crée une copie modifiable d'un object unique. */ +typedef GSingletonCandidate * (* dup_candidate_fc) (const GSingletonCandidate *); + /* Instance d'objet visant à être unique (interface) */ -struct _GSingletonCandidateIface +struct _GSingletonCandidateInterface { 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 */ - - set_candidate_ro_fc set_ro; /* Bascule en mode figé */ + mark_candidate_as_ro_fc mark_as_ro; /* Bascule en mode figé */ is_candidate_ro_fc is_ro; /* Consultation de l'état */ + dup_candidate_fc dup; /* Création de copie modifiable*/ + +}; + + + +/* ------------------------- COLLECTION D'INSTANCES UNIQUES ------------------------- */ + + +/* Définition d'un compacteur d'instances de types (instance) */ +struct _GSingletonFactory +{ + GObject parent; /* A laisser en premier */ + + GHashTable *table; /* Suivi des conservations */ + GMutex access; /* Verrou pour la concurrence */ + }; +/* Définition d'un compacteur d'instances de types (classe) */ +struct _GSingletonFactoryClass +{ + GObjectClass parent; /* A laisser en premier */ -/* Redéfinition */ -typedef GSingletonCandidateIface GSingletonCandidateInterface; +}; diff --git a/src/glibext/singleton.c b/src/glibext/singleton.c index 78a3ad4..ed49934 100644 --- a/src/glibext/singleton.c +++ b/src/glibext/singleton.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * singleton.c - réduction du nombre d'instances d'un même type * - * Copyright (C) 2021 Cyrille Bagard + * Copyright (C) 2021-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -25,52 +25,35 @@ #include <assert.h> +#include <malloc.h> +#include "comparable.h" +#include "hashable.h" #include "singleton-int.h" /* ------------------ INTERFACE POUR CANDIDAT A UNE CENTRALISATION ------------------ */ + /* Procède à l'initialisation de l'interface de rassemblement. */ static void g_singleton_candidate_default_init(GSingletonCandidateInterface *); +/* Fournit une liste de candidats embarqués par un candidat. */ +static GSingletonCandidate **g_singleton_candidate_list_inner_instances(const GSingletonCandidate *, size_t *); + /* 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 **); +static void g_singleton_candidate_mark_as_read_only(GSingletonCandidate *); /* ------------------------- COLLECTION D'INSTANCES UNIQUES ------------------------- */ -/* Définition d'un compacteur d'instances de types (instance) */ -struct _GSingletonFactory -{ - GObject parent; /* A laisser en premier */ - - GHashTable *table; /* Suivi des conservations */ - GMutex access; /* Verrou pour la concurrence */ - -}; - -/* Définition d'un compacteur d'instances de types (classe) */ -struct _GSingletonFactoryClass -{ - GObjectClass parent; /* A laisser en premier */ - -}; - - /* Initialise la classe des compacteurs d'instances de types. */ static void g_singleton_factory_class_init(GSingletonFactoryClass *); @@ -90,8 +73,10 @@ static void g_singleton_factory_finalize(GSingletonFactory *); /* ---------------------------------------------------------------------------------- */ -/* Détermine le type d'une interface pour la lecture de binaire. */ -G_DEFINE_INTERFACE(GSingletonCandidate, g_singleton_candidate, G_TYPE_OBJECT) +/* Détermine le type d'une interface pour la constitution d'objets uniques. */ +G_DEFINE_INTERFACE_WITH_CODE(GSingletonCandidate, g_singleton_candidate, G_TYPE_OBJECT,; + g_type_interface_add_prerequisite(g_define_type_id, G_TYPE_COMPARABLE_OBJECT); + g_type_interface_add_prerequisite(g_define_type_id, G_TYPE_HASHABLE_OBJECT)) /****************************************************************************** @@ -108,6 +93,13 @@ G_DEFINE_INTERFACE(GSingletonCandidate, g_singleton_candidate, G_TYPE_OBJECT) static void g_singleton_candidate_default_init(GSingletonCandidateInterface *iface) { + iface->list_inner = NULL; + iface->update_inner = NULL; + + iface->mark_as_ro = NULL; + iface->is_ro = NULL; + + iface->dup = NULL; } @@ -128,14 +120,17 @@ static void g_singleton_candidate_default_init(GSingletonCandidateInterface *ifa GSingletonCandidate **g_singleton_candidate_list_inner_instances(const GSingletonCandidate *candidate, size_t *count) { GSingletonCandidate **result; /* Instances à retourner */ - GSingletonCandidateIface *iface; /* Interface utilisée */ + GSingletonCandidateInterface *iface; /* Interface utilisée */ iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate); if (iface->list_inner == NULL) { + assert(iface->update_inner == NULL); + *count = 0; result = NULL; + } else @@ -162,7 +157,7 @@ GSingletonCandidate **g_singleton_candidate_list_inner_instances(const GSingleto static void g_singleton_candidate_update_inner_instances(GSingletonCandidate *candidate, GSingletonCandidate **instances, size_t count) { - GSingletonCandidateIface *iface; /* Interface utilisée */ + GSingletonCandidateInterface *iface; /* Interface utilisée */ iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate); @@ -181,82 +176,36 @@ static void g_singleton_candidate_update_inner_instances(GSingletonCandidate *ca /****************************************************************************** * * * 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. * +* Description : Marque un candidat comme figé. * * * -* Retour : Empreinte de l'élément représenté. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static guint _g_singleton_candidate_hash(GSingletonCandidate *candidate, GList **processed) +static void g_singleton_candidate_mark_as_read_only(GSingletonCandidate *candidate) { - guint result; /* Valeur à retourner */ - GList *skip; /* Détection de boucle */ - GSingletonCandidateIface *iface; /* Interface utilisée */ + GSingletonCandidateInterface *iface; /* Interface utilisée */ GSingletonCandidate **children; /* Instances internes */ size_t count; /* Quantité de ces instances */ size_t i; /* Boucle de parcours */ - 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); + iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate); - for (i = 0; i < count; i++) - { - result ^= _g_singleton_candidate_hash(children[i], processed); - g_object_unref(G_OBJECT(children[i])); - } + iface->mark_as_ro(candidate); - if (children != NULL) - free(children); + children = g_singleton_candidate_list_inner_instances(candidate, &count); + for (i = 0; i < count; i++) + { + g_singleton_candidate_mark_as_read_only(children[i]); + unref_object(G_OBJECT(children[i])); } - 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); - - assert(processed != NULL); - - g_list_free(processed); - - return result; + if (children != NULL) + free(children); } @@ -264,116 +213,43 @@ guint g_singleton_candidate_hash(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. * +* Description : Indique si le candidat est figé. * * * -* Retour : Bilan de la comparaison. * +* Retour : true si le contenu du candidat ne peut plus être modifié. * * * * Remarques : - * * * ******************************************************************************/ -static gboolean _g_singleton_candidate_is_equal(GSingletonCandidate *candidate, GSingletonCandidate *other, GList **processed) +bool g_singleton_candidate_is_read_only(const GSingletonCandidate *candidate) { - 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 */ + bool result; /* Etat à retourner */ + GSingletonCandidateInterface *iface; /* Interface utilisée */ +#ifndef NDEBUG + GSingletonCandidate **children; /* Instances internes */ + size_t count; /* Quantité de ces instances */ size_t i; /* Boucle de parcours */ +#endif - 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])); - - 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])); - } + iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate); - if (children[0] != NULL) - free(children[0]); + result = iface->is_ro(candidate); - if (children[1] != NULL) - free(children[1]); +#ifndef NDEBUG - } + children = g_singleton_candidate_list_inner_instances(candidate, &count); + for (i = 0; i < count; i++) + { + assert(result == g_singleton_candidate_is_read_only(children[i])); + unref_object(G_OBJECT(children[i])); } - 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 */ + if (children != NULL) + free(children); - 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]); +#endif return result; @@ -383,97 +259,60 @@ gboolean g_singleton_candidate_is_equal(GSingletonCandidate *candidate, GSinglet /****************************************************************************** * * * Paramètres : candidate = objet dont l'instance se veut unique. * -* processed = liste de candidats déjà traités. * * * -* Description : Marque un candidat comme figé. * +* Description : Crée une copie modifiable d'un object unique. * * * -* Retour : - * +* Retour : Nouvelle instance mise en place. * * * * Remarques : - * * * ******************************************************************************/ -static void _g_singleton_candidate_set_read_only(GSingletonCandidate *candidate, GList **processed) +GSingletonCandidate *g_singleton_candidate_dup(const GSingletonCandidate *candidate) { - GList *skip; /* Détection de boucle */ - GSingletonCandidateIface *iface; /* Interface utilisée */ - GSingletonCandidate **children; /* Instances internes */ - size_t count; /* Quantité de ces instances */ + GSingletonCandidate *result; /* Instance à retourner */ + GSingletonCandidateInterface *iface; /* Interface utilisée */ + size_t count; /* Quantité d'objets internes */ + GSingletonCandidate **children; /* Liste d'instances internes */ size_t i; /* Boucle de parcours */ + GSingletonCandidate **new_children; /* Nouvelle liste d'instances */ - skip = g_list_find(*processed, candidate); + iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate); - if (skip == NULL) - { - iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate); + result = iface->dup(candidate); - iface->set_ro(candidate); + assert(!g_singleton_candidate_is_read_only(result)); - *processed = g_list_append(*processed, candidate); + children = g_singleton_candidate_list_inner_instances(candidate, &count); - children = g_singleton_candidate_list_inner_instances(candidate, &count); + if (count > 0) + { + new_children = malloc(count * sizeof(GSingletonCandidate *)); 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); - - } - -} + new_children[i] = g_singleton_candidate_dup(children[i]); + assert(!g_singleton_candidate_is_read_only(new_children[i])); -/****************************************************************************** -* * -* Paramètres : candidate = objet dont l'instance se veut unique. * -* * -* Description : Marque un candidat comme figé. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_singleton_candidate_set_read_only(GSingletonCandidate *candidate) -{ - GList *processed; /* Suivi des traitements */ - - processed = NULL; - - _g_singleton_candidate_set_read_only(candidate, &processed); - - assert(processed != NULL); + } - g_list_free(processed); - -} + g_singleton_candidate_update_inner_instances(result, new_children, count); + for (i = 0; i < count; i++) + { + unref_object(G_OBJECT(new_children[i])); + unref_object(G_OBJECT(children[i])); + } -/****************************************************************************** -* * -* 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 : - * -* * -******************************************************************************/ + free(new_children); -bool g_singleton_candidate_is_read_only(const GSingletonCandidate *candidate) -{ - bool result; /* Etat à retourner */ - GSingletonCandidateIface *iface; /* Interface utilisée */ + } - iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate); + if (children != NULL) + free(children); - result = iface->is_ro(candidate); + assert(G_OBJECT_TYPE(result) == G_OBJECT_TYPE(candidate)); return result; @@ -528,8 +367,8 @@ static void g_singleton_factory_class_init(GSingletonFactoryClass *klass) static void g_singleton_factory_init(GSingletonFactory *factory) { - factory->table = g_hash_table_new_full((GHashFunc)g_singleton_candidate_hash, - (GEqualFunc)g_singleton_candidate_is_equal, + factory->table = g_hash_table_new_full((GHashFunc)g_hashable_object_hash, + (GEqualFunc)g_comparable_object_is_equal, g_object_unref, NULL); g_mutex_init(&factory->access); @@ -688,7 +527,7 @@ GSingletonCandidate *g_singleton_factory_get_instance(GSingletonFactory *factory g_hash_table_add(factory->table, candidate); #endif - g_singleton_candidate_set_read_only(candidate); + g_singleton_candidate_mark_as_read_only(candidate); result = candidate; diff --git a/src/glibext/singleton.h b/src/glibext/singleton.h index 629687a..11afffd 100644 --- a/src/glibext/singleton.h +++ b/src/glibext/singleton.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * singleton.h - prototypes pour la réduction du nombre d'instances d'un même type * - * Copyright (C) 2021 Cyrille Bagard + * Copyright (C) 2021-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -25,71 +25,36 @@ #define _GLIBEXT_SINGLETON_H -#include <glib-object.h> #include <stdbool.h> - -/* Définition d'un compacteur d'instances de types (instance) */ -typedef struct _GSingletonFactory GSingletonFactory; +#include "helpers.h" /* ------------------ INTERFACE POUR CANDIDAT A UNE CENTRALISATION ------------------ */ -#define G_TYPE_SINGLETON_CANDIDATE (g_singleton_candidate_get_type()) -#define G_SINGLETON_CANDIDATE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_SINGLETON_CANDIDATE, GSingletonCandidate)) -#define G_SINGLETON_CANDIDATE_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST((vtable), G_TYPE_SINGLETON_CANDIDATE, GSingletonCandidateIface)) -#define G_IS_SINGLETON_CANDIDATE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_SINGLETON_CANDIDATE)) -#define G_IS_SINGLETON_CANDIDATE_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE((vtable), G_TYPE_SINGLETON_CANDIDATE)) -#define G_SINGLETON_CANDIDATE_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE((inst), G_TYPE_SINGLETON_CANDIDATE, GSingletonCandidateIface)) - - -/* Instance d'objet visant à être unique (coquille vide) */ -typedef struct _GSingletonCandidate GSingletonCandidate; - -/* Instance d'objet visant à être unique (interface) */ -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; +#define G_TYPE_SINGLETON_CANDIDATE (g_singleton_candidate_get_type()) -/* Fournit une liste de candidats embarqués par un candidat. */ -GSingletonCandidate **g_singleton_candidate_list_inner_instances(const GSingletonCandidate *, size_t *); +DECLARE_INTERFACE(GSingletonCandidate, g_singleton_candidate, G, SINGLETON_CANDIDATE); -/* Fournit l'empreinte d'un candidat à une centralisation. */ -guint g_singleton_candidate_hash(GSingletonCandidate *); - -/* Détermine si deux candidats à l'unicité sont identiques. */ -gboolean g_singleton_candidate_is_equal(GSingletonCandidate *, GSingletonCandidate *); - -/* Marque un candidat comme figé. */ -void g_singleton_candidate_set_read_only(GSingletonCandidate *); /* Indique si le candidat est figé. */ bool g_singleton_candidate_is_read_only(const GSingletonCandidate *); +/* Crée une copie modifiable d'un object unique. */ +GSingletonCandidate *g_singleton_candidate_dup(const GSingletonCandidate *); -/* ------------------------- COLLECTION D'INSTANCES UNIQUES ------------------------- */ - -#define G_TYPE_SINGLETON_FACTORY g_singleton_factory_get_type() -#define G_SINGLETON_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_SINGLETON_FACTORY, GSingletonFactory)) -#define G_IS_SINGLETON_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_SINGLETON_FACTORY)) -#define G_SINGLETON_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_SINGLETON_FACTORY, GSingletonFactoryClass)) -#define G_IS_SINGLETON_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_SINGLETON_FACTORY)) -#define G_SINGLETON_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_SINGLETON_FACTORY, GSingletonFactoryClass)) +/* ------------------------- COLLECTION D'INSTANCES UNIQUES ------------------------- */ -/* Définition d'un compacteur d'instances de types (classe) */ -typedef struct _GSingletonFactoryClass GSingletonFactoryClass; +#define G_TYPE_SINGLETON_FACTORY (g_singleton_factory_get_type()) +DECLARE_GTYPE(GSingletonFactory, g_singleton_factory, G, SINGLETON_FACTORY); -/* Indique le type défini pour une mémoire de types d'objets. */ -GType g_singleton_factory_get_type(void); /* Crée un compacteur d'instances de types. */ GSingletonFactory *g_singleton_factory_new(void); diff --git a/src/glibext/storage-int.h b/src/glibext/storage-int.h new file mode 100644 index 0000000..d89e1c8 --- /dev/null +++ b/src/glibext/storage-int.h @@ -0,0 +1,75 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * storage-int.h - prototypes internes pour la conservation sur disque d'objets construits + * + * Copyright (C) 2020-2025 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_STORAGE_INT_H +#define _GLIBEXT_STORAGE_INT_H + + +#include "storage.h" +#include "tpmem.h" +#include "../common/szbin.h" + + + +/* Gestion d'enregistrements spécifiques */ +typedef struct _storage_backend_t +{ + char *name; /* Désignation du groupe */ + + char *filename; /* Nom du fichier associé */ + int fd; /* Flux d'accès correspondant */ + +} storage_backend_t; + +/* Définition d'une conservation d'objets construits (instance) */ +struct _GObjectStorage +{ + GObject parent; /* A laisser en premier */ + + sized_binary_t type; /* Type de conservation */ + uint8_t version; /* Version correspondante */ + + sized_binary_t uid; /* Identifiant de distinction */ + + GTypeMemory *tpmem; /* Mémorisation de types */ + + storage_backend_t *backends; /* Gestionnaires existants */ + size_t count; /* Quantité de gestionnaires */ + GMutex mutex; /* Contrôle d'accès à la liste */ + +}; + +/* Définition d'une conservation d'objets construits (classe) */ +struct _GObjectStorageClass +{ + GObjectClass parent; /* A laisser en premier */ + +}; + + +/* Met en place un support d'une conservation d'objets en place. */ +bool g_object_storage_create(GObjectStorage *, const char *, uint8_t, const char *); + + + +#endif /* _GLIBEXT_STORAGE_INT_H */ diff --git a/src/glibext/storage.c b/src/glibext/storage.c new file mode 100644 index 0000000..0a3c4e7 --- /dev/null +++ b/src/glibext/storage.c @@ -0,0 +1,1147 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * storage.c - conservation hors mémoire d'objets choisis + * + * Copyright (C) 2020-2025 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 <http://www.gnu.org/licenses/>. + */ + + +#include "storage.h" + + +#include <assert.h> +#include <malloc.h> +#include <string.h> +#include <unistd.h> +#include <zip.h> + + +#include "storage-int.h" +#include "../common/cpp.h" +#include "../common/pathname.h" +#include "../core/logs.h" + + +/** + * Historique du format : + * + * - 09/03/25 : 1.0 (version initiale) + * + */ +#define STORAGE_MAGIC "COBSTR" +#define STORAGE_NUMBER "\x01\x00" + + +/* Initialise la classe des conservations d'objets en place. */ +static void g_object_storage_class_init(GObjectStorageClass *); + +/* Initialise une instance de conservation d'objets en place. */ +static void g_object_storage_init(GObjectStorage *); + +/* Supprime toutes les références externes. */ +static void g_object_storage_dispose(GObject *); + +/* Procède à la libération totale de la mémoire. */ +static void g_object_storage_finalize(GObject *); + +/* Assure l'inexistence d'un groupe avec un nom donné. */ +static bool g_object_storage_has_no_backend_named(GObjectStorage *, const char *); + +/* Retrouve l'encadrement pour un nouveau groupe d'objets. */ +static storage_backend_t *g_object_storage_find_backend(GObjectStorage *, const char *); + +/* Ajoute le support d'un nouveau groupe d'objets construits. */ +static bool g_object_storage_add_backend(GObjectStorage *, const char *, storage_backend_t **); + +/* Charge un objet à partir de données rassemblées. */ +static GSerializableObject *g_object_storage_load_object_unlocked(GObjectStorage *, const char *, off64_t); + + + +/* Indique le type défini pour une conservation d'objets construits. */ +G_DEFINE_TYPE(GObjectStorage, g_object_storage, G_TYPE_OBJECT); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des conservations d'objets en place. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_object_storage_class_init(GObjectStorageClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = g_object_storage_dispose; + object->finalize = g_object_storage_finalize; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = instance à initialiser. * +* * +* Description : Initialise une instance de conservation d'objets en place. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_object_storage_init(GObjectStorage *storage) +{ + init_sized_binary(&storage->type); + storage->version = 0; + + init_sized_binary(&storage->uid); + + storage->tpmem = g_type_memory_new(); + + storage->backends = NULL; + storage->count = 0; + g_mutex_init(&storage->mutex); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_object_storage_dispose(GObject *object) +{ + GObjectStorage *storage; /* Version spécialisée */ + + storage = G_OBJECT_STORAGE(object); + + g_clear_object(&storage->tpmem); + + G_OBJECT_CLASS(g_object_storage_parent_class)->dispose(object); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_object_storage_finalize(GObject *object) +{ + GObjectStorage *storage; /* Version spécialisée */ + size_t i; /* Boucle de parcours */ + storage_backend_t *backend; /* Gestionnaire à manipuler */ + int ret; /* Bilan d'un appel */ + + storage = G_OBJECT_STORAGE(object); + + g_mutex_lock(&storage->mutex); + + for (i = 0; i < storage->count; i++) + { + backend = &storage->backends[i]; + + /** + * Chargement incomplet depuis g_object_storage_load(). + */ + if (backend->name == NULL) + break; + + if (backend->fd != -1) + close(backend->fd); + else + assert(false); + + ret = access(backend->filename, W_OK); + if (ret == 0) + { + ret = unlink(backend->filename); + if (ret != 0) LOG_ERROR_N("unlink"); + } + + free(backend->name); + + free(backend->filename); + + } + + if (storage->backends != NULL) + free(storage->backends); + + g_mutex_unlock(&storage->mutex); + + g_mutex_clear(&storage->mutex); + + exit_sized_binary(&storage->type); + + exit_sized_binary(&storage->uid); + + G_OBJECT_CLASS(g_object_storage_parent_class)->finalize(object); + +} + + +/****************************************************************************** +* * +* Paramètres : type = type global à indiquer dans une conservation. * +* version = numéro de version associé. * +* uid = identifiant arbitraire mais unique pour distinguer.* +* * +* Description : Crée le support d'une conservation d'objets en place. * +* * +* Retour : Mécanismes mis en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GObjectStorage *g_object_storage_new(const char *type, uint8_t version, const char *uid) +{ + GObjectStorage *result; /* Structure à retourner */ + + result = g_object_new(G_TYPE_OBJECT_STORAGE, NULL); + + if (!g_object_storage_create(result, type, version, uid)) + g_clear_object(&result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = stockage d'objets à initialiser. * +* type = type global à indiquer dans une conservation. * +* version = numéro de version associé. * +* uid = identifiant arbitraire mais unique pour distinguer.* +* * +* Description : Met en place un support d'une conservation d'objets en place.* +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_object_storage_create(GObjectStorage *storage, const char *type, uint8_t version, const char *uid) +{ + bool result; /* Bilan à retourner */ + + result = true; + + dup_into_sized_binary(&storage->type, type, strlen(type)); + + storage->version = version; + + dup_into_sized_binary(&storage->uid, uid, strlen(uid) + 1); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : filename = fichier de source à traiter. * +* * +* Description : Charge le support d'une conservation d'objets en place. * +* * +* Retour : Gestionnaire de conservations construit ou NULL si erreur. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GObjectStorage *g_object_storage_load(const char *filename) +{ + GObjectStorage *result; /* Structure à retourner */ + GObjectStorage *storage; /* Structure en construction */ + int err; /* Eventuel code d'erreur */ + zip_t *archive; /* Archive ZIP à manipuler */ + zip_error_t error; /* Suivi des erreurs obtenues */ + char *tpmem_filename; /* Chemin d'accès pour types */ + zip_int64_t entries_count; /* Nombre d'éléments ZIP */ + void *data; /* Données (décompressées) */ + zip_stat_t stats; /* Information sur les données */ + zip_file_t *file; /* Echantillon à extraire */ + zip_int64_t got; /* Nombre d'octets lus */ + int ret; /* Bilan d'un appel */ + const void *pos; /* Tête de lecture */ + const void *max; /* Fin des données lisibles */ + bool status; /* Bilan d'une extraction */ + char *prefix; /* Début de nom de fichier */ + int fd; /* Descripteur de flux ouvert */ + off_t moved; /* Nouvelle position établie */ + zip_int64_t i; /* Boucle de parcours */ + storage_backend_t *backend; /* Informations à intégrer */ + const char *slash; /* Pointeur vers un caractère /*/ + + result = NULL; + + storage = g_object_new(G_TYPE_OBJECT_STORAGE, NULL); + + archive = zip_open(filename, ZIP_RDONLY, &err); + if (archive == NULL) + { + zip_error_init_with_code(&error, err); + LOG_ERROR_ZIP("zip_open", &error); + goto exit; + } + + zip_error_init(&error); + + tpmem_filename = NULL; + + /* Validation du nombre d'entrées */ + + entries_count = zip_get_num_entries(archive, ZIP_FL_UNCHANGED); + + if (entries_count < 2) + goto exit_with_archive; + + data = NULL; + + /* Extraction de la partie de contrôle */ + + ret = zip_stat_index(archive, 0, ZIP_FL_UNCHANGED, &stats); + if (ret != 0) + { + LOG_ERROR_ZIP("zip_stat_index", zip_get_error(archive)); + goto exit_with_archive; + } + + if ((stats.valid & (ZIP_STAT_NAME | ZIP_STAT_SIZE)) != (ZIP_STAT_NAME | ZIP_STAT_SIZE)) + goto exit_with_archive; + + if (strcmp(stats.name, "control") != 0) + goto exit_with_archive; + + if (stats.size < (6 + 2 + 1 + 1 + 1)) + goto exit_with_archive; + + file = zip_fopen_index(archive, 0, ZIP_FL_UNCHANGED); + if (file == NULL) + { + LOG_ERROR_ZIP("zip_fopen_index", zip_get_error(archive)); + goto exit_with_archive; + } + + data = malloc(stats.size); + + got = zip_fread(file, data, stats.size); + + ret = zip_fclose(file); + if (ret != 0) + { + zip_error_set(&error, ret, 0); + LOG_ERROR_ZIP("zip_fclose", &error); + goto exit_with_data; + } + + if (got != stats.size) + goto exit_with_data; + + if (memcmp(data, STORAGE_MAGIC, 6) != 0) + goto exit_with_data; + + if (memcmp(((uint8_t *)data) + 6, STORAGE_NUMBER, 2) != 0) + goto exit_with_data; + + pos = (uint8_t *)data + 8; + max = (uint8_t *)data + got; + + status = unpack_sized_binary(&storage->type, &pos, max); + if (!status) goto exit_with_data; + + if (pos >= max) + goto exit_with_data; + + storage->version = *(uint8_t *)pos; + pos = (uint8_t *)pos + 1; + + unpack_sized_binary_as_string(&storage->uid, &pos, max); + if (!status) goto exit_with_data; + + if (pos != max) + goto exit_with_data; + + free(data); + data = NULL; + + /* Extraction de la conservation des types */ + + ret = zip_stat_index(archive, 1, ZIP_FL_UNCHANGED, &stats); + if (ret != 0) + { + LOG_ERROR_ZIP("zip_stat_index", zip_get_error(archive)); + goto exit_with_archive; + } + + if ((stats.valid & (ZIP_STAT_NAME | ZIP_STAT_SIZE)) != (ZIP_STAT_NAME | ZIP_STAT_SIZE)) + goto exit_with_archive; + + if (strcmp(stats.name, "types") != 0) + goto exit_with_archive; + + file = zip_fopen_index(archive, 1, ZIP_FL_UNCHANGED); + if (file == NULL) + { + LOG_ERROR_ZIP("zip_fopen_index", zip_get_error(archive)); + goto exit_with_archive; + } + + data = malloc(stats.size); + + got = zip_fread(file, data, stats.size); + + ret = zip_fclose(file); + if (ret != 0) + { + zip_error_set(&error, ret, 0); + LOG_ERROR_ZIP("zip_fclose", &error); + goto exit_with_data; + } + + asprintf(&prefix, "%s-types", storage->uid.static_data); + + fd = make_tmp_file(prefix, "cache", &tpmem_filename); + + free(prefix); + + if (fd == -1) + goto exit_with_data; + + status = safe_write(fd, data, stats.size); + if (!status) + { + close(fd); + goto exit_with_data; + } + + moved = lseek(fd, 0, SEEK_SET); + if (moved == ((off_t)-1)) + { + LOG_ERROR_N("lseek"); + close(fd); + goto exit_with_data; + } + + status = g_type_memory_load(storage->tpmem, fd); + + close(fd); + + if (!status) + goto exit_with_data; + + free(data); + data = NULL; + + /* Extraction des différents objects restants */ + + if (entries_count > 2) + { + storage->count = entries_count - 2; + storage->backends = calloc(storage->count, sizeof(storage_backend_t)); + + for (i = 2; i < entries_count; i++) + { + backend = &storage->backends[i - 2]; + + ret = zip_stat_index(archive, i, ZIP_FL_UNCHANGED, &stats); + if (ret != 0) + { + LOG_ERROR_ZIP("zip_stat_index", zip_get_error(archive)); + goto exit_with_archive; + } + + if ((stats.valid & (ZIP_STAT_NAME | ZIP_STAT_SIZE)) != (ZIP_STAT_NAME | ZIP_STAT_SIZE)) + goto exit_with_archive; + + if (strncmp(stats.name, SL("backends/")) != 0) + goto exit_with_archive; + + slash = strchr(stats.name, '/'); + + if (slash == NULL) + goto exit_with_archive; + + if (strchr(slash + 1, '/') != NULL) + goto exit_with_archive; + + if (!g_object_storage_has_no_backend_named(storage, slash + 1)) + goto exit_with_archive; + + file = zip_fopen_index(archive, i, ZIP_FL_UNCHANGED); + if (file == NULL) + { + LOG_ERROR_ZIP("zip_fopen_index", zip_get_error(archive)); + goto exit_with_archive; + } + + data = malloc(stats.size); + + got = zip_fread(file, data, stats.size); + + ret = zip_fclose(file); + if (ret != 0) + { + zip_error_set(&error, ret, 0); + LOG_ERROR_ZIP("zip_fclose", &error); + goto exit_with_data; + } + + backend->name = strdup(slash + 1); + + asprintf(&prefix, "%s-%s", storage->uid.static_data, backend->name); + + backend->fd = make_tmp_file(prefix, "cache", &backend->filename); + + free(prefix); + + if (backend->fd == -1) + goto exit_with_data; + + status = safe_write(backend->fd, data, stats.size); + if (!status) goto exit_with_data; + + moved = lseek(backend->fd, 0, SEEK_SET); + if (moved == ((off_t)-1)) + { + LOG_ERROR_N("lseek"); + goto exit_with_data; + } + + free(data); + data = NULL; + + } + + } + + /* Clôture des opérations */ + + result = storage; + ref_object(storage); + + exit_with_data: + + if (data != NULL) + free(data); + + exit_with_archive: + + ret = zip_close(archive); + + if (ret != 0) + LOG_ERROR_ZIP("zip_close", zip_get_error(archive)); + + if (tpmem_filename != NULL) + unlink(tpmem_filename); + + zip_error_fini(&error); + + exit: + + unref_object(storage); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = gestionnaire de conservations à manipuler. * +* filename = fichier de destination à constituer. * +* * +* Description : Sauvegarde le support d'une conservation d'objets en place. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_object_storage_store(GObjectStorage *storage, const char *filename) +{ + bool result; /* Bilan à retourner */ + int err; /* Eventuel code d'erreur */ + zip_t *archive; /* Archive ZIP à manipuler */ + zip_error_t error; /* Suivi des erreurs obtenues */ + char *tpmem_filename; /* Chemin d'accès pour types */ + void *type_buf; /* Données pour le type */ + size_t type_buflen; /* Quantité de ces données */ + void *uid_buf; /* Données pour l'identifiant */ + size_t uid_buflen; /* Quantité de ces données */ + size_t control_len; /* Taille des premières données*/ + uint8_t *control; /* Premières données du fichier*/ + zip_source_t *zip_data; /* Données ZIP à intégrer */ + zip_int64_t index; /* Nouvel index du contenu */ + int ret; /* Bilan d'un appel */ + char *prefix; /* Début de nom de fichier */ + int fd; /* Descripteur de flux ouvert */ + bool status; /* Bilan d'une écriture */ + size_t i; /* Boucle de parcours */ + char *zip_name; /* Destination pour l'archive */ + + result = false; + + archive = zip_open(filename, ZIP_CREATE | ZIP_TRUNCATE, &err); + if (archive == NULL) + { + zip_error_init_with_code(&error, err); + LOG_ERROR_ZIP("zip_open", &error); + goto exit; + } + + zip_error_init(&error); + + tpmem_filename = NULL; + + /* Fichier de contrôle */ + + type_buf = pack_sized_binary(&storage->type, &type_buflen); + + uid_buf = pack_sized_binary_as_string(&storage->uid, &uid_buflen); + + assert((sizeof(STORAGE_MAGIC) - 1 + sizeof(STORAGE_NUMBER) - 1) == 8); + + control_len = 8 + type_buflen + 1 + uid_buflen; + control = malloc(control_len * sizeof(uint8_t)); + + memcpy(control, STORAGE_MAGIC, 6); + memcpy(control + 6, STORAGE_NUMBER, 2); + + memcpy(control + 8, type_buf, type_buflen); + + control[8 + type_buflen] = storage->version; + + memcpy(control + 8 + type_buflen + 1, uid_buf, uid_buflen); + + zip_data = zip_source_buffer_create(control, control_len, 0, &error); + if (zip_data == NULL) + { + LOG_ERROR_ZIP("zip_source_buffer_create", &error); + goto exit_with_control; + } + + index = zip_file_add(archive, "control", zip_data, ZIP_FL_ENC_UTF_8); + if (index == -1) + { + zip_source_free(zip_data); + LOG_ERROR_ZIP("zip_file_add", zip_get_error(archive)); + goto exit_with_control; + } + + ret = zip_set_file_compression(archive, index, ZIP_CM_STORE, 0 /* comp_flags */); + if (ret == -1) + { + LOG_ERROR_ZIP("zip_set_file_compression", zip_get_error(archive)); + goto exit_with_control; + } + + /* Composants de la conservation */ + + g_mutex_lock(&storage->mutex); + + /* Conservation des types */ + + asprintf(&prefix, "%s-types", storage->uid.static_data); + + fd = make_tmp_file(prefix, "cache", &tpmem_filename); + + free(prefix); + + if (fd == -1) + goto exit_with_lock; + + status = g_type_memory_store(storage->tpmem, fd); + + close(fd); + + if (!status) + goto exit_with_lock; + + zip_data = zip_source_file_create(tpmem_filename, 0, -1 /* ZIP_LENGTH_TO_END */, &error); + if (zip_data == NULL) + { + LOG_ERROR_ZIP("zip_source_file_create", &error); + goto exit_with_lock; + } + + index = zip_file_add(archive, "types", zip_data, ZIP_FL_ENC_UTF_8); + if (index == -1) + { + zip_source_free(zip_data); + LOG_ERROR_ZIP("zip_file_add", zip_get_error(archive)); + goto exit_with_lock; + } + + ret = zip_set_file_compression(archive, index, ZIP_CM_DEFLATE, 9 /* comp_flags */); + if (ret == -1) + { + LOG_ERROR_ZIP("zip_set_file_compression", zip_get_error(archive)); + goto exit_with_lock; + } + + /* Conservation des objets */ + + for (i = 0; i < storage->count; i++) + { + zip_data = zip_source_file_create(storage->backends[i].filename, 0, -1 /* ZIP_LENGTH_TO_END */, &error); + if (zip_data == NULL) + { + LOG_ERROR_ZIP("zip_source_file_create", &error); + goto exit_with_lock; + } + + /** + * Pas besoin de distinguer les chemins UNIX et Windows ici. + * + * Cf. https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT : + * + * 4.4.17 file name: (Variable) + * + * The path stored MUST NOT contain a drive or + * device letter, or a leading slash. All slashes + * MUST be forward slashes '/' as opposed to + * backwards slashes '\' for compatibility with Amiga + * and UNIX file systems etc. If input came from standard + * input, there is no file name field. + * + */ + + asprintf(&zip_name, "backends/%s", storage->backends[i].name); + + index = zip_file_add(archive, zip_name, zip_data, ZIP_FL_ENC_UTF_8); + + free(zip_name); + + if (index == -1) + { + zip_source_free(zip_data); + LOG_ERROR_ZIP("zip_file_add", zip_get_error(archive)); + goto exit_with_lock; + } + + ret = zip_set_file_compression(archive, index, ZIP_CM_DEFLATE, 9 /* comp_flags */); + if (ret == -1) + { + LOG_ERROR_ZIP("zip_set_file_compression", zip_get_error(archive)); + goto exit_with_lock; + } + + } + + result = true; + + /* Clôture des opérations */ + + exit_with_lock: + + g_mutex_unlock(&storage->mutex); + + exit_with_control: + + ret = zip_close(archive); + + if (ret != 0) + LOG_ERROR_ZIP("zip_close", zip_get_error(archive)); + + free(control); + + if (tpmem_filename != NULL) + unlink(tpmem_filename); + + zip_error_fini(&error); + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = gestionnaire de conservations à consulter. * +* name = désignation d'un nouveau groupe d'objets. * +* * +* Description : Assure l'inexistence d'un groupe avec un nom donné. * +* * +* Retour : Bilan des recherches. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_object_storage_has_no_backend_named(GObjectStorage *storage, const char *name) +{ + bool result; /* Bilan à retourner */ + size_t i; /* Boucle de parcours */ + + result = true; + + for (i = 0; i < storage->count && result; i++) + { + if (storage->backends[i].name == NULL) + break; + + if (strcmp(storage->backends[i].name, name) == 0) + result = false; + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = gestionnaire de conservations à compléter. * +* name = désignation d'un nouveau groupe d'objets. * +* * +* Description : Retrouve l'encadrement pour un nouveau groupe d'objets. * +* * +* Retour : Informations liées à un groupe ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static storage_backend_t *g_object_storage_find_backend(GObjectStorage *storage, const char *name) +{ + storage_backend_t *result; /* Encadrement à retourner */ + size_t i; /* Boucle de parcours */ + + assert(!g_mutex_trylock(&storage->mutex)); + + for (i = 0; i < storage->count; i++) + if (strcmp(storage->backends[i].name, name) == 0) + break; + + if (i == storage->count) + result = NULL; + else + result = &storage->backends[i]; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = gestionnaire de conservations à compléter. * +* name = désignation d'un nouveau groupe d'objets. * +* backend = support mis en place pour les enregistrements. * +* * +* Description : Ajoute le support d'un nouveau groupe d'objets construits. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_object_storage_add_backend(GObjectStorage *storage, const char *name, storage_backend_t **backend) +{ + bool result; /* Bilan à retourner */ + char *prefix; /* Début de nom de fichier */ + char *filename; /* Chemin d'accès aux données */ + int fd; /* Descripteur de flux ouvert */ + + result = false; + + *backend = NULL; + + assert(!g_mutex_trylock(&storage->mutex)); + + if (g_object_storage_find_backend(storage, name) != NULL) + goto exit; + + /* Préparatifs */ + + asprintf(&prefix, "%s-%s", storage->uid.static_data, name); + + fd = make_tmp_file(prefix, "cache", &filename); + + free(prefix); + + if (fd == -1) + goto exit; + + /* Inscription en bonne et due forme */ + + storage->backends = realloc(storage->backends, ++storage->count * sizeof(storage_backend_t)); + + *backend = &storage->backends[storage->count - 1]; + + (*backend)->name = strdup(name); + + (*backend)->filename = filename; + (*backend)->fd = fd; + + result = true; + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = gestionnaire à manipuler. * +* name = désignation d'un groupe d'objets à consulter. * +* pos = tête de lecture avant écriture. * +* * +* Description : Charge un objet à partir de données rassemblées. * +* * +* Retour : Objet restauré en mémoire ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static GSerializableObject *g_object_storage_load_object_unlocked(GObjectStorage *storage, const char *name, off64_t pos) +{ + GSerializableObject *result; /* Instance à retourner */ + storage_backend_t *backend; /* Informations à consulter */ + off64_t new; /* Nouvelle position de lecture*/ + bool status; /* Bilan d'une opération */ + + result = NULL; + + assert(!g_mutex_trylock(&storage->mutex)); + + /* Chargement */ + + backend = g_object_storage_find_backend(storage, name); + if (backend == NULL) goto exit; + + new = lseek64(backend->fd, pos, SEEK_SET); + if (new == (off_t)-1) + { + LOG_ERROR_N("lseek64"); + goto exit; + } + + assert (new == pos); + + /* Phase de conversion */ + + result = G_SERIALIZABLE_OBJECT(g_type_memory_create_object_from_gtype(storage->tpmem, backend->fd)); + + if (result) + { + status = g_serializable_object_load(result, storage, backend->fd); + + if (!status) + g_clear_object(&result); + + } + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = gestionnaire à manipuler. * +* name = désignation d'un groupe d'objets à consulter. * +* pos = tête de lecture avant écriture. * +* * +* Description : Charge un objet à partir de données rassemblées. * +* * +* Retour : Objet restauré en mémoire ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GSerializableObject *g_object_storage_load_object(GObjectStorage *storage, const char *name, off64_t pos) +{ + GSerializableObject *result; /* Instance à retourner */ + + g_mutex_lock(&storage->mutex); + + result = g_object_storage_load_object_unlocked(storage, name, pos); + + g_mutex_unlock(&storage->mutex); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = gestionnaire à manipuler. * +* fd = flux de données de l'objet courant. * +* name = désignation du groupe de l'objets à extraire. * +* * +* Description : Charge un objet interne à partir d'une référence embarquée. * +* * +* Retour : Objet restauré en mémoire ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GSerializableObject *g_object_storage_unpack_object(GObjectStorage *storage, int fd, const char *name) +{ + GSerializableObject *result; /* Instance à retourner */ + storage_backend_t *backend; /* Informations à consulter */ + uleb128_t pos; /* Localisation des données */ + bool status; /* Bilan d'une opération */ + off64_t saved; /* Sauvegarde de position */ + off64_t new; /* Nouvelle position de lecture*/ + + result = NULL; + + g_mutex_lock(&storage->mutex); + + /* Récupération de la position */ + + backend = g_object_storage_find_backend(storage, name); + if (backend == NULL) goto exit; + + status = load_uleb128(&pos, backend->fd); + if (!status) goto exit; + + saved = lseek64(backend->fd, 0, SEEK_CUR); + if (saved == (off_t)-1) + { + LOG_ERROR_N("lseek64"); + goto exit; + } + + /* Chargement */ + + result = g_object_storage_load_object_unlocked(storage, name, pos); + + if (result == NULL) goto exit; + + /* Restauration de la position courante */ + + new = lseek64(backend->fd, saved, SEEK_SET); + if (new == (off_t)-1) + { + LOG_ERROR_N("lseek64"); + + g_clear_object(&result); + goto exit; + + } + + assert (new == saved); + + exit: + + g_mutex_unlock(&storage->mutex); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : storage = gestionnaire à manipuler. * +* name = désignation d'un groupe d'objets, nouveau ou non. * +* object = objet sérialisable à traiter. * +* pos = tête de lecture avant écriture. [OUT] * +* * +* Description : Sauvegarde un object sous forme de données rassemblées. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_object_storage_store_object(GObjectStorage *storage, const char *name, const GSerializableObject *object, off64_t *pos) +{ + bool result; /* Bilan à retourner */ + storage_backend_t *backend; /* Informations à consulter */ + off64_t tmp; /* Conservation éphémère */ + + result = false; + + g_mutex_lock(&storage->mutex); + + backend = g_object_storage_find_backend(storage, name); + + if (backend == NULL) + g_object_storage_add_backend(storage, name, &backend); + + if (backend != NULL) + { + if (pos == NULL) + pos = &tmp; + + *pos = lseek64(backend->fd, 0, SEEK_CUR); + + if (*pos != (off64_t)-1) + { + result = g_type_memory_store_object_gtype(storage->tpmem, G_OBJECT(object), backend->fd); + + if (result) + result = g_serializable_object_store(object, storage, backend->fd); + + } + + } + + g_mutex_unlock(&storage->mutex); + + return result; + +} diff --git a/src/glibext/storage.h b/src/glibext/storage.h new file mode 100644 index 0000000..ea06ed4 --- /dev/null +++ b/src/glibext/storage.h @@ -0,0 +1,81 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * storage.h - prototypes pour la conservation sur disque d'objets construits + * + * Copyright (C) 2020-2025 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_STORAGE_H +#define _GLIBEXT_STORAGE_H + + +#include <stdint.h> + + +#include "helpers.h" +#include "serialize.h" + + + +#define G_TYPE_OBJECT_STORAGE (g_object_storage_get_type()) + +DECLARE_GTYPE(GObjectStorage, g_object_storage, G, OBJECT_STORAGE); + + +/* Crée le support d'une conservation d'objets en place. */ +GObjectStorage *g_object_storage_new(const char *, uint8_t, const char *); + +/* Charge le support d'une conservation d'objets en place. */ +GObjectStorage *g_object_storage_load(const char *); + +/* Sauvegarde le support d'une conservation d'objets en place. */ +bool g_object_storage_store(GObjectStorage *, const char *); + +/* Charge un objet à partir de données rassemblées. */ +GSerializableObject *g_object_storage_load_object(GObjectStorage *, const char *, off64_t); + +/* Charge un objet interne à partir d'une référence embarquée. */ +GSerializableObject *g_object_storage_unpack_object(GObjectStorage *, int, const char *); + +/* Sauvegarde un object sous forme de données rassemblées. */ +bool g_object_storage_store_object(GObjectStorage *, const char *, const GSerializableObject *, off64_t *); + + + + +#if 0 + +/** + * TODO : REMME ? + */ + +#define get_storage_linked_format(s) \ + ({ \ + void*__result; \ + __result = g_object_get_data(G_OBJECT(s), "format"); \ + g_object_ref(G_OBJECT(__result)); \ + __result; \ + }) + +#endif + + + + +#endif /* _GLIBEXT_STORAGE_H */ diff --git a/src/glibext/strbuilder-int.h b/src/glibext/strbuilder-int.h new file mode 100644 index 0000000..d74e65c --- /dev/null +++ b/src/glibext/strbuilder-int.h @@ -0,0 +1,47 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * strbuilder-int.h - définitions internes propres aux éléments exportant une chaîne de caractères + * + * Copyright (C) 2025 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_STRBUILDER_INT_H +#define _GLIBEXT_STRBUILDER_INT_H + + +#include "strbuilder.h" + + + +/* Exporte une chaîne de caractères à partir d'un objet. */ +typedef bool (* strbuilder_to_string_fc) (const GStringBuilder *, unsigned int, sized_binary_t *); + + +/* Instance d'objet visant à être unique (interface) */ +struct _GStringBuilderInterface +{ + GTypeInterface base_iface; /* A laisser en premier */ + + strbuilder_to_string_fc to_string; /* Conversion en chaîne */ + +}; + + + +#endif /* _GLIBEXT_STRBUILDER_INT_H */ diff --git a/src/glibext/strbuilder.c b/src/glibext/strbuilder.c new file mode 100644 index 0000000..e48674c --- /dev/null +++ b/src/glibext/strbuilder.c @@ -0,0 +1,89 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * strbuilder.c - exportation d'une chaîne de caractères depuis un élément + * + * Copyright (C) 2025 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 <http://www.gnu.org/licenses/>. + */ + + +#include "strbuilder.h" + + +#include "strbuilder-int.h" + + + +/* Procède à l'initialisation de l'interface d'exportation. */ +static void g_string_builder_default_init(GStringBuilderInterface *); + + + +/* Détermine le type d'une interface pour la constitution d'objets uniques. */ +G_DEFINE_INTERFACE(GStringBuilder, g_string_builder, G_TYPE_OBJECT) + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* * +* Description : Procède à l'initialisation de l'interface d'exportation. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_string_builder_default_init(GStringBuilderInterface *iface) +{ + iface->to_string = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : builder = objet dont l'instance est exportable. * +* flags = éventuelles indications pour l'opération. * +* out = chaîne de caractères mise en place. [OUT] * +* * +* Description : Exporte une chaîne de caractères à partir d'un objet. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : La sortie out est à nettoyer avec exit_sized_binary() après * +* usage. * +* * +******************************************************************************/ + +bool g_string_builder_to_string(const GStringBuilder *builder, unsigned int flags, sized_binary_t *out) +{ + bool result; /* Bilan à retourner */ + GStringBuilderInterface *iface; /* Interface utilisée */ + + iface = G_STRING_BUILDER_GET_IFACE(builder); + + if (iface->to_string == NULL) + result = false; + + else + result = iface->to_string(builder, flags, out); + + return result; + +} diff --git a/src/glibext/strbuilder.h b/src/glibext/strbuilder.h new file mode 100644 index 0000000..b6422f7 --- /dev/null +++ b/src/glibext/strbuilder.h @@ -0,0 +1,46 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * strbuilder.h - prototypes pour l'exportation d'une chaîne de caractères depuis un élément + * + * Copyright (C) 2025 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_STRBUILDER_H +#define _GLIBEXT_STRBUILDER_H + + +#include <stdbool.h> + + +#include "helpers.h" +#include "../common/szbin.h" + + + +#define G_TYPE_STRING_BUILDER (g_string_builder_get_type()) + +DECLARE_INTERFACE(GStringBuilder, g_string_builder, G, STRING_BUILDER); + + +/* Exporte une chaîne de caractères à partir d'un objet. */ +bool g_string_builder_to_string(const GStringBuilder *, unsigned int, sized_binary_t *); + + + +#endif /* _GLIBEXT_STRBUILDER_H */ diff --git a/src/glibext/tpmem-int.h b/src/glibext/tpmem-int.h new file mode 100644 index 0000000..b1b7eec --- /dev/null +++ b/src/glibext/tpmem-int.h @@ -0,0 +1,78 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * tpmem-int.h - définitions internes propres à la mémorisation des types d'objets mis en cache + * + * Copyright (C) 2020-2025 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_TPMEM_INT_H +#define _GLIBEXT_TPMEM_INT_H + + +#include "tpmem.h" + + +/* Conservation d'une référence sur un type */ +typedef struct _gtype_ref_info_t +{ + GType gtype; /* Type pour la GLib */ + gpointer gclass; /* Lien vers sa classe */ + + /** + * La GLib n'est pas très claire sur la taille de GType comme le montre le + * code issu de <sources>/gobject/gtype.h : + * + * #if GLIB_SIZEOF_SIZE_T != GLIB_SIZEOF_LONG || !defined __cplusplus + * typedef gsize GType; + * #else // for historic reasons, C++ links against gulong GTypes + * typedef gulong GType; + * #endif + * + * Et : + * + * typedef unsigned $glib_size_type_define gsize; + * + * On prend donc le parti de conserver ces types sous forme de valeurs 64 bits + * lors des enregistrements. + */ + +} gtype_ref_info_t; + + +/* Définition d'une mémoire de types d'objets (instance) */ +struct _GTypeMemory +{ + GObject parent; /* A laisser en premier */ + + gtype_ref_info_t *gtypes; /* Types des objets reconnus */ + size_t count; /* Quantité de ces objets */ + GMutex mutex; /* Contrôle d'accès à la liste */ + +}; + +/* Définition d'une mémoire de types d'objets (classe) */ +struct _GTypeMemoryClass +{ + GObjectClass parent; /* A laisser en premier */ + +}; + + + +#endif /* _GLIBEXT_TPMEM_INT_H */ diff --git a/src/glibext/tpmem.c b/src/glibext/tpmem.c new file mode 100644 index 0000000..14b5e33 --- /dev/null +++ b/src/glibext/tpmem.c @@ -0,0 +1,363 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * tpmem.c - mémorisation des types d'objets mis en cache + * + * Copyright (C) 2020-2025 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 <http://www.gnu.org/licenses/>. + */ + + +#include "tpmem.h" + + +#include <assert.h> + + +#include "tpmem-int.h" +#include "../common/szbin.h" +#include "../core/logs.h" + + + +/* Initialise la classe des mémoires de types d'objets. */ +static void g_type_memory_class_init(GTypeMemoryClass *); + +/* Initialise une instance de mémoire de types d'objets. */ +static void g_type_memory_init(GTypeMemory *); + +/* Supprime toutes les références externes. */ +static void g_type_memory_dispose(GObject *); + +/* Procède à la libération totale de la mémoire. */ +static void g_type_memory_finalize(GObject *); + + + +/* Indique le type défini pour une mémoire de types d'objets. */ +G_DEFINE_TYPE(GTypeMemory, g_type_memory, G_TYPE_OBJECT); + + +/****************************************************************************** +* * +* Paramètres : klass = classe à initialiser. * +* * +* Description : Initialise la classe des mémoires de types d'objets. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_type_memory_class_init(GTypeMemoryClass *klass) +{ + GObjectClass *object; /* Autre version de la classe */ + + object = G_OBJECT_CLASS(klass); + + object->dispose = g_type_memory_dispose; + object->finalize = g_type_memory_finalize; + +} + + +/****************************************************************************** +* * +* Paramètres : tpmem = instance à initialiser. * +* * +* Description : Initialise une instance de mémoire de types d'objets. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_type_memory_init(GTypeMemory *tpmem) +{ + tpmem->gtypes = NULL; + tpmem->count = 0; + g_mutex_init(&tpmem->mutex); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Supprime toutes les références externes. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_type_memory_dispose(GObject *object) +{ + GTypeMemory *tpmem; /* Version spécialisée */ + uint64_t i; /* Boucle de parcours */ + + tpmem = G_TYPE_MEMORY(object); + + g_mutex_lock(&tpmem->mutex); + + for (i = 0; i < tpmem->count; i++) + if (tpmem->gtypes[i].gclass != NULL) + g_type_class_unref(tpmem->gtypes[i].gclass); + + g_mutex_unlock(&tpmem->mutex); + + g_mutex_clear(&tpmem->mutex); + + G_OBJECT_CLASS(g_type_memory_parent_class)->dispose(object); + +} + + +/****************************************************************************** +* * +* Paramètres : object = instance d'objet GLib à traiter. * +* * +* Description : Procède à la libération totale de la mémoire. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_type_memory_finalize(GObject *object) +{ + GTypeMemory *tpmem; /* Version spécialisée */ + + tpmem = G_TYPE_MEMORY(object); + + if (tpmem->gtypes != NULL) + free(tpmem->gtypes); + + G_OBJECT_CLASS(g_type_memory_parent_class)->finalize(object); + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Crée une mémoire pour types d'objets. * +* * +* Retour : Instance mise en place. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GTypeMemory *g_type_memory_new(void) +{ + GTypeMemory *result; /* Structure à retourner */ + + result = g_object_new(G_TYPE_TYPE_MEMORY, NULL); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : tpmem = mémoire à compléter. * +* fd = flux ouvert en lecture. * +* * +* Description : Apprend tous les types mémorisés dans un tampon. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_type_memory_load(GTypeMemory *tpmem, int fd) +{ + bool result; /* Bilan à enregistrer */ + uleb128_t count; /* Nombre d'éléments détectés */ + uleb128_t i; /* Boucle de parcours */ + sized_binary_t str; /* Chaîne à charger */ + + result = load_uleb128(&count, fd); + + if (result) + { + g_mutex_lock(&tpmem->mutex); + + tpmem->count = count; + + assert(tpmem->gtypes == NULL); + tpmem->gtypes = calloc(count, sizeof(gtype_ref_info_t)); + + for (i = 0; i < tpmem->count && result; i++) + { + result = load_sized_binary_as_string(&str, fd); + if (!result) break; + + tpmem->gtypes[i].gtype = g_type_from_name(str.data); + result = (tpmem->gtypes[i].gtype != 0); + + if (!result) + log_variadic_message(LMT_ERROR, "Unknown type: '%s'", str.data); + + else + tpmem->gtypes[i].gclass = g_type_class_ref(tpmem->gtypes[i].gtype); + + exit_sized_binary(&str); + + } + + g_mutex_unlock(&tpmem->mutex); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : tpmem = mémoire à consulter. * +* fd = flux ouvert en écriture. * +* * +* Description : Enregistre tous les types mémorisés dans un tampon. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_type_memory_store(GTypeMemory *tpmem, int fd) +{ + bool result; /* Bilan à enregistrer */ + uint64_t i; /* Boucle de parcours */ + const gchar *name; /* Désignation d'un type */ + sized_binary_t str; /* Chaîne à conserver */ + + g_mutex_lock(&tpmem->mutex); + + result = store_uleb128((uleb128_t []){ tpmem->count }, fd); + + for (i = 0; i < tpmem->count && result; i++) + { + name = g_type_name(tpmem->gtypes[i].gtype); + + setup_sized_binary_from_static_string(&str, name); + + store_sized_binary_as_string(&str, fd); + + } + + g_mutex_unlock(&tpmem->mutex); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : tpmem = mémoire à manipuler. * +* fd = flux ouvert en lecture. * +* * +* Description : Crée une nouvelle instance d'objet à partir de son type. * +* * +* Retour : Instance issue de l'opération ou NULL. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GObject *g_type_memory_create_object_from_gtype(GTypeMemory *tpmem, int fd) +{ + GObject *result; /* Nouvelle instance à renvoyer*/ + uleb128_t index; /* Indice du point d'insertion */ + bool status; /* Bilan d'une récupération */ + + result = NULL; + + status = load_uleb128(&index, fd); + + if (status) + { + g_mutex_lock(&tpmem->mutex); + + if (index < tpmem->count) + result = g_object_new(tpmem->gtypes[index].gtype, NULL); + + g_mutex_unlock(&tpmem->mutex); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : tpmem = mémoire à manipuler. * +* obj = instance dont le type est à mémoriser. * +* fd = flux ouvert en écriture. * +* * +* Description : Sauvegarde le type d'un objet instancié. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_type_memory_store_object_gtype(GTypeMemory *tpmem, GObject *obj, int fd) +{ + bool result; /* Bilan à retourner */ + GType gtype; /* Type à enregistrer */ + size_t index; /* Indice du point d'insertion */ + + gtype = G_TYPE_FROM_INSTANCE(obj); + + g_mutex_lock(&tpmem->mutex); + + for (index = 0; index < tpmem->count; index++) + if (tpmem->gtypes[index].gtype == gtype) + break; + + if (index == tpmem->count) + { + tpmem->gtypes = realloc(tpmem->gtypes, ++tpmem->count * sizeof(gtype_ref_info_t)); + + tpmem->gtypes[index].gtype = gtype; + tpmem->gtypes[index].gclass = g_type_class_ref(gtype); + + } + + g_mutex_unlock(&tpmem->mutex); + + result = store_uleb128((uleb128_t []){ index }, fd); + + return result; + +} diff --git a/src/glibext/tpmem.h b/src/glibext/tpmem.h new file mode 100644 index 0000000..ccb8323 --- /dev/null +++ b/src/glibext/tpmem.h @@ -0,0 +1,57 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * tpmem.h - prototypes pour la mémorisation des types d'objets mis en cache + * + * Copyright (C) 2020-2025 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_TPMEM_H +#define _GLIBEXT_TPMEM_H + + +#include <stdbool.h> + + +#include "helpers.h" + + + +#define G_TYPE_TYPE_MEMORY (g_type_memory_get_type()) + +DECLARE_GTYPE(GTypeMemory, g_type_memory, G, TYPE_MEMORY); + + +/* Crée une mémoire pour types d'objets. */ +GTypeMemory *g_type_memory_new(void); + +/* Apprend tous les types mémorisés dans un tampon. */ +bool g_type_memory_load(GTypeMemory *, int); + +/* Enregistre tous les types mémorisés dans un tampon. */ +bool g_type_memory_store(GTypeMemory *, int); + +/* Crée une nouvelle instance d'objet à partir de son type. */ +GObject *g_type_memory_create_object_from_gtype(GTypeMemory *, int); + +/* Sauvegarde le type d'un objet instancié. */ +bool g_type_memory_store_object_gtype(GTypeMemory *, GObject *, int); + + + +#endif /* _GLIBEXT_TPMEM_H */ diff --git a/src/glibext/widthtracker.c b/src/glibext/widthtracker.c index 7e06578..0a3dbe6 100644 --- a/src/glibext/widthtracker.c +++ b/src/glibext/widthtracker.c @@ -330,13 +330,17 @@ void g_width_tracker_set_column_min_width(GWidthTracker *tracker, size_t col, in void g_width_tracker_compute_width(const GWidthTracker *tracker, int *width) { + size_t col_count; /* Nombre maximum de colonnes */ size_t i; /* Boucle de parcours */ *width = 0; - // FIX access to col_count - for (i = 0; i < tracker->col_count; i++) + + col_count = tracker->opt_count + tracker->reg_count; + + + for (i = 0; i < col_count; i++) *width += tracker->min_widths[i]; } diff --git a/src/glibext/work-int.h b/src/glibext/work-int.h index 4f84e86..58293e5 100644 --- a/src/glibext/work-int.h +++ b/src/glibext/work-int.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * delayed-int.h - définitions internes pour la gestion des travaux différés + * work-int.h - définitions internes pour la gestion des travaux différés * - * Copyright (C) 2009-2018 Cyrille Bagard + * Copyright (C) 2009-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,27 +21,26 @@ */ -#ifndef _GLIBEXT_DELAYED_INT_H -#define _GLIBEXT_DELAYED_INT_H +#ifndef _GLIBEXT_WORK_INT_H +#define _GLIBEXT_WORK_INT_H -#include "delayed.h" +#include "work.h" -#include "notifier.h" -#include "../common/dllist.h" +#include <stdbool.h> +#include "../common/dllist.h" -/* -------------------------- TACHE DIFFEREE DANS LE TEMPS -------------------------- */ /* Traite un travail programmé. */ -typedef void (* run_task_fc) (GDelayedWork *, GtkStatusStack *); +typedef void (* run_work_fc) (GGenericWork *); /* Travail différé (instance) */ -struct _GDelayedWork +struct _GGenericWork { GObject parent; /* A laisser en premier */ @@ -54,22 +53,18 @@ struct _GDelayedWork }; /* Travail différé (classe) */ -struct _GDelayedWorkClass +struct _GGenericWorkClass { GObjectClass parent; /* A laisser en premier */ - run_task_fc run; /* Traitement externalisé */ + run_work_fc run; /* Traitement externalisé */ /* Signaux */ - void (* work_completed) (GDelayedWork *); + void (* work_completed) (GGenericWork *); }; -#define delayed_work_list_add_tail(new, head) dl_list_add_tail(new, head, GDelayedWork, link) -#define delayed_work_list_del(item, head) dl_list_del(item, head, GDelayedWork, link) - - -#endif /* _GLIBEXT_DELAYED_INT_H */ +#endif /* _GLIBEXT_WORK_INT_H */ diff --git a/src/glibext/work.c b/src/glibext/work.c index 6b5ac35..218fcf8 100644 --- a/src/glibext/work.c +++ b/src/glibext/work.c @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * delayed.c - gestion des travaux différés + * work.c - gestion des travaux différés * - * Copyright (C) 2009-2018 Cyrille Bagard + * Copyright (C) 2009-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,178 +21,29 @@ */ -#include "delayed.h" +#include "work.h" -#include <assert.h> -#include <inttypes.h> -#include <malloc.h> -#include <stdio.h> -#include <string.h> +#include "work-int.h" -#include "delayed-int.h" -#include "../core/nproc.h" -#ifdef INCLUDE_GTK_SUPPORT -# include "../gui/core/global.h" -#endif - - - -/* -------------------------- TACHE DIFFEREE DANS LE TEMPS -------------------------- */ - /* Initialise la classe des travaux différés. */ -static void g_delayed_work_class_init(GDelayedWorkClass *); +static void g_generic_work_class_init(GGenericWorkClass *); /* Initialise une instance de travail différé. */ -static void g_delayed_work_init(GDelayedWork *); +static void g_generic_work_init(GGenericWork *); /* Supprime toutes les références externes. */ -static void g_delayed_work_dispose(GDelayedWork *); +static void g_generic_work_dispose(GGenericWork *); /* Procède à la libération totale de la mémoire. */ -static void g_delayed_work_finalize(GDelayedWork *); - -/* Mène l'opération programmée. */ -static void g_delayed_work_process(GDelayedWork *, GtkStatusStack *); - - - -/* -------------------------- THREAD DE TRAITEMENTS DEDIES -------------------------- */ - - -#define G_TYPE_WORK_GROUP g_work_group_get_type() -#define G_WORK_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_work_group_get_type(), GWorkGroup)) -#define G_IS_WORK_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_work_group_get_type())) -#define G_WORK_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_WORK_GROUP, GWorkGroupClass)) -#define G_IS_WORK_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_WORK_GROUP)) -#define G_WORK_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_WORK_GROUP, GWorkGroupClass)) - - -/* File de traitement pour un type donné (instance) */ -typedef struct _GWorkGroup -{ - GObject parent; /* A laisser en premier */ - - wgroup_id_t id; /* Identifiant de travaux menés*/ - - GDelayedWork *works; /* Tâches à mener à bien */ - GMutex mutex; /* Verrou pour l'accès */ - GCond cond; /* Réveil pour un traitement */ - GCond wait_cond; /* Réveil d'attente de fin */ - gint pending; /* Tâches en cours d'exécution */ - - GThread **threads; /* Procédure de traitement */ - guint threads_count; /* Nombre de procédures */ - bool force_exit; /* Procédure d'arrêt */ - - wait_for_incoming_works_cb callback; /* Encadre les attentes de fin */ - void *data; /* Données à associer */ - -} GWorkGroup; - -/* File de traitement pour un type donné (classe) */ -typedef struct _GWorkGroupClass -{ - GObjectClass parent; /* A laisser en premier */ - -} GWorkGroupClass; - - -/* Indique le type défini pour les groupes de travail. */ -static GType g_work_group_get_type(void); - -/* Initialise la classe des groupes de travail. */ -static void g_work_group_class_init(GWorkGroupClass *); - -/* Initialise une instance de groupe de travail. */ -static void g_work_group_init(GWorkGroup *); - -/* Supprime toutes les références externes. */ -static void g_work_group_dispose(GWorkGroup *); - -/* Procède à la libération totale de la mémoire. */ -static void g_work_group_finalize(GWorkGroup *); - -/* Crée un nouveau thread dédié à un type de travaux donné. */ -static GWorkGroup *g_work_group_new(wgroup_id_t, const guint *); - -/* Fournit l'identifiant associé à un groupe de travail. */ -static wgroup_id_t g_work_group_get_id(const GWorkGroup *); - -/* Place une nouvelle tâche en attente dans une file dédiée. */ -static void g_work_group_schedule(GWorkGroup *, GDelayedWork *); - -/* Assure le traitement en différé. */ -static void *g_work_group_process(GWorkGroup *); - -/* Détermine si le groupe est vide de toute programmation. */ -static bool g_work_group_is_empty(GWorkGroup *); - -/* Attend que toutes les tâches d'un groupe soient traitées. */ -static void g_work_group_wait_for_completion(GWorkGroup *, GWorkQueue *); - -/* Modifie les conditions d'attente des fins d'exécutions. */ -static void g_work_group_set_extra_wait_callback(GWorkGroup *, wait_for_incoming_works_cb, void *); - -/* Force un réveil d'une attente en cours pour la confirmer. */ -static void g_work_group_wake_up_waiters(GWorkGroup *); - - +static void g_generic_work_finalize(GGenericWork *); -/* ------------------------- TRAITEMENT DE TACHES DIFFEREES ------------------------- */ - - -/* Gestionnaire des travaux différés (instance) */ -struct _GWorkQueue -{ - GObject parent; /* A laisser en premier */ - - wgroup_id_t generator; /* Générateur d'identifiants */ - - GWorkGroup **groups; /* Files de traitement */ - size_t groups_count; /* Nombre de files internes */ - GMutex mutex; /* Verrou pour l'accès */ - GCond wait_all; /* Réveil d'attente globale */ - -}; - -/* Gestionnaire des travaux différés (classe) */ -struct _GWorkQueueClass -{ - GObjectClass parent; /* A laisser en premier */ - -}; - - -/* Initialise la classe des travaux différés. */ -static void g_work_queue_class_init(GWorkQueueClass *); - -/* Initialise une instance de gestionnaire de travaux différés. */ -static void g_work_queue_init(GWorkQueue *); - -/* Supprime toutes les références externes. */ -static void g_work_queue_dispose(GWorkQueue *); - -/* Procède à la libération totale de la mémoire. */ -static void g_work_queue_finalize(GWorkQueue *); - -/* Donne l'assurance de l'existence d'un groupe de travail. */ -static bool g_work_queue_ensure_group_exists(GWorkQueue *, wgroup_id_t, const guint *); - -/* Fournit le groupe de travail correspondant à un identifiant. */ -static GWorkGroup *g_work_queue_find_group_for_id(GWorkQueue *, wgroup_id_t); - - - -/* ---------------------------------------------------------------------------------- */ -/* TACHE DIFFEREE DANS LE TEMPS */ -/* ---------------------------------------------------------------------------------- */ /* Indique le type défini pour les travaux différés. */ -G_DEFINE_TYPE(GDelayedWork, g_delayed_work, G_TYPE_OBJECT); +G_DEFINE_TYPE(GGenericWork, g_generic_work, G_TYPE_OBJECT); /****************************************************************************** @@ -207,19 +58,19 @@ G_DEFINE_TYPE(GDelayedWork, g_delayed_work, G_TYPE_OBJECT); * * ******************************************************************************/ -static void g_delayed_work_class_init(GDelayedWorkClass *klass) +static void g_generic_work_class_init(GGenericWorkClass *klass) { GObjectClass *object; /* Autre version de la classe */ object = G_OBJECT_CLASS(klass); - object->dispose = (GObjectFinalizeFunc/* ! */)g_delayed_work_dispose; - object->finalize = (GObjectFinalizeFunc)g_delayed_work_finalize; + object->dispose = (GObjectFinalizeFunc/* ! */)g_generic_work_dispose; + object->finalize = (GObjectFinalizeFunc)g_generic_work_finalize; g_signal_new("work-completed", - G_TYPE_DELAYED_WORK, + G_TYPE_GENERIC_WORK, G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET(GDelayedWorkClass, work_completed), + G_STRUCT_OFFSET(GGenericWorkClass, work_completed), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); @@ -239,8 +90,10 @@ static void g_delayed_work_class_init(GDelayedWorkClass *klass) * * ******************************************************************************/ -static void g_delayed_work_init(GDelayedWork *work) +static void g_generic_work_init(GGenericWork *work) { + DL_LIST_ITEM_INIT(&work->link); + work->completed = false; g_mutex_init(&work->mutex); g_cond_init(&work->cond); @@ -260,12 +113,12 @@ static void g_delayed_work_init(GDelayedWork *work) * * ******************************************************************************/ -static void g_delayed_work_dispose(GDelayedWork *work) +static void g_generic_work_dispose(GGenericWork *work) { g_mutex_clear(&work->mutex); g_cond_clear(&work->cond); - G_OBJECT_CLASS(g_delayed_work_parent_class)->dispose(G_OBJECT(work)); + G_OBJECT_CLASS(g_generic_work_parent_class)->dispose(G_OBJECT(work)); } @@ -282,281 +135,19 @@ static void g_delayed_work_dispose(GDelayedWork *work) * * ******************************************************************************/ -static void g_delayed_work_finalize(GDelayedWork *work) -{ - G_OBJECT_CLASS(g_delayed_work_parent_class)->finalize(G_OBJECT(work)); - -} - - -/****************************************************************************** -* * -* Paramètres : work = travail à effectuer. * -* status = barre de statut à tenir informée. * -* * -* Description : Mène l'opération programmée. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_delayed_work_process(GDelayedWork *work, GtkStatusStack *status) -{ - G_DELAYED_WORK_GET_CLASS(work)->run(work, status); - - g_mutex_lock(&work->mutex); - - work->completed = true; - - g_cond_signal(&work->cond); - g_mutex_unlock(&work->mutex); - - g_signal_emit_by_name(work, "work-completed"); - -} - - -/****************************************************************************** -* * -* Paramètres : work = travail à surveiller. * -* * -* Description : Attend la fin de l'exécution d'une tâche donnée. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_delayed_work_wait_for_completion(GDelayedWork *work) -{ - g_mutex_lock(&work->mutex); - - while (!work->completed) - g_cond_wait(&work->cond, &work->mutex); - - g_mutex_unlock(&work->mutex); - -} - - - -/* ---------------------------------------------------------------------------------- */ -/* THREADS DES TRAITEMENTS DEDIES */ -/* ---------------------------------------------------------------------------------- */ - - -/* Indique le type défini pour les groupes de travail. */ -G_DEFINE_TYPE(GWorkGroup, g_work_group, G_TYPE_OBJECT); - - -/****************************************************************************** -* * -* Paramètres : klass = classe à initialiser. * -* * -* Description : Initialise la classe des groupes de travail. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_group_class_init(GWorkGroupClass *klass) -{ - GObjectClass *object; /* Autre version de la classe */ - - object = G_OBJECT_CLASS(klass); - - object->dispose = (GObjectFinalizeFunc/* ! */)g_work_group_dispose; - object->finalize = (GObjectFinalizeFunc)g_work_group_finalize; - -} - - -/****************************************************************************** -* * -* Paramètres : group = instance à initialiser. * -* * -* Description : Initialise une instance de groupe de travail. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_group_init(GWorkGroup *group) -{ - group->works = NULL; - - g_mutex_init(&group->mutex); - g_cond_init(&group->cond); - g_cond_init(&group->wait_cond); - - g_atomic_int_set(&group->pending, 0); - - group->threads = NULL; - group->threads_count = 0; - group->force_exit = false; - - group->callback = NULL; - group->data = NULL; - -} - - -/****************************************************************************** -* * -* Paramètres : queue = instance d'objet GLib à traiter. * -* * -* Description : Supprime toutes les références externes. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_group_dispose(GWorkGroup *group) -{ - guint i; /* Boucle de parcours */ - GDelayedWork *work; /* Travail à oublier */ - - group->force_exit = true; - - /** - * Concernant la pose du verrou, se référer aux commentaires de la - * fonction g_work_group_process(). - */ - - g_mutex_lock(&group->mutex); - - g_cond_broadcast(&group->cond); - - g_mutex_unlock(&group->mutex); - - for (i = 0; i < group->threads_count; i++) - g_thread_join(group->threads[i]); - - while (!dl_list_empty(group->works)) - { - work = group->works; - delayed_work_list_del(work, &group->works); - - g_object_unref(G_OBJECT(work)); - - } - - g_mutex_clear(&group->mutex); - g_cond_clear(&group->cond); - g_cond_clear(&group->wait_cond); - - G_OBJECT_CLASS(g_work_group_parent_class)->dispose(G_OBJECT(group)); - -} - - -/****************************************************************************** -* * -* Paramètres : group = instance d'objet GLib à traiter. * -* * -* Description : Procède à la libération totale de la mémoire. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_group_finalize(GWorkGroup *group) -{ - if (group->threads != NULL) - free(group->threads); - - G_OBJECT_CLASS(g_work_group_parent_class)->finalize(G_OBJECT(group)); - -} - - -/****************************************************************************** -* * -* Paramètres : id = identifiant accordé au nouveau groupe. * -* count = quantité de threads à allouer. * -* * -* Description : Crée un nouveau thread dédié à un type de travaux donné. * -* * -* Retour : Structure associée au thread mise en place. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static GWorkGroup *g_work_group_new(wgroup_id_t id, const guint *count) -{ - GWorkGroup *result; /* Traiteur à retourner */ - guint i; /* Boucle de parcours */ - char name[16]; /* Désignation humaine */ - - result = g_object_new(G_TYPE_WORK_GROUP, NULL); - - result->id = id; - - result->threads_count = get_max_online_threads(); - - if (count != NULL && *count < result->threads_count) - result->threads_count = *count; - - result->threads = (GThread **)calloc(result->threads_count, sizeof(GThread *)); - - for (i = 0; i < result->threads_count; i++) - { - snprintf(name, sizeof(name), "wgrp_%" PRIu64 "-%u", id, i); - - result->threads[i] = g_thread_new(name, (GThreadFunc)g_work_group_process, result); - if (!result->threads[i]) - goto start_error; - - } - - start_error: - - result->threads_count = i; - - assert(i > 0); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : group = gestionnaire des actions à mener. * -* * -* Description : Fournit l'identifiant associé à un groupe de travail. * -* * -* Retour : Identifiant unique attribué au groupe de travail. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static wgroup_id_t g_work_group_get_id(const GWorkGroup *group) +static void g_generic_work_finalize(GGenericWork *work) { - return group->id; + G_OBJECT_CLASS(g_generic_work_parent_class)->finalize(G_OBJECT(work)); } /****************************************************************************** * * -* Paramètres : group = gestionnaire des actions à mener. * -* work = nouvelle tâche à programmer, puis effectuer. * +* Paramètres : work = travail à traiter. * +* list = ensemble de travaux à considérer. [OUT] * * * -* Description : Place une nouvelle tâche en attente dans une file dédiée. * +* Description : Intègre un travail dans une liste de tâches à effectuer. * * * * Retour : - * * * @@ -564,161 +155,19 @@ static wgroup_id_t g_work_group_get_id(const GWorkGroup *group) * * ******************************************************************************/ -static void g_work_group_schedule(GWorkGroup *group, GDelayedWork *work) +void g_generic_work_add_to_list(GGenericWork *work, GGenericWork **list) { - g_mutex_lock(&group->mutex); - - g_atomic_int_inc(&group->pending); - - delayed_work_list_add_tail(work, &group->works); - - g_cond_signal(&group->cond); - - g_mutex_unlock(&group->mutex); + dl_list_add_tail(work, list, GGenericWork, link); } /****************************************************************************** * * -* Paramètres : group = gestionnaire des actions à mener. * -* * -* Description : Assure le traitement en différé. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void *g_work_group_process(GWorkGroup *group) -{ - GDelayedWork *work; /* Traitement à mener */ - GtkStatusStack *status; /* Zone d'info éventuelle */ - - while (1) - { - g_mutex_lock(&group->mutex); - - while (dl_list_empty(group->works) && !group->force_exit) - g_cond_wait(&group->cond, &group->mutex); - - if (group->force_exit) - { - g_mutex_unlock(&group->mutex); - break; - } - - work = group->works; - delayed_work_list_del(work, &group->works); - - g_mutex_unlock(&group->mutex); - -#ifdef INCLUDE_GTK_SUPPORT - status = get_global_status(); -#else - status = NULL; -#endif - g_delayed_work_process(work, status); - - g_object_unref(G_OBJECT(work)); - - /** - * Verrou ou pas verrou ? - * - * La documentation de la GLib indique que ce n'est pas nécessaire : - * - * ''' - * It is good practice to lock the same mutex as the waiting threads - * while calling this function, though not required. - * ''' - * - * Ce conseil se trouve verbatim à l'adresse : - * - * https://developer.gnome.org/glib/stable/glib-Threads.html#g-cond-broadcast - * - * Dans la pratique, il peut arriver que l'attente de la fonction - * g_work_group_wait_for_completion() ne soit jamais interrompue. - * - * La documentation POSIX est un peu plus orientée : - * - * ''' - * The pthread_cond_broadcast() functions may be called by a thread - * whether or not it currently owns the mutex that threads calling - * pthread_cond_wait() have associated with the condition variable - * during their waits; however, if predictable scheduling behavior is - * required, then that mutex shall be locked by the thread calling - * pthread_cond_broadcast(). - * ''' - * - * Ce passage complet est consultable à l'adresse : - * - * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_broadcast.html - * - * La page de manuel pthread_cond_broadcast(3) est quant à elle plus - * directrice : aucun complément d'information sur le sujet n'est fourni - * et les exemples associés utilisent implicement un verrou pendant - * sont appel. - */ - - g_mutex_lock(&group->mutex); - - if (g_atomic_int_dec_and_test(&group->pending)) - g_cond_broadcast(&group->wait_cond); - - g_mutex_unlock(&group->mutex); - - } - - return NULL; - -} - - -/****************************************************************************** -* * -* Paramètres : group = gestionnaire des actions à consulter. * -* * -* Description : Détermine si le groupe est vide de toute programmation. * +* Paramètres : work = travail à traiter. * +* list = ensemble de travaux à considérer. [OUT] * * * -* Retour : Etat du groupe de travail. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static bool g_work_group_is_empty(GWorkGroup *group) -{ - bool result; /* Etat à retourner */ - - /** - * Pour que le résultat soit exploitable, il ne doit pas varier - * en dehors de la zone couverte par le verrou du groupe avant - * son utilisation par l'appelant. - * - * Il doit donc logiquement y avoir un autre verrou en amont et, - * comme à priori on ne devrait pas bloquer les groupes principaux - * pour un traitement particulier, cette procédure ne devrait concerner - * que des groupes dynamiques. - */ - - g_mutex_lock(&group->mutex); - - result = dl_list_empty(group->works); - - g_mutex_unlock(&group->mutex); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : group = groupe dont les conclusions sont attendues. * -* queue = queue d'appartenance pour les appels externes. * -* * -* Description : Attend que toutes les tâches d'un groupe soient traitées. * +* Description : Supprime un travail d'une liste de tâches à effectuer. * * * * Retour : - * * * @@ -726,530 +175,18 @@ static bool g_work_group_is_empty(GWorkGroup *group) * * ******************************************************************************/ -static void g_work_group_wait_for_completion(GWorkGroup *group, GWorkQueue *queue) +void g_generic_work_remove_from_list(GGenericWork *work, GGenericWork **list) { - wait_for_incoming_works_cb callback; /* Procédure complémentaire */ - - bool no_extra_check(GWorkQueue *_q, wgroup_id_t _id, void *_data) - { - return false; - } - - callback = group->callback != NULL ? group->callback : no_extra_check; - - g_mutex_lock(&group->mutex); - - /** - * On attend que : - * - la liste des tâches programmées soit vide. - * - il n'existe plus de tâche en cours. - * - rien n'indique que de nouvelles tâches supplémentaires vont arriver. - */ - - while ((g_atomic_int_get(&group->pending) > 0 || callback(queue, group->id, group->data)) - && !group->force_exit) - { - g_cond_wait(&group->wait_cond, &group->mutex); - } - - g_mutex_unlock(&group->mutex); + dl_list_del(work, list, GGenericWork, link); } /****************************************************************************** * * -* Paramètres : group = groupe dont les paramètres sont à modifier. * -* callback = éventuelle fonction à appeler ou NULL. * -* data = données devant accompagner l'appel. * -* * -* Description : Modifie les conditions d'attente des fins d'exécutions. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_group_set_extra_wait_callback(GWorkGroup *group, wait_for_incoming_works_cb callback, void *data) -{ - group->callback = callback; - group->data = data; - -} - - -/****************************************************************************** +* Paramètres : work = travail à effectuer. * * * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.* -* id = identifiant d'un groupe de travail. * -* * -* Description : Force un réveil d'une attente en cours pour la confirmer. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_group_wake_up_waiters(GWorkGroup *group) -{ - /** - * Concernant la pose du verrou, se référer aux commentaires de la - * fonction g_work_group_process(). - */ - - g_mutex_lock(&group->mutex); - - g_cond_broadcast(&group->wait_cond); - - g_mutex_unlock(&group->mutex); - -} - - - -/* ---------------------------------------------------------------------------------- */ -/* TRAITEMENT DE TACHES DIFFEREES */ -/* ---------------------------------------------------------------------------------- */ - - -/* Indique le type défini pour le gestionnaire des travaux différés. */ -G_DEFINE_TYPE(GWorkQueue, g_work_queue, G_TYPE_OBJECT); - - -/****************************************************************************** -* * -* Paramètres : klass = classe à initialiser. * -* * -* Description : Initialise la classe des travaux différés. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_queue_class_init(GWorkQueueClass *klass) -{ - GObjectClass *object; /* Autre version de la classe */ - - object = G_OBJECT_CLASS(klass); - - object->dispose = (GObjectFinalizeFunc/* ! */)g_work_queue_dispose; - object->finalize = (GObjectFinalizeFunc)g_work_queue_finalize; - -} - - -/****************************************************************************** -* * -* Paramètres : queue = instance à initialiser. * -* * -* Description : Initialise une instance de gestionnaire de travaux différés. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_queue_init(GWorkQueue *queue) -{ - queue->generator = 0; - - queue->groups = NULL; - queue->groups_count = 0; - g_mutex_init(&queue->mutex); - g_cond_init(&queue->wait_all); - -} - - -/****************************************************************************** -* * -* Paramètres : queue = instance d'objet GLib à traiter. * -* * -* Description : Supprime toutes les références externes. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_queue_dispose(GWorkQueue *queue) -{ - size_t i; /* Boucle de parcours */ - - g_mutex_lock(&queue->mutex); - - for (i = 0; i < queue->groups_count; i++) - g_clear_object(&queue->groups[i]); - - g_mutex_unlock(&queue->mutex); - - g_mutex_clear(&queue->mutex); - g_cond_clear(&queue->wait_all); - - G_OBJECT_CLASS(g_work_queue_parent_class)->dispose(G_OBJECT(queue)); - -} - - -/****************************************************************************** -* * -* Paramètres : queue = instance d'objet GLib à traiter. * -* * -* Description : Procède à la libération totale de la mémoire. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_queue_finalize(GWorkQueue *queue) -{ - if (queue->groups != NULL) - free(queue->groups); - - G_OBJECT_CLASS(g_work_queue_parent_class)->finalize(G_OBJECT(queue)); - -} - - -/****************************************************************************** -* * -* Paramètres : - * -* * -* Description : Créé un nouveau gestionnaire de tâches parallèles. * -* * -* Retour : Gestionnaire de traitements mis en place. * -* * -* Remarques : - * -* * -******************************************************************************/ - -GWorkQueue *g_work_queue_new(void) -{ - GWorkQueue *result; /* Instance à retourner */ - - result = g_object_new(G_TYPE_WORK_QUEUE, NULL); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * -* id = identifiant d'un groupe de travail. * -* count = quantité de threads à allouer. * -* * -* Description : Donne l'assurance de l'existence d'un groupe de travail. * -* * -* Retour : true si un nouveau groupe a été constitué, false sinon. * -* * -* Remarques : Le verrou d'accès doit être posé par l'appelant. * -* * -******************************************************************************/ - -static bool g_work_queue_ensure_group_exists(GWorkQueue *queue, wgroup_id_t id, const guint *count) -{ - bool found; /* Bilan des recherches */ - size_t i; /* Boucle de parcours */ - GWorkGroup *group; /* Groupe à consulter */ - - assert(!g_mutex_trylock(&queue->mutex)); - - found = false; - - for (i = 0; i < queue->groups_count && !found; i++) - { - group = queue->groups[i]; - found = (g_work_group_get_id(group) == id); - } - - if (!found) - { - queue->groups_count++; - queue->groups = (GWorkGroup **)realloc(queue->groups, - queue->groups_count * sizeof(GWorkGroup *)); - - group = g_work_group_new(id, count); - queue->groups[queue->groups_count - 1] = group; - - } - - return !found; - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * -* * -* Description : Constitue un nouveau groupe de travail. * -* * -* Retour : Nouvel identifiant unique d'un nouveau groupe de travail. * -* * -* Remarques : - * -* * -******************************************************************************/ - -wgroup_id_t g_work_queue_define_work_group(GWorkQueue *queue) -{ - wgroup_id_t result; /* Valeur à retourner */ - bool created; /* Bilan d'une tentative */ - - g_mutex_lock(&queue->mutex); - - do - { - result = queue->generator++; - created = g_work_queue_ensure_group_exists(queue, result, NULL); - } - while (!created); - - g_mutex_unlock(&queue->mutex); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * -* count = quantité de threads à allouer. * -* * -* Description : Constitue un nouveau petit groupe de travail. * -* * -* Retour : Nouvel identifiant unique d'un nouveau groupe de travail. * -* * -* Remarques : - * -* * -******************************************************************************/ - -wgroup_id_t g_work_queue_define_tiny_work_group(GWorkQueue *queue, guint count) -{ - wgroup_id_t result; /* Valeur à retourner */ - bool created; /* Bilan d'une tentative */ - - g_mutex_lock(&queue->mutex); - - do - { - result = queue->generator++; - created = g_work_queue_ensure_group_exists(queue, result, &count); - } - while (!created); - - g_mutex_unlock(&queue->mutex); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * -* id = identifiant d'un groupe de travail. * -* * -* Description : Dissout un groupe de travail existant. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_work_queue_delete_work_group(GWorkQueue *queue, wgroup_id_t id) -{ - size_t i; /* Boucle de parcours */ - GWorkGroup *group; /* Groupe de travail manipulé */ -#ifndef NDEBUG - bool found; /* Repérage du groupe visé */ -#endif - -#ifndef NDEBUG - found = false; -#endif - - g_mutex_lock(&queue->mutex); - - for (i = 0; i < queue->groups_count; i++) - { - group = queue->groups[i]; - - if (g_work_group_get_id(group) == id) - { - g_object_unref(G_OBJECT(group)); - - memmove(&queue->groups[i], &queue->groups[i + 1], - (queue->groups_count - i - 1) * sizeof(GWorkGroup *)); - - queue->groups_count--; - queue->groups = (GWorkGroup **)realloc(queue->groups, - queue->groups_count * sizeof(GWorkGroup *)); - -#ifndef NDEBUG - found = true; -#endif - - break; - - } - - } - - assert(found); - - g_cond_broadcast(&queue->wait_all); - - g_mutex_unlock(&queue->mutex); - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire des actions à mener. * -* work = nouvelle tâche à programmer, puis effectuer. * -* id = identifiant du groupe de travail d'affectation. * -* * -* Description : Place une nouvelle tâche en attente. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_work_queue_schedule_work(GWorkQueue *queue, GDelayedWork *work, wgroup_id_t id) -{ - GWorkGroup *group; /* Groupe de travail à attendre*/ - - group = g_work_queue_find_group_for_id(queue, id); - assert(group != NULL); - - g_work_group_schedule(group, work); - - g_object_unref(G_OBJECT(group)); - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * -* id = identifiant d'un groupe de travail. * -* * -* Description : Fournit le groupe de travail correspondant à un identifiant. * -* * -* Retour : Eventuel groupe existant trouvé ou NULL si aucun. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static GWorkGroup *g_work_queue_find_group_for_id(GWorkQueue *queue, wgroup_id_t id) -{ - GWorkGroup *result; /* Trouvaille à retourner */ - size_t i; /* Boucle de parcours */ - - result = NULL; - - g_mutex_lock(&queue->mutex); - - for (i = 0; i < queue->groups_count; i++) - if (g_work_group_get_id(queue->groups[i]) == id) - { - result = queue->groups[i]; - g_object_ref(G_OBJECT(result)); - break; - } - - g_mutex_unlock(&queue->mutex); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * -* id = identifiant d'un groupe de travail. * -* * -* Description : Détermine si un groupe est vide de toute programmation. * -* * -* Retour : Etat du groupe de travail. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool g_work_queue_is_empty(GWorkQueue *queue, wgroup_id_t id) -{ - bool result; /* Etat à retourner */ - GWorkGroup *group; /* Groupe de travail à attendre*/ - - group = g_work_queue_find_group_for_id(queue, id); - - if (group != NULL) - { - result = g_work_group_is_empty(group); - g_object_unref(G_OBJECT(group)); - } - - else - result = true; - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * -* id = identifiant d'un groupe de travail. * -* * -* Description : Attend que toutes les tâches d'un groupe soient traitées. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_work_queue_wait_for_completion(GWorkQueue *queue, wgroup_id_t id) -{ - GWorkGroup *group; /* Groupe de travail à attendre*/ - - group = g_work_queue_find_group_for_id(queue, id); - - if (group != NULL) - { - g_work_group_wait_for_completion(group, queue); - g_object_unref(G_OBJECT(group)); - } - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.* -* gb_ids = identifiants de groupes globaux. * -* gb_count = nombre de ces groupes globaux. * -* * -* Description : Attend que toutes les tâches de tout groupe soient traitées. * +* Description : Mène l'opération programmée. * * * * Retour : - * * * @@ -1257,80 +194,27 @@ void g_work_queue_wait_for_completion(GWorkQueue *queue, wgroup_id_t id) * * ******************************************************************************/ -void g_work_queue_wait_for_all_completions(GWorkQueue *queue, const wgroup_id_t *gb_ids, size_t gb_count) +void g_generic_work_process(GGenericWork *work) { - size_t i; /* Boucle de parcours */ - - g_mutex_lock(&queue->mutex); - - wait_again: - - /** - * Attente d'éventuels groupes isolés. - */ - - while (queue->groups_count > gb_count) - g_cond_wait(&queue->wait_all, &queue->mutex); - - g_mutex_unlock(&queue->mutex); - - /** - * Attente des groupes principaux. - */ - - for (i = 0; i < gb_count; i++) - g_work_queue_wait_for_completion(queue, gb_ids[i]); + G_GENERIC_WORK_GET_CLASS(work)->run(work); - /** - * Si le groupe par défaut a généré de nouveaux groupes, on recommence ! - */ - - g_mutex_lock(&queue->mutex); - - if (queue->groups_count > gb_count) - goto wait_again; - - g_mutex_unlock(&queue->mutex); - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.* -* id = identifiant d'un groupe de travail. * -* callback = éventuelle fonction à appeler ou NULL. * -* data = données devant accompagner l'appel. * -* * -* Description : Modifie les conditions d'attente des fins d'exécutions. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ + g_mutex_lock(&work->mutex); -void g_work_queue_set_extra_wait_callback(GWorkQueue *queue, wgroup_id_t id, wait_for_incoming_works_cb callback, void *data) -{ - GWorkGroup *group; /* Groupe de travail à traiter */ + work->completed = true; - group = g_work_queue_find_group_for_id(queue, id); + g_cond_signal(&work->cond); + g_mutex_unlock(&work->mutex); - if (group != NULL) - { - g_work_group_set_extra_wait_callback(group, callback, data); - g_object_unref(G_OBJECT(group)); - } + g_signal_emit_by_name(work, "work-completed"); } /****************************************************************************** * * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.* -* id = identifiant d'un groupe de travail. * +* Paramètres : work = travail à surveiller. * * * -* Description : Force un réveil d'une attente en cours pour la confirmer. * +* Description : Attend la fin de l'exécution d'une tâche donnée. * * * * Retour : - * * * @@ -1338,16 +222,13 @@ void g_work_queue_set_extra_wait_callback(GWorkQueue *queue, wgroup_id_t id, wai * * ******************************************************************************/ -void g_work_queue_wake_up_waiters(GWorkQueue *queue, wgroup_id_t id) +void g_generic_work_wait_for_completion(GGenericWork *work) { - GWorkGroup *group; /* Groupe de travail à traiter */ + g_mutex_lock(&work->mutex); - group = g_work_queue_find_group_for_id(queue, id); + while (!work->completed) + g_cond_wait(&work->cond, &work->mutex); - if (group != NULL) - { - g_work_group_wake_up_waiters(group); - g_object_unref(G_OBJECT(group)); - } + g_mutex_unlock(&work->mutex); } diff --git a/src/glibext/work.h b/src/glibext/work.h index 89eed12..1084942 100644 --- a/src/glibext/work.h +++ b/src/glibext/work.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * delayed.h - prototypes pour la gestion des travaux différés + * work.h - prototypes pour la gestion des travaux différés * - * Copyright (C) 2009-2018 Cyrille Bagard + * Copyright (C) 2009-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,107 +21,31 @@ */ -#ifndef _GLIBEXT_DELAYED_H -#define _GLIBEXT_DELAYED_H +#ifndef _GLIBEXT_WORK_H +#define _GLIBEXT_WORK_H -#include <glib-object.h> -#include <stdbool.h> -#include <stdint.h> +#include "helpers.h" -/* -------------------------- TACHE DIFFEREE DANS LE TEMPS -------------------------- */ +#define G_TYPE_GENERIC_WORK (g_generic_work_get_type()) +DECLARE_GTYPE(GGenericWork, g_generic_work, G, GENERIC_WORK); -#define G_TYPE_DELAYED_WORK g_delayed_work_get_type() -#define G_DELAYED_WORK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_delayed_work_get_type(), GDelayedWork)) -#define G_IS_DELAYED_WORK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_delayed_work_get_type())) -#define G_DELAYED_WORK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_DELAYED_WORK, GDelayedWorkClass)) -#define G_IS_DELAYED_WORK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_DELAYED_WORK)) -#define G_DELAYED_WORK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_DELAYED_WORK, GDelayedWorkClass)) +/* Intègre un travail dans une liste de tâches à effectuer. */ +void g_generic_work_add_to_list(GGenericWork *, GGenericWork **); -/* Travail différé (instance) */ -typedef struct _GDelayedWork GDelayedWork; +/* Supprime un travail d'une liste de tâches à effectuer. */ +void g_generic_work_remove_from_list(GGenericWork *, GGenericWork **); -/* Travail différé (classe) */ -typedef struct _GDelayedWorkClass GDelayedWorkClass; - - -/* Indique le type défini pour les travaux différés. */ -GType g_delayed_work_get_type(void); +/* Mène l'opération programmée. */ +void g_generic_work_process(GGenericWork *); /* Attend la fin de l'exécution d'une tâche donnée. */ -void g_delayed_work_wait_for_completion(GDelayedWork *); - - - -/* ------------------------- TRAITEMENT DE TACHES DIFFEREES ------------------------- */ - - -#define G_TYPE_WORK_QUEUE g_work_queue_get_type() -#define G_WORK_QUEUE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_work_queue_get_type(), GWorkQueue)) -#define G_IS_WORK_QUEUE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_work_queue_get_type())) -#define G_WORK_QUEUE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_WORK_QUEUE, GWorkQueueClass)) -#define G_IS_WORK_QUEUE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_WORK_QUEUE)) -#define G_WORK_QUEUE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_WORK_QUEUE, GWorkQueueClass)) - - -/* Gestionnaire des travaux différés (instance) */ -typedef struct _GWorkQueue GWorkQueue; - -/* Gestionnaire des travaux différés (classe) */ -typedef struct _GWorkQueueClass GWorkQueueClass; - - -/** - * Identifiant unique pour groupe de travail. - * - * Le nombre de bits est forcé à 64 bits car glib-genmarshal ne reconnait - * pas explicitement le type 'unsigned long long'. - */ -typedef uint64_t wgroup_id_t; - - -/* Indique le type défini pour le gestionnaire des travaux différés. */ -GType g_work_queue_get_type(void); - -/* Créé un nouveau gestionnaire de tâches parallèles. */ -GWorkQueue *g_work_queue_new(void); - -/* Constitue un nouveau groupe de travail. */ -wgroup_id_t g_work_queue_define_work_group(GWorkQueue *); - -/* Constitue un nouveau petit groupe de travail. */ -wgroup_id_t g_work_queue_define_tiny_work_group(GWorkQueue *, guint); - -/* Dissout un groupe de travail existant. */ -void g_work_queue_delete_work_group(GWorkQueue *, wgroup_id_t); - -/* Place une nouvelle tâche en attente. */ -void g_work_queue_schedule_work(GWorkQueue *, GDelayedWork *, wgroup_id_t); - -/* Détermine si un groupe est vide de toute programmation. */ -bool g_work_queue_is_empty(GWorkQueue *, wgroup_id_t); - -/* Attend que toutes les tâches d'un groupe soient traitées. */ -void g_work_queue_wait_for_completion(GWorkQueue *, wgroup_id_t); - -/* Attend que toutes les tâches de tout groupe soient traitées. */ -void g_work_queue_wait_for_all_completions(GWorkQueue *, const wgroup_id_t *, size_t); - - -/* Etudie le besoin d'attendre d'avantage de prochaines tâches. */ -typedef bool (* wait_for_incoming_works_cb) (GWorkQueue *, wgroup_id_t, void *); - - -/* Modifie les conditions d'attente des fins d'exécutions. */ -void g_work_queue_set_extra_wait_callback(GWorkQueue *, wgroup_id_t, wait_for_incoming_works_cb, void *); - -/* Force un réveil d'une attente en cours pour la confirmer. */ -void g_work_queue_wake_up_waiters(GWorkQueue *, wgroup_id_t); +void g_generic_work_wait_for_completion(GGenericWork *); -#endif /* _GLIBEXT_DELAYED_H */ +#endif /* _GLIBEXT_WORK_H */ diff --git a/src/glibext/workgroup-int.h b/src/glibext/workgroup-int.h index 4f84e86..7224cf9 100644 --- a/src/glibext/workgroup-int.h +++ b/src/glibext/workgroup-int.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * delayed-int.h - définitions internes pour la gestion des travaux différés + * workgroup-int.h - définitions internes pour la gestion des travaux différés * - * Copyright (C) 2009-2018 Cyrille Bagard + * Copyright (C) 2009-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,55 +21,40 @@ */ -#ifndef _GLIBEXT_DELAYED_INT_H -#define _GLIBEXT_DELAYED_INT_H +#ifndef _GLIBEXT_WORKGROUP_INT_H +#define _GLIBEXT_WORKGROUP_INT_H -#include "delayed.h" +#include "workgroup.h" -#include "notifier.h" -#include "../common/dllist.h" - - -/* -------------------------- TACHE DIFFEREE DANS LE TEMPS -------------------------- */ - - -/* Traite un travail programmé. */ -typedef void (* run_task_fc) (GDelayedWork *, GtkStatusStack *); - - -/* Travail différé (instance) */ -struct _GDelayedWork +/* File de traitement pour un type donné (instance) */ +typedef struct _GWorkGroup { GObject parent; /* A laisser en premier */ - DL_LIST_ITEM(link); /* Lien vers les maillons */ + wgroup_id_t id; /* Identifiant de travaux menés*/ - bool completed; /* Fin de la tâche ? */ - GMutex mutex; /* Accès à la variable */ - GCond cond; /* Attente de changement */ + GGenericWork *works; /* Tâches à mener à bien */ + GMutex mutex; /* Verrou pour l'accès */ + GCond cond; /* Réveil pour un traitement */ + GCond wait_cond; /* Réveil d'attente de fin */ + gint pending; /* Tâches en cours d'exécution */ -}; + GThread **threads; /* Procédure de traitement */ + guint threads_count; /* Nombre de procédures */ + bool force_exit; /* Procédure d'arrêt */ -/* Travail différé (classe) */ -struct _GDelayedWorkClass +} GWorkGroup; + +/* File de traitement pour un type donné (classe) */ +typedef struct _GWorkGroupClass { GObjectClass parent; /* A laisser en premier */ - run_task_fc run; /* Traitement externalisé */ - - /* Signaux */ - - void (* work_completed) (GDelayedWork *); - -}; - - -#define delayed_work_list_add_tail(new, head) dl_list_add_tail(new, head, GDelayedWork, link) -#define delayed_work_list_del(item, head) dl_list_del(item, head, GDelayedWork, link) +} GWorkGroupClass; -#endif /* _GLIBEXT_DELAYED_INT_H */ +#endif /* _GLIBEXT_WORKGROUP_INT_H */ diff --git a/src/glibext/workgroup.c b/src/glibext/workgroup.c index 6b5ac35..c603d54 100644 --- a/src/glibext/workgroup.c +++ b/src/glibext/workgroup.c @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * delayed.c - gestion des travaux différés + * workgroup.c - gestion des travaux différés * - * Copyright (C) 2009-2018 Cyrille Bagard + * Copyright (C) 2009-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,88 +21,21 @@ */ -#include "delayed.h" +#include "workgroup.h" #include <assert.h> #include <inttypes.h> #include <malloc.h> #include <stdio.h> -#include <string.h> -#include "delayed-int.h" +#include "workgroup-int.h" +#include "../common/dllist.h" #include "../core/nproc.h" -#ifdef INCLUDE_GTK_SUPPORT -# include "../gui/core/global.h" -#endif -/* -------------------------- TACHE DIFFEREE DANS LE TEMPS -------------------------- */ - - -/* Initialise la classe des travaux différés. */ -static void g_delayed_work_class_init(GDelayedWorkClass *); - -/* Initialise une instance de travail différé. */ -static void g_delayed_work_init(GDelayedWork *); - -/* Supprime toutes les références externes. */ -static void g_delayed_work_dispose(GDelayedWork *); - -/* Procède à la libération totale de la mémoire. */ -static void g_delayed_work_finalize(GDelayedWork *); - -/* Mène l'opération programmée. */ -static void g_delayed_work_process(GDelayedWork *, GtkStatusStack *); - - - -/* -------------------------- THREAD DE TRAITEMENTS DEDIES -------------------------- */ - - -#define G_TYPE_WORK_GROUP g_work_group_get_type() -#define G_WORK_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_work_group_get_type(), GWorkGroup)) -#define G_IS_WORK_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_work_group_get_type())) -#define G_WORK_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_WORK_GROUP, GWorkGroupClass)) -#define G_IS_WORK_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_WORK_GROUP)) -#define G_WORK_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_WORK_GROUP, GWorkGroupClass)) - - -/* File de traitement pour un type donné (instance) */ -typedef struct _GWorkGroup -{ - GObject parent; /* A laisser en premier */ - - wgroup_id_t id; /* Identifiant de travaux menés*/ - - GDelayedWork *works; /* Tâches à mener à bien */ - GMutex mutex; /* Verrou pour l'accès */ - GCond cond; /* Réveil pour un traitement */ - GCond wait_cond; /* Réveil d'attente de fin */ - gint pending; /* Tâches en cours d'exécution */ - - GThread **threads; /* Procédure de traitement */ - guint threads_count; /* Nombre de procédures */ - bool force_exit; /* Procédure d'arrêt */ - - wait_for_incoming_works_cb callback; /* Encadre les attentes de fin */ - void *data; /* Données à associer */ - -} GWorkGroup; - -/* File de traitement pour un type donné (classe) */ -typedef struct _GWorkGroupClass -{ - GObjectClass parent; /* A laisser en premier */ - -} GWorkGroupClass; - - -/* Indique le type défini pour les groupes de travail. */ -static GType g_work_group_get_type(void); - /* Initialise la classe des groupes de travail. */ static void g_work_group_class_init(GWorkGroupClass *); @@ -115,237 +48,9 @@ static void g_work_group_dispose(GWorkGroup *); /* Procède à la libération totale de la mémoire. */ static void g_work_group_finalize(GWorkGroup *); -/* Crée un nouveau thread dédié à un type de travaux donné. */ -static GWorkGroup *g_work_group_new(wgroup_id_t, const guint *); - -/* Fournit l'identifiant associé à un groupe de travail. */ -static wgroup_id_t g_work_group_get_id(const GWorkGroup *); - -/* Place une nouvelle tâche en attente dans une file dédiée. */ -static void g_work_group_schedule(GWorkGroup *, GDelayedWork *); - /* Assure le traitement en différé. */ static void *g_work_group_process(GWorkGroup *); -/* Détermine si le groupe est vide de toute programmation. */ -static bool g_work_group_is_empty(GWorkGroup *); - -/* Attend que toutes les tâches d'un groupe soient traitées. */ -static void g_work_group_wait_for_completion(GWorkGroup *, GWorkQueue *); - -/* Modifie les conditions d'attente des fins d'exécutions. */ -static void g_work_group_set_extra_wait_callback(GWorkGroup *, wait_for_incoming_works_cb, void *); - -/* Force un réveil d'une attente en cours pour la confirmer. */ -static void g_work_group_wake_up_waiters(GWorkGroup *); - - - -/* ------------------------- TRAITEMENT DE TACHES DIFFEREES ------------------------- */ - - -/* Gestionnaire des travaux différés (instance) */ -struct _GWorkQueue -{ - GObject parent; /* A laisser en premier */ - - wgroup_id_t generator; /* Générateur d'identifiants */ - - GWorkGroup **groups; /* Files de traitement */ - size_t groups_count; /* Nombre de files internes */ - GMutex mutex; /* Verrou pour l'accès */ - GCond wait_all; /* Réveil d'attente globale */ - -}; - -/* Gestionnaire des travaux différés (classe) */ -struct _GWorkQueueClass -{ - GObjectClass parent; /* A laisser en premier */ - -}; - - -/* Initialise la classe des travaux différés. */ -static void g_work_queue_class_init(GWorkQueueClass *); - -/* Initialise une instance de gestionnaire de travaux différés. */ -static void g_work_queue_init(GWorkQueue *); - -/* Supprime toutes les références externes. */ -static void g_work_queue_dispose(GWorkQueue *); - -/* Procède à la libération totale de la mémoire. */ -static void g_work_queue_finalize(GWorkQueue *); - -/* Donne l'assurance de l'existence d'un groupe de travail. */ -static bool g_work_queue_ensure_group_exists(GWorkQueue *, wgroup_id_t, const guint *); - -/* Fournit le groupe de travail correspondant à un identifiant. */ -static GWorkGroup *g_work_queue_find_group_for_id(GWorkQueue *, wgroup_id_t); - - - -/* ---------------------------------------------------------------------------------- */ -/* TACHE DIFFEREE DANS LE TEMPS */ -/* ---------------------------------------------------------------------------------- */ - - -/* Indique le type défini pour les travaux différés. */ -G_DEFINE_TYPE(GDelayedWork, g_delayed_work, G_TYPE_OBJECT); - - -/****************************************************************************** -* * -* Paramètres : klass = classe à initialiser. * -* * -* Description : Initialise la classe des travaux différés. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_delayed_work_class_init(GDelayedWorkClass *klass) -{ - GObjectClass *object; /* Autre version de la classe */ - - object = G_OBJECT_CLASS(klass); - - object->dispose = (GObjectFinalizeFunc/* ! */)g_delayed_work_dispose; - object->finalize = (GObjectFinalizeFunc)g_delayed_work_finalize; - - g_signal_new("work-completed", - G_TYPE_DELAYED_WORK, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET(GDelayedWorkClass, work_completed), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - -} - - -/****************************************************************************** -* * -* Paramètres : work = instance à initialiser. * -* * -* Description : Initialise une instance de travail différé. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_delayed_work_init(GDelayedWork *work) -{ - work->completed = false; - g_mutex_init(&work->mutex); - g_cond_init(&work->cond); - -} - - -/****************************************************************************** -* * -* Paramètres : work = instance d'objet GLib à traiter. * -* * -* Description : Supprime toutes les références externes. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_delayed_work_dispose(GDelayedWork *work) -{ - g_mutex_clear(&work->mutex); - g_cond_clear(&work->cond); - - G_OBJECT_CLASS(g_delayed_work_parent_class)->dispose(G_OBJECT(work)); - -} - - -/****************************************************************************** -* * -* Paramètres : work = instance d'objet GLib à traiter. * -* * -* Description : Procède à la libération totale de la mémoire. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_delayed_work_finalize(GDelayedWork *work) -{ - G_OBJECT_CLASS(g_delayed_work_parent_class)->finalize(G_OBJECT(work)); - -} - - -/****************************************************************************** -* * -* Paramètres : work = travail à effectuer. * -* status = barre de statut à tenir informée. * -* * -* Description : Mène l'opération programmée. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_delayed_work_process(GDelayedWork *work, GtkStatusStack *status) -{ - G_DELAYED_WORK_GET_CLASS(work)->run(work, status); - - g_mutex_lock(&work->mutex); - - work->completed = true; - - g_cond_signal(&work->cond); - g_mutex_unlock(&work->mutex); - - g_signal_emit_by_name(work, "work-completed"); - -} - - -/****************************************************************************** -* * -* Paramètres : work = travail à surveiller. * -* * -* Description : Attend la fin de l'exécution d'une tâche donnée. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_delayed_work_wait_for_completion(GDelayedWork *work) -{ - g_mutex_lock(&work->mutex); - - while (!work->completed) - g_cond_wait(&work->cond, &work->mutex); - - g_mutex_unlock(&work->mutex); - -} - - - -/* ---------------------------------------------------------------------------------- */ -/* THREADS DES TRAITEMENTS DEDIES */ -/* ---------------------------------------------------------------------------------- */ /* Indique le type défini pour les groupes de travail. */ @@ -390,7 +95,7 @@ static void g_work_group_class_init(GWorkGroupClass *klass) static void g_work_group_init(GWorkGroup *group) { - group->works = NULL; + DL_LIST_HEAD_INIT(group->works); g_mutex_init(&group->mutex); g_cond_init(&group->cond); @@ -402,15 +107,12 @@ static void g_work_group_init(GWorkGroup *group) group->threads_count = 0; group->force_exit = false; - group->callback = NULL; - group->data = NULL; - } /****************************************************************************** * * -* Paramètres : queue = instance d'objet GLib à traiter. * +* Paramètres : group = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * @@ -423,7 +125,7 @@ static void g_work_group_init(GWorkGroup *group) static void g_work_group_dispose(GWorkGroup *group) { guint i; /* Boucle de parcours */ - GDelayedWork *work; /* Travail à oublier */ + GGenericWork *work; /* Travail à oublier */ group->force_exit = true; @@ -444,9 +146,9 @@ static void g_work_group_dispose(GWorkGroup *group) while (!dl_list_empty(group->works)) { work = group->works; - delayed_work_list_del(work, &group->works); + g_generic_work_remove_from_list(work, &group->works); - g_object_unref(G_OBJECT(work)); + unref_object(work); } @@ -484,7 +186,7 @@ static void g_work_group_finalize(GWorkGroup *group) /****************************************************************************** * * * Paramètres : id = identifiant accordé au nouveau groupe. * -* count = quantité de threads à allouer. * +* count = quantité de threads à allouer (0 pour un défaut). * * * * Description : Crée un nouveau thread dédié à un type de travaux donné. * * * @@ -494,33 +196,47 @@ static void g_work_group_finalize(GWorkGroup *group) * * ******************************************************************************/ -static GWorkGroup *g_work_group_new(wgroup_id_t id, const guint *count) +GWorkGroup *g_work_group_new(wgroup_id_t id, guint count) { - GWorkGroup *result; /* Traiteur à retourner */ + GWorkGroup *result; /* Traiteur à retourner */ guint i; /* Boucle de parcours */ - char name[16]; /* Désignation humaine */ + int ret; /* Bilan d'un appel */ + char *name; /* Désignation humaine */ result = g_object_new(G_TYPE_WORK_GROUP, NULL); result->id = id; - result->threads_count = get_max_online_threads(); + if (count == 0) + count = get_max_online_threads(); - if (count != NULL && *count < result->threads_count) - result->threads_count = *count; + result->threads_count = count; - result->threads = (GThread **)calloc(result->threads_count, sizeof(GThread *)); + result->threads = calloc(result->threads_count, sizeof(GThread *)); for (i = 0; i < result->threads_count; i++) { - snprintf(name, sizeof(name), "wgrp_%" PRIu64 "-%u", id, i); + /** + * La documentation précise : + * + * Some systems restrict the length of name to 16 bytes. + * + * On laisse ces systèmes tronquer. + */ + + ret = asprintf(&name, "wgrp_%" PRIu64 "-%u", id, i); + if (ret == -1) goto naming_error; result->threads[i] = g_thread_new(name, (GThreadFunc)g_work_group_process, result); + + free(name); + if (!result->threads[i]) goto start_error; } + naming_error: start_error: result->threads_count = i; @@ -544,7 +260,7 @@ static GWorkGroup *g_work_group_new(wgroup_id_t id, const guint *count) * * ******************************************************************************/ -static wgroup_id_t g_work_group_get_id(const GWorkGroup *group) +wgroup_id_t g_work_group_get_id(const GWorkGroup *group) { return group->id; @@ -564,13 +280,14 @@ static wgroup_id_t g_work_group_get_id(const GWorkGroup *group) * * ******************************************************************************/ -static void g_work_group_schedule(GWorkGroup *group, GDelayedWork *work) +void g_work_group_schedule(GWorkGroup *group, GGenericWork *work) { g_mutex_lock(&group->mutex); g_atomic_int_inc(&group->pending); - delayed_work_list_add_tail(work, &group->works); + ref_object(work); + g_generic_work_add_to_list(work, &group->works); g_cond_signal(&group->cond); @@ -593,8 +310,7 @@ static void g_work_group_schedule(GWorkGroup *group, GDelayedWork *work) static void *g_work_group_process(GWorkGroup *group) { - GDelayedWork *work; /* Traitement à mener */ - GtkStatusStack *status; /* Zone d'info éventuelle */ + GGenericWork *work; /* Traitement à mener */ while (1) { @@ -610,18 +326,13 @@ static void *g_work_group_process(GWorkGroup *group) } work = group->works; - delayed_work_list_del(work, &group->works); + g_generic_work_remove_from_list(work, &group->works); g_mutex_unlock(&group->mutex); -#ifdef INCLUDE_GTK_SUPPORT - status = get_global_status(); -#else - status = NULL; -#endif - g_delayed_work_process(work, status); + g_generic_work_process(work); - g_object_unref(G_OBJECT(work)); + unref_object(work); /** * Verrou ou pas verrou ? @@ -687,7 +398,7 @@ static void *g_work_group_process(GWorkGroup *group) * * ******************************************************************************/ -static bool g_work_group_is_empty(GWorkGroup *group) +bool g_work_group_is_empty(GWorkGroup *group) { bool result; /* Etat à retourner */ @@ -716,7 +427,6 @@ static bool g_work_group_is_empty(GWorkGroup *group) /****************************************************************************** * * * Paramètres : group = groupe dont les conclusions sont attendues. * -* queue = queue d'appartenance pour les appels externes. * * * * Description : Attend que toutes les tâches d'un groupe soient traitées. * * * @@ -726,17 +436,8 @@ static bool g_work_group_is_empty(GWorkGroup *group) * * ******************************************************************************/ -static void g_work_group_wait_for_completion(GWorkGroup *group, GWorkQueue *queue) +void g_work_group_wait_for_completion(GWorkGroup *group) { - wait_for_incoming_works_cb callback; /* Procédure complémentaire */ - - bool no_extra_check(GWorkQueue *_q, wgroup_id_t _id, void *_data) - { - return false; - } - - callback = group->callback != NULL ? group->callback : no_extra_check; - g_mutex_lock(&group->mutex); /** @@ -746,7 +447,7 @@ static void g_work_group_wait_for_completion(GWorkGroup *group, GWorkQueue *queu * - rien n'indique que de nouvelles tâches supplémentaires vont arriver. */ - while ((g_atomic_int_get(&group->pending) > 0 || callback(queue, group->id, group->data)) + while ((g_atomic_int_get(&group->pending) > 0) && !group->force_exit) { g_cond_wait(&group->wait_cond, &group->mutex); @@ -759,456 +460,43 @@ static void g_work_group_wait_for_completion(GWorkGroup *group, GWorkQueue *queu /****************************************************************************** * * -* Paramètres : group = groupe dont les paramètres sont à modifier. * -* callback = éventuelle fonction à appeler ou NULL. * -* data = données devant accompagner l'appel. * +* Paramètres : group = groupe dont les conclusions sont attendues. * +* rel = durée relative à patienter au max. en microsecondes. * * * -* Description : Modifie les conditions d'attente des fins d'exécutions. * +* Description : Attend que toutes les tâches d'un groupe soient traitées. * * * -* Retour : - * +* Retour : Bilan de l'attente : false en cas d'expiration, true sinon. * * * -* Remarques : - * +* Remarques : Cette fonction est originellement dédiée à un usage Python. * * * ******************************************************************************/ -static void g_work_group_set_extra_wait_callback(GWorkGroup *group, wait_for_incoming_works_cb callback, void *data) +bool g_work_group_wait_timed_for_completion(GWorkGroup *group, gint64 rel) { - group->callback = callback; - group->data = data; + bool result; /* Bilan d'attente à renvoyer */ + gint64 end_time; /* Borne de fin de l'attente */ -} + result = true; + g_mutex_lock(&group->mutex); -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.* -* id = identifiant d'un groupe de travail. * -* * -* Description : Force un réveil d'une attente en cours pour la confirmer. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ + end_time = g_get_monotonic_time() + rel; -static void g_work_group_wake_up_waiters(GWorkGroup *group) -{ /** - * Concernant la pose du verrou, se référer aux commentaires de la - * fonction g_work_group_process(). + * On attend que : + * - la liste des tâches programmées soit vide. + * - il n'existe plus de tâche en cours. + * - rien n'indique que de nouvelles tâches supplémentaires vont arriver. */ - g_mutex_lock(&group->mutex); - - g_cond_broadcast(&group->wait_cond); - - g_mutex_unlock(&group->mutex); - -} - - - -/* ---------------------------------------------------------------------------------- */ -/* TRAITEMENT DE TACHES DIFFEREES */ -/* ---------------------------------------------------------------------------------- */ - - -/* Indique le type défini pour le gestionnaire des travaux différés. */ -G_DEFINE_TYPE(GWorkQueue, g_work_queue, G_TYPE_OBJECT); - - -/****************************************************************************** -* * -* Paramètres : klass = classe à initialiser. * -* * -* Description : Initialise la classe des travaux différés. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_queue_class_init(GWorkQueueClass *klass) -{ - GObjectClass *object; /* Autre version de la classe */ - - object = G_OBJECT_CLASS(klass); - - object->dispose = (GObjectFinalizeFunc/* ! */)g_work_queue_dispose; - object->finalize = (GObjectFinalizeFunc)g_work_queue_finalize; - -} - - -/****************************************************************************** -* * -* Paramètres : queue = instance à initialiser. * -* * -* Description : Initialise une instance de gestionnaire de travaux différés. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_queue_init(GWorkQueue *queue) -{ - queue->generator = 0; - - queue->groups = NULL; - queue->groups_count = 0; - g_mutex_init(&queue->mutex); - g_cond_init(&queue->wait_all); - -} - - -/****************************************************************************** -* * -* Paramètres : queue = instance d'objet GLib à traiter. * -* * -* Description : Supprime toutes les références externes. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_queue_dispose(GWorkQueue *queue) -{ - size_t i; /* Boucle de parcours */ - - g_mutex_lock(&queue->mutex); - - for (i = 0; i < queue->groups_count; i++) - g_clear_object(&queue->groups[i]); - - g_mutex_unlock(&queue->mutex); - - g_mutex_clear(&queue->mutex); - g_cond_clear(&queue->wait_all); - - G_OBJECT_CLASS(g_work_queue_parent_class)->dispose(G_OBJECT(queue)); - -} - - -/****************************************************************************** -* * -* Paramètres : queue = instance d'objet GLib à traiter. * -* * -* Description : Procède à la libération totale de la mémoire. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_queue_finalize(GWorkQueue *queue) -{ - if (queue->groups != NULL) - free(queue->groups); - - G_OBJECT_CLASS(g_work_queue_parent_class)->finalize(G_OBJECT(queue)); - -} - - -/****************************************************************************** -* * -* Paramètres : - * -* * -* Description : Créé un nouveau gestionnaire de tâches parallèles. * -* * -* Retour : Gestionnaire de traitements mis en place. * -* * -* Remarques : - * -* * -******************************************************************************/ - -GWorkQueue *g_work_queue_new(void) -{ - GWorkQueue *result; /* Instance à retourner */ - - result = g_object_new(G_TYPE_WORK_QUEUE, NULL); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * -* id = identifiant d'un groupe de travail. * -* count = quantité de threads à allouer. * -* * -* Description : Donne l'assurance de l'existence d'un groupe de travail. * -* * -* Retour : true si un nouveau groupe a été constitué, false sinon. * -* * -* Remarques : Le verrou d'accès doit être posé par l'appelant. * -* * -******************************************************************************/ - -static bool g_work_queue_ensure_group_exists(GWorkQueue *queue, wgroup_id_t id, const guint *count) -{ - bool found; /* Bilan des recherches */ - size_t i; /* Boucle de parcours */ - GWorkGroup *group; /* Groupe à consulter */ - - assert(!g_mutex_trylock(&queue->mutex)); - - found = false; - - for (i = 0; i < queue->groups_count && !found; i++) - { - group = queue->groups[i]; - found = (g_work_group_get_id(group) == id); - } - - if (!found) - { - queue->groups_count++; - queue->groups = (GWorkGroup **)realloc(queue->groups, - queue->groups_count * sizeof(GWorkGroup *)); - - group = g_work_group_new(id, count); - queue->groups[queue->groups_count - 1] = group; - - } - - return !found; - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * -* * -* Description : Constitue un nouveau groupe de travail. * -* * -* Retour : Nouvel identifiant unique d'un nouveau groupe de travail. * -* * -* Remarques : - * -* * -******************************************************************************/ - -wgroup_id_t g_work_queue_define_work_group(GWorkQueue *queue) -{ - wgroup_id_t result; /* Valeur à retourner */ - bool created; /* Bilan d'une tentative */ - - g_mutex_lock(&queue->mutex); - - do - { - result = queue->generator++; - created = g_work_queue_ensure_group_exists(queue, result, NULL); - } - while (!created); - - g_mutex_unlock(&queue->mutex); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * -* count = quantité de threads à allouer. * -* * -* Description : Constitue un nouveau petit groupe de travail. * -* * -* Retour : Nouvel identifiant unique d'un nouveau groupe de travail. * -* * -* Remarques : - * -* * -******************************************************************************/ - -wgroup_id_t g_work_queue_define_tiny_work_group(GWorkQueue *queue, guint count) -{ - wgroup_id_t result; /* Valeur à retourner */ - bool created; /* Bilan d'une tentative */ - - g_mutex_lock(&queue->mutex); - - do - { - result = queue->generator++; - created = g_work_queue_ensure_group_exists(queue, result, &count); - } - while (!created); - - g_mutex_unlock(&queue->mutex); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * -* id = identifiant d'un groupe de travail. * -* * -* Description : Dissout un groupe de travail existant. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_work_queue_delete_work_group(GWorkQueue *queue, wgroup_id_t id) -{ - size_t i; /* Boucle de parcours */ - GWorkGroup *group; /* Groupe de travail manipulé */ -#ifndef NDEBUG - bool found; /* Repérage du groupe visé */ -#endif - -#ifndef NDEBUG - found = false; -#endif - - g_mutex_lock(&queue->mutex); - - for (i = 0; i < queue->groups_count; i++) - { - group = queue->groups[i]; - - if (g_work_group_get_id(group) == id) - { - g_object_unref(G_OBJECT(group)); - - memmove(&queue->groups[i], &queue->groups[i + 1], - (queue->groups_count - i - 1) * sizeof(GWorkGroup *)); - - queue->groups_count--; - queue->groups = (GWorkGroup **)realloc(queue->groups, - queue->groups_count * sizeof(GWorkGroup *)); - -#ifndef NDEBUG - found = true; -#endif - - break; - - } - - } - - assert(found); - - g_cond_broadcast(&queue->wait_all); - - g_mutex_unlock(&queue->mutex); - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire des actions à mener. * -* work = nouvelle tâche à programmer, puis effectuer. * -* id = identifiant du groupe de travail d'affectation. * -* * -* Description : Place une nouvelle tâche en attente. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_work_queue_schedule_work(GWorkQueue *queue, GDelayedWork *work, wgroup_id_t id) -{ - GWorkGroup *group; /* Groupe de travail à attendre*/ - - group = g_work_queue_find_group_for_id(queue, id); - assert(group != NULL); - - g_work_group_schedule(group, work); - - g_object_unref(G_OBJECT(group)); - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * -* id = identifiant d'un groupe de travail. * -* * -* Description : Fournit le groupe de travail correspondant à un identifiant. * -* * -* Retour : Eventuel groupe existant trouvé ou NULL si aucun. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static GWorkGroup *g_work_queue_find_group_for_id(GWorkQueue *queue, wgroup_id_t id) -{ - GWorkGroup *result; /* Trouvaille à retourner */ - size_t i; /* Boucle de parcours */ - - result = NULL; - - g_mutex_lock(&queue->mutex); - - for (i = 0; i < queue->groups_count; i++) - if (g_work_group_get_id(queue->groups[i]) == id) - { - result = queue->groups[i]; - g_object_ref(G_OBJECT(result)); - break; - } - - g_mutex_unlock(&queue->mutex); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * -* id = identifiant d'un groupe de travail. * -* * -* Description : Détermine si un groupe est vide de toute programmation. * -* * -* Retour : Etat du groupe de travail. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool g_work_queue_is_empty(GWorkQueue *queue, wgroup_id_t id) -{ - bool result; /* Etat à retourner */ - GWorkGroup *group; /* Groupe de travail à attendre*/ - - group = g_work_queue_find_group_for_id(queue, id); - - if (group != NULL) + while ((g_atomic_int_get(&group->pending) > 0) + && !group->force_exit) { - result = g_work_group_is_empty(group); - g_object_unref(G_OBJECT(group)); + result = g_cond_wait_until(&group->wait_cond, &group->mutex, end_time); + if (!result) break; } - else - result = true; + g_mutex_unlock(&group->mutex); return result; @@ -1217,39 +505,9 @@ bool g_work_queue_is_empty(GWorkQueue *queue, wgroup_id_t id) /****************************************************************************** * * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * -* id = identifiant d'un groupe de travail. * -* * -* Description : Attend que toutes les tâches d'un groupe soient traitées. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_work_queue_wait_for_completion(GWorkQueue *queue, wgroup_id_t id) -{ - GWorkGroup *group; /* Groupe de travail à attendre*/ - - group = g_work_queue_find_group_for_id(queue, id); - - if (group != NULL) - { - g_work_group_wait_for_completion(group, queue); - g_object_unref(G_OBJECT(group)); - } - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.* -* gb_ids = identifiants de groupes globaux. * -* gb_count = nombre de ces groupes globaux. * +* Paramètres : group = groupes de travail à manipuler. * * * -* Description : Attend que toutes les tâches de tout groupe soient traitées. * +* Description : Force un réveil d'une attente en cours pour la confirmer. * * * * Retour : - * * * @@ -1257,97 +515,17 @@ void g_work_queue_wait_for_completion(GWorkQueue *queue, wgroup_id_t id) * * ******************************************************************************/ -void g_work_queue_wait_for_all_completions(GWorkQueue *queue, const wgroup_id_t *gb_ids, size_t gb_count) +void g_work_group_wake_up_waiters(GWorkGroup *group) { - size_t i; /* Boucle de parcours */ - - g_mutex_lock(&queue->mutex); - - wait_again: - - /** - * Attente d'éventuels groupes isolés. - */ - - while (queue->groups_count > gb_count) - g_cond_wait(&queue->wait_all, &queue->mutex); - - g_mutex_unlock(&queue->mutex); - /** - * Attente des groupes principaux. - */ - - for (i = 0; i < gb_count; i++) - g_work_queue_wait_for_completion(queue, gb_ids[i]); - - /** - * Si le groupe par défaut a généré de nouveaux groupes, on recommence ! + * Concernant la pose du verrou, se référer aux commentaires de la + * fonction g_work_group_process(). */ - g_mutex_lock(&queue->mutex); - - if (queue->groups_count > gb_count) - goto wait_again; - - g_mutex_unlock(&queue->mutex); - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.* -* id = identifiant d'un groupe de travail. * -* callback = éventuelle fonction à appeler ou NULL. * -* data = données devant accompagner l'appel. * -* * -* Description : Modifie les conditions d'attente des fins d'exécutions. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_work_queue_set_extra_wait_callback(GWorkQueue *queue, wgroup_id_t id, wait_for_incoming_works_cb callback, void *data) -{ - GWorkGroup *group; /* Groupe de travail à traiter */ - - group = g_work_queue_find_group_for_id(queue, id); - - if (group != NULL) - { - g_work_group_set_extra_wait_callback(group, callback, data); - g_object_unref(G_OBJECT(group)); - } - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.* -* id = identifiant d'un groupe de travail. * -* * -* Description : Force un réveil d'une attente en cours pour la confirmer. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_work_queue_wake_up_waiters(GWorkQueue *queue, wgroup_id_t id) -{ - GWorkGroup *group; /* Groupe de travail à traiter */ + g_mutex_lock(&group->mutex); - group = g_work_queue_find_group_for_id(queue, id); + g_cond_broadcast(&group->wait_cond); - if (group != NULL) - { - g_work_group_wake_up_waiters(group); - g_object_unref(G_OBJECT(group)); - } + g_mutex_unlock(&group->mutex); } diff --git a/src/glibext/workgroup.h b/src/glibext/workgroup.h index 89eed12..f40155e 100644 --- a/src/glibext/workgroup.h +++ b/src/glibext/workgroup.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * delayed.h - prototypes pour la gestion des travaux différés + * workgroup.h - prototypes pour la gestion des travaux différés * - * Copyright (C) 2009-2018 Cyrille Bagard + * Copyright (C) 2009-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,58 +21,22 @@ */ -#ifndef _GLIBEXT_DELAYED_H -#define _GLIBEXT_DELAYED_H +#ifndef _GLIBEXT_WORKGROUP_H +#define _GLIBEXT_WORKGROUP_H -#include <glib-object.h> #include <stdbool.h> #include <stdint.h> +#include "helpers.h" +#include "work.h" -/* -------------------------- TACHE DIFFEREE DANS LE TEMPS -------------------------- */ -#define G_TYPE_DELAYED_WORK g_delayed_work_get_type() -#define G_DELAYED_WORK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_delayed_work_get_type(), GDelayedWork)) -#define G_IS_DELAYED_WORK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_delayed_work_get_type())) -#define G_DELAYED_WORK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_DELAYED_WORK, GDelayedWorkClass)) -#define G_IS_DELAYED_WORK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_DELAYED_WORK)) -#define G_DELAYED_WORK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_DELAYED_WORK, GDelayedWorkClass)) +#define G_TYPE_WORK_GROUP (g_work_group_get_type()) - -/* Travail différé (instance) */ -typedef struct _GDelayedWork GDelayedWork; - -/* Travail différé (classe) */ -typedef struct _GDelayedWorkClass GDelayedWorkClass; - - -/* Indique le type défini pour les travaux différés. */ -GType g_delayed_work_get_type(void); - -/* Attend la fin de l'exécution d'une tâche donnée. */ -void g_delayed_work_wait_for_completion(GDelayedWork *); - - - -/* ------------------------- TRAITEMENT DE TACHES DIFFEREES ------------------------- */ - - -#define G_TYPE_WORK_QUEUE g_work_queue_get_type() -#define G_WORK_QUEUE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_work_queue_get_type(), GWorkQueue)) -#define G_IS_WORK_QUEUE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_work_queue_get_type())) -#define G_WORK_QUEUE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_WORK_QUEUE, GWorkQueueClass)) -#define G_IS_WORK_QUEUE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_WORK_QUEUE)) -#define G_WORK_QUEUE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_WORK_QUEUE, GWorkQueueClass)) - - -/* Gestionnaire des travaux différés (instance) */ -typedef struct _GWorkQueue GWorkQueue; - -/* Gestionnaire des travaux différés (classe) */ -typedef struct _GWorkQueueClass GWorkQueueClass; +DECLARE_GTYPE(GWorkGroup, g_work_group, G, WORK_GROUP); /** @@ -83,45 +47,33 @@ typedef struct _GWorkQueueClass GWorkQueueClass; */ typedef uint64_t wgroup_id_t; +/** + * Marque de non initialisation. + */ +#define INVALID_GROUP_ID 0 -/* Indique le type défini pour le gestionnaire des travaux différés. */ -GType g_work_queue_get_type(void); - -/* Créé un nouveau gestionnaire de tâches parallèles. */ -GWorkQueue *g_work_queue_new(void); - -/* Constitue un nouveau groupe de travail. */ -wgroup_id_t g_work_queue_define_work_group(GWorkQueue *); -/* Constitue un nouveau petit groupe de travail. */ -wgroup_id_t g_work_queue_define_tiny_work_group(GWorkQueue *, guint); +/* Crée un nouveau thread dédié à un type de travaux donné. */ +GWorkGroup *g_work_group_new(wgroup_id_t, guint); -/* Dissout un groupe de travail existant. */ -void g_work_queue_delete_work_group(GWorkQueue *, wgroup_id_t); +/* Fournit l'identifiant associé à un groupe de travail. */ +wgroup_id_t g_work_group_get_id(const GWorkGroup *); -/* Place une nouvelle tâche en attente. */ -void g_work_queue_schedule_work(GWorkQueue *, GDelayedWork *, wgroup_id_t); +/* Place une nouvelle tâche en attente dans une file dédiée. */ +void g_work_group_schedule(GWorkGroup *, GGenericWork *); -/* Détermine si un groupe est vide de toute programmation. */ -bool g_work_queue_is_empty(GWorkQueue *, wgroup_id_t); +/* Détermine si le groupe est vide de toute programmation. */ +bool g_work_group_is_empty(GWorkGroup *); /* Attend que toutes les tâches d'un groupe soient traitées. */ -void g_work_queue_wait_for_completion(GWorkQueue *, wgroup_id_t); +void g_work_group_wait_for_completion(GWorkGroup *); -/* Attend que toutes les tâches de tout groupe soient traitées. */ -void g_work_queue_wait_for_all_completions(GWorkQueue *, const wgroup_id_t *, size_t); - - -/* Etudie le besoin d'attendre d'avantage de prochaines tâches. */ -typedef bool (* wait_for_incoming_works_cb) (GWorkQueue *, wgroup_id_t, void *); - - -/* Modifie les conditions d'attente des fins d'exécutions. */ -void g_work_queue_set_extra_wait_callback(GWorkQueue *, wgroup_id_t, wait_for_incoming_works_cb, void *); +/* Attend que toutes les tâches d'un groupe soient traitées. */ +bool g_work_group_wait_timed_for_completion(GWorkGroup *, gint64); /* Force un réveil d'une attente en cours pour la confirmer. */ -void g_work_queue_wake_up_waiters(GWorkQueue *, wgroup_id_t); +void g_work_group_wake_up_waiters(GWorkGroup *); -#endif /* _GLIBEXT_DELAYED_H */ +#endif /* _GLIBEXT_WORKGROUP_H */ diff --git a/src/glibext/workqueue-int.h b/src/glibext/workqueue-int.h index 4f84e86..40afa19 100644 --- a/src/glibext/workqueue-int.h +++ b/src/glibext/workqueue-int.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * delayed-int.h - définitions internes pour la gestion des travaux différés + * workqueue-int.h - définitions internes pour la gestion des travaux différés * - * Copyright (C) 2009-2018 Cyrille Bagard + * Copyright (C) 2009-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,55 +21,35 @@ */ -#ifndef _GLIBEXT_DELAYED_INT_H -#define _GLIBEXT_DELAYED_INT_H +#ifndef _GLIBEXT_WORKQUEUE_INT_H +#define _GLIBEXT_WORKQUEUE_INT_H -#include "delayed.h" +#include "workqueue.h" -#include "notifier.h" -#include "../common/dllist.h" - - -/* -------------------------- TACHE DIFFEREE DANS LE TEMPS -------------------------- */ - - -/* Traite un travail programmé. */ -typedef void (* run_task_fc) (GDelayedWork *, GtkStatusStack *); - - -/* Travail différé (instance) */ -struct _GDelayedWork +/* Gestionnaire des travaux différés (instance) */ +struct _GWorkQueue { GObject parent; /* A laisser en premier */ - DL_LIST_ITEM(link); /* Lien vers les maillons */ + wgroup_id_t generator; /* Générateur d'identifiants */ - bool completed; /* Fin de la tâche ? */ - GMutex mutex; /* Accès à la variable */ - GCond cond; /* Attente de changement */ + GWorkGroup **groups; /* Files de traitement */ + size_t groups_count; /* Nombre de files internes */ + GMutex mutex; /* Verrou pour l'accès */ + GCond wait_all; /* Réveil d'attente globale */ }; -/* Travail différé (classe) */ -struct _GDelayedWorkClass +/* Gestionnaire des travaux différés (classe) */ +struct _GWorkQueueClass { GObjectClass parent; /* A laisser en premier */ - run_task_fc run; /* Traitement externalisé */ - - /* Signaux */ - - void (* work_completed) (GDelayedWork *); - }; -#define delayed_work_list_add_tail(new, head) dl_list_add_tail(new, head, GDelayedWork, link) -#define delayed_work_list_del(item, head) dl_list_del(item, head, GDelayedWork, link) - - -#endif /* _GLIBEXT_DELAYED_INT_H */ +#endif /* _GLIBEXT_WORKQUEUE_INT_H */ diff --git a/src/glibext/workqueue.c b/src/glibext/workqueue.c index 6b5ac35..fe7f1bb 100644 --- a/src/glibext/workqueue.c +++ b/src/glibext/workqueue.c @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * delayed.c - gestion des travaux différés + * workqueue.c - gestion des travaux différés * - * Copyright (C) 2009-2018 Cyrille Bagard + * Copyright (C) 2009-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,149 +21,15 @@ */ -#include "delayed.h" +#include "workqueue.h" #include <assert.h> -#include <inttypes.h> #include <malloc.h> -#include <stdio.h> -#include <string.h> -#include "delayed-int.h" -#include "../core/nproc.h" -#ifdef INCLUDE_GTK_SUPPORT -# include "../gui/core/global.h" -#endif - - - -/* -------------------------- TACHE DIFFEREE DANS LE TEMPS -------------------------- */ - - -/* Initialise la classe des travaux différés. */ -static void g_delayed_work_class_init(GDelayedWorkClass *); - -/* Initialise une instance de travail différé. */ -static void g_delayed_work_init(GDelayedWork *); - -/* Supprime toutes les références externes. */ -static void g_delayed_work_dispose(GDelayedWork *); - -/* Procède à la libération totale de la mémoire. */ -static void g_delayed_work_finalize(GDelayedWork *); - -/* Mène l'opération programmée. */ -static void g_delayed_work_process(GDelayedWork *, GtkStatusStack *); - - - -/* -------------------------- THREAD DE TRAITEMENTS DEDIES -------------------------- */ - - -#define G_TYPE_WORK_GROUP g_work_group_get_type() -#define G_WORK_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_work_group_get_type(), GWorkGroup)) -#define G_IS_WORK_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_work_group_get_type())) -#define G_WORK_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_WORK_GROUP, GWorkGroupClass)) -#define G_IS_WORK_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_WORK_GROUP)) -#define G_WORK_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_WORK_GROUP, GWorkGroupClass)) - - -/* File de traitement pour un type donné (instance) */ -typedef struct _GWorkGroup -{ - GObject parent; /* A laisser en premier */ - - wgroup_id_t id; /* Identifiant de travaux menés*/ - - GDelayedWork *works; /* Tâches à mener à bien */ - GMutex mutex; /* Verrou pour l'accès */ - GCond cond; /* Réveil pour un traitement */ - GCond wait_cond; /* Réveil d'attente de fin */ - gint pending; /* Tâches en cours d'exécution */ - - GThread **threads; /* Procédure de traitement */ - guint threads_count; /* Nombre de procédures */ - bool force_exit; /* Procédure d'arrêt */ - - wait_for_incoming_works_cb callback; /* Encadre les attentes de fin */ - void *data; /* Données à associer */ - -} GWorkGroup; - -/* File de traitement pour un type donné (classe) */ -typedef struct _GWorkGroupClass -{ - GObjectClass parent; /* A laisser en premier */ - -} GWorkGroupClass; - - -/* Indique le type défini pour les groupes de travail. */ -static GType g_work_group_get_type(void); - -/* Initialise la classe des groupes de travail. */ -static void g_work_group_class_init(GWorkGroupClass *); - -/* Initialise une instance de groupe de travail. */ -static void g_work_group_init(GWorkGroup *); +#include "workqueue-int.h" -/* Supprime toutes les références externes. */ -static void g_work_group_dispose(GWorkGroup *); - -/* Procède à la libération totale de la mémoire. */ -static void g_work_group_finalize(GWorkGroup *); - -/* Crée un nouveau thread dédié à un type de travaux donné. */ -static GWorkGroup *g_work_group_new(wgroup_id_t, const guint *); - -/* Fournit l'identifiant associé à un groupe de travail. */ -static wgroup_id_t g_work_group_get_id(const GWorkGroup *); - -/* Place une nouvelle tâche en attente dans une file dédiée. */ -static void g_work_group_schedule(GWorkGroup *, GDelayedWork *); - -/* Assure le traitement en différé. */ -static void *g_work_group_process(GWorkGroup *); - -/* Détermine si le groupe est vide de toute programmation. */ -static bool g_work_group_is_empty(GWorkGroup *); - -/* Attend que toutes les tâches d'un groupe soient traitées. */ -static void g_work_group_wait_for_completion(GWorkGroup *, GWorkQueue *); - -/* Modifie les conditions d'attente des fins d'exécutions. */ -static void g_work_group_set_extra_wait_callback(GWorkGroup *, wait_for_incoming_works_cb, void *); - -/* Force un réveil d'une attente en cours pour la confirmer. */ -static void g_work_group_wake_up_waiters(GWorkGroup *); - - - -/* ------------------------- TRAITEMENT DE TACHES DIFFEREES ------------------------- */ - - -/* Gestionnaire des travaux différés (instance) */ -struct _GWorkQueue -{ - GObject parent; /* A laisser en premier */ - - wgroup_id_t generator; /* Générateur d'identifiants */ - - GWorkGroup **groups; /* Files de traitement */ - size_t groups_count; /* Nombre de files internes */ - GMutex mutex; /* Verrou pour l'accès */ - GCond wait_all; /* Réveil d'attente globale */ - -}; - -/* Gestionnaire des travaux différés (classe) */ -struct _GWorkQueueClass -{ - GObjectClass parent; /* A laisser en premier */ - -}; /* Initialise la classe des travaux différés. */ @@ -179,641 +45,13 @@ static void g_work_queue_dispose(GWorkQueue *); static void g_work_queue_finalize(GWorkQueue *); /* Donne l'assurance de l'existence d'un groupe de travail. */ -static bool g_work_queue_ensure_group_exists(GWorkQueue *, wgroup_id_t, const guint *); +static bool g_work_queue_ensure_group_exists(GWorkQueue *, wgroup_id_t, guint); /* Fournit le groupe de travail correspondant à un identifiant. */ static GWorkGroup *g_work_queue_find_group_for_id(GWorkQueue *, wgroup_id_t); -/* ---------------------------------------------------------------------------------- */ -/* TACHE DIFFEREE DANS LE TEMPS */ -/* ---------------------------------------------------------------------------------- */ - - -/* Indique le type défini pour les travaux différés. */ -G_DEFINE_TYPE(GDelayedWork, g_delayed_work, G_TYPE_OBJECT); - - -/****************************************************************************** -* * -* Paramètres : klass = classe à initialiser. * -* * -* Description : Initialise la classe des travaux différés. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_delayed_work_class_init(GDelayedWorkClass *klass) -{ - GObjectClass *object; /* Autre version de la classe */ - - object = G_OBJECT_CLASS(klass); - - object->dispose = (GObjectFinalizeFunc/* ! */)g_delayed_work_dispose; - object->finalize = (GObjectFinalizeFunc)g_delayed_work_finalize; - - g_signal_new("work-completed", - G_TYPE_DELAYED_WORK, - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET(GDelayedWorkClass, work_completed), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - -} - - -/****************************************************************************** -* * -* Paramètres : work = instance à initialiser. * -* * -* Description : Initialise une instance de travail différé. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_delayed_work_init(GDelayedWork *work) -{ - work->completed = false; - g_mutex_init(&work->mutex); - g_cond_init(&work->cond); - -} - - -/****************************************************************************** -* * -* Paramètres : work = instance d'objet GLib à traiter. * -* * -* Description : Supprime toutes les références externes. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_delayed_work_dispose(GDelayedWork *work) -{ - g_mutex_clear(&work->mutex); - g_cond_clear(&work->cond); - - G_OBJECT_CLASS(g_delayed_work_parent_class)->dispose(G_OBJECT(work)); - -} - - -/****************************************************************************** -* * -* Paramètres : work = instance d'objet GLib à traiter. * -* * -* Description : Procède à la libération totale de la mémoire. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_delayed_work_finalize(GDelayedWork *work) -{ - G_OBJECT_CLASS(g_delayed_work_parent_class)->finalize(G_OBJECT(work)); - -} - - -/****************************************************************************** -* * -* Paramètres : work = travail à effectuer. * -* status = barre de statut à tenir informée. * -* * -* Description : Mène l'opération programmée. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_delayed_work_process(GDelayedWork *work, GtkStatusStack *status) -{ - G_DELAYED_WORK_GET_CLASS(work)->run(work, status); - - g_mutex_lock(&work->mutex); - - work->completed = true; - - g_cond_signal(&work->cond); - g_mutex_unlock(&work->mutex); - - g_signal_emit_by_name(work, "work-completed"); - -} - - -/****************************************************************************** -* * -* Paramètres : work = travail à surveiller. * -* * -* Description : Attend la fin de l'exécution d'une tâche donnée. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_delayed_work_wait_for_completion(GDelayedWork *work) -{ - g_mutex_lock(&work->mutex); - - while (!work->completed) - g_cond_wait(&work->cond, &work->mutex); - - g_mutex_unlock(&work->mutex); - -} - - - -/* ---------------------------------------------------------------------------------- */ -/* THREADS DES TRAITEMENTS DEDIES */ -/* ---------------------------------------------------------------------------------- */ - - -/* Indique le type défini pour les groupes de travail. */ -G_DEFINE_TYPE(GWorkGroup, g_work_group, G_TYPE_OBJECT); - - -/****************************************************************************** -* * -* Paramètres : klass = classe à initialiser. * -* * -* Description : Initialise la classe des groupes de travail. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_group_class_init(GWorkGroupClass *klass) -{ - GObjectClass *object; /* Autre version de la classe */ - - object = G_OBJECT_CLASS(klass); - - object->dispose = (GObjectFinalizeFunc/* ! */)g_work_group_dispose; - object->finalize = (GObjectFinalizeFunc)g_work_group_finalize; - -} - - -/****************************************************************************** -* * -* Paramètres : group = instance à initialiser. * -* * -* Description : Initialise une instance de groupe de travail. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_group_init(GWorkGroup *group) -{ - group->works = NULL; - - g_mutex_init(&group->mutex); - g_cond_init(&group->cond); - g_cond_init(&group->wait_cond); - - g_atomic_int_set(&group->pending, 0); - - group->threads = NULL; - group->threads_count = 0; - group->force_exit = false; - - group->callback = NULL; - group->data = NULL; - -} - - -/****************************************************************************** -* * -* Paramètres : queue = instance d'objet GLib à traiter. * -* * -* Description : Supprime toutes les références externes. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_group_dispose(GWorkGroup *group) -{ - guint i; /* Boucle de parcours */ - GDelayedWork *work; /* Travail à oublier */ - - group->force_exit = true; - - /** - * Concernant la pose du verrou, se référer aux commentaires de la - * fonction g_work_group_process(). - */ - - g_mutex_lock(&group->mutex); - - g_cond_broadcast(&group->cond); - - g_mutex_unlock(&group->mutex); - - for (i = 0; i < group->threads_count; i++) - g_thread_join(group->threads[i]); - - while (!dl_list_empty(group->works)) - { - work = group->works; - delayed_work_list_del(work, &group->works); - - g_object_unref(G_OBJECT(work)); - - } - - g_mutex_clear(&group->mutex); - g_cond_clear(&group->cond); - g_cond_clear(&group->wait_cond); - - G_OBJECT_CLASS(g_work_group_parent_class)->dispose(G_OBJECT(group)); - -} - - -/****************************************************************************** -* * -* Paramètres : group = instance d'objet GLib à traiter. * -* * -* Description : Procède à la libération totale de la mémoire. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_group_finalize(GWorkGroup *group) -{ - if (group->threads != NULL) - free(group->threads); - - G_OBJECT_CLASS(g_work_group_parent_class)->finalize(G_OBJECT(group)); - -} - - -/****************************************************************************** -* * -* Paramètres : id = identifiant accordé au nouveau groupe. * -* count = quantité de threads à allouer. * -* * -* Description : Crée un nouveau thread dédié à un type de travaux donné. * -* * -* Retour : Structure associée au thread mise en place. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static GWorkGroup *g_work_group_new(wgroup_id_t id, const guint *count) -{ - GWorkGroup *result; /* Traiteur à retourner */ - guint i; /* Boucle de parcours */ - char name[16]; /* Désignation humaine */ - - result = g_object_new(G_TYPE_WORK_GROUP, NULL); - - result->id = id; - - result->threads_count = get_max_online_threads(); - - if (count != NULL && *count < result->threads_count) - result->threads_count = *count; - - result->threads = (GThread **)calloc(result->threads_count, sizeof(GThread *)); - - for (i = 0; i < result->threads_count; i++) - { - snprintf(name, sizeof(name), "wgrp_%" PRIu64 "-%u", id, i); - - result->threads[i] = g_thread_new(name, (GThreadFunc)g_work_group_process, result); - if (!result->threads[i]) - goto start_error; - - } - - start_error: - - result->threads_count = i; - - assert(i > 0); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : group = gestionnaire des actions à mener. * -* * -* Description : Fournit l'identifiant associé à un groupe de travail. * -* * -* Retour : Identifiant unique attribué au groupe de travail. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static wgroup_id_t g_work_group_get_id(const GWorkGroup *group) -{ - return group->id; - -} - - -/****************************************************************************** -* * -* Paramètres : group = gestionnaire des actions à mener. * -* work = nouvelle tâche à programmer, puis effectuer. * -* * -* Description : Place une nouvelle tâche en attente dans une file dédiée. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_group_schedule(GWorkGroup *group, GDelayedWork *work) -{ - g_mutex_lock(&group->mutex); - - g_atomic_int_inc(&group->pending); - - delayed_work_list_add_tail(work, &group->works); - - g_cond_signal(&group->cond); - - g_mutex_unlock(&group->mutex); - -} - - -/****************************************************************************** -* * -* Paramètres : group = gestionnaire des actions à mener. * -* * -* Description : Assure le traitement en différé. * -* * -* Retour : Bilan de l'opération. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void *g_work_group_process(GWorkGroup *group) -{ - GDelayedWork *work; /* Traitement à mener */ - GtkStatusStack *status; /* Zone d'info éventuelle */ - - while (1) - { - g_mutex_lock(&group->mutex); - - while (dl_list_empty(group->works) && !group->force_exit) - g_cond_wait(&group->cond, &group->mutex); - - if (group->force_exit) - { - g_mutex_unlock(&group->mutex); - break; - } - - work = group->works; - delayed_work_list_del(work, &group->works); - - g_mutex_unlock(&group->mutex); - -#ifdef INCLUDE_GTK_SUPPORT - status = get_global_status(); -#else - status = NULL; -#endif - g_delayed_work_process(work, status); - - g_object_unref(G_OBJECT(work)); - - /** - * Verrou ou pas verrou ? - * - * La documentation de la GLib indique que ce n'est pas nécessaire : - * - * ''' - * It is good practice to lock the same mutex as the waiting threads - * while calling this function, though not required. - * ''' - * - * Ce conseil se trouve verbatim à l'adresse : - * - * https://developer.gnome.org/glib/stable/glib-Threads.html#g-cond-broadcast - * - * Dans la pratique, il peut arriver que l'attente de la fonction - * g_work_group_wait_for_completion() ne soit jamais interrompue. - * - * La documentation POSIX est un peu plus orientée : - * - * ''' - * The pthread_cond_broadcast() functions may be called by a thread - * whether or not it currently owns the mutex that threads calling - * pthread_cond_wait() have associated with the condition variable - * during their waits; however, if predictable scheduling behavior is - * required, then that mutex shall be locked by the thread calling - * pthread_cond_broadcast(). - * ''' - * - * Ce passage complet est consultable à l'adresse : - * - * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cond_broadcast.html - * - * La page de manuel pthread_cond_broadcast(3) est quant à elle plus - * directrice : aucun complément d'information sur le sujet n'est fourni - * et les exemples associés utilisent implicement un verrou pendant - * sont appel. - */ - - g_mutex_lock(&group->mutex); - - if (g_atomic_int_dec_and_test(&group->pending)) - g_cond_broadcast(&group->wait_cond); - - g_mutex_unlock(&group->mutex); - - } - - return NULL; - -} - - -/****************************************************************************** -* * -* Paramètres : group = gestionnaire des actions à consulter. * -* * -* Description : Détermine si le groupe est vide de toute programmation. * -* * -* Retour : Etat du groupe de travail. * -* * -* Remarques : - * -* * -******************************************************************************/ - -static bool g_work_group_is_empty(GWorkGroup *group) -{ - bool result; /* Etat à retourner */ - - /** - * Pour que le résultat soit exploitable, il ne doit pas varier - * en dehors de la zone couverte par le verrou du groupe avant - * son utilisation par l'appelant. - * - * Il doit donc logiquement y avoir un autre verrou en amont et, - * comme à priori on ne devrait pas bloquer les groupes principaux - * pour un traitement particulier, cette procédure ne devrait concerner - * que des groupes dynamiques. - */ - - g_mutex_lock(&group->mutex); - - result = dl_list_empty(group->works); - - g_mutex_unlock(&group->mutex); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : group = groupe dont les conclusions sont attendues. * -* queue = queue d'appartenance pour les appels externes. * -* * -* Description : Attend que toutes les tâches d'un groupe soient traitées. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_group_wait_for_completion(GWorkGroup *group, GWorkQueue *queue) -{ - wait_for_incoming_works_cb callback; /* Procédure complémentaire */ - - bool no_extra_check(GWorkQueue *_q, wgroup_id_t _id, void *_data) - { - return false; - } - - callback = group->callback != NULL ? group->callback : no_extra_check; - - g_mutex_lock(&group->mutex); - - /** - * On attend que : - * - la liste des tâches programmées soit vide. - * - il n'existe plus de tâche en cours. - * - rien n'indique que de nouvelles tâches supplémentaires vont arriver. - */ - - while ((g_atomic_int_get(&group->pending) > 0 || callback(queue, group->id, group->data)) - && !group->force_exit) - { - g_cond_wait(&group->wait_cond, &group->mutex); - } - - g_mutex_unlock(&group->mutex); - -} - - -/****************************************************************************** -* * -* Paramètres : group = groupe dont les paramètres sont à modifier. * -* callback = éventuelle fonction à appeler ou NULL. * -* data = données devant accompagner l'appel. * -* * -* Description : Modifie les conditions d'attente des fins d'exécutions. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_group_set_extra_wait_callback(GWorkGroup *group, wait_for_incoming_works_cb callback, void *data) -{ - group->callback = callback; - group->data = data; - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.* -* id = identifiant d'un groupe de travail. * -* * -* Description : Force un réveil d'une attente en cours pour la confirmer. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_work_group_wake_up_waiters(GWorkGroup *group) -{ - /** - * Concernant la pose du verrou, se référer aux commentaires de la - * fonction g_work_group_process(). - */ - - g_mutex_lock(&group->mutex); - - g_cond_broadcast(&group->wait_cond); - - g_mutex_unlock(&group->mutex); - -} - - - -/* ---------------------------------------------------------------------------------- */ -/* TRAITEMENT DE TACHES DIFFEREES */ -/* ---------------------------------------------------------------------------------- */ - - /* Indique le type défini pour le gestionnaire des travaux différés. */ G_DEFINE_TYPE(GWorkQueue, g_work_queue, G_TYPE_OBJECT); @@ -946,7 +184,7 @@ GWorkQueue *g_work_queue_new(void) * * * Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * * id = identifiant d'un groupe de travail. * -* count = quantité de threads à allouer. * +* count = quantité de threads à allouer (0 pour un défaut). * * * * Description : Donne l'assurance de l'existence d'un groupe de travail. * * * @@ -956,7 +194,7 @@ GWorkQueue *g_work_queue_new(void) * * ******************************************************************************/ -static bool g_work_queue_ensure_group_exists(GWorkQueue *queue, wgroup_id_t id, const guint *count) +static bool g_work_queue_ensure_group_exists(GWorkQueue *queue, wgroup_id_t id, guint count) { bool found; /* Bilan des recherches */ size_t i; /* Boucle de parcours */ @@ -975,8 +213,7 @@ static bool g_work_queue_ensure_group_exists(GWorkQueue *queue, wgroup_id_t id, if (!found) { queue->groups_count++; - queue->groups = (GWorkGroup **)realloc(queue->groups, - queue->groups_count * sizeof(GWorkGroup *)); + queue->groups = realloc(queue->groups, queue->groups_count * sizeof(GWorkGroup *)); group = g_work_group_new(id, count); queue->groups[queue->groups_count - 1] = group; @@ -991,6 +228,7 @@ static bool g_work_queue_ensure_group_exists(GWorkQueue *queue, wgroup_id_t id, /****************************************************************************** * * * Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * +* count = quantité de threads à allouer (0 pour un défaut). * * * * Description : Constitue un nouveau groupe de travail. * * * @@ -1000,7 +238,7 @@ static bool g_work_queue_ensure_group_exists(GWorkQueue *queue, wgroup_id_t id, * * ******************************************************************************/ -wgroup_id_t g_work_queue_define_work_group(GWorkQueue *queue) +wgroup_id_t g_work_queue_define_group(GWorkQueue *queue, guint count) { wgroup_id_t result; /* Valeur à retourner */ bool created; /* Bilan d'une tentative */ @@ -1009,42 +247,13 @@ wgroup_id_t g_work_queue_define_work_group(GWorkQueue *queue) do { - result = queue->generator++; - created = g_work_queue_ensure_group_exists(queue, result, NULL); - } - while (!created); + result = ++queue->generator; - g_mutex_unlock(&queue->mutex); + if (result == INVALID_GROUP_ID) + continue; - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * -* count = quantité de threads à allouer. * -* * -* Description : Constitue un nouveau petit groupe de travail. * -* * -* Retour : Nouvel identifiant unique d'un nouveau groupe de travail. * -* * -* Remarques : - * -* * -******************************************************************************/ + created = g_work_queue_ensure_group_exists(queue, result, count); -wgroup_id_t g_work_queue_define_tiny_work_group(GWorkQueue *queue, guint count) -{ - wgroup_id_t result; /* Valeur à retourner */ - bool created; /* Bilan d'une tentative */ - - g_mutex_lock(&queue->mutex); - - do - { - result = queue->generator++; - created = g_work_queue_ensure_group_exists(queue, result, &count); } while (!created); @@ -1068,7 +277,7 @@ wgroup_id_t g_work_queue_define_tiny_work_group(GWorkQueue *queue, guint count) * * ******************************************************************************/ -void g_work_queue_delete_work_group(GWorkQueue *queue, wgroup_id_t id) +void g_work_queue_delete_group(GWorkQueue *queue, wgroup_id_t id) { size_t i; /* Boucle de parcours */ GWorkGroup *group; /* Groupe de travail manipulé */ @@ -1088,14 +297,13 @@ void g_work_queue_delete_work_group(GWorkQueue *queue, wgroup_id_t id) if (g_work_group_get_id(group) == id) { - g_object_unref(G_OBJECT(group)); + unref_object(group); memmove(&queue->groups[i], &queue->groups[i + 1], (queue->groups_count - i - 1) * sizeof(GWorkGroup *)); queue->groups_count--; - queue->groups = (GWorkGroup **)realloc(queue->groups, - queue->groups_count * sizeof(GWorkGroup *)); + queue->groups = realloc(queue->groups, queue->groups_count * sizeof(GWorkGroup *)); #ifndef NDEBUG found = true; @@ -1124,22 +332,31 @@ void g_work_queue_delete_work_group(GWorkQueue *queue, wgroup_id_t id) * * * Description : Place une nouvelle tâche en attente. * * * -* Retour : - * +* Retour : Bilan, qui correspond à l'existence du groupe ciblé. * * * * Remarques : - * * * ******************************************************************************/ -void g_work_queue_schedule_work(GWorkQueue *queue, GDelayedWork *work, wgroup_id_t id) +bool g_work_queue_schedule(GWorkQueue *queue, GGenericWork *work, wgroup_id_t id) { + bool result; /* Bilan à retourner */ GWorkGroup *group; /* Groupe de travail à attendre*/ group = g_work_queue_find_group_for_id(queue, id); assert(group != NULL); - g_work_group_schedule(group, work); + result = (group != NULL); - g_object_unref(G_OBJECT(group)); + if (result) + { + g_work_group_schedule(group, work); + + unref_object(group); + + } + + return result; } @@ -1170,7 +387,7 @@ static GWorkGroup *g_work_queue_find_group_for_id(GWorkQueue *queue, wgroup_id_t if (g_work_group_get_id(queue->groups[i]) == id) { result = queue->groups[i]; - g_object_ref(G_OBJECT(result)); + ref_object(result); break; } @@ -1204,7 +421,7 @@ bool g_work_queue_is_empty(GWorkQueue *queue, wgroup_id_t id) if (group != NULL) { result = g_work_group_is_empty(group); - g_object_unref(G_OBJECT(group)); + unref_object(group); } else @@ -1236,8 +453,8 @@ void g_work_queue_wait_for_completion(GWorkQueue *queue, wgroup_id_t id) if (group != NULL) { - g_work_group_wait_for_completion(group, queue); - g_object_unref(G_OBJECT(group)); + g_work_group_wait_for_completion(group); + unref_object(group); } } @@ -1245,6 +462,40 @@ void g_work_queue_wait_for_completion(GWorkQueue *queue, wgroup_id_t id) /****************************************************************************** * * +* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail. * +* id = identifiant d'un groupe de travail. * +* rel = durée relative à patienter au max. en microsecondes. * +* * +* Description : Attend que toutes les tâches d'un groupe soient traitées. * +* * +* Retour : Bilan de l'attente : false en cas d'expiration, true sinon. * +* * +* Remarques : Cette fonction est originellement dédiée à un usage Python. * +* * +******************************************************************************/ + +bool g_work_queue_wait_timed_for_completion(GWorkQueue *queue, wgroup_id_t id, gint64 rel) +{ + bool result; /* Bilan d'attente à renvoyer */ + GWorkGroup *group; /* Groupe de travail à attendre*/ + + group = g_work_queue_find_group_for_id(queue, id); + + if (group != NULL) + { + result = g_work_group_wait_timed_for_completion(group, rel); + unref_object(group); + } + else + result = true; + + return result; + +} + + +/****************************************************************************** +* * * Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.* * gb_ids = identifiants de groupes globaux. * * gb_count = nombre de ces groupes globaux. * @@ -1299,36 +550,6 @@ void g_work_queue_wait_for_all_completions(GWorkQueue *queue, const wgroup_id_t * * * Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.* * id = identifiant d'un groupe de travail. * -* callback = éventuelle fonction à appeler ou NULL. * -* data = données devant accompagner l'appel. * -* * -* Description : Modifie les conditions d'attente des fins d'exécutions. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -void g_work_queue_set_extra_wait_callback(GWorkQueue *queue, wgroup_id_t id, wait_for_incoming_works_cb callback, void *data) -{ - GWorkGroup *group; /* Groupe de travail à traiter */ - - group = g_work_queue_find_group_for_id(queue, id); - - if (group != NULL) - { - g_work_group_set_extra_wait_callback(group, callback, data); - g_object_unref(G_OBJECT(group)); - } - -} - - -/****************************************************************************** -* * -* Paramètres : queue = gestionnaire de l'ensemble des groupes de travail.* -* id = identifiant d'un groupe de travail. * * * * Description : Force un réveil d'une attente en cours pour la confirmer. * * * @@ -1347,7 +568,7 @@ void g_work_queue_wake_up_waiters(GWorkQueue *queue, wgroup_id_t id) if (group != NULL) { g_work_group_wake_up_waiters(group); - g_object_unref(G_OBJECT(group)); + unref_object(group); } } diff --git a/src/glibext/workqueue.h b/src/glibext/workqueue.h index 89eed12..963d86a 100644 --- a/src/glibext/workqueue.h +++ b/src/glibext/workqueue.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * delayed.h - prototypes pour la gestion des travaux différés + * workqueue.h - prototypes pour la gestion des travaux différés * - * Copyright (C) 2009-2018 Cyrille Bagard + * Copyright (C) 2009-2024 Cyrille Bagard * * This file is part of Chrysalide. * @@ -21,86 +21,34 @@ */ -#ifndef _GLIBEXT_DELAYED_H -#define _GLIBEXT_DELAYED_H +#ifndef _GLIBEXT_WORKQUEUE_H +#define _GLIBEXT_WORKQUEUE_H -#include <glib-object.h> #include <stdbool.h> -#include <stdint.h> +#include "helpers.h" +#include "workgroup.h" -/* -------------------------- TACHE DIFFEREE DANS LE TEMPS -------------------------- */ -#define G_TYPE_DELAYED_WORK g_delayed_work_get_type() -#define G_DELAYED_WORK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_delayed_work_get_type(), GDelayedWork)) -#define G_IS_DELAYED_WORK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_delayed_work_get_type())) -#define G_DELAYED_WORK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_DELAYED_WORK, GDelayedWorkClass)) -#define G_IS_DELAYED_WORK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_DELAYED_WORK)) -#define G_DELAYED_WORK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_DELAYED_WORK, GDelayedWorkClass)) +#define G_TYPE_WORK_QUEUE (g_work_queue_get_type()) +DECLARE_GTYPE(GWorkQueue, g_work_queue, G, WORK_QUEUE); -/* Travail différé (instance) */ -typedef struct _GDelayedWork GDelayedWork; - -/* Travail différé (classe) */ -typedef struct _GDelayedWorkClass GDelayedWorkClass; - - -/* Indique le type défini pour les travaux différés. */ -GType g_delayed_work_get_type(void); - -/* Attend la fin de l'exécution d'une tâche donnée. */ -void g_delayed_work_wait_for_completion(GDelayedWork *); - - - -/* ------------------------- TRAITEMENT DE TACHES DIFFEREES ------------------------- */ - - -#define G_TYPE_WORK_QUEUE g_work_queue_get_type() -#define G_WORK_QUEUE(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), g_work_queue_get_type(), GWorkQueue)) -#define G_IS_WORK_QUEUE(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_work_queue_get_type())) -#define G_WORK_QUEUE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_WORK_QUEUE, GWorkQueueClass)) -#define G_IS_WORK_QUEUE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_WORK_QUEUE)) -#define G_WORK_QUEUE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_WORK_QUEUE, GWorkQueueClass)) - - -/* Gestionnaire des travaux différés (instance) */ -typedef struct _GWorkQueue GWorkQueue; - -/* Gestionnaire des travaux différés (classe) */ -typedef struct _GWorkQueueClass GWorkQueueClass; - - -/** - * Identifiant unique pour groupe de travail. - * - * Le nombre de bits est forcé à 64 bits car glib-genmarshal ne reconnait - * pas explicitement le type 'unsigned long long'. - */ -typedef uint64_t wgroup_id_t; - - -/* Indique le type défini pour le gestionnaire des travaux différés. */ -GType g_work_queue_get_type(void); /* Créé un nouveau gestionnaire de tâches parallèles. */ GWorkQueue *g_work_queue_new(void); /* Constitue un nouveau groupe de travail. */ -wgroup_id_t g_work_queue_define_work_group(GWorkQueue *); - -/* Constitue un nouveau petit groupe de travail. */ -wgroup_id_t g_work_queue_define_tiny_work_group(GWorkQueue *, guint); +wgroup_id_t g_work_queue_define_group(GWorkQueue *, guint); /* Dissout un groupe de travail existant. */ -void g_work_queue_delete_work_group(GWorkQueue *, wgroup_id_t); +void g_work_queue_delete_group(GWorkQueue *, wgroup_id_t); /* Place une nouvelle tâche en attente. */ -void g_work_queue_schedule_work(GWorkQueue *, GDelayedWork *, wgroup_id_t); +bool g_work_queue_schedule(GWorkQueue *, GGenericWork *, wgroup_id_t); /* Détermine si un groupe est vide de toute programmation. */ bool g_work_queue_is_empty(GWorkQueue *, wgroup_id_t); @@ -108,20 +56,15 @@ bool g_work_queue_is_empty(GWorkQueue *, wgroup_id_t); /* Attend que toutes les tâches d'un groupe soient traitées. */ void g_work_queue_wait_for_completion(GWorkQueue *, wgroup_id_t); +/* Attend que toutes les tâches d'un groupe soient traitées. */ +bool g_work_queue_wait_timed_for_completion(GWorkQueue *, wgroup_id_t, gint64); + /* Attend que toutes les tâches de tout groupe soient traitées. */ void g_work_queue_wait_for_all_completions(GWorkQueue *, const wgroup_id_t *, size_t); - -/* Etudie le besoin d'attendre d'avantage de prochaines tâches. */ -typedef bool (* wait_for_incoming_works_cb) (GWorkQueue *, wgroup_id_t, void *); - - -/* Modifie les conditions d'attente des fins d'exécutions. */ -void g_work_queue_set_extra_wait_callback(GWorkQueue *, wgroup_id_t, wait_for_incoming_works_cb, void *); - /* Force un réveil d'une attente en cours pour la confirmer. */ void g_work_queue_wake_up_waiters(GWorkQueue *, wgroup_id_t); -#endif /* _GLIBEXT_DELAYED_H */ +#endif /* _GLIBEXT_WORKQUEUE_H */ |