summaryrefslogtreecommitdiff
path: root/src/glibext
diff options
context:
space:
mode:
Diffstat (limited to 'src/glibext')
-rw-r--r--src/glibext/Makefile.am102
-rw-r--r--src/glibext/bufferline.c334
-rw-r--r--src/glibext/bufferline.h14
-rw-r--r--src/glibext/comparable-int.h47
-rw-r--r--src/glibext/comparable.c124
-rw-r--r--src/glibext/comparable.h46
-rw-r--r--src/glibext/comparison-int.h58
-rw-r--r--src/glibext/comparison.c199
-rw-r--r--src/glibext/comparison.h80
-rw-r--r--src/glibext/hashable-int.h47
-rw-r--r--src/glibext/hashable.c82
-rw-r--r--src/glibext/hashable.h42
-rw-r--r--src/glibext/helpers.h29
-rw-r--r--src/glibext/log-int.h58
-rw-r--r--src/glibext/log.c306
-rw-r--r--src/glibext/log.h43
-rw-r--r--src/glibext/objhole-int.h169
-rw-r--r--src/glibext/objhole.c256
-rw-r--r--src/glibext/objhole.h118
-rw-r--r--src/glibext/options/Makefile.am3
-rw-r--r--src/glibext/options/asm.h65
-rw-r--r--src/glibext/portion-int-ui.h (renamed from src/glibext/gbinportion-int.h)0
-rw-r--r--src/glibext/portion-int.h69
-rw-r--r--src/glibext/portion-ui.c (renamed from src/glibext/gbinportion.c)0
-rw-r--r--src/glibext/portion-ui.h (renamed from src/glibext/gbinportion.h)0
-rw-r--r--src/glibext/portion.c974
-rw-r--r--src/glibext/portion.h121
-rw-r--r--src/glibext/secstorage-int.h60
-rw-r--r--src/glibext/secstorage.c950
-rw-r--r--src/glibext/secstorage.h74
-rw-r--r--src/glibext/serialize-int.h54
-rw-r--r--src/glibext/serialize.c113
-rw-r--r--src/glibext/serialize.h52
-rw-r--r--src/glibext/sigredir.c (renamed from src/glibext/signal.c)159
-rw-r--r--src/glibext/sigredir.h (renamed from src/glibext/_signal.h)21
-rw-r--r--src/glibext/singleton-int.h47
-rw-r--r--src/glibext/singleton.c341
-rw-r--r--src/glibext/singleton.h53
-rw-r--r--src/glibext/storage-int.h75
-rw-r--r--src/glibext/storage.c1147
-rw-r--r--src/glibext/storage.h81
-rw-r--r--src/glibext/strbuilder-int.h47
-rw-r--r--src/glibext/strbuilder.c89
-rw-r--r--src/glibext/strbuilder.h46
-rw-r--r--src/glibext/tpmem-int.h78
-rw-r--r--src/glibext/tpmem.c363
-rw-r--r--src/glibext/tpmem.h57
-rw-r--r--src/glibext/widthtracker.c8
-rw-r--r--src/glibext/work-int.h31
-rw-r--r--src/glibext/work.c1213
-rw-r--r--src/glibext/work.h106
-rw-r--r--src/glibext/workgroup-int.h59
-rw-r--r--src/glibext/workgroup.c966
-rw-r--r--src/glibext/workgroup.h98
-rw-r--r--src/glibext/workqueue-int.h50
-rw-r--r--src/glibext/workqueue.c917
-rw-r--r--src/glibext/workqueue.h87
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 */