/* Chrysalide - Outil d'analyse de fichiers binaires
 * context.c - contexte de décodage à la sauce ABI C++ Itanium
 *
 * Copyright (C) 2013-2017 Cyrille Bagard
 *
 *  This file is part of Chrysalide.
 *
 *  Chrysalide is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Chrysalide is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Chrysalide.  If not, see <http://www.gnu.org/licenses/>.
 */


#include "context.h"


#include <malloc.h>
#include <string.h>


#include "abi.h"
#include "component-int.h"
#include "../context-int.h"



/* Taille des extensions d'allocation */
#define ITCOMP_ALLOC_BULK 200

/* Marqueur de fin pour les disponibilités */
#define ITCOMP_INVALID ((size_t)-1)


/* Contexte de décodage Itanium (instance) */
struct _GItaniumDContext
{
    GDemanglingContext parent;              /* A laisser en premier        */

    char *mangled;                          /* Caractères à traiter        */
    size_t len;                             /* Quantité de caractères      */
    size_t pos;                             /* Position d'analyse          */

    itanium_component *components;          /* Tableaux des composants     */
    size_t *next_links;                     /* Chemins d'allocation        */
    size_t length;                          /* Taille allouée              */
    size_t last_used;                       /* Lien vers la disponibilité  */

    itanium_component **substitutions;      /* Table de substitutions      */
    size_t subst_count;                     /* Quantité utilisée           */

};

/* Contexte de décodage Itanium (classe) */
struct _GItaniumDContextClass
{
    GDemanglingContextClass parent;         /* A laisser en premier        */

};



/* Indique le type défini pour un contexte de décodage. */
G_DEFINE_TYPE(GItaniumDContext, g_itanium_dcontext, G_TYPE_DEMANGLING_CONTEXT);


/******************************************************************************
*                                                                             *
*  Paramètres  : klass = classe à initialiser.                                *
*                                                                             *
*  Description : Initialise la classe des contextes de décodage.              *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_itanium_dcontext_class_init(GItaniumDContextClass *klass)
{

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = instance à initialiser.                            *
*                                                                             *
*  Description : Initialise une instance de contexte pour décodage.           *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void g_itanium_dcontext_init(GItaniumDContext *context)
{
    context->last_used = ITCOMP_INVALID;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : -                                                            *
*                                                                             *
*  Description : Prépare de quoi effectuer un décodage Itanium.               *
*                                                                             *
*  Retour      : Instance du contexte mis en place.                           *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

GDemanglingContext *g_itanium_dcontext_new(void)
{
    GDemanglingContext *result;             /* Structure à retourner       */

    result = g_object_new(G_TYPE_ITANIUM_DCONTEXT, NULL);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage sur lequel s'appuyer.         *
