/* Chrysalide - Outil d'analyse de fichiers binaires
* loading.c - reconnaissance de contenus binaires
*
* Copyright (C) 2017-2019 Cyrille Bagard
*
* This file is part of Chrysalide.
*
* Chrysalide is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* Chrysalide is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Chrysalide. If not, see .
*/
#include "loading.h"
#include
#include
#include
#include "../core/global.h"
#include "../glibext/chrysamarshal.h"
#include "../glibext/delayed-int.h"
#include "../plugins/pglist.h"
/* ------------------------- TACHE D'EXPLORATION DE CONTENU ------------------------- */
#define G_TYPE_EXPLORING_WORK g_exploring_work_get_type()
#define G_EXPLORING_WORK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_EXPLORING_WORK, GExploringWork))
#define G_IS_EXPLORING_WORK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_EXPLORING_WORK))
#define G_EXPLORING_WORK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_EXPLORING_WORK, GExploringWorkClass))
#define G_IS_EXPLORING_WORK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_EXPLORING_WORK))
#define G_EXPLORING_WORK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_EXPLORING_WORK, GExploringWorkClass))
/* Conversion de contenu binaire en contenu chargé (instance) */
typedef struct _GExploringWork
{
GDelayedWork parent; /* A laisser en premier */
wgroup_id_t wid; /* Groupe d'appartenance */
#ifndef NDEBUG
bool wid_defined; /* Validation de l'identifiant */
#endif
GBinContent *content; /* Contenu brut à disposition */
} GExploringWork;
/* Conversion de contenu binaire en contenu chargé (classe) */
typedef struct _GExploringWorkClass
{
GDelayedWorkClass parent; /* A laisser en premier */
} GExploringWorkClass;
/* Indique le type défini pour l'exploration de contenu binaire. */
GType g_exploring_work_get_type(void);
/* Initialise la classe des tâches d'exploration de contenu. */
static void g_exploring_work_class_init(GExploringWorkClass *);
/* Initialise une tâche d'exploration de contenu. */
static void g_exploring_work_init(GExploringWork *);
/* Supprime toutes les références externes. */
static void g_exploring_work_dispose(GExploringWork *);
/* Procède à la libération totale de la mémoire. */
static void g_exploring_work_finalize(GExploringWork *);
/* Prépare la conversion non bloquée d'un contenu binaire. */
static GExploringWork *g_exploring_work_new(GBinContent *);
/* Fournit l'identifiant du groupe de rattachement de la tâche. */
static wgroup_id_t g_exploring_work_get_group_id(const GExploringWork *);
/* Définit l'identifiant du groupe de rattachement de la tâche. */
static void g_exploring_work_set_group_id(GExploringWork *, wgroup_id_t);
/* Réalise l'exploration effective de formes de contenus. */
static void g_exploring_work_process(GExploringWork *, GtkStatusStack *);
/* --------------------- EXPLORATION NON BLOQUANTE DES CONTENUS --------------------- */
/* Regroupement des chargements */
typedef struct _exploring_group
{
GBinContent *original; /* Contenu binaire initial */
size_t remaining; /* Nombre de tâches restantes */
wgroup_id_t wid; /* Groupe d'appartenance */
GBinContent **contents; /* Contenus reconnus dispos. */
size_t count; /* Taille de cette liste */
} exploring_group;
/* Exploration de contenus binaires (instance) */
struct _GContentExplorer
{
GObject parent; /* A laisser en premier */
exploring_group *groups; /* Rassemblement de chargements*/
size_t count; /* Nombre de ces groupes */
GMutex mutex; /* Accès protégé à la liste */
};
/* Exploration de contenus binaires (classe) */
struct _GContentExplorerClass
{
GObjectClass parent; /* A laisser en premier */
/* Signaux */
void (* explored) (GContentExplorer *, wgroup_id_t);
};
/* Initialise la classe les explorations de contenus binaires. */
static void g_content_explorer_class_init(GContentExplorerClass *);
/* Initialise une exploration de contenus binaires. */
static void g_content_explorer_init(GContentExplorer *);
/* Supprime toutes les références externes. */
static void g_content_explorer_dispose(GContentExplorer *);
/* Procède à la libération totale de la mémoire. */
static void g_content_explorer_finalize(GContentExplorer *);
/* Retrouve le groupe correspondant à un identifiant donné. */
static exploring_group *g_content_explorer_find_group(GContentExplorer *, wgroup_id_t);
/* Note la fin d'une phase d'exploration de contenu. */
static void g_content_explorer_ack(GContentExplorer *, GExploringWork *);
/* ------------------------- TACHE DE RESOLUTION DE CONTENU ------------------------- */
#define G_TYPE_RESOLVING_WORK g_resolving_work_get_type()
#define G_RESOLVING_WORK(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_RESOLVING_WORK, GResolvingWork))
#define G_IS_RESOLVING_WORK(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_RESOLVING_WORK))
#define G_RESOLVING_WORK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_RESOLVING_WORK, GResolvingWorkClass))
#define G_IS_RESOLVING_WORK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_RESOLVING_WORK))
#define G_RESOLVING_WORK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_RESOLVING_WORK, GResolvingWorkClass))
/* Conversion de contenu binaire en contenu chargé (instance) */
typedef struct _GResolvingWork
{
GDelayedWork parent; /* A laisser en premier */
wgroup_id_t wid; /* Groupe d'appartenance */
#ifndef NDEBUG
bool wid_defined; /* Validation de l'identifiant */
#endif
GBinContent *content; /* Contenu brut à disposition */
} GResolvingWork;
/* Conversion de contenu binaire en contenu chargé (classe) */
typedef struct _GResolvingWorkClass
{
GDelayedWorkClass parent; /* A laisser en premier */
} GResolvingWorkClass;
/* Indique le type défini pour la conversion de contenu binaire en contenu chargé. */
GType g_resolving_work_get_type(void);
/* Initialise la classe des tâches de conversion de contenu. */
static void g_resolving_work_class_init(GResolvingWorkClass *);
/* Initialise une tâche de conversion de contenu. */
static void g_resolving_work_init(GResolvingWork *);
/* Supprime toutes les références externes. */
static void g_resolving_work_dispose(GResolvingWork *);
/* Procède à la libération totale de la mémoire. */
static void g_resolving_work_finalize(GResolvingWork *);
/* Prépare la conversion non bloquée d'un contenu binaire. */
static GResolvingWork *g_resolving_work_new(GBinContent *);
/* Fournit l'identifiant du groupe de rattachement de la tâche. */
static wgroup_id_t g_resolving_work_get_group_id(const GResolvingWork *);
/* Définit l'identifiant du groupe de rattachement de la tâche. */
static void g_resolving_work_set_group_id(GResolvingWork *, wgroup_id_t);
/* Réalise la conversion effective de formes de contenus. */
static void g_resolving_work_process(GResolvingWork *, GtkStatusStack *);
/* ------------------- RESOLUTION DE CONTENUS BINAIRES EN CHARGES ------------------- */
/* Regroupement des chargements */
typedef struct _resolving_group
{
size_t remaining; /* Nombre de tâches restantes */
wgroup_id_t wid; /* Groupe d'appartenance */
GLoadedContent **loaded; /* Contenus reconnus à intégrer*/
size_t count; /* Taille de cette liste */
} resolving_group;
/* Résolution de contenus binaires en formats chargés (instance) */
struct _GContentResolver
{
GObject parent; /* A laisser en premier */
resolving_group *groups; /* Rassemblement de chargements*/
size_t count; /* Nombre de ces groupes */
GMutex mutex; /* Accès protégé à la liste */
};
/* Résolution de contenus binaires en formats chargés (classe) */
struct _GContentResolverClass
{
GObjectClass parent; /* A laisser en premier */
/* Signaux */
void (* resolved) (GContentResolver *, wgroup_id_t);
};
/* Initialise la classe des résolutions de contenus binaires. */
static void g_content_resolver_class_init(GContentResolverClass *);
/* Initialise une résolution de contenus binaires. */
static void g_content_resolver_init(GContentResolver *);
/* Supprime toutes les références externes. */
static void g_content_resolver_dispose(GContentResolver *);
/* Procède à la libération totale de la mémoire. */
static void g_content_resolver_finalize(GContentResolver *);
/* Retrouve le groupe correspondant à un identifiant donné. */
static resolving_group *g_content_resolver_find_group(GContentResolver *, wgroup_id_t);
/* Note la fin d'une phase de resolution de contenu. */
static void g_content_resolver_ack(GContentResolver *, GResolvingWork *);
/* ---------------------------------------------------------------------------------- */
/* TACHE D'EXPLORATION DE CONTENU */
/* ---------------------------------------------------------------------------------- */
/* Indique le type défini pour l'exploration de contenu binaire. */
G_DEFINE_TYPE(GExploringWork, g_exploring_work, G_TYPE_DELAYED_WORK);
/******************************************************************************
* *
* Paramètres : klass = classe à initialiser. *
* *
* Description : Initialise la classe des tâches d'exploration de contenu. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_exploring_work_class_init(GExploringWorkClass *klass)
{
GObjectClass *object; /* Autre version de la classe */
GDelayedWorkClass *work; /* Version en classe parente */
object = G_OBJECT_CLASS(klass);
object->dispose = (GObjectFinalizeFunc/* ! */)g_exploring_work_dispose;
object->finalize = (GObjectFinalizeFunc)g_exploring_work_finalize;
work = G_DELAYED_WORK_CLASS(klass);
work->run = (run_task_fc)g_exploring_work_process;
}
/******************************************************************************
* *
* Paramètres : work = instance à initialiser. *
* *
* Description : Initialise une tâche d'exploration de contenu. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_exploring_work_init(GExploringWork *work)
{
#ifndef NDEBUG
work->wid_defined = false;
#endif
work->content = NULL;
}
/******************************************************************************
* *
* Paramètres : work = instance d'objet GLib à traiter. *
* *
* Description : Supprime toutes les références externes. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_exploring_work_dispose(GExploringWork *work)
{
g_clear_object(&work->content);
G_OBJECT_CLASS(g_exploring_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_exploring_work_finalize(GExploringWork *work)
{
G_OBJECT_CLASS(g_exploring_work_parent_class)->finalize(G_OBJECT(work));
}
/******************************************************************************
* *
* Paramètres : content = contenu binaire disponible pour traitements. *
* *
* Description : Prépare l'exploration non bloquée d'un contenu binaire. *
* *
* Retour : Tâche de travail mise en place. *
* *
* Remarques : - *
* *
******************************************************************************/
static GExploringWork *g_exploring_work_new(GBinContent *content)
{
GExploringWork *result; /* Tâche à retourner */
result = g_object_new(G_TYPE_EXPLORING_WORK, NULL);
result->content = content;
g_object_ref(G_OBJECT(content));
return result;
}
/******************************************************************************
* *
* Paramètres : work = instance à consulter. *
* *
* Description : Fournit l'identifiant du groupe de rattachement de la tâche. *
* *
* Retour : Identifiant d'un même ensemble d'explorations. *
* *
* Remarques : - *
* *
******************************************************************************/
static wgroup_id_t g_exploring_work_get_group_id(const GExploringWork *work)
{
wgroup_id_t result; /* Identifiant à retourner */
assert(work->wid_defined);
result = work->wid;
return result;
}
/******************************************************************************
* *
* Paramètres : work = instance à compléter. *
* wid = identifiant d'un même ensemble d'explorations. *
* *
* Description : Définit l'identifiant du groupe de rattachement de la tâche. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_exploring_work_set_group_id(GExploringWork *work, wgroup_id_t wid)
{
#ifndef NDEBUG
work->wid_defined = true;
#endif
work->wid = wid;
}
/******************************************************************************
* *
* Paramètres : work = encadrement de conversion à mener. *
* status = barre de statut à tenir informée. *
* *
* Description : Réalise l'exploration effective de formes de contenus. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_exploring_work_process(GExploringWork *work, GtkStatusStack *status)
{
wgroup_id_t wid; /* Groupe d'appartenance */
wid = g_exploring_work_get_group_id(work);
handle_binary_content(PGA_CONTENT_EXPLORER, work->content, wid, status);
}
/* ---------------------------------------------------------------------------------- */
/* EXPLORATION NON BLOQUANTE DES CONTENUS */
/* ---------------------------------------------------------------------------------- */
/* Indique le type défini pour l'exploration de contenus binaires. */
G_DEFINE_TYPE(GContentExplorer, g_content_explorer, G_TYPE_OBJECT);
/******************************************************************************
* *
* Paramètres : klass = classe à initialiser. *
* *
* Description : Initialise la classe les explorations de contenus binaires. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_content_explorer_class_init(GContentExplorerClass *klass)
{
GObjectClass *object; /* Autre version de la classe */
object = G_OBJECT_CLASS(klass);
object->dispose = (GObjectFinalizeFunc/* ! */)g_content_explorer_dispose;
object->finalize = (GObjectFinalizeFunc)g_content_explorer_finalize;
g_signal_new("explored",
G_TYPE_CONTENT_EXPLORER,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GContentExplorerClass, explored),
NULL, NULL,
g_cclosure_user_marshal_VOID__UINT64,
G_TYPE_NONE, 1, G_TYPE_UINT64);
}
/******************************************************************************
* *
* Paramètres : explorer = instance à initialiser. *
* *
* Description : Initialise une exploration de contenus binaires. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_content_explorer_init(GContentExplorer *explorer)
{
explorer->groups = NULL;
explorer->count = 0;
g_mutex_init(&explorer->mutex);
}
/******************************************************************************
* *
* Paramètres : explorer = instance d'objet GLib à traiter. *
* *
* Description : Supprime toutes les références externes. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_content_explorer_dispose(GContentExplorer *explorer)
{
while (explorer->count > 0)
g_content_explorer_delete_group(explorer, explorer->groups[0].wid);
if (explorer->groups != NULL)
free(explorer->groups);
g_mutex_clear(&explorer->mutex);
G_OBJECT_CLASS(g_content_explorer_parent_class)->dispose(G_OBJECT(explorer));
}
/******************************************************************************
* *
* Paramètres : explorer = instance d'objet GLib à traiter. *
* *
* Description : Procède à la libération totale de la mémoire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_content_explorer_finalize(GContentExplorer *explorer)
{
G_OBJECT_CLASS(g_content_explorer_parent_class)->finalize(G_OBJECT(explorer));
}
/******************************************************************************
* *
* Paramètres : - *
* *
* Description : Crée un gestionnaire des explorations de contenus binaires. *
* *
* Retour : Instance mise en place. *
* *
* Remarques : - *
* *
******************************************************************************/
GContentExplorer *g_content_explorer_new(void)
{
GContentExplorer *result; /* Tâche à retourner */
result = g_object_new(G_TYPE_CONTENT_EXPLORER, NULL);
return result;
}
/******************************************************************************
* *
* Paramètres : explorer = gestionnaire d'explorations à consulter. *
* wid = identifiant du groupe recherché. *
* *
* Description : Retrouve le groupe correspondant à un identifiant donné. *
* *
* Retour : Groupe trouvé ou NULL en cas d'échec. *
* *
* Remarques : - *
* *
******************************************************************************/
static exploring_group *g_content_explorer_find_group(GContentExplorer *explorer, wgroup_id_t wid)
{
exploring_group *result; /* Trouvaille à retourner */
size_t i; /* Boucle de parcours */
assert(!g_mutex_trylock(&explorer->mutex));
result = NULL;
for (i = 0; i < explorer->count && result == NULL; i++)
if (explorer->groups[i].wid == wid)
result = &explorer->groups[i];
return result;
}
/******************************************************************************
* *
* Paramètres : explorer = gestionnaire d'explorations à consulter. *
* work = exploration qui vient de se terminer. *
* *
* Description : Note la fin d'une phase d'exploration de contenu. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_content_explorer_ack(GContentExplorer *explorer, GExploringWork *work)
{
wgroup_id_t wid; /* Groupe d'appartenance */
exploring_group *group; /* Groupe d'opération concerné */
bool empty; /* Fin de l'exploration ? */
wid = g_exploring_work_get_group_id(work);
g_mutex_lock(&explorer->mutex);
group = g_content_explorer_find_group(explorer, wid);
assert(group != NULL);
assert(group->remaining > 0);
empty = (--group->remaining == 0);
g_mutex_unlock(&explorer->mutex);
if (empty)
g_signal_emit_by_name(explorer, "explored", wid);
}
/******************************************************************************
* *
* Paramètres : explorer = gestionnaire d'explorations à manipuler. *
* content = contenu initial à découvrir. *
* *
* Description : Initie une nouvelle vague d'exploration de contenu. *
* *
* Retour : Identifiant du nouveau groupe mis en place. *
* *
* Remarques : - *
* *
******************************************************************************/
wgroup_id_t g_content_explorer_create_group(GContentExplorer *explorer, GBinContent *content)
{
wgroup_id_t result; /* Identifiant à retourner */
GWorkQueue *queue; /* Gestionnaire de différés */
exploring_group *group; /* Groupe ciblé par l'opération*/
GExploringWork *work; /* Nouvelle vague d'exploration*/
g_mutex_lock(&explorer->mutex);
/* Récupération d'un identifiant libre */
queue = get_work_queue();
result = g_work_queue_define_work_group(queue);
#ifndef NDEBUG
group = g_content_explorer_find_group(explorer, result);
assert(group == NULL);
#endif
/* Mise en place du groupe */
explorer->groups = (exploring_group *)realloc(explorer->groups, ++explorer->count * sizeof(exploring_group));
group = &explorer->groups[explorer->count - 1];
group->original = content;
g_object_ref(G_OBJECT(content));
group->remaining = 1;
group->wid = result;
group->contents = NULL;
group->count = 0;
/* Alimentation du contenu initial */
work = g_exploring_work_new(content);
g_exploring_work_set_group_id(work, result);
g_signal_connect_swapped(work, "work-completed", G_CALLBACK(g_content_explorer_ack), explorer);
g_work_queue_schedule_work(queue, G_DELAYED_WORK(work), result);
g_mutex_unlock(&explorer->mutex);
return result;
}
/******************************************************************************
* *
* Paramètres : explorer = gestionnaire d'explorations à manipuler. *
* wid = identifiant du groupe à supprimer. *
* *
* Description : Termine une vague d'exploration de contenu. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_content_explorer_delete_group(GContentExplorer *explorer, wgroup_id_t wid)
{
exploring_group *group; /* Groupe ciblé par l'opération*/
GWorkQueue *queue; /* Gestionnaire de différés */
size_t i; /* Boucle de parcours */
size_t index; /* Indice des paramètres */
g_mutex_lock(&explorer->mutex);
group = g_content_explorer_find_group(explorer, wid);
assert(group != NULL);
/* Supression des contenus chargés */
queue = get_work_queue();
g_work_queue_delete_work_group(queue, group->wid);
g_object_unref(G_OBJECT(group->original));
for (i = 0; i < group->count; i++)
g_object_unref(G_OBJECT(group->contents[i]));
if (group->contents != NULL)
free(group->contents);
/* Réorganisation de la liste */
index = group - explorer->groups;
if ((index + 1) < explorer->count)
memmove(&explorer->groups[index], &explorer->groups[index + 1],
(explorer->count - index - 1) * sizeof(exploring_group));
explorer->groups = realloc(explorer->groups, --explorer->count * sizeof(exploring_group));
/* Sortie */
g_mutex_unlock(&explorer->mutex);
}
/******************************************************************************
* *
* Paramètres : explorer = gestionnaire d'explorations à consulter. *
* wid = identifiant du groupe à parcourir. *
* content = nouveau contenu à intégrer. *
* *
* Description : Ajoute un nouveau contenu découvert au crédit d'un groupe. *
* *
* Retour : - *
* *
* Remarques : La propritété du contenu fourni est cédée. *
* *
******************************************************************************/
void g_content_explorer_populate_group(GContentExplorer *explorer, wgroup_id_t wid, GBinContent *content)
{
exploring_group *group; /* Groupe d'opération concerné */
GWorkQueue *queue; /* Gestionnaire de différés */
GExploringWork *work; /* Nouvelle vague d'exploration*/
g_mutex_lock(&explorer->mutex);
group = g_content_explorer_find_group(explorer, wid);
assert(group != NULL);
/* Conservation du résultat */
group->contents = realloc(group->contents, ++group->count * sizeof(GBinContent *));
group->contents[group->count - 1] = content;
g_object_ref_sink(G_OBJECT(content));
/* Relancement des explorations */
group->remaining++;
work = g_exploring_work_new(content);
g_exploring_work_set_group_id(work, group->wid);
g_signal_connect_swapped(work, "work-completed", G_CALLBACK(g_content_explorer_ack), explorer);
queue = get_work_queue();
g_work_queue_schedule_work(queue, G_DELAYED_WORK(work), group->wid);
g_mutex_unlock(&explorer->mutex);
}
/******************************************************************************
* *
* Paramètres : explorer = gestionnaire d'explorations à consulter. *
* wid = identifiant du groupe à parcourir. *
* count = nombre de contenus binaires retournés. [OUT] *
* *
* Description : Fournit la liste de tous les contenus disponibles. *
* *
* Retour : Liste de contenus binaires enregistrés. *
* *
* Remarques : - *
* *
******************************************************************************/
GBinContent **g_content_explorer_get_all(GContentExplorer *explorer, wgroup_id_t wid, size_t *count)
{
GBinContent **result; /* Trouvailles à retourner */
exploring_group *group; /* Groupe d'opération concerné */
size_t i; /* Boucle de parcours */
g_mutex_lock(&explorer->mutex);
group = g_content_explorer_find_group(explorer, wid);
assert(group != NULL);
/* Allocation de la liste finale */
*count = 1 + group->count;
result = malloc(*count * sizeof(GBinContent *));
/* On regarde déjà du côté de la source */
result[0] = group->original;
g_object_ref(G_OBJECT(result[0]));
/* On parcourt les éventuels contenus encapsulés découverts */
for (i = 0; i < group->count; i++)
{
result[1 + i] = group->contents[i];
g_object_ref(G_OBJECT(result[1 + i]));
}
g_mutex_unlock(&explorer->mutex);
return result;
}
/* ---------------------------------------------------------------------------------- */
/* TACHE DE RESOLUTION DE CONTENU */
/* ---------------------------------------------------------------------------------- */
/* Indique le type défini pour la conversion de contenu binaire en contenu chargé. */
G_DEFINE_TYPE(GResolvingWork, g_resolving_work, G_TYPE_DELAYED_WORK);
/******************************************************************************
* *
* Paramètres : klass = classe à initialiser. *
* *
* Description : Initialise la classe des tâches de conversion de contenu. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_resolving_work_class_init(GResolvingWorkClass *klass)
{
GObjectClass *object; /* Autre version de la classe */
GDelayedWorkClass *work; /* Version en classe parente */
object = G_OBJECT_CLASS(klass);
object->dispose = (GObjectFinalizeFunc/* ! */)g_resolving_work_dispose;
object->finalize = (GObjectFinalizeFunc)g_resolving_work_finalize;
work = G_DELAYED_WORK_CLASS(klass);
work->run = (run_task_fc)g_resolving_work_process;
}
/******************************************************************************
* *
* Paramètres : work = instance à initialiser. *
* *
* Description : Initialise une tâche de conversion de contenu. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_resolving_work_init(GResolvingWork *work)
{
#ifndef NDEBUG
work->wid_defined = false;
#endif
work->content = NULL;
}
/******************************************************************************
* *
* Paramètres : work = instance d'objet GLib à traiter. *
* *
* Description : Supprime toutes les références externes. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_resolving_work_dispose(GResolvingWork *work)
{
g_clear_object(&work->content);
G_OBJECT_CLASS(g_resolving_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_resolving_work_finalize(GResolvingWork *work)
{
G_OBJECT_CLASS(g_resolving_work_parent_class)->finalize(G_OBJECT(work));
}
/******************************************************************************
* *
* Paramètres : content = contenu binaire disponible pour traitements. *
* *
* Description : Prépare la conversion non bloquée d'un contenu binaire. *
* *
* Retour : Tâche de travail mise en place. *
* *
* Remarques : - *
* *
******************************************************************************/
static GResolvingWork *g_resolving_work_new(GBinContent *content)
{
GResolvingWork *result; /* Tâche à retourner */
result = g_object_new(G_TYPE_RESOLVING_WORK, NULL);
result->content = content;
g_object_ref(G_OBJECT(content));
return result;
}
/******************************************************************************
* *
* Paramètres : work = instance à consulter. *
* *
* Description : Fournit l'identifiant du groupe de rattachement de la tâche. *
* *
* Retour : Identifiant d'un même ensemble de conversions. *
* *
* Remarques : - *
* *
******************************************************************************/
static wgroup_id_t g_resolving_work_get_group_id(const GResolvingWork *work)
{
wgroup_id_t result; /* Identifiant à retourner */
assert(work->wid_defined);
result = work->wid;
return result;
}
/******************************************************************************
* *
* Paramètres : work = instance à compléter. *
* wid = identifiant d'un même ensemble de conversions. *
* *
* Description : Définit l'identifiant du groupe de rattachement de la tâche. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_resolving_work_set_group_id(GResolvingWork *work, wgroup_id_t wid)
{
#ifndef NDEBUG
work->wid_defined = true;
#endif
work->wid = wid;
}
/******************************************************************************
* *
* Paramètres : work = encadrement de conversion à mener. *
* status = barre de statut à tenir informée. *
* *
* Description : Réalise la conversion effective de formes de contenus. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_resolving_work_process(GResolvingWork *work, GtkStatusStack *status)
{
wgroup_id_t wid; /* Groupe d'appartenance */
wid = g_resolving_work_get_group_id(work);
handle_binary_content(PGA_CONTENT_RESOLVER, work->content, wid, status);
}
/* ---------------------------------------------------------------------------------- */
/* RESOLUTION DE CONTENUS BINAIRES EN CHARGES */
/* ---------------------------------------------------------------------------------- */
/* Indique le type défini pour la résolution de contenus binaires en formats chargés. */
G_DEFINE_TYPE(GContentResolver, g_content_resolver, G_TYPE_OBJECT);
/******************************************************************************
* *
* Paramètres : klass = classe à initialiser. *
* *
* Description : Initialise la classe des résolutions de contenus binaires. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_content_resolver_class_init(GContentResolverClass *klass)
{
GObjectClass *object; /* Autre version de la classe */
object = G_OBJECT_CLASS(klass);
object->dispose = (GObjectFinalizeFunc/* ! */)g_content_resolver_dispose;
object->finalize = (GObjectFinalizeFunc)g_content_resolver_finalize;
g_signal_new("resolved",
G_TYPE_CONTENT_RESOLVER,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(GContentResolverClass, resolved),
NULL, NULL,
g_cclosure_user_marshal_VOID__UINT64,
G_TYPE_NONE, 1, G_TYPE_UINT64);
}
/******************************************************************************
* *
* Paramètres : resolver = instance à initialiser. *
* *
* Description : Initialise une résolution de contenus binaires. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_content_resolver_init(GContentResolver *resolver)
{
}
/******************************************************************************
* *
* Paramètres : resolver = instance d'objet GLib à traiter. *
* *
* Description : Supprime toutes les références externes. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_content_resolver_dispose(GContentResolver *resolver)
{
while (resolver->count > 0)
g_content_resolver_delete_group(resolver, resolver->groups[0].wid);
if (resolver->groups != NULL)
free(resolver->groups);
g_mutex_clear(&resolver->mutex);
G_OBJECT_CLASS(g_content_resolver_parent_class)->dispose(G_OBJECT(resolver));
}
/******************************************************************************
* *
* Paramètres : resolver = instance d'objet GLib à traiter. *
* *
* Description : Procède à la libération totale de la mémoire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_content_resolver_finalize(GContentResolver *resolver)
{
G_OBJECT_CLASS(g_content_resolver_parent_class)->finalize(G_OBJECT(resolver));
}
/******************************************************************************
* *
* Paramètres : - *
* *
* Description : Crée un gestionnaire des résolutions de contenus binaires. *
* *
* Retour : Instance mise en place. *
* *
* Remarques : - *
* *
******************************************************************************/
GContentResolver *g_content_resolver_new(void)
{
GContentResolver *result; /* Tâche à retourner */
result = g_object_new(G_TYPE_CONTENT_RESOLVER, NULL);
return result;
}
/******************************************************************************
* *
* Paramètres : resolver = gestionnaire de résolutions à consulter. *
* wid = identifiant du groupe recherché. *
* *
* Description : Retrouve le groupe correspondant à un identifiant donné. *
* *
* Retour : Groupe trouvé ou NULL en cas d'échec. *
* *
* Remarques : - *
* *
******************************************************************************/
static resolving_group *g_content_resolver_find_group(GContentResolver *resolver, wgroup_id_t wid)
{
resolving_group *result; /* Trouvaille à retourner */
size_t i; /* Boucle de parcours */
assert(!g_mutex_trylock(&resolver->mutex));
result = NULL;
for (i = 0; i < resolver->count && result == NULL; i++)
if (resolver->groups[i].wid == wid)
result = &resolver->groups[i];
return result;
}
/******************************************************************************
* *
* Paramètres : resolver = gestionnaire de résolutions à consulter. *
* work = resolvation qui vient de se terminer. *
* *
* Description : Note la fin d'une phase de resolution de contenu. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_content_resolver_ack(GContentResolver *resolver, GResolvingWork *work)
{
wgroup_id_t wid; /* Groupe d'appartenance */
resolving_group *group; /* Groupe d'opération concerné */
bool empty; /* Fin de l'resolvation ? */
wid = g_resolving_work_get_group_id(work);
g_mutex_lock(&resolver->mutex);
group = g_content_resolver_find_group(resolver, wid);
assert(group != NULL);
assert(group->remaining > 0);
empty = (--group->remaining == 0);
g_mutex_unlock(&resolver->mutex);
if (empty)
g_signal_emit_by_name(resolver, "resolved", wid);
}
/******************************************************************************
* *
* Paramètres : resolver = gestionnaire de résolutions à manipuler. *
* wid = identifiant du groupe de tâches réservé. *
* contents = contenus à analyser. *
* count = nombre de ces contenus. *
* *
* Description : Initie une nouvelle vague de résolution de contenus. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_content_resolver_create_group(GContentResolver *resolver, wgroup_id_t wid, GBinContent **contents, size_t count)
{
resolving_group *group; /* Groupe ciblé par l'opération*/
GWorkQueue *queue; /* Gestionnaire de différés */
size_t i; /* Boucle de parcours */
GResolvingWork *work; /* Nouvelle vague de résolution*/
g_mutex_lock(&resolver->mutex);
/* Mise en place du groupe */
resolver->groups = (resolving_group *)realloc(resolver->groups, ++resolver->count * sizeof(resolving_group));
group = &resolver->groups[resolver->count - 1];
group->remaining = count;
group->wid = wid;
group->loaded = NULL;
group->count = 0;
/* Alimentation du contenu initial */
queue = get_work_queue();
for (i = 0; i < count; i++)
{
work = g_resolving_work_new(contents[i]);
g_resolving_work_set_group_id(work, wid);
g_signal_connect_swapped(work, "work-completed", G_CALLBACK(g_content_resolver_ack), resolver);
g_work_queue_schedule_work(queue, G_DELAYED_WORK(work), wid);
}
g_mutex_unlock(&resolver->mutex);
if (count == 0)
g_signal_emit_by_name(resolver, "resolved", wid);
}
/******************************************************************************
* *
* Paramètres : resolver = gestionnaire d'explorations à manipuler. *
* wid = identifiant du groupe à supprimer. *
* *
* Description : Termine une vague de résolution de contenu. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_content_resolver_delete_group(GContentResolver *resolver, wgroup_id_t wid)
{
resolving_group *group; /* Groupe ciblé par l'opération*/
size_t i; /* Boucle de parcours */
size_t index; /* Indice des paramètres */
g_mutex_lock(&resolver->mutex);
group = g_content_resolver_find_group(resolver, wid);
/* Supression des contenus chargés */
for (i = 0; i < group->count; i++)
g_object_unref(G_OBJECT(group->loaded[i]));
if (group->loaded != NULL)
free(group->loaded);
/* Réorganisation de la liste */
index = group - resolver->groups;
if ((index + 1) < resolver->count)
memmove(&resolver->groups[index], &resolver->groups[index + 1],
(resolver->count - index - 1) * sizeof(resolving_group));
resolver->groups = realloc(resolver->groups, --resolver->count * sizeof(resolving_group));
/* Sortie */
g_mutex_unlock(&resolver->mutex);
}
/******************************************************************************
* *
* Paramètres : resolver = gestionnaire de résolutions à consulter. *
* wid = identifiant du groupe recherché. *
* loaded = contenu chargé et pouvant être représenté. *
* *
* Description : Intègre un contenu chargé dans les résultats. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_content_resolver_add_detected(GContentResolver *resolver, wgroup_id_t wid, GLoadedContent *loaded)
{
resolving_group *group; /* Groupe ciblé par l'opération*/
g_mutex_lock(&resolver->mutex);
group = g_content_resolver_find_group(resolver, wid);
assert(group != NULL);
group->loaded = realloc(group->loaded, ++group->count * sizeof(GLoadedContent *));
group->loaded[group->count - 1] = loaded;
g_object_ref(G_OBJECT(loaded));
g_mutex_unlock(&resolver->mutex);
}
/******************************************************************************
* *
* Paramètres : resolver = gestionnaire de resolutions à consulter. *
* wid = identifiant du groupe à parcourir. *
* count = nombre de contenus binaires retournés. [OUT] *
* *
* Description : Fournit la liste de tous les contenus chargés valables. *
* *
* Retour : Liste de contenus chargés enregistrés. *
* *
* Remarques : - *
* *
******************************************************************************/
GLoadedContent **g_content_resolver_get_all(GContentResolver *resolver, wgroup_id_t wid, size_t *count)
{
GLoadedContent **result; /* Trouvailles à retourner */
resolving_group *group; /* Groupe d'opération concerné */
size_t i; /* Boucle de parcours */
g_mutex_lock(&resolver->mutex);
group = g_content_resolver_find_group(resolver, wid);
assert(group != NULL);
/* Allocation de la liste finale */
*count = group->count;
result = malloc(*count * sizeof(GLoadedContent *));
/* On parcourt les éventuels contenus encapsulés découverts */
for (i = 0; i < group->count; i++)
{
result[i] = group->loaded[i];
g_object_ref(G_OBJECT(result[i]));
}
g_mutex_unlock(&resolver->mutex);
return result;
}