Index: user/gabor/tre-integration/contrib/tre/lib/regexec.c =================================================================== --- user/gabor/tre-integration/contrib/tre/lib/regexec.c (revision 226176) +++ user/gabor/tre-integration/contrib/tre/lib/regexec.c (revision 226177) @@ -1,465 +1,463 @@ /* tre_regexec.c - TRE POSIX compatible matching functions (and more). This software is released under a BSD-style license. See the file LICENSE for details and copyright. */ #ifdef HAVE_CONFIG_H #include #endif /* HAVE_CONFIG_H */ #ifdef TRE_USE_ALLOCA /* AIX requires this to be the first thing in the file. */ #ifndef __GNUC__ # if HAVE_ALLOCA_H # include # else # ifdef _AIX #pragma alloca # else # ifndef alloca /* predefined by HP cc +Olibcalls */ char *alloca (); # endif # endif # endif #endif #endif /* TRE_USE_ALLOCA */ #include #include #include #include #ifdef HAVE_WCHAR_H #include #endif /* HAVE_WCHAR_H */ #ifdef HAVE_WCTYPE_H #include #endif /* HAVE_WCTYPE_H */ #ifndef TRE_WCHAR #include #endif /* !TRE_WCHAR */ #ifdef HAVE_MALLOC_H #include #endif /* HAVE_MALLOC_H */ #include #include "tre-fastmatch.h" #include "tre-heuristic.h" #include "tre-internal.h" #include "xmalloc.h" #ifdef TRE_LIBC_BUILD __weak_reference(tre_regexec, regexec); __weak_reference(tre_regnexec, regnexec); __weak_reference(tre_regwexec, regwexec); __weak_reference(tre_regwnexec, regwnexec); __weak_reference(tre_reguexec, reguexec); __weak_reference(tre_regaexec, regaexec); __weak_reference(tre_reganexec, reganexec); __weak_reference(tre_regawexec, regawexec); __weak_reference(tre_regawnexec, regawnexec); #endif /* Fills the POSIX.2 regmatch_t array according to the TNFA tag and match endpoint values. */ void tre_fill_pmatch(size_t nmatch, regmatch_t pmatch[], int cflags, const tre_tnfa_t *tnfa, int *tags, int match_eo) { tre_submatch_data_t *submatch_data; unsigned int i, j; int *parents; i = 0; if (match_eo >= 0 && !(cflags & REG_NOSUB)) { /* Construct submatch offsets from the tags. */ DPRINT(("end tag = t%d = %d\n", tnfa->end_tag, match_eo)); submatch_data = tnfa->submatch_data; while (i < tnfa->num_submatches && i < nmatch) { if (submatch_data[i].so_tag == tnfa->end_tag) pmatch[i].rm_so = match_eo; else pmatch[i].rm_so = tags[submatch_data[i].so_tag]; if (submatch_data[i].eo_tag == tnfa->end_tag) pmatch[i].rm_eo = match_eo; else pmatch[i].rm_eo = tags[submatch_data[i].eo_tag]; /* If either of the endpoints were not used, this submatch was not part of the match. */ if (pmatch[i].rm_so == -1 || pmatch[i].rm_eo == -1) pmatch[i].rm_so = pmatch[i].rm_eo = -1; DPRINT(("pmatch[%d] = {t%d = %d, t%d = %d}\n", i, submatch_data[i].so_tag, pmatch[i].rm_so, submatch_data[i].eo_tag, pmatch[i].rm_eo)); i++; } /* Reset all submatches that are not within all of their parent submatches. */ i = 0; while (i < tnfa->num_submatches && i < nmatch) { if (pmatch[i].rm_eo == -1) assert(pmatch[i].rm_so == -1); assert(pmatch[i].rm_so <= pmatch[i].rm_eo); parents = submatch_data[i].parents; if (parents != NULL) for (j = 0; parents[j] >= 0; j++) { DPRINT(("pmatch[%d] parent %d\n", i, parents[j])); if (pmatch[i].rm_so < pmatch[parents[j]].rm_so || pmatch[i].rm_eo > pmatch[parents[j]].rm_eo) pmatch[i].rm_so = pmatch[i].rm_eo = -1; } i++; } } while (i < nmatch) { pmatch[i].rm_so = -1; pmatch[i].rm_eo = -1; i++; } } /* Wrapper functions for POSIX compatible regexp matching. */ int tre_have_backrefs(const regex_t *preg) { tre_tnfa_t *tnfa = (void *)preg->TRE_REGEX_T_FIELD; return tnfa->have_backrefs; } int tre_have_approx(const regex_t *preg) { tre_tnfa_t *tnfa = (void *)preg->TRE_REGEX_T_FIELD; return tnfa->have_approx; } static int tre_match(const tre_tnfa_t *tnfa, const void *string, size_t len, tre_str_type_t type, size_t nmatch, regmatch_t pmatch[], int eflags, fastmatch_t *shortcut, heur_t *heur) { reg_errcode_t status; int *tags = NULL, eo; /* Check if we can cheat with a faster algorithm. */ if ((shortcut != NULL) && (type != STR_USER)) { DPRINT("tre_match: using tre_match_fast() instead of the full NFA\n"); return tre_match_fast(shortcut, string, len, type, nmatch, pmatch, eflags); } #define FIX_OFFSETS \ if (ret == REG_NOMATCH) \ { \ st += n; \ continue; \ } \ else if ((ret == REG_OK) && !(tnfa->cflags & REG_NOSUB)) \ for (int i = 0; i < nmatch; i++) \ { \ pmatch[i].rm_so += st; \ pmatch[i].rm_eo += st; \ } \ return ret; #define SEEK_TO(off) \ string = (type == STR_WIDE) ? (void *)&data_wide[off] : \ (void *)&data_byte[off]; /* Check if we have a heuristic to speed up the search. */ if ((heur != NULL) && (type != STR_USER)) { int ret; - size_t st = 0, n; + size_t st = 0, i = 1, n; const char *data_byte = string; const tre_char_t *data_wide = string; DPRINT(("tre_match: using a heuristic [%s/%s] to speed up the " "search\n", heur->start->pattern, heur->end->pattern)); while (st < len) { SEEK_TO(st); - /* Look for the beginning of possibly matching text. */ - ret = tre_match_fast(heur->start, string, len - st, type, nmatch, + /* Prefix heuristic */ + ret = tre_match_fast(heur->heurs[0], string, len - st, type, nmatch, pmatch, eflags); if (ret != REG_OK) return ret; st += pmatch[0].rm_so; n = pmatch[0].rm_eo; - /* - * When having a fixed-length pattern there is only - * one heuristic. - */ - if (heur->end == NULL) + /* Intermediate heuristics */ + while (!((heur->heurs[i] == NULL) || + (heur->prefix && heur->heurs[i + 1] == NULL))) { - SEEK_TO(st); + SEEK_TO(st + n); + ret = tre_match_fast(heur->heurs[i], string, len - st - n, type, + nmatch, pmatch, eflags); + if (ret != REG_OK) + return ret; + n += pmatch[0].rm_eo; + i++; + } - DPRINT(("tre_match: calling NFA with offsets [%u/%u]\n", - st, heur->prefix ? len : n + st)); + /* Suffix heuristic available */ + if (heur->prefix && heur->heurs[i] != NULL) + { + SEEK_TO(st + n); + ret = tre_match_fast(heur->heurs[i], string, len - st - n, type, + nmatch, pmatch, eflags); + if (ret != REG_OK) + return ret; + n += pmatch[0].rm_eo; - ret = tre_match(tnfa, string, - heur->prefix ? (len - st) : - n, type, nmatch, - pmatch, eflags, NULL, NULL); - + SEEK_TO(st); + ret = tre_match(tnfa, string, n, type, nmatch, pmatch, + eflags, NULL, NULL); FIX_OFFSETS; } - - SEEK_TO(st + n); - - /* Look for the end of possibly matching text. */ - ret = tre_match_fast(heur->end, string, len - st - n, type, - nmatch, pmatch, eflags); - n += pmatch[0].rm_eo; - - if (ret != REG_OK) - return ret; - - SEEK_TO(st); - - DPRINT(("tre_match: calling NFA with offsets [%u/%u]\n", - st, st + n)); - - ret = tre_match(tnfa, string, n, - type, nmatch, pmatch, eflags, NULL, NULL); - - FIX_OFFSETS; + /* Suffix heuristic not available */ + else + { + SEEK_TO(st); + ret = tre_match(tnfa, string, len - st, type, nmatch, pmatch, + eflags, NULL, NULL); + FIX_OFFSETS; + } } } if (tnfa->num_tags > 0 && nmatch > 0) { #ifdef TRE_USE_ALLOCA tags = alloca(sizeof(*tags) * tnfa->num_tags); #else /* !TRE_USE_ALLOCA */ tags = xmalloc(sizeof(*tags) * tnfa->num_tags); #endif /* !TRE_USE_ALLOCA */ if (tags == NULL) return REG_ESPACE; } /* Dispatch to the appropriate matcher. */ if (tnfa->have_backrefs || eflags & REG_BACKTRACKING_MATCHER) { /* The regex has back references, use the backtracking matcher. */ if (type == STR_USER) { const tre_str_source *source = string; if (source->rewind == NULL || source->compare == NULL) /* The backtracking matcher requires rewind and compare capabilities from the input stream. */ return REG_BADPAT; } status = tre_tnfa_run_backtrack(tnfa, string, (int)len, type, tags, eflags, &eo); } #ifdef TRE_APPROX else if (tnfa->have_approx || eflags & REG_APPROX_MATCHER) { /* The regex uses approximate matching, use the approximate matcher. */ regamatch_t match; regaparams_t params; tre_regaparams_default(¶ms); params.max_err = 0; params.max_cost = 0; status = tre_tnfa_run_approx(tnfa, string, (int)len, type, tags, &match, params, eflags, &eo); } #endif /* TRE_APPROX */ else { /* Exact matching, no back references, use the parallel matcher. */ status = tre_tnfa_run_parallel(tnfa, string, (int)len, type, tags, eflags, &eo); } if (status == REG_OK) /* A match was found, so fill the submatch registers. */ tre_fill_pmatch(nmatch, pmatch, tnfa->cflags, tnfa, tags, eo); #ifndef TRE_USE_ALLOCA if (tags) xfree(tags); #endif /* !TRE_USE_ALLOCA */ return status; } int tre_regnexec(const regex_t *preg, const char *str, size_t len, size_t nmatch, regmatch_t pmatch[], int eflags) { tre_tnfa_t *tnfa = (void *)preg->TRE_REGEX_T_FIELD; tre_str_type_t type = (TRE_MB_CUR_MAX == 1) ? STR_BYTE : STR_MBS; if (eflags & REG_STARTEND) CALL_WITH_OFFSET(tre_match(tnfa, &str[offset], slen, type, nmatch, pmatch, eflags, preg->shortcut, preg->heur)); else return tre_match(tnfa, str, len, type, nmatch, pmatch, eflags, preg->shortcut, preg->heur); } int tre_regexec(const regex_t *preg, const char *str, size_t nmatch, regmatch_t pmatch[], int eflags) { return tre_regnexec(preg, str, (unsigned)-1, nmatch, pmatch, eflags); } #ifdef TRE_WCHAR int tre_regwnexec(const regex_t *preg, const wchar_t *str, size_t len, size_t nmatch, regmatch_t pmatch[], int eflags) { tre_tnfa_t *tnfa = (void *)preg->TRE_REGEX_T_FIELD; tre_str_type_t type = STR_WIDE; if (eflags & REG_STARTEND) CALL_WITH_OFFSET(tre_match(tnfa, &str[offset], slen, type, nmatch, pmatch, eflags, preg->shortcut, preg->heur)); else return tre_match(tnfa, str, len, STR_WIDE, nmatch, pmatch, eflags, preg->shortcut, preg->heur); } int tre_regwexec(const regex_t *preg, const wchar_t *str, size_t nmatch, regmatch_t pmatch[], int eflags) { return tre_regwnexec(preg, str, (unsigned)-1, nmatch, pmatch, eflags); } #endif /* TRE_WCHAR */ int tre_reguexec(const regex_t *preg, const tre_str_source *str, size_t nmatch, regmatch_t pmatch[], int eflags) { tre_tnfa_t *tnfa = (void *)preg->TRE_REGEX_T_FIELD; return tre_match(tnfa, str, (unsigned)-1, STR_USER, nmatch, pmatch, eflags, preg->shortcut, preg->heur); } #ifdef TRE_APPROX /* Wrapper functions for approximate regexp matching. */ static int tre_match_approx(const tre_tnfa_t *tnfa, const void *string, size_t len, tre_str_type_t type, regamatch_t *match, regaparams_t params, int eflags) { reg_errcode_t status; int *tags = NULL, eo; /* If the regexp does not use approximate matching features, the maximum cost is zero, and the approximate matcher isn't forced, use the exact matcher instead. */ if (params.max_cost == 0 && !tnfa->have_approx && !(eflags & REG_APPROX_MATCHER)) return tre_match(tnfa, string, len, type, match->nmatch, match->pmatch, eflags, NULL, NULL); /* Back references are not supported by the approximate matcher. */ if (tnfa->have_backrefs) return REG_BADPAT; if (tnfa->num_tags > 0 && match->nmatch > 0) { #if TRE_USE_ALLOCA tags = alloca(sizeof(*tags) * tnfa->num_tags); #else /* !TRE_USE_ALLOCA */ tags = xmalloc(sizeof(*tags) * tnfa->num_tags); #endif /* !TRE_USE_ALLOCA */ if (tags == NULL) return REG_ESPACE; } status = tre_tnfa_run_approx(tnfa, string, (int)len, type, tags, match, params, eflags, &eo); if (status == REG_OK) tre_fill_pmatch(match->nmatch, match->pmatch, tnfa->cflags, tnfa, tags, eo); #ifndef TRE_USE_ALLOCA if (tags) xfree(tags); #endif /* !TRE_USE_ALLOCA */ return status; } int tre_reganexec(const regex_t *preg, const char *str, size_t len, regamatch_t *match, regaparams_t params, int eflags) { tre_tnfa_t *tnfa = (void *)preg->TRE_REGEX_T_FIELD; tre_str_type_t type = (TRE_MB_CUR_MAX == 1) ? STR_BYTE : STR_MBS; return tre_match_approx(tnfa, str, len, type, match, params, eflags); } int tre_regaexec(const regex_t *preg, const char *str, regamatch_t *match, regaparams_t params, int eflags) { return tre_reganexec(preg, str, (unsigned)-1, match, params, eflags); } #ifdef TRE_WCHAR int tre_regawnexec(const regex_t *preg, const wchar_t *str, size_t len, regamatch_t *match, regaparams_t params, int eflags) { tre_tnfa_t *tnfa = (void *)preg->TRE_REGEX_T_FIELD; return tre_match_approx(tnfa, str, len, STR_WIDE, match, params, eflags); } int tre_regawexec(const regex_t *preg, const wchar_t *str, regamatch_t *match, regaparams_t params, int eflags) { return tre_regawnexec(preg, str, (unsigned)-1, match, params, eflags); } #endif /* TRE_WCHAR */ void tre_regaparams_default(regaparams_t *params) { memset(params, 0, sizeof(*params)); params->cost_ins = 1; params->cost_del = 1; params->cost_subst = 1; params->max_cost = INT_MAX; params->max_ins = INT_MAX; params->max_del = INT_MAX; params->max_subst = INT_MAX; params->max_err = INT_MAX; } #endif /* TRE_APPROX */ /* EOF */ Index: user/gabor/tre-integration/contrib/tre/lib/tre-heuristic.c =================================================================== --- user/gabor/tre-integration/contrib/tre/lib/tre-heuristic.c (revision 226176) +++ user/gabor/tre-integration/contrib/tre/lib/tre-heuristic.c (revision 226177) @@ -1,409 +1,421 @@ /*- * Copyright (C) 2011 Gabor Kovesdan * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include #endif /* HAVE_CONFIG_H */ #include #include #include #ifdef TRE_WCHAR #include #endif #include "tre-fastmatch.h" #include "tre-heuristic.h" #include "tre-internal.h" #include "xmalloc.h" /* * A full regex implementation requires a finite state automaton * and using an automaton is always about a trade-off. A DFA is * fast but complex and requires more memory because of the * high number of states. NFA is slower but simpler and uses less * memory. Regular expression matching is an underlying common task * that is required to be efficient but correctness, clean and * maintanable code are also requirements. So what we do is using * an NFA implementation and heuristically locate the possible matches * with a cheaper algorithm and only apply the heavy one to the * possibly matching segments. This allows us to benefit from the * advantages of an NFA implementation reducing the effect of the * performance impact. */ +#define MAX_FRAGMENTS 32 + /* * Parses bracket expression seeking to the end of the enclosed text. * The parameters are the opening (oe) and closing elements (ce). * Can handle nested bracket expressions. */ #define PARSE_UNIT(oe, ce) \ { \ int level = 0; \ \ while (i < len) \ { \ if (regex[i] == TRE_CHAR(oe)) \ level++; \ else if (regex[i] == TRE_CHAR(ce)) \ level--; \ if (level == 0) \ break; \ i++; \ } \ } #define PARSE_BRACKETS \ { \ i++; \ if (regex[i] == TRE_CHAR('^')) \ i++; \ if (regex[i] == TRE_CHAR(']')) \ i++; \ \ for (; i < len; i++) \ { \ if (regex[i] == TRE_CHAR('[')) \ return REG_BADPAT; \ if (regex[i] == TRE_CHAR(']')) \ break; \ } \ } /* * Finishes a segment (fixed-length text fragment). */ #define END_SEGMENT \ do \ { \ st = i + 1; \ escaped = false; \ goto end_segment; \ } while (0) #define STORE_CHAR(esc) \ do \ { \ if (esc) \ heur[pos++] = TRE_CHAR('\\'); \ heur[pos++] = regex[i]; \ escaped = false; \ continue; \ } while (0) /* * Parses a regular expression and constructs a heuristic in heur_t and * returns REG_OK if successful or the corresponding error code if a * heuristic cannot be constructed. */ int tre_compile_heur(heur_t *h, const tre_char_t *regex, size_t len, int cflags) { - tre_char_t *heur; - int st = 0, pos = 0; + tre_char_t *arr[MAX_FRAGMENTS], *heur; + size_t length[MAX_FRAGMENTS]; + int errcode, j = 0, pos = 0, st = 0; bool escaped = false; - int errcode, ret; - /* Temporary space, len will be enough. */ - heur = xmalloc(len); + heur = xmalloc(len * sizeof(tre_char_t)); if (!heur) return REG_ESPACE; - memset(h, 0, sizeof(*h)); - while (true) { /* * Process the pattern char-by-char. * * i: position in regex * st: start offset of current segment (fixed-length fragment) * to be processed * pos: current position (and length) in the temporary space where * we copy the current segment */ for (int i = st; i < len; i++) { switch (regex[i]) { /* * Bracketed expression is substituted with a dot or the * brackets are treated as normal if at least the opening * bracket is escaped. */ case TRE_CHAR('['): if (escaped) STORE_CHAR(true); else { PARSE_BRACKETS; heur[pos++] = TRE_CHAR('.'); } continue; /* * If a repetition marker, erases the repeting character * and terminates the segment, otherwise treated as a normal * character. */ case TRE_CHAR('{'): if (escaped && (i == 1)) STORE_CHAR(true); else if ((i == 0) && !(cflags & REG_EXTENDED)) STORE_CHAR(true); else if ((i == 0) && (cflags & REG_EXTENDED)) continue; PARSE_UNIT('{', '}'); if (escaped ^ (cflags & REG_EXTENDED)) { pos--; END_SEGMENT; } else STORE_CHAR(cflags & REG_EXTENDED); continue; /* * Terminates the current segment when escaped, * otherwise treated as a normal character. */ case TRE_CHAR('('): if (escaped ^ (cflags & REG_EXTENDED)) { PARSE_UNIT('(', ')'); END_SEGMENT; } else STORE_CHAR(cflags & REG_EXTENDED); continue; /* * Sets escaped flag. * Escaped escape is treated as a normal character. * (This is also the GNU behaviour.) */ case TRE_CHAR('\\'): if (escaped) STORE_CHAR(true); else escaped = true; continue; /* * BRE: If not the first character and not escaped, erases the * last character and terminates the segment. * Otherwise treated as a normal character. * ERE: Skipped if first character (GNU), rest is like in BRE. */ case TRE_CHAR('*'): if (escaped || (!(cflags & REG_EXTENDED) && (i == 0))) STORE_CHAR(true); else if ((i != 0)) { pos--; END_SEGMENT; } continue; /* * In BRE, it is a normal character, behavior is undefined * when escaped. * In ERE, it is special unless escaped. Terminate segment * when not escaped. Last character is not removed because it * must occur at least once. It is skipped when first * character (GNU). */ case TRE_CHAR('+'): if ((cflags & REG_EXTENDED) && (i == 0)) continue; else if ((cflags & REG_EXTENDED) ^ escaped) END_SEGMENT; else STORE_CHAR(cflags & REG_EXTENDED); continue; /* * In BRE, it is a normal character, behavior is undefined * when escaped. * In ERE, it is special unless escaped. Terminate segment * when not escaped. Last character is removed. Skipped when * first character (GNU). */ case TRE_CHAR('?'): if ((cflags & REG_EXTENDED) && (i == 0)) continue; if ((cflags & REG_EXTENDED) ^ escaped) { pos--; END_SEGMENT; } else STORE_CHAR(true); continue; /* * Fail if it is an ERE alternation marker. */ case TRE_CHAR('|'): if ((cflags & REG_EXTENDED) && !escaped) { errcode = REG_BADPAT; - goto badpat2; + goto err; } else if (!(cflags & REG_EXTENDED) && escaped) END_SEGMENT; else STORE_CHAR(cflags & REG_EXTENDED); continue; /* * Cut the segment at an escaped dot because the fast matcher * cannot handle it. */ case TRE_CHAR('.'): STORE_CHAR(escaped); continue; /* * If escaped, terminates segment. * Otherwise adds current character to the current segment * by copying it to the temporary space. */ default: if (escaped) END_SEGMENT; else STORE_CHAR(false); continue; } } /* We are done with the pattern if we got here. */ st = len; end_segment: - /* If it is not initialized yet, then we just got the first segment. */ - if (h->start == NULL) + if (st == len && pos == 0) { - - /* - * An empty or a one-char prefix segment is useless, - * better to just fail. - */ - if (pos <= 1) + if (j == 0) { errcode = REG_BADPAT; - DPRINT("tre_compile_heur: pattern does not have a " - " fixed-length prefix that is long enough\n"); - goto badpat1; + goto err; } - - h->start = xmalloc(sizeof(fastmatch_t)); - if (!h->start) - { - errcode = REG_ESPACE; - goto space1; - } - - ret = tre_compile_fast(h->start, heur, pos, 0); - if (ret != REG_OK) - { - errcode = REG_BADPAT; - goto badpat2; - } - DPRINT(("tre_compile_heur: fixed-length prefix is %s\n", - h->start->pattern)); + h->prefix = true; + goto ok; } - /* - * If true, this is the last segment. We do not care about the - * middle ones. - */ - else if (st == len) + if (j == MAX_FRAGMENTS) { - - /* If empty, we only have a prefix heuristic. */ - if (pos == 0) - { - h->prefix = true; - errcode = REG_OK; - DPRINT("tre-compile_heur: using only a fixed-length prefix; " - "no fixed-length suffix is available\n"); - goto ok; - } - - h->end = xmalloc(sizeof(fastmatch_t)); - if (!h->end) - { - errcode = REG_ESPACE; - goto space2; - } - - ret = tre_compile_fast(h->end, heur, pos, 0); - if (ret != REG_OK) - { - xfree(h->end); - h->prefix = true; - } - errcode = REG_OK; - DPRINT(("tre_compile_heur: fixed-length suffix is %s\n", - h->end->pattern)); - goto ok; + errcode = REG_BADPAT; + goto err; } - /* Just drop middle segments by overwriting the temporary space. */ + arr[j] = xmalloc((pos + 1) * sizeof(tre_char_t)); + if (!arr[j]) + { + errcode = REG_ESPACE; + goto err; + } + memcpy(&arr[j], &heur, pos); + arr[j][pos] = TRE_CHAR('\0'); + length[j] = pos; + j++; pos = 0; } -badpat2: -space2: - if (h->start != NULL) - xfree(h->start); -badpat1: -space1: - DPRINT("tre_compile_heur: compiling a heuristic failed\n"); ok: + + { + size_t m = 1; + int ret; + + for(int i = 1; i < j; i++) + m = (length[i] > length[m]) ? i : m; + + if (!h->heurs) + { + errcode = REG_ESPACE; + goto err; + } + + for (int i = 0; i < MIN(3, j + 1); i++) + { + h->heurs[i] = xmalloc(sizeof(fastmatch_t)); + if (!h->heurs[i]) + { + errcode = REG_ESPACE; + goto err; + } + } + +#define CHECK_ERR + if (ret != REG_OK) + { + errcode = REG_BADPAT; + goto err2; + } + + ret = tre_compile_fast(h->heurs[0], arr[0], length[0], 0); + CHECK_ERR + if (j == 1) + { + xfree(h->heurs[1]); + h->heurs[1] = NULL; + goto finish; + } + else + ret = tre_compile_fast(h->heurs[1], arr[m], length[m], 0); + CHECK_ERR + if (h->prefix) + { + xfree(h->heurs[2]); + h->heurs[2] = NULL; + goto finish; + } + else + ret = tre_compile_fast(h->heurs[2], arr[j - 1], length[j - 1], 0); + CHECK_ERR + h->heurs[3] = NULL; + + errcode = REG_OK; + goto finish; + } + +err2: + for (int i = 0; h->heurs[i] != NULL; i++) + tre_free_fast(h->heurs[i]); + xfree(h->heurs); +err: +finish: + for (int i = 0; i < j; i++) + xfree(arr[i]); xfree(heur); return errcode; } /* * Frees a heuristic. */ void tre_free_heur(heur_t *h) { - if (h->start != NULL) - xfree(h->start); - if (h->end != NULL) - xfree(h->end); + for (int i = 0; h->heurs[i] != NULL; i++) + tre_free_fast(h->heurs[i]); DPRINT("tre_free_heur: resources are freed\n"); } Index: user/gabor/tre-integration/contrib/tre/lib/tre-heuristic.h =================================================================== --- user/gabor/tre-integration/contrib/tre/lib/tre-heuristic.h (revision 226176) +++ user/gabor/tre-integration/contrib/tre/lib/tre-heuristic.h (revision 226177) @@ -1,21 +1,20 @@ #ifndef TRE_HEURISTIC_H #define TRE_HEURISTIC_H 1 #include #include #include "tre-fastmatch.h" #include "tre-internal.h" typedef struct { - fastmatch_t *start; - fastmatch_t *end; + fastmatch_t *heurs[4]; bool prefix; + bool newline; } heur_t; - extern int tre_compile_heur(heur_t *h, const tre_char_t *regex, size_t len, int cflags); extern void tre_free_heur(heur_t *h); #endif /* TRE_HEURISTIC_H */