summaryrefslogtreecommitdiff
path: root/src/glibext/gwidthtracker.c
diff options
context:
space:
mode:
authorCyrille Bagard <nocbos@gmail.com>2016-05-01 01:35:26 (GMT)
committerCyrille Bagard <nocbos@gmail.com>2016-05-01 01:35:26 (GMT)
commitef68a3dd8ff259200ca7f088eecc9ce35e7ffe8f (patch)
treef89cccbe3d0ac421b41cc49a28bee32e8903e45a /src/glibext/gwidthtracker.c
parent89ceb1e27afed0bac789e33c2f10eade01747d88 (diff)
Handled all width measures per view in a dedicated manager.
Diffstat (limited to 'src/glibext/gwidthtracker.c')
-rw-r--r--src/glibext/gwidthtracker.c830
1 files changed, 830 insertions, 0 deletions
diff --git a/src/glibext/gwidthtracker.c b/src/glibext/gwidthtracker.c
new file mode 100644
index 0000000..11ea0b1
--- /dev/null
+++ b/src/glibext/gwidthtracker.c
@@ -0,0 +1,830 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * gwidthtracker.c - suivi des largeurs associées à un ensemble de lignes
+ *
+ * Copyright (C) 2016 Cyrille Bagard
+ *
+ * This file is part of Chrysalide.
+ *
+ * OpenIDA 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.
+ *
+ * OpenIDA 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 "gwidthtracker.h"
+
+
+#include <assert.h>
+#include <malloc.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+#include "gcodebuffer.h"
+
+
+
+/* Portions de largeurs communes */
+typedef struct _common_metrics
+{
+ size_t first; /* Premier indice de portion */
+ size_t last; /* Dernier indice de portion */
+
+ line_width_summary summary; /* Compilation de largeurs */
+
+} common_metrics;
+
+
+/* Gestionnaire de largeurs associées aux lignes (instance) */
+struct _GWidthTracker
+{
+ GObject parent; /* A laisser en premier */
+
+ GCodeBuffer *buffer; /* Ensemble complet de lignes */
+
+ common_metrics *portions; /* Portions représentées */
+ size_t count; /* Quantité de ces portions */
+
+ line_width_summary summary; /* Largeurs requises suivies */
+ bool cached; /* Mise en cache des calculs */
+
+};
+
+/* Gestionnaire de largeurs associées aux lignes (classe) */
+struct _GWidthTrackerClass
+{
+ GObjectClass parent; /* A laisser en premier */
+
+};
+
+
+/* Procède à l'initialisation d'une classe de suivi de largeurs. */
+static void g_width_tracker_class_init(GWidthTrackerClass *);
+
+/* Procède à l'initialisation d'un suivi de largeurs de lignes. */
+static void g_width_tracker_init(GWidthTracker *);
+
+/* Supprime toutes les références externes. */
+static void g_width_tracker_dispose(GWidthTracker *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_width_tracker_finalize(GWidthTracker *);
+
+/* Recherche la portion contenant un indice de ligne donné. */
+static size_t g_width_tracker_find_metrics(const GWidthTracker *, size_t);
+
+/* Prend en compte une évolution du volume de lignes. */
+static void g_width_tracker_update_ranges(GWidthTracker *, size_t, size_t);
+
+/* Recalcule les largeurs requises par une portion de lignes. */
+static void g_width_tracker_update_widths(GWidthTracker *, size_t);
+
+/* Calcule les largeurs requises par un ensemble de lignes. */
+static void g_width_tracker_ensure_valid_required_widths(GWidthTracker *);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/* TAMPON POUR CODE DESASSEMBLE */
+/* ---------------------------------------------------------------------------------- */
+
+
+/* Détermine le type du gestionnaire de largeurs associées aux lignes. */
+G_DEFINE_TYPE(GWidthTracker, g_width_tracker, G_TYPE_OBJECT);
+
+
+/******************************************************************************
+* *
+* Paramètres : class = classe de composant GTK à initialiser. *
+* *
+* Description : Procède à l'initialisation d'une classe de suivi de largeurs.*
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_width_tracker_class_init(GWidthTrackerClass *class)
+{
+ GObjectClass *object; /* Autre version de la classe */
+
+ object = G_OBJECT_CLASS(class);
+
+ object->dispose = (GObjectFinalizeFunc/* ! */)g_width_tracker_dispose;
+ object->finalize = (GObjectFinalizeFunc)g_width_tracker_finalize;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : tracker = composant GLib à initialiser. *
+* *
+* Description : Procède à l'initialisation d'un suivi de largeurs de lignes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_width_tracker_init(GWidthTracker *tracker)
+{
+ tracker->portions = NULL;
+ tracker->count = 0;
+
+ memset(&tracker->summary, 0, sizeof(line_width_summary));
+ tracker->cached = false;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : tracker = instance d'objet GLib à traiter. *
+* *
+* Description : Supprime toutes les références externes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_width_tracker_dispose(GWidthTracker *tracker)
+{
+ g_object_unref(G_OBJECT(tracker->buffer));
+
+ G_OBJECT_CLASS(g_width_tracker_parent_class)->dispose(G_OBJECT(tracker));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : tracker = instance d'objet GLib à traiter. *
+* *
+* Description : Procède à la libération totale de la mémoire. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_width_tracker_finalize(GWidthTracker *tracker)
+{
+ G_OBJECT_CLASS(g_width_tracker_parent_class)->finalize(G_OBJECT(tracker));
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : buffer = tampon contenant les lignes à surveiller. *
+* first = adresse contenant l'indice de la première ligne. *
+* last = adresse contenant l'indice de la dernière ligne. *
+* *
+* Description : Crée un nouveau suivi de largeurs au sein de lignes. *
+* *
+* Retour : Composant GLib créé. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GWidthTracker *g_width_tracker_new(GCodeBuffer *buffer)
+{
+ GWidthTracker *result; /* Composant à retourner */
+
+ result = g_object_new(G_TYPE_WIDTH_TRACKER, NULL);
+
+ g_object_ref(G_OBJECT(buffer));
+ result->buffer = buffer;
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : buffer = tampon contenant les lignes à surveiller. *
+* first = indice de la première ligne d'une zone réduite. *
+* last = indice de la dernière ligne d'une zone réduite. *
+* *
+* Description : Crée un nouveau suivi de largeurs au sein de lignes. *
+* *
+* Retour : Composant GLib créé. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+GWidthTracker *g_width_tracker_new_restricted(const GWidthTracker *template, size_t first, size_t last)
+{
+ GWidthTracker *result; /* Composant à retourner */
+ size_t start; /* Début de la zone à copier */
+ size_t end; /* Fin de cette même zone */
+ size_t i; /* Boucle de parcours */
+
+ result = g_object_new(G_TYPE_WIDTH_TRACKER, NULL);
+
+ g_object_ref(G_OBJECT(template->buffer));
+ result->buffer = template->buffer;
+
+ start = g_width_tracker_find_metrics(template, first);
+ assert(start < template->count);
+
+ end = g_width_tracker_find_metrics(template, last);
+ assert(end < template->count);
+
+ result->count = end - start + 1;
+ result->portions = (common_metrics *)calloc(result->count, sizeof(common_metrics));
+
+ for (i = 0; i < result->count; i++)
+ memcpy(&result->portions[i], &template->portions[start + i], sizeof(common_metrics));
+
+ if (result->portions[0].first != first)
+ {
+ result->portions[0].first = first;
+ g_width_tracker_update_widths(result, 0);
+ }
+
+ if (result->portions[result->count - 1].last != last)
+ {
+ result->portions[result->count - 1].last = last;
+ g_width_tracker_update_widths(result, result->count - 1);
+ }
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : tracker = gestionnaire de suivi à consulter. *
+* index = indice d'une ligne dont la portion est inconnue. *
+* *
+* Description : Recherche la portion contenant un indice de ligne donné. *
+* *
+* Retour : Indice de portion trouvée ou le nombre de portions sinon. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static size_t g_width_tracker_find_metrics(const GWidthTracker *tracker, size_t index)
+{
+ size_t result; /* Indice trouvé à retourner */
+ common_metrics *found; /* Portion trouvée ou NULL */
+
+ int look_for_metrics(const size_t *idx, const common_metrics *m)
+ {
+ int status;
+
+ if (*idx < m->first)
+ status = -1;
+
+ else if (*idx > m->last)
+ status = 1;
+
+ else
+ status = 0;
+
+ return status;
+
+ }
+
+ found = bsearch(&index, tracker->portions, tracker->count,
+ sizeof(common_metrics), (__compar_fn_t)look_for_metrics);
+
+ if (found == NULL)
+ result = tracker->count;
+ else
+ result = found - tracker->portions;
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : tracker = gestionnaire de largeurs de lignes à mettre jour. *
+* start = première ligne à traiter. *
+* diff = nombre de lignes ajoutées ou supprimées. *
+* *
+* Description : Prend en compte une évolution du volume de lignes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_width_tracker_update_ranges(GWidthTracker *tracker, size_t start, size_t diff)
+{
+ size_t i; /* Boucle de parcours */
+
+ for (i = start; i < tracker->count; i++)
+ {
+ tracker->portions[i].first += diff;
+ tracker->portions[i].last += diff;
+ }
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : tracker = gestionnaire de largeurs de lignes à mettre jour. *
+* index = indice de la portion à rafraîchir. *
+* *
+* Description : Recalcule les largeurs requises par une portion de lignes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_width_tracker_update_widths(GWidthTracker *tracker, size_t index)
+{
+ common_metrics *portion; /* Portion à actualiser */
+ BufferLineColumn k; /* Boucle de parcours #1 */
+ size_t i; /* Boucle de parcours #2 */
+ GBufferLine *line; /* Ligne à manipuler */
+
+ assert(index < tracker->count);
+
+ portion = &tracker->portions[index];
+
+ /* Réinitialisation globale ? */
+
+ for (k = 0; k < BLC_COUNT && tracker->cached; k++)
+ tracker->cached &= (tracker->summary.max_widths[k] != portion->summary.max_widths[k]);
+
+ tracker->cached &= (tracker->summary.merged_width != portion->summary.merged_width);
+
+ /* Réinitialisation locale */
+
+ memset(&portion->summary, 0, sizeof(line_width_summary));
+
+ /* Collecte */
+
+ for (i = portion->first; i <= portion->last; i++)
+ {
+ line = g_code_buffer_find_line_by_index(tracker->buffer, i);
+
+ g_buffer_line_collect_widths(line, &portion->summary);
+
+ }
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : tracker = gestionnaire de largeurs de lignes à mettre jour. *
+* index = position de la première des lignes à ajouter. *
+* count = quantité de lignes devant être ajoutées. *
+* *
+* Description : Prend acte de l'ajout de lignes pour les largeurs. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_width_tracker_update_added(GWidthTracker *tracker, size_t index, size_t count)
+{
+ size_t current; /* Indice de portion visée */
+ common_metrics *portion; /* Portion sélectionnée */
+ size_t next; /* Prochaine portion à décaller*/
+ size_t i; /* Boucle de parcours */
+ GBufferLine *line; /* Ligne à manipuler */
+ size_t dest; /* Destination d'une recopie */
+ size_t src; /* Source d'une recopie */
+
+ /* Cas particulier du premier ajout */
+ if (tracker->count == 0)
+ {
+ assert(index == 0);
+
+ tracker->portions = (common_metrics *)calloc(1, sizeof(common_metrics));
+ tracker->count = 1;
+
+ tracker->portions[0].first = 0;
+ tracker->portions[0].last = count - 1;
+
+ g_width_tracker_update_widths(tracker, 0);
+
+ return;
+
+ }
+
+ current = g_width_tracker_find_metrics(tracker, index);
+
+ /* Si la ligne est rajoutée en fin d'ensemble */
+ if (current == tracker->count)
+ {
+ current = tracker->count - 1;
+ portion = &tracker->portions[current];
+
+ assert(index == (portion->last + 1));
+
+ }
+ else
+ portion = &tracker->portions[current];
+
+ portion->last += count;
+
+ g_width_tracker_update_widths(tracker, current);
+
+ next = current + 1;
+
+ /* Un découpage s'impose-t-il quelque part ? */
+
+ for (i = index + count - 1; i >= index; i--)
+ {
+ line = g_code_buffer_find_line_by_index(tracker->buffer, i);
+
+ if (g_buffer_line_get_flags(line) & BLF_WIDTH_MANAGER)
+ {
+ /* Insertion d'une nouvelle place */
+
+ tracker->count++;
+
+ tracker->portions = (common_metrics *)realloc(tracker->portions,
+ tracker->count * sizeof(common_metrics));
+
+ dest = current + 2;
+ src = current + 1;
+
+ if ((tracker->count - src) > 0)
+ memmove(&tracker->portions[dest], &tracker->portions[src],
+ (tracker->count - src - 1) * sizeof(common_metrics));
+
+ next++;
+
+ /* Insertion au début */
+ if (i == portion->first)
+ {
+ assert(i == index);
+
+ tracker->portions[current + 1].first = portion->first + 1;
+ tracker->portions[current + 1].last = portion->last;
+
+ portion->first = i;
+ portion->last = i;
+
+ }
+
+ /* Insertion au sein de la portion ou à la fin */
+ else
+ {
+ tracker->portions[current + 1].first = i;
+ tracker->portions[current + 1].last = portion->last;
+
+ portion->last = i - 1;
+
+ }
+
+ /* Mise à jour des largeurs */
+
+ g_width_tracker_update_widths(tracker, current);
+
+ g_width_tracker_update_widths(tracker, current + 1);
+
+ }
+
+ }
+
+ /* Suite impérative : accroître les indices ! */
+
+ g_width_tracker_update_ranges(tracker, next, 1);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : tracker = gestionnaire de largeurs de lignes à mettre jour. *
+* start = première ligne devant être supprimée. *
+* end = dernière ligne devant être supprimée. *
+* *
+* Description : Prend acte de la suppression de lignes pour les largeurs. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_width_tracker_update_deleted(GWidthTracker *tracker, size_t start, size_t end)
+{
+ size_t first; /* Première portion concernée */
+ size_t last; /* Dernière portion concernée */
+ size_t diff; /* Nombre de lignes supprimées */
+ bool keep_first; /* Conservation de portion #1 */
+ size_t dest; /* Destination du transfert */
+ bool keep_last; /* Conservation de portion #2 */
+ size_t src; /* Source du transfert */
+
+ first = g_width_tracker_find_metrics(tracker, start);
+ assert(first < tracker->count);
+
+ last = g_width_tracker_find_metrics(tracker, end);
+ assert(last < tracker->count);
+
+ diff = end - start + 1;
+
+ /* Suppression de portions inutiles ? */
+
+ keep_first = (tracker->portions[first].first > start || end < tracker->portions[first].last);
+
+ dest = (keep_first ? first + 1 : first);
+
+ keep_last = (tracker->portions[last].first > start || end < tracker->portions[last].last);
+
+ src = (keep_last ? last : last + 1);
+
+ if (src > dest)
+ {
+ memmove(&tracker->portions[dest], &tracker->portions[src],
+ (tracker->count - src) * sizeof(common_metrics));
+
+ tracker->count -= (src - dest);
+
+ tracker->portions = (common_metrics *)realloc(tracker->portions,
+ tracker->count * sizeof(common_metrics));
+
+ }
+
+ /* Si une fusion s'impose */
+
+ if (keep_first && keep_last && last != first)
+ {
+ tracker->portions[first].last = tracker->portions[dest].last;
+
+ memmove(&tracker->portions[first + 1], &tracker->portions[first + 2],
+ (tracker->count - first - 2) * sizeof(common_metrics));
+
+ tracker->count--;
+
+ tracker->portions = (common_metrics *)realloc(tracker->portions,
+ tracker->count * sizeof(common_metrics));
+
+ keep_last = false;
+
+ }
+
+ /* Avant toute chose : faire décroître les indices ! */
+
+ if (keep_first)
+ {
+ if (end < tracker->portions[first].last)
+ tracker->portions[first].last -= diff;
+ else
+ tracker->portions[first].last = start;
+ }
+
+ if (keep_last && last != first)
+ {
+ tracker->portions[dest].first = end + 1;
+ tracker->portions[dest].last -= diff;
+ }
+
+ g_width_tracker_update_ranges(tracker, keep_last ? dest + 1 : dest, -diff);
+
+ /* Mise à jour des largeurs aux extrémités */
+
+ if (keep_first)
+ g_width_tracker_update_widths(tracker, first);
+
+ if (keep_last && last != first)
+ g_width_tracker_update_widths(tracker, dest);
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : tracker = suivi de largeurs à mettre à jour si besoin est. *
+* *
+* Description : Calcule les largeurs requises par un ensemble de lignes. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+static void g_width_tracker_ensure_valid_required_widths(GWidthTracker *tracker)
+{
+ line_width_summary *global; /* Valeurs collectées */
+ size_t i; /* Boucle de parcours #1 */
+ const line_width_summary *summary; /* Valeurs à intégrer */
+ BufferLineColumn k; /* Boucle de parcours #2 */
+
+ if (!tracker->cached)
+ {
+ /* Réinitialisation */
+
+ memset(&tracker->summary, 0, sizeof(line_width_summary));
+
+ /* Collecte */
+
+ global = &tracker->summary;
+
+ for (i = 0; i < tracker->count; i++)
+ {
+ summary = &tracker->portions[i].summary;
+
+ for (k = 0; k < BLC_COUNT; k++)
+ global->max_widths[k] = MAX(global->max_widths[k], summary->max_widths[k]);
+
+ global->merged_width = MAX(global->merged_width, summary->merged_width);
+
+ }
+
+ tracker->cached = true;
+
+ }
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : tracker = suivi de largeurs à consulter. *
+* *
+* Description : Fournit un bon résumé des largeurs en vigueur. *
+* *
+* Retour : Ensemble des largeurs collectées. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+const line_width_summary *g_width_tracker_get_width_summary(GWidthTracker *tracker)
+{
+ g_width_tracker_ensure_valid_required_widths(tracker);
+
+ return &tracker->summary;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : tracker = suivi de largeurs à consulter. *
+* index = indice de la ligne dont la portion est recherchée. *
+* summary = ensemble ciblé de largeurs collectées. [OUT] *
+* *
+* Description : Fournit un résumé local des largeurs en vigueur. *
+* *
+* Retour : - *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+void g_width_tracker_get_local_width_summary(GWidthTracker *tracker, size_t index, line_width_summary *summary)
+{
+ size_t current; /* Indice de portion visée */
+ common_metrics *portion; /* Portion locale à consulter */
+ BufferLineColumn i; /* Boucle de parcours */
+
+ g_width_tracker_ensure_valid_required_widths(tracker);
+
+ current = g_width_tracker_find_metrics(tracker, index);
+ assert(current < tracker->count);
+
+ portion = &tracker->portions[current];
+
+ for (i = BLC_FIRST; i < BLC_DISPLAY; i++)
+ summary->max_widths[i] = tracker->summary.max_widths[i];
+
+ for (i = BLC_DISPLAY; i < BLC_COUNT; i++)
+ summary->max_widths[i] = portion->summary.max_widths[i];
+
+ summary->merged_width = portion->summary.merged_width;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : tracker = suivi de largeurs à consulter. *
+* display = règles d'affichage des colonnes modulables. *
+* *
+* Description : Fournit la largeur requise par une visualisation. *
+* *
+* Retour : Dimension calculée. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+gint g_width_tracker_get_width(GWidthTracker *tracker, const bool *display)
+{
+ gint result; /* Taille à retourner */
+ const line_width_summary *summary; /* Accès rapide aux mesures */
+ gint col_width; /* Calcul selon les colonnes */
+ gint full_width; /* Calcul selon les fusions */
+ BufferLineColumn i; /* Boucle de parcours */
+
+ g_width_tracker_ensure_valid_required_widths(tracker);
+
+ result = 0;
+
+ summary = &tracker->summary;
+
+ col_width = 0;
+ full_width = 0;
+
+ /* Première méthode */
+
+ for (i = 0; i < BLC_COUNT; i++)
+ {
+ if (i < BLC_DISPLAY && !display[i]) continue;
+
+ col_width += summary->max_widths[i];
+
+ if ((i + 1) < BLC_COUNT)
+ col_width += COL_MARGIN;
+
+ }
+
+ /* Seconde méthode */
+
+ for (i = 0; i < BLC_DISPLAY; i++)
+ {
+ if (!display[i]) continue;
+
+ full_width += summary->max_widths[i] + COL_MARGIN;
+
+ }
+
+ full_width += summary->merged_width;
+
+ /* Mise en concurrence et poursuite... */
+
+ result += + MAX(col_width, full_width);
+
+ return result;
+
+}
+
+
+/******************************************************************************
+* *
+* Paramètres : tracker = suivi de largeurs à consulter. *
+* display = règles d'affichage des colonnes modulables. *
+* *
+* Description : Fournit la largeur requise pour dépasser les marges gauches. *
+* *
+* Retour : Dimension calculée. *
+* *
+* Remarques : - *
+* *
+******************************************************************************/
+
+gint g_width_tracker_get_margin(GWidthTracker *tracker, const bool *display)
+{
+ gint result; /* Taille à retourner */
+ const line_width_summary *summary; /* Accès rapide aux mesures */
+ BufferLineColumn i; /* Boucle de parcours */
+
+ g_width_tracker_ensure_valid_required_widths(tracker);
+
+ result = 0;
+
+ summary = &tracker->summary;
+
+ for (i = 0; i < BLC_DISPLAY; i++)
+ {
+ if (!display[i]) continue;
+
+ result += summary->max_widths[i] + COL_MARGIN;
+
+ }
+
+ return result;
+
+}