/* Chrysalide - Outil d'analyse de fichiers binaires * masked.c - gestion d'une recherche de motif partielle * * Copyright (C) 2023 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 "masked.h" #include #include "masked-int.h" #include "../../backends/bitap.h" /* ------------------------ DECOMPOSITION DE MOTIF RECHERCHE ------------------------ */ /* Initialise la classe des bribes de motif partielles. */ static void g_scan_token_node_masked_class_init(GScanTokenNodeMaskedClass *); /* Initialise une instance de bribe de motif partielle. */ static void g_scan_token_node_masked_init(GScanTokenNodeMasked *); /* Supprime toutes les références externes. */ static void g_scan_token_node_masked_dispose(GScanTokenNodeMasked *); /* Procède à la libération totale de la mémoire. */ static void g_scan_token_node_masked_finalize(GScanTokenNodeMasked *); /* --------------------- IMPLEMENTATION DES FONCTIONS DE CLASSE --------------------- */ /* Parcourt une arborescence de noeuds et y relève des éléments. */ static void g_scan_token_node_masked_visit(GScanTokenNodeMasked *, scan_tree_points_t *); /* Inscrit la définition d'un motif dans un moteur de recherche. */ static bool g_scan_token_node_masked_enroll(GScanTokenNodeMasked *, GEngineBackend *, size_t, size_t *); /* Récupère un identifiant final pour un atome d'octets. */ static bool g_scan_token_node_masked_build_id(GScanTokenNodeMasked *, GEngineBackend *); /* Détermine si un contenu d'intérêt est présent à une position. */ static bool check_scan_token_node_masked_content(const masked_byte_t *, size_t, phys_t, GBinContent *); /* Transforme les correspondances locales en trouvailles. */ static void g_scan_token_node_masked_check_forward(const GScanTokenNodeMasked *, scan_node_check_params_t *, TokenNodeCheckFlags, bool *); /* Transforme les correspondances locales en trouvailles. */ static void g_scan_token_node_masked_check_backward(const GScanTokenNodeMasked *, scan_node_check_params_t *, TokenNodeCheckFlags, bool *); /* ---------------------------------------------------------------------------------- */ /* DECOMPOSITION DE MOTIF RECHERCHE */ /* ---------------------------------------------------------------------------------- */ /* Indique le type défini pour un noeud représentant une bribe partielle à retrouver. */ G_DEFINE_TYPE(GScanTokenNodeMasked, g_scan_token_node_masked, G_TYPE_SCAN_TOKEN_NODE); /****************************************************************************** * * * Paramètres : klass = classe à initialiser. * * * * Description : Initialise la classe des bribes de motif partielles. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_scan_token_node_masked_class_init(GScanTokenNodeMaskedClass *klass) { GObjectClass *object; /* Autre version de la classe */ GScanTokenNodeClass *node; /* Version de classe parente */ object = G_OBJECT_CLASS(klass); object->dispose = (GObjectFinalizeFunc/* ! */)g_scan_token_node_masked_dispose; object->finalize = (GObjectFinalizeFunc)g_scan_token_node_masked_finalize; node = G_SCAN_TOKEN_NODE_CLASS(klass); node->apply = (apply_scan_token_node_flags_fc)NULL; node->visit = (visit_scan_token_node_fc)g_scan_token_node_masked_visit; node->enroll = (enroll_scan_token_node_fc)g_scan_token_node_masked_enroll; node->build_id = (build_scan_token_node_id_fc)g_scan_token_node_masked_build_id; node->check_forward = (check_scan_token_node_fc)g_scan_token_node_masked_check_forward; node->check_backward = (check_scan_token_node_fc)g_scan_token_node_masked_check_backward; } /****************************************************************************** * * * Paramètres : masked = instance à initialiser. * * * * Description : Initialise une instance de bribe de motif partielle. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_scan_token_node_masked_init(GScanTokenNodeMasked *masked) { masked->bytes = NULL; masked->len = 0; masked->raw = NULL; masked->raw_count = 0; masked->enrolled_atoms = NULL; masked->enrolled_count = 0; } /****************************************************************************** * * * Paramètres : masked = instance d'objet GLib à traiter. * * * * Description : Supprime toutes les références externes. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_scan_token_node_masked_dispose(GScanTokenNodeMasked *masked) { G_OBJECT_CLASS(g_scan_token_node_masked_parent_class)->dispose(G_OBJECT(masked)); } /****************************************************************************** * * * Paramètres : masked = instance d'objet GLib à traiter. * * * * Description : Procède à la libération totale de la mémoire. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_scan_token_node_masked_finalize(GScanTokenNodeMasked *masked) { size_t i; /* Boucle de parcours */ if (masked->bytes != NULL) free(masked->bytes); for (i = 0; i < masked->raw_count; i++) exit_szstr(&masked->raw[i]); if (masked->raw != NULL) free(masked->raw); if (masked->enrolled_atoms != NULL) free(masked->enrolled_atoms); G_OBJECT_CLASS(g_scan_token_node_masked_parent_class)->finalize(G_OBJECT(masked)); } /****************************************************************************** * * * Paramètres : byte = valeur masquée à intégrer. * * * * Description : Construit une bribe de motif partielle. * * * * Retour : Mécanismes mis en place. * * * * Remarques : - * * * ******************************************************************************/ GScanTokenNode *g_scan_token_node_masked_new(const masked_byte_t *byte) { GScanTokenNode *result; /* Structure à retourner */ result = g_object_new(G_TYPE_SCAN_TOKEN_NODE_MASKED, NULL); if (!g_scan_token_node_masked_create(G_SCAN_TOKEN_NODE_MASKED(result), byte)) g_clear_object(&result); return result; } /****************************************************************************** * * * Paramètres : masked = bribe partielle à initialiser pleinement. * * byte = valeur masquée à intégrer. * * * * Description : Met en place une bribe de motif partielle. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ bool g_scan_token_node_masked_create(GScanTokenNodeMasked *masked, const masked_byte_t *byte) { bool result; /* Bilan à retourner */ result = true; g_scan_token_node_masked_add(masked, byte); return result; } /****************************************************************************** * * * Paramètres : masked = ensemble de noeuds à compléter. * * byte = valeur masquée à intégrer. * * * * Description : Enregistre la valeur d'octet à rechercher avec son masque. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ void g_scan_token_node_masked_add(GScanTokenNodeMasked *masked, const masked_byte_t *byte) { assert((byte->value & 0x0f) == 0 || (byte->value & 0xf0) == 0); masked->bytes = realloc(masked->bytes, ++masked->len * sizeof(masked_byte_t)); masked->bytes[masked->len - 1] = *byte; } /* ---------------------------------------------------------------------------------- */ /* IMPLEMENTATION DES FONCTIONS DE CLASSE */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : node = point de départ du parcours à effectuer. * * points = points capitaux de l'arborescence. [OUT] * * * * Description : Parcourt une arborescence de noeuds et y relève des éléments.* * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_scan_token_node_masked_visit(GScanTokenNodeMasked *node, scan_tree_points_t *points) { GScanTokenNodeMasked *other; /* Concurrence à mesurer */ if (points->best_masked == NULL) points->best_masked = G_SCAN_TOKEN_NODE(node); else { other = G_SCAN_TOKEN_NODE_MASKED(points->best_masked); if (node->len > other->len) points->best_masked = G_SCAN_TOKEN_NODE(node); } } /****************************************************************************** * * * Paramètres : node = définition de la bribe à enregistrer. * * backend = moteur de recherche à préchauffer. * * maxsize = taille max. des atomes (mise en commun optimisée). * * slow = niveau de ralentissement induit (0 = idéal). [OUT] * * * * Description : Inscrit la définition d'un motif dans un moteur de recherche.* * * * Retour : Bilan de l'opération à renvoyer. * * * * Remarques : - * * * ******************************************************************************/ static bool g_scan_token_node_masked_enroll(GScanTokenNodeMasked *node, GEngineBackend *backend, size_t maxsize, size_t *slow) { bool result; /* Statut à retourner */ bool forced; /* Inclusion dans un scan ? */ //size_t len_to_enroll; /* Taille à considérer */ size_t i; /* Boucle de parcours */ result = true; forced = (g_scan_token_node_get_flags(G_SCAN_TOKEN_NODE(node)) & STNF_MAIN); if (forced) { *slow += (maxsize * 2); node->raw = make_atoms_from_masked_bytes(node->bytes, node->len, &node->raw_count); /** * Dans le cas bien précis de l'usage de l'algorithme Bitap pour les recherches * dans le contenu binaire à analyser, on tire parti du coût nul des recherches * multiples pour une même position. */ if (G_IS_BITAP_BACKEND(backend)) { //len_to_enroll = (node->len < maxsize ? node->len : maxsize); /* TODO */ assert(false); node->enrolled_count = 1; } else { node->enrolled_atoms = malloc(node->raw_count * sizeof(tracked_scan_atom_t)); node->enrolled_count = node->raw_count; for (i = 0; i < node->enrolled_count && result; i++) { find_best_atom(&node->raw[i], maxsize, &node->enrolled_atoms[i], NULL); /** * Correction : si l'atome ne représente qu'une vue partielle, * la validation rapide ne peut s'appliquer. */ if (node->enrolled_atoms[i].fast_check) node->enrolled_atoms[i].fast_check = (node->enrolled_atoms[i].len == node->len); result = enroll_prepared_atom(&node->raw[i], backend, &node->enrolled_atoms[i]); } } } return result; } /****************************************************************************** * * * Paramètres : node = définition de la bribe à peaufiner. * * backend = moteur de recherche à préchauffer. * * * * Description : Récupère un identifiant final pour un atome d'octets. * * * * Retour : Bilan de l'opération à renvoyer. * * * * Remarques : - * * * ******************************************************************************/ static bool g_scan_token_node_masked_build_id(GScanTokenNodeMasked *node, GEngineBackend *backend) { bool result; /* Statut à retourner */ size_t i; /* Boucle de parcours #1 */ result = true; for (i = 0; i < node->enrolled_count && result; i++) result = build_atom_pattern_id(&node->enrolled_atoms[i], backend); return result; } /****************************************************************************** * * * Paramètres : bytes = octets partiels avec leur masque à interpréter. * * len = quantité d'octets à interpréter. * * start = point d'analyse à respecter. * * content = accès au contenu brut pour vérifications (optim.) * * * * Description : Détermine si un contenu d'intérêt est présent à une position.* * * * Retour : Bilan de l'analyse : true pour une correspondance. * * * * Remarques : - * * * ******************************************************************************/ static bool check_scan_token_node_masked_content(const masked_byte_t *bytes, size_t len, phys_t start, GBinContent *content) { bool result; /* Bilan à retourner */ vmpa2t pos; /* Position dans les données */ const bin_t *ptr; /* Accès aux données brutes */ size_t i; /* Boucle de parcours */ result = false; init_vmpa(&pos, start, VMPA_NO_VIRTUAL); ptr = g_binary_content_get_raw_access(content, &pos, len); for (i = 0; i < len; i++) { if ((ptr[i] & bytes[i].mask) != bytes[i].value) break; } result = (i == len); return result; } /****************************************************************************** * * * Paramètres : node = définition de la bribe à manipuler. * * params = accès direct aux éléments utiles aux validations. * * cflags = altérations de traitement à respecter. * * skip = détermine si l'analyse est différée. [OUT] * * * * Description : Transforme les correspondances locales en trouvailles. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_scan_token_node_masked_check_forward(const GScanTokenNodeMasked *node, scan_node_check_params_t *params, TokenNodeCheckFlags cflags, bool *skip) { #ifndef NDEBUG bool forced; /* Inclusion dans un scan ? */ #endif match_area_t **areas; /* Liste de zones à constituer */ size_t *count; /* Taille de cette liste */ bool copy; /* Besoin d'une copie ? */ bool inverted; /* Inversion des bilans ? */ size_t i; /* Boucle de parcours #1 */ const tracked_scan_atom_t *atom; /* Atome correspondant */ match_area_t *atoms; /* Localisations des bribes */ bool first_round; /* Premier tour de traitement */ match_area_t *area; /* Correspondance à valider */ match_area_t *next; /* Correspondance suivante */ phys_t start; /* Début potentiel de motif */ bool status; /* Bilan d'une correspondance */ phys_t after; /* Espace disposible après */ phys_t min_end; /* Fin la plus proche possible */ phys_t max_end; /* Fin la plus éloignée trouvée*/ bool updated; /* Existence de correspondance */ size_t rcount; /* Quantité de bornes présentes*/ const node_offset_range_t *ranges; /* Bornes d'espace à parcourir */ size_t r; /* Boucle de parcours #xxxxxxxxxxxx*/ phys_t p; /* Boucle de parcours #xxxxxxxxxxxx*/ phys_t updated_edge; /* Nouvelle bordure de motif */ match_area_t *new_area; /* Copie de correspondance */ if (0x0) printf("=================== CHECK :: %s (skip? %d)\n", __FUNCTION__, *skip); if (*skip) return; /** * Si l'analyse arrive à un ou plusieurs octets masqués, soit il s'agit du * premier noeud, et la génération d'atomes a été forcée pour obtenir des * points de départ, soit des correspondances ont été établies au préalable, * et il ne doit alors pas y avoir d'atome mis en place (si l'initialisation * ne provient pas d'une mise en place artificielle par une inversion NOT). */ #ifndef NDEBUG forced = (g_scan_token_node_get_flags(G_SCAN_TOKEN_NODE(node)) & STNF_MAIN); assert((!params->initialized && forced) || (params->initialized && (!forced || cflags & TNCF_KEEP_DISCARDED))); #endif if (!params->initialized) { /* Destinations établies une fois pour toutes */ if (cflags & TNCF_KEEP_DISCARDED) { areas = ¶ms->kept_areas; count = ¶ms->kept_count; copy = false; inverted = true; } else if (cflags & TNCF_CREATE_NEW) { areas = ¶ms->created_areas; count = ¶ms->created_count; copy = true; inverted = false; } else { assert(cflags & TNCF_UPDATE_IN_PLACE); areas = ¶ms->main_areas; count = ¶ms->main_count; copy = false; inverted = false; } /* Parcours des combinaisons enregistrées */ for (i = 0; i < node->enrolled_count; i++) { atom = &node->enrolled_atoms[i]; atoms = g_scan_context_get_atom_matches(params->context, atom->pid); first_round = (*count == 0); if (atom->fast_check) { /** * Toutes les correspondances sont validées d'office car le motif identifié * correspondant au motif complet. */ if (!inverted) { for_each_match_area(area, atoms) { /** * La modification de la zone d'origine est possible dans tous les cas * car cette zone a été allouée de façon dédiée à un type de correspondances * et ne sera pas réutilisée comme autre source de correspondance ailleurs. */ assert(area->end >= atom->len); area->start = area->end - atom->len; (*count)++; } } else atoms = NULL; } else { for_each_match_area_safe(area, &atoms, next) { start = area->end - atom->len - atom->pos; status = check_scan_token_node_masked_content(node->bytes, node->len, start, params->content); if (status) { /** * La modification de la zone d'origine est possible dans tous les cas * car cette zone a été allouée de façon dédiée à un type de correspondances * et ne sera pas réutilisée comme autre source de correspondance ailleurs. */ if (!inverted) { area->start = start; area->end += atom->rem; (*count)++; } else del_match_area(area, &atoms); } else { /** * Les principes de modifications restent valables, même inversés. */ if (inverted) { area->start = start; area->end += atom->rem; (*count)++; } else del_match_area(area, &atoms); } } } /* Mise à jour de la liste */ if (atoms != NULL) { if (first_round) *areas = atoms; else merge_match_areas(areas, &atoms); } } } /** * Poursuite des traitements sur des correspondances déjà amorcées, impliquant * des comparaisons entières de motifs. */ else { if (0x0) printf(" LIST : %p (sz = %zu)\n", params->main_areas, params->main_count); for_each_match_area_safe(area, ¶ms->main_areas, next) { assert(area->end <= params->content_end); after = params->content_end - area->end; if (0x0) printf("---------- iter @ %u -> %u\n", (unsigned int)area->start, (unsigned int)area->end); /** * S'il s'avère qu'il existe de multiples correspondances dans l'espace * analysé, c'est la prise en compte d'une éventuelle avarice quant aux * distances consommées qui va sélectionner la position d'une bribe de * correspondance retenue. * * Par exemple, deux correspondances '?1 ?1 [1-3] ?2 ?2' peuvent être * valides pour un même contenu : * * aa.bbb -> correspondance 'aa.bb' * ^ * * aa.bbb -> correspondance 'aa..bb' * ^ */ min_end = params->content_end; max_end = params->content_start; updated = false; /* Souplesse dans les positions ? */ if (offsets_exist(¶ms->offset)) { ranges = get_node_search_offset_ranges_2(¶ms->offset, &rcount); for (r = 0; r < rcount; r++) { assert(ranges[r].has_max); assert((ranges[r].max - ranges[r].min) <= MAX_RANGE_FOR_MANUAL_CHECK); for (p = ranges[r].min; p <= ranges[r].max; p++) { /** * Si la fin d'une correspondance potentielle est trop près de * la fin du contenu binaire et ne peut contenir le motif * représenté, alors la corresponance est écartée sans appel. */ if ((p + node->len) > after) break; status = check_scan_token_node_masked_content(node->bytes, node->len, area->end + p, params->content); if (status) { updated_edge = area->end + p + node->len; if (updated_edge < min_end) min_end = updated_edge; if (updated_edge > max_end) max_end = updated_edge; updated = true; } } } } /* Position immédiatement attendue */ else { /** * Si la fin d'une correspondance potentielle est trop près de * la fin du contenu binaire et ne peut contenir le motif * représenté, alors la corresponance est écartée sans appel. */ if (node->len <= after) { status = check_scan_token_node_masked_content(node->bytes, node->len, area->end, params->content); if (status) { updated_edge = area->end + node->len; min_end = updated_edge; max_end = updated_edge; updated = true; } } } if (updated) { /** * Si seuls les rejets sont d'intérêt, les correspondances établies * ne se voient pas mises à jours ni retirées. */ if ((cflags & TNCF_KEEP_DISCARDED) == 0) { if (cflags & TNCF_UPDATE_IN_PLACE) area->end = (1 /* greedy */ ? min_end : max_end); else if (cflags & TNCF_CREATE_NEW) { new_area = g_umem_slice_alloc(params->allocator); *new_area = *area; new_area->end = (1 /* greedy */ ? min_end : max_end); add_tail_match_area(new_area, ¶ms->created_areas); params->created_count++; } #ifndef NDEBUG else assert(false); #endif } } else { /** * Si la liste principale doit être mise à jour... */ if (cflags & TNCF_UPDATE_IN_PLACE) { del_match_area(area, ¶ms->main_areas); assert(params->main_count > 0); params->main_count--; } /** * Au cas où le rejet est d'intérêt, l'absence de correspondance * est conservée dans une liste dédiée. */ if (cflags & TNCF_KEEP_DISCARDED) { if (cflags & TNCF_UPDATE_IN_PLACE) { add_tail_match_area(area, ¶ms->kept_areas); params->kept_count++; } else if (cflags & TNCF_CREATE_NEW) { new_area = g_umem_slice_alloc(params->allocator); *new_area = *area; new_area->end = (1 /* greedy */ ? min_end : max_end); add_tail_match_area(new_area, ¶ms->kept_areas); params->kept_count++; } #ifndef NDEBUG else assert(false); #endif } } } } params->initialized = true; disable_all_ranges_in_node_search_offset(¶ms->offset); } /****************************************************************************** * * * Paramètres : node = définition de la bribe à manipuler. * * params = accès direct aux éléments utiles aux validations. * * cflags = altérations de traitement à respecter. * * skip = détermine si l'analyse est différée. [OUT] * * * * Description : Transforme les correspondances locales en trouvailles. * * * * Retour : - * * * * Remarques : - * * * ******************************************************************************/ static void g_scan_token_node_masked_check_backward(const GScanTokenNodeMasked *node, scan_node_check_params_t *params, TokenNodeCheckFlags cflags, bool *skip) { #ifndef NDEBUG bool forced; /* Inclusion dans un scan ? */ #endif bool status; /* Bilan d'une correspondance */ match_area_t *area; /* Correspondance à valider */ match_area_t *next; /* Correspondance suivante */ phys_t before; /* Espace disposible avant */ phys_t min_start; /* Début le plus proche trouvé */ phys_t max_start; /* Début le plus distant trouvé*/ bool updated; /* Existence de correspondance */ size_t rcount; /* Quantité de bornes présentes*/ const node_offset_range_t *ranges; /* Bornes d'espace à parcourir */ size_t r; /* Boucle de parcours #xxxxxxxxxxxx*/ phys_t p; /* Boucle de parcours #xxxxxxxxxxxx*/ phys_t updated_edge; /* Nouvelle bordure de motif */ match_area_t *new_area; /* Copie de correspondance */ if (0x0) printf("=================== CHECK :: %s (skip? %d)\n", __FUNCTION__, *skip); if (*skip) return; /** * En lecture à rebourd, au moins un noeud a été solicité pour analyse (lors * du sens de lecteur normal). Donc l'initialisation a déjà dû avoir lieu. */ assert(params->initialized); /** * Si les recherches associées au noeud ont été forcées, alors les traitements * liés ont déjà été effectués, et l'appel de cette fonction aurait dû être sauté. */ #ifndef NDEBUG forced = (g_scan_token_node_get_flags(G_SCAN_TOKEN_NODE(node)) & STNF_MAIN); assert(!forced); #endif /** * ............. */ if (0) { ; } /** * Poursuite des traitements sur des correspondances déjà amorcées, impliquant * des comparaisons entières de motifs. */ else { if (0x0) printf(" LIST : %p (sz = %zu)\n", params->main_areas, params->main_count); for_each_match_area_safe(area, ¶ms->main_areas, next) { assert(params->content_start <= area->start); before = area->start - params->content_start; if (0x0) printf("---------- iter @ %u -> %u\n", (unsigned int)area->start, (unsigned int)area->end); /** * Il ne peut y avoir qu'une seule séquence d'octets à un même * emplacement. En revanche, des modificateurs peuvent construire * possédant une même base, mais offrant des suffixes différents * (par exemple, un marqueur nul UTF-16 final en complément). * * L'ensemble des combinaisons produites doit ainsi être exploré. */ min_start = params->content_start; max_start = params->content_end; updated = false; /* Souplesse dans les positions ? */ if (offsets_exist(¶ms->offset)) { ranges = get_node_search_offset_ranges_2(¶ms->offset, &rcount); for (r = 0; r < rcount; r++) { assert(ranges[r].has_max); assert((ranges[r].max - ranges[r].min) <= MAX_RANGE_FOR_MANUAL_CHECK); for (p = ranges[r].min; p <= ranges[r].max; p++) { /** * Si la fin d'une correspondance potentielle est trop près de * la fin du contenu binaire et ne peut contenir le motif * représenté, alors la corresponance est écartée sans appel. */ if ((p + node->len) > before) break; status = check_scan_token_node_masked_content(node->bytes, node->len, area->start - node->len - p, params->content); if (status) { updated_edge = area->start - node->len - p; if (updated_edge > min_start) min_start = updated_edge; if (updated_edge < max_start) max_start = updated_edge; updated = true; } } } } /* Position immédiatement attendue */ else { /** * Si la fin d'une correspondance potentielle est trop près du * début du contenu binaire et ne peut contenir le motif * représenté, alors la corresponance est écartée sans appel. */ if (node->len <= before) { status = check_scan_token_node_masked_content(node->bytes, node->len, area->start - node->len, params->content); if (status) { updated_edge = area->start - node->len; if (updated_edge > min_start) min_start = updated_edge; if (updated_edge < max_start) max_start = updated_edge; updated = true; } } } if (updated) { /** * Si seuls les rejets sont d'intérêt, les correspondances établies * ne se voient pas mises à jours ni retirées. */ if ((cflags & TNCF_KEEP_DISCARDED) == 0) { if (cflags & TNCF_UPDATE_IN_PLACE) area->start = (1 /* greedy */ ? min_start : max_start); else if (cflags & TNCF_CREATE_NEW) { new_area = g_umem_slice_alloc(params->allocator); *new_area = *area; new_area->start = (1 /* greedy */ ? min_start : max_start); add_tail_match_area(new_area, ¶ms->created_areas); params->created_count++; } #ifndef NDEBUG else assert(false); #endif } } else { /** * Si la liste principale doit être mise à jour... */ if (cflags & TNCF_UPDATE_IN_PLACE) { del_match_area(area, ¶ms->main_areas); assert(params->main_count > 0); params->main_count--; } /** * Au cas où le rejet est d'intérêt, l'absence de correspondance * est conservée dans une liste dédiée. */ if (cflags & TNCF_KEEP_DISCARDED) { if (cflags & TNCF_UPDATE_IN_PLACE) { add_tail_match_area(area, ¶ms->kept_areas); params->kept_count++; } else if (cflags & TNCF_CREATE_NEW) { new_area = g_umem_slice_alloc(params->allocator); *new_area = *area; new_area->start = (1 /* greedy */ ? min_start : max_start); add_tail_match_area(new_area, ¶ms->kept_areas); params->kept_count++; } #ifndef NDEBUG else assert(false); #endif } } } } disable_all_ranges_in_node_search_offset(¶ms->offset); }