/* Chrysalide - Outil d'analyse de fichiers binaires
 * instriter.h - prototypes pour le parcours simplifié d'un ensemble d'instructions
 *
 * Copyright (C) 2016-2017 Cyrille Bagard
 *
 *  This file is part of Chrysalide.
 *
 *  Chrysalide is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Chrysalide is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Foobar.  If not, see .
 */
#include "instriter.h"
#include 
#include "processor.h"
/* Suivi d'un parcours d'instructions */
typedef struct _instr_iter_t
{
    GArchProcessor *proc;                   /* Conteneur associé           */
    unsigned int stamp;                     /* Suivi d'évolutions externes */
    size_t index;                           /* Instruction courante        */
    mrange_t restriction;                   /* Enventuelle limite de zone  */
    bool is_restricted;                     /* Validité de l'étendue       */
} instr_iter_t;
/******************************************************************************
*                                                                             *
*  Paramètres  : proc  = processeur recensant diverses instructions.          *
*                index = indice de la première instruction à fournir.         *
*                                                                             *
*  Description : Construit un itérateur pour parcourir des instructions.      *
*                                                                             *
*  Retour      : Itérateur prêt à emploi.                                     *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
instr_iter_t *create_instruction_iterator(GArchProcessor *proc, size_t index)
{
    instr_iter_t *result;                   /* Structure à retourner       */
    result = (instr_iter_t *)malloc(sizeof(instr_iter_t));
    g_object_ref(G_OBJECT(proc));
    result->proc = proc;
    result->stamp = g_arch_processor_get_stamp(proc);
    result->index = index;
    result->is_restricted = false;
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : iter = itérateur à traiter.                                  *
*                                                                             *
*  Description : Détruit un itérateur mis en place.                           *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
void delete_instruction_iterator(instr_iter_t *iter)
{
    g_object_unref(G_OBJECT(iter->proc));
    free(iter);
}
/******************************************************************************
*                                                                             *
*  Paramètres  : iter  = itérateur à traiter.                                 *
*                range = bornes de l'espace de parcours.                      *
*                                                                             *
*  Description : Limite le parcours des instructions à une zone donnée.       *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
void restrict_instruction_iterator(instr_iter_t *iter, const mrange_t *range)
{
    copy_mrange(&iter->restriction, range);
    iter->is_restricted = true;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : iter = itérateur à manipuler.                                *
*                                                                             *
*  Description : Fournit l'instruction courante de l'itérateur.               *
*                                                                             *
*  Retour      : Instruction suivante trouvée, ou NULL.                       *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
GArchInstruction *get_instruction_iterator_current(instr_iter_t *iter)
{
    GArchInstruction *result;               /* Résultat à retourner        */
    const mrange_t *irange;                 /* Emplacement d'instruction   */
    g_arch_processor_lock(iter->proc);
    if (iter->stamp != g_arch_processor_get_stamp(iter->proc))
        result = NULL;
    else
    {
        if (iter->index < g_arch_processor_count_instructions(iter->proc))
        {
            result = g_arch_processor_get_instruction(iter->proc, iter->index);
            /* L'instruction sort-elle des clous ? */
            if (iter->is_restricted)
            {
                irange = g_arch_instruction_get_range(result);
                if (!mrange_contains_mrange(&iter->restriction, irange))
                {
                    g_object_unref(G_OBJECT(result));
                    result = NULL;
                }
            }
        }
        else
            result = NULL;
    }
    g_arch_processor_unlock(iter->proc);
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : iter = itérateur à manipuler.                                *
*                                                                             *
*  Description : Fournit l'instruction qui en précède une autre.              *
*                                                                             *
*  Retour      : Instruction suivante trouvée, ou NULL.                       *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
GArchInstruction *get_instruction_iterator_prev(instr_iter_t *iter)
{
    GArchInstruction *result;               /* Résultat à retourner        */
    const mrange_t *irange;                 /* Emplacement d'instruction   */
    g_arch_processor_lock(iter->proc);
    if (iter->stamp != g_arch_processor_get_stamp(iter->proc))
        result = NULL;
    else
    {
        if (iter->index > 1)
        {
            iter->index--;
            result = g_arch_processor_get_instruction(iter->proc, iter->index);
            /* L'instruction sort-elle des clous ? */
            if (iter->is_restricted)
            {
                irange = g_arch_instruction_get_range(result);
                if (!mrange_contains_mrange(&iter->restriction, irange))
                {
                    g_object_unref(G_OBJECT(result));
                    result = NULL;
                }
            }
        }
        else
            result = NULL;
    }
    g_arch_processor_unlock(iter->proc);
    return result;
}
/******************************************************************************
*                                                                             *
*  Paramètres  : iter = itérateur à manipuler.                                *
*                                                                             *
*  Description : Fournit l'instruction qui en suit une autre.                 *
*                                                                             *
*  Retour      : Instruction suivante trouvée, ou NULL.                       *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
GArchInstruction *get_instruction_iterator_next(instr_iter_t *iter)
{
    GArchInstruction *result;               /* Résultat à retourner        */
    const mrange_t *irange;                 /* Emplacement d'instruction   */
    g_arch_processor_lock(iter->proc);
    if (iter->stamp != g_arch_processor_get_stamp(iter->proc))
        result = NULL;
    else
    {
        if ((iter->index + 1) < g_arch_processor_count_instructions(iter->proc))
        {
            iter->index++;
            result = g_arch_processor_get_instruction(iter->proc, iter->index);
            /* L'instruction sort-elle des clous ? */
            if (iter->is_restricted)
            {
                irange = g_arch_instruction_get_range(result);
                if (!mrange_contains_mrange(&iter->restriction, irange))
                {
                    g_object_unref(G_OBJECT(result));
                    result = NULL;
                }
            }
        }
        else
            result = NULL;
    }
    g_arch_processor_unlock(iter->proc);
    return result;
}