*                desc    = chaîne de caractères à décoder.                    *
*                                                                             *
*  Description : Tente de décoder une chaîne de caractères donnée.            *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void /*GBinRoutine **/g_itanium_dcontext_demangle_routine(GItaniumDContext *context, const char *desc)
{
    itanium_component *comp;

    char *str;

    printf("<<== %s\n", desc);

    context->mangled = strdup(desc);
    context->len = strlen(desc);
    context->pos = 0;


    /**
     * On part du principe qu'il n'y aura jamais plus de substitutions
     * à enregistrer que de caractères dans la chaîne à traiter.
     * Du coup, on peut tout allouer d'un coup !
     */
    context->substitutions = (itanium_component **)calloc(context->len, sizeof(itanium_component *));


    comp = itd_mangled_name(context);


    printf("Got :: %p\n", comp);

    str = itd_translate_component(context, comp, NULL);

    printf("==>> %s\n", str);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à manipuler.                  *
*                state   = état courant à sauvegarder. [OUT]                  *
*                                                                             *
*  Description : Fournit l'état courant à une fin de retour en arrière.       *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void g_itanium_dcontext_push_state(const GItaniumDContext *context, itd_state *state)
{
    state->pos = context->pos;
    state->subst_count = context->subst_count;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à manipuler.                  *
*                state   = état courant à restaurer.                          *
*                                                                             *
*  Description : Définit l'état courant suite à un retour en arrière.         *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void g_itanium_dcontext_pop_state(GItaniumDContext *context, const itd_state *state)
{
    size_t i;                               /* Boucle de parcours          */

    /*
    printf("--deleting subst-- from %zu to %zu\n",
           state->subst_count, context->subst_count);
    */

    for (i = state->subst_count; i < context->subst_count; i++)
        itd_unref_comp(context->substitutions[i]);

    context->pos = state->pos;
    context->subst_count = state->subst_count;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                                                                             *
*  Description : Fournit la valeur du caractère courant.                      *
*                                                                             *
*  Retour      : Caractère courant.                                           *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

char g_itanium_dcontext_peek_char(const GItaniumDContext *context)
{
    return *(context->mangled + context->pos);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context  = contexte de décodage à utiliser.                  *
*                quantity = quantié de caractères à marquer comme traités.    *
*                                                                             *
*  Description : Avance la tête de lecture courante.                          *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void g_itanium_dcontext_advance(GItaniumDContext *context, size_t quantity)
{
    context->pos += quantity;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                                                                             *
*  Description : Fournit et avance la tête de lecture courante.               *
*                                                                             *
*  Retour      : Caractère courant.                                           *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

char g_itanium_dcontext_next_char(GItaniumDContext *context)
{
    return *(context->mangled + context->pos++);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à utiliser.                   *
*                c       = caractère à retrouver.                             *
*                                                                             *
*  Description : Vérifie la nature du caractère courant.                      *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool g_itanium_dcontext_check_char(GItaniumDContext *context, char c)
{
    bool result;                            /* Validation à retourner      */

    if (g_itanium_dcontext_peek_char(context) == c)
    {
        result = true;
        g_itanium_dcontext_advance(context, 1);
    }
    else
        result = false;

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context   = contexte de décodage à utiliser.                 *
*                remaining = taille de la chaîne retournée. [OUT]             *
*                                                                             *
*  Description : Fournit la chaîne de caractère restant à traiter.            *
*                                                                             *
*  Retour      : Pointeur vers les données courantes.                         *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

const char *g_itanium_dcontext_get_string(const GItaniumDContext *context, size_t *remaining)
{
    const char *result;                     /* Données à renvoyer          */

    *remaining = context->len - context->pos;

    result = &context->mangled[context->pos];

    return result;

}





/*

################define d_peek_char(di) (*((di)->n))
#define d_peek_next_char(di) ((di)->n[1])
################define d_advance(di, i) ((di)->n += (i))
################define d_check_char(di, c) (d_peek_char(di) == c ? ((di)->n++, 1) : 0)
#define d_next_char(di) (d_peek_char(di) == '\0' ? '\0' : *((di)->n++))
################define d_str(di) ((di)->n)


*/




/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à manipuler.                  *
*                                                                             *
*  Description : Fournit un nouveau composant vierge.                         *
*                                                                             *
*  Retour      : Composant prêt à être défini.                                *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

itanium_component *g_itanium_dcontext_get_empty_component(GItaniumDContext *context)
{
    itanium_component *result;              /* Disponibilité à retourner   */
    size_t i;                               /* Boucle de parcours          */
    size_t next;                            /* Indice de disponibilité     */

    if (context->last_used == ITCOMP_INVALID
        || context->next_links[context->last_used] == ITCOMP_INVALID)
    {
        /* Création d'extensions */

        context->components = (itanium_component *)
            realloc(context->components,
                    (context->length + ITCOMP_ALLOC_BULK) * sizeof(itanium_component));

        context->next_links = (size_t *)
            realloc(context->next_links, (context->length + ITCOMP_ALLOC_BULK) * sizeof(size_t));

        /* Inscription des liens d'allocation */

        for (i = context->length; i < (context->length + ITCOMP_ALLOC_BULK - 1); i++)
            context->next_links[i] = i + 1;

        context->next_links[context->length + ITCOMP_ALLOC_BULK - 1] = ITCOMP_INVALID;

        if (context->last_used != ITCOMP_INVALID)
            context->next_links[context->last_used] = context->length;

        /* Mise à jour globale */

        context->length += ITCOMP_ALLOC_BULK;

    }

    /* Extraction d'un composant disponible */

    if (context->last_used == ITCOMP_INVALID)
        next = 0;
    else
        next = context->next_links[context->last_used];

    result = &context->components[next];
    context->last_used = next;

    memset(result, 0, sizeof(itanium_component));

    result->context = context;
    g_object_ref(G_OBJECT(result->context));
    result->refcount = 1;

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à manipuler.                  *
*                comp    = composant à libérer.                               *
*                                                                             *
*  Description : Marque un composant comme étant disponible pour un usage.    *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void g_itanium_dcontext_mark_component_as_free(GItaniumDContext *context, itanium_component *comp)
{
    size_t index;                           /* Indice du composant concerné*/

    /*BUG_ON(comp->refcount != 0);*/

    g_object_unref(G_OBJECT(comp->context));

    index = comp - context->components;

    context->next_links[index] = context->next_links[context->last_used];
    context->next_links[context->last_used] = index;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à manipuler.                  *
*                comp    = composant à conserver en mémoire.                  *
*                                                                             *
*  Description : Indexe un composant comme future substitution potentielle.   *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

void g_itanium_dcontext_add_substitution(GItaniumDContext *context, itanium_component *comp)
{
    fnv64_t hash;                           /* Empreinte du candidat       */
    size_t i;                               /* Boucle de parcours          */

    hash = itd_hash_comp(comp);

    for (i = 0; i < context->subst_count; i++)
        if (cmp_fnv_64a(itd_hash_comp(context->substitutions[i]), hash) == 0)
            break;

    if (i == context->subst_count)
    {

        printf("[ADDING SUBST] [%zu] '%s'\n",
               context->subst_count,
               itd_translate_component(context, comp, NULL));


        context->substitutions[context->subst_count++] = comp;
        itd_ref_comp(comp);

    }

}


/******************************************************************************
*                                                                             *
*  Paramètres  : context = contexte de décodage à manipuler.                  *
*                index   = indice de la substitution visée.                   *
*                                                                             *
*  Description : Fournit un composant en place pour une substitution.         *
*                                                                             *
*  Retour      : Composant déjà extrait et conservé.                          *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

itanium_component *g_itanium_dcontext_get_substitution(GItaniumDContext *context, size_t index)
{
    itanium_component *result;              /* Composant à retourner       */

    if (index < context->subst_count)
    {
        result = context->substitutions[index];
        itd_ref_comp(result);
    }
    else
        result = NULL;

    return result;

}















void test_itanium(void)
{




#define TEST_DEMANG(v)                                          \
    do                                                          \
    {                                                           \
        GDemanglingContext *ctx;                                \
        /*char *str;*/                                          \
        ctx = g_itanium_dcontext_new();     \
    /*str = */g_itanium_dcontext_demangle_routine(G_ITANIUM_DCONTEXT(ctx), v); \
        /*printf("==> %s\n", str);*/                            \
    }                                                           \
    while (0)


    TEST_DEMANG("_Z3fooILi2EEvRAplT_Li1E_i");

    exit(0);

    TEST_DEMANG("_Z1fv");
    TEST_DEMANG("_Z3foo3bar");


}