LCOV - code coverage report
Current view: top level - contrib/pg_trgm - trgm_regexp.c (source / functions) Hit Total Coverage
Test: test.info Lines: 477 531 89.8 %
Date: 2013-01-22 Functions: 24 25 96.0 %
Branches: 235 330 71.2 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * contrib/pg_trgm/trgm_regexp.c - regular expression matching using trigrams
       3                 :            :  *
       4                 :            :  * The general idea of index support for a regular expression (regex) search
       5                 :            :  * is to transform the regex to a logical expression on trigrams. For example:
       6                 :            :  *
       7                 :            :  *   (ab|cd)efg => ((abe & bef) | (cde & def)) & efg
       8                 :            :  *
       9                 :            :  * If a string matches the regex, then it must match the logical expression of
      10                 :            :  * trigrams. The opposite is not necessary true, however: a string that matches
      11                 :            :  * the logical expression might not match the original regex. Such false
      12                 :            :  * positives are removed during recheck.
      13                 :            :  *
      14                 :            :  * The algorithm to convert a regex to a logical expression is based on
      15                 :            :  * analysis of an automaton that corresponds to regex. The algorithm consists
      16                 :            :  * of four stages:
      17                 :            :  *
      18                 :            :  * 1) Compile the regexp to CNFA form. This is handled by the PostgreSQL
      19                 :            :  *    regexp library, but we have peek into the data structures it produces.
      20                 :            :  *
      21                 :            :  * 2) Transform the original CNFA into an automaton-like graph, where arcs
      22                 :            :  *    are labeled with trigrams that must be present in order to move from
      23                 :            :  *    state to another via the arc. The trigrams used in this stage consist
      24                 :            :  *    of colors, not characters, like the original CNFA.
      25                 :            :  *
      26                 :            :  * 3) Expand the color trigrams into regular trigrams consisting of
      27                 :            :  *    characters. If too many distinct trigrams are produced, trigrams are
      28                 :            :  *    eliminated and the graph is simplified until it's simple enough.
      29                 :            :  *
      30                 :            :  * 4) Finally, the resulting graph is packed into a PackedGraph struct, and
      31                 :            :  *    returned to the caller.
      32                 :            :  *
      33                 :            :  *
      34                 :            :  * 1) Compile the regexp to CNFA form
      35                 :            :  * ----------------------------------
      36                 :            :  * The automaton returned by the regexp compiler is a graph where vertices
      37                 :            :  * are "states" and arcs are labeled with colors. Each color represents
      38                 :            :  * a group of characters, so that all characters assigned to the same color
      39                 :            :  * are interchangeable, as far as matching the regexp is concerned. There are
      40                 :            :  * two special states: "initial" and "final". There can be multiple outgoing
      41                 :            :  * arcs from a state labeled with the same color, which makes the automaton
      42                 :            :  * non-deterministic, because it can be in many states simultaneously.
      43                 :            :  *
      44                 :            :  * 2) Transform the original CNFA into an automaton-like graph
      45                 :            :  * -----------------------------------------------------------
      46                 :            :  * In the 2nd stage, the automaton is transformed into a graph that resembles
      47                 :            :  * the original CNFA. Each state in the transformed graph represents a state
      48                 :            :  * from the original CNFA, with an "enter key". The enter key consists of the
      49                 :            :  * last two characters (colors, to be precise) read before entering the state.
      50                 :            :  * There can be one or more states in the transformed graph for each state in
      51                 :            :  * the original CNFA, depending on what characters can precede it. Each arc
      52                 :            :  * is labelled with a trigram that must be present in the string to match.
      53                 :            :  *
      54                 :            :  * So the transformed graph resembles the original CNFA, but the arcs are
      55                 :            :  * labeled with trigrams instead of individual characters, and there are
      56                 :            :  * more states. It is a lossy representation of the original CNFA: any string
      57                 :            :  * that matches the original regexp must match the transformed graph, but the
      58                 :            :  * reverse is not true.
      59                 :            :  *
      60                 :            :  * When building the graph, if the number of states or arcs exceed pre-defined
      61                 :            :  * limits, we give up and simply mark any states not yet processed as final
      62                 :            :  * states. Roughly speaking, that means that we make use of some portion from
      63                 :            :  * the beginning of the regexp.
      64                 :            :  *
      65                 :            :  * 3) Expand the color trigrams into regular trigrams
      66                 :            :  * --------------------------------------------------
      67                 :            :  * The trigrams in the transformed graph are "color trigrams", consisting
      68                 :            :  * of three consecutive colors that must be present in the string. But for
      69                 :            :  * search, we need regular trigrams consisting of characters. In the 3rd
      70                 :            :  * stage, the color trigrams are expanded into regular trigrams. Since each
      71                 :            :  * color can represent many characters, the total number of regular trigrams
      72                 :            :  * after expansion could be very large. Because searching an index with
      73                 :            :  * thousands of trigrams would be slow, and would likely produce so many
      74                 :            :  * "false positives" that you would have to traverse a large fraction of the
      75                 :            :  * index, the graph is simplified further in a lossy fashion by removing
      76                 :            :  * color trigrams until the number of trigrams after expansion is below
      77                 :            :  * MAX_TRGM_COUNT threshold. When a color trigram is removed, the states
      78                 :            :  * connected by any arcs labelled with that trigram are merged.
      79                 :            :  *
      80                 :            :  * 4) Pack the graph into a compact representation
      81                 :            :  * -----------------------------------------------
      82                 :            :  * The 2nd and 3rd stages might have eliminated or merged many of the states
      83                 :            :  * and trigrams created earlier, so in this final stage, the graph is
      84                 :            :  * compacted and packed into a simpler struct that contains only the
      85                 :            :  * information needed to evaluate it.
      86                 :            :  *
      87                 :            :  *
      88                 :            :  * OLD COMMENTS:
      89                 :            :  * States of the graph produced in the first stage are marked with "keys". Key
      90                 :            :  * is a pair of a "prefix" and a state of the original automaton. "Prefix" is
      91                 :            :  * pair of colors of last two read characters. So, knowing the prefix is enough
      92                 :            :  * to know what is a color trigram when we read new character with some
      93                 :            :  * particular color. However, we can know single color of prefix or don't know
      94                 :            :  * any color of it. Each state of resulting graph have an "enter key" (with that
      95                 :            :  * key we've entered this state) and a set of keys which are reachable without
      96                 :            :  * reading any predictable color trigram. The algorithm of processing each state
      97                 :            :  * of resulting graph are so:
      98                 :            :  * 1) Add all keys which achievable without reading of any predictable color
      99                 :            :  *    trigram.
     100                 :            :  * 2) Add outgoing arcs labeled with trigrams.
     101                 :            :  * Step 2 leads to creation of new states. We use breadth-first algorithm for
     102                 :            :  * processing them.
     103                 :            :  *
     104                 :            :  * Consider this on example of regex ab[cd]. This regex are transformed into
     105                 :            :  * following CNFA (for simplicity of example we don't use colors):
     106                 :            :  *
     107                 :            :  *                    4#
     108                 :            :  *                  c/
     109                 :            :  *       a     b    /
     110                 :            :  *   1* --- 2 ---- 3
     111                 :            :  *                  \
     112                 :            :  *                  d\
     113                 :            :  *                    5#
     114                 :            :  *
     115                 :            :  * We use * to mark initial state and # to mark final state. It's not depicted,
     116                 :            :  * but states 1, 4, 5 have self-referencing arcs for all possible characters,
     117                 :            :  * because pattern can match to any part of string.
     118                 :            :  *
     119                 :            :  * As the result of first stage we will have following graph:
     120                 :            :  *
     121                 :            :  *     abc      abd
     122                 :            :  * 2# <---- 1* ----> 3#
     123                 :            :  *
     124                 :            :  * Let us consider the sequence of algorithm work for this graph producing.
     125                 :            :  * We will designate state key as (prefix.s, prefix.ambiguity, nstate).
     126                 :            :  * 1) Create state 1 with enter key ("  ", true, 1).
     127                 :            :  * 2) Add key (" a", true, 2) to state 1.
     128                 :            :  * 3) Add key ("ab", false, 3) to state 1.
     129                 :            :  * 4) Add arc from output state 1 to new state 2 with enter key
     130                 :            :  *    ("bc", false, 4).
     131                 :            :  * 5) Mark state 2 final because state 4 of source CNFA is marked as final.
     132                 :            :  * 6) Add arc from output state 1 to new state 3 with enter key
     133                 :            :  *    ("bd", false, 5).
     134                 :            :  * 7) Mark state 3 final because state 4 of source CNFA is marked as final.
     135                 :            :  *
     136                 :            :  * At the second stage we select color trigrams to expand into simple trigrams.
     137                 :            :  * We're removing color trigrams starting from the most wide. When removing
     138                 :            :  * color trigram we have to merge states connected by corresponding arcs.
     139                 :            :  * It's necessary to not merge initial and final states, because our graph
     140                 :            :  * becomes useless in this case.
     141                 :            :  */
     142                 :            : #include "postgres.h"
     143                 :            : 
     144                 :            : #include "trgm.h"
     145                 :            : 
     146                 :            : #include "catalog/pg_collation.h"
     147                 :            : #include "fmgr.h"
     148                 :            : #include "miscadmin.h"
     149                 :            : #include "mb/pg_wchar.h"
     150                 :            : #include "nodes/pg_list.h"
     151                 :            : #include "regex/regex.h"
     152                 :            : #undef INFINITY /* avoid conflict of INFINITY definition */
     153                 :            : #include "regex/regguts.h"
     154                 :            : #include "tsearch/ts_locale.h"
     155                 :            : #include "utils/hsearch.h"
     156                 :            : 
     157                 :            : /*
     158                 :            :  * Uncomment to print intermediate stages, for exploring and debugging the
     159                 :            :  * algorithm implementation. This produces three graphs in Graphviz .dot
     160                 :            :  * format, in /tmp.
     161                 :            :  */
     162                 :            : #define TRGM_REGEXP_DEBUG
     163                 :            : 
     164                 :            : /*---
     165                 :            :  * Following group of parameters are used in order to limit our computations.
     166                 :            :  * Otherwise regex processing could be too slow and memory-consuming.
     167                 :            :  *
     168                 :            :  *  MAX_RESULT_STATES - How many states we allow in result CNFA-like graph
     169                 :            :  *  MAX_RESULT_ARCS - How many arcs we allow in result CNFA-like graph
     170                 :            :  *  MAX_TRGM_COUNT - How many simple trigrams we allow to extract
     171                 :            :  */
     172                 :            : #define MAX_RESULT_STATES       128
     173                 :            : #define MAX_RESULT_ARCS         1024
     174                 :            : #define MAX_TRGM_COUNT          256
     175                 :            : 
     176                 :            : /* Virtual color for representation in prefixes and color trigrams. */
     177                 :            : #define EMPTY_COLOR                     ((color) -1)
     178                 :            : #define UNKNOWN_COLOR           ((color) -2)
     179                 :            : 
     180                 :            : /*
     181                 :            :  * Widechar trigram datatype for holding trigram before possible conversion into
     182                 :            :  * CRC32
     183                 :            :  */
     184                 :            : typedef color ColorTrgm[3];
     185                 :            : 
     186                 :            : /*
     187                 :            :  * Maximum length of multibyte encoding character is 4. So, we can hold it in
     188                 :            :  * 32 bit integer for handling simplicity.
     189                 :            :  */
     190                 :            : typedef uint32 mb_char;
     191                 :            : 
     192                 :            : /*----
     193                 :            :  * Attributes of CNFA colors:
     194                 :            :  *
     195                 :            :  *  expandable               - flag indicates we potentially can expand this
     196                 :            :  *                             color into distinct characters.
     197                 :            :  *  containNonAlpha          - flag indicates if color might contain
     198                 :            :  *                             non-alphanumeric characters (which aren't
     199                 :            :  *                             extracted into trigrams)
     200                 :            :  *  alphaCharsCount          - count of characters in color
     201                 :            :  *  alphaCharsCountAllocated - allocated size of alphaChars array
     202                 :            :  *  alphaChars               - array of alphanumeric characters of this color
     203                 :            :  *                             (which are extracted into trigrams)
     204                 :            :  *
     205                 :            :  * When expandable is false, all other attributes doesn't matter we just think
     206                 :            :  * this color as always unknown character.
     207                 :            :  */
     208                 :            : typedef struct
     209                 :            : {
     210                 :            :         bool            expandable;
     211                 :            :         bool            containNonAlpha;
     212                 :            :         int                     alphaCharsCount;
     213                 :            :         int                     alphaCharsCountAllocated;
     214                 :            :         mb_char    *alphaChars;
     215                 :            : } ColorInfo;
     216                 :            : 
     217                 :            : /*
     218                 :            :  * Prefix is information about colors of last two read characters when coming
     219                 :            :  * into specific CNFA state. These colors could have special values
     220                 :            :  * UNKNOWN_COLOR and EMPTY_COLOR. UNKNOWN_COLOR means that we read some
     221                 :            :  * character of unexpandable color. EMPTY_COLOR means that we read
     222                 :            :  * non-alphanumeric character.
     223                 :            :  */
     224                 :            : typedef struct
     225                 :            : {
     226                 :            :         color   s[2];
     227                 :            : } TrgmPrefix;
     228                 :            : 
     229                 :            : /*
     230                 :            :  * "Key" of resulting state: pair of prefix and source CNFA state.
     231                 :            :  */
     232                 :            : typedef struct
     233                 :            : {
     234                 :            :         TrgmPrefix      prefix;
     235                 :            :         int                     nstate;
     236                 :            : } TrgmStateKey;
     237                 :            : 
     238                 :            : /*---
     239                 :            :  * State of resulting graph.
     240                 :            :  *
     241                 :            :  *  enterKey - a key with which we can enter this state
     242                 :            :  *  keys     - all keys achievable without reading any predictable trigram
     243                 :            :  *  arcs     - outgoing arcs of this state
     244                 :            :  *  parent   - parent state if this state has been merged
     245                 :            :  *  children - children states if this state has been merged
     246                 :            :  *  fin      - flag indicated this state is final
     247                 :            :  *  init     - flag indicated this state is initial
     248                 :            :  *  queued   - flag indicated this state is queued in CNFA-like graph
     249                 :            :  *             construction
     250                 :            :  *  number   - number of this state (use at the package stage)
     251                 :            :  */
     252                 :            : typedef struct TrgmState
     253                 :            : {
     254                 :            :         TrgmStateKey enterKey;
     255                 :            :         List       *arcs;
     256                 :            :         bool            fin;
     257                 :            :         bool            init;
     258                 :            : 
     259                 :            :         /* (stage 2) */
     260                 :            :         List       *keys;
     261                 :            :         bool            queued;
     262                 :            : 
     263                 :            :         struct TrgmState *parent;
     264                 :            :         List       *children;
     265                 :            :         int                     number;
     266                 :            : } TrgmState;
     267                 :            : 
     268                 :            : /*
     269                 :            :  * Arc in the transformed graph. Arc is labeled with trigram.
     270                 :            :  */
     271                 :            : typedef struct
     272                 :            : {
     273                 :            :         TrgmState  *target;
     274                 :            :         ColorTrgm       trgm;
     275                 :            : } TrgmArc;
     276                 :            : 
     277                 :            : /*
     278                 :            :  * Information about arc of specific color trigram: contain pointers to the
     279                 :            :  * source and target states. (stage 3)
     280                 :            :  */
     281                 :            : typedef struct
     282                 :            : {
     283                 :            :         TrgmState  *source;
     284                 :            :         TrgmState  *target;
     285                 :            : } ArcInfo;
     286                 :            : 
     287                 :            : /*---
     288                 :            :  * Information about color trigram: (stage 3)
     289                 :            :  *
     290                 :            :  * trgm     - trigram itself
     291                 :            :  * number   - number of this trigram (used in the package stage)
     292                 :            :  * count    - number of simple trigrams fitting into this color trigram
     293                 :            :  * expanded - flag indicates this color trigram is expanded into simple trigrams
     294                 :            :  * arcs     - list of all arcs labeled with this color trigram.
     295                 :            :  */
     296                 :            : typedef struct
     297                 :            : {
     298                 :            :         ColorTrgm       trgm;
     299                 :            :         int                     number;
     300                 :            :         int                     count;
     301                 :            :         bool            expanded;
     302                 :            :         List       *arcs;
     303                 :            : } ColorTrgmInfo;
     304                 :            : 
     305                 :            : /*---
     306                 :            :  * Data structure representing all the data we need during regex processing.
     307                 :            :  * Initially we set "cnfa" to cnfa of regex, write color information info
     308                 :            :  * "colorInfo" and set "overflowed" flag to false. And the stage of trigram
     309                 :            :  * CFNA-like graph creation "states", "initState" and "arcsCount" are filled.
     310                 :            :  * "owerflowed" flag could be set in case of overflow. Then we collect array
     311                 :            :  * of all present color trigrams to "colorTrgms" and "colorTrgmsCount" and
     312                 :            :  * expand them into  "trg" and "totalTrgmCount".
     313                 :            :  *
     314                 :            :  *  cnfa            - source CFNA of regex
     315                 :            :  *  colorInfo       - processed information of regex colors
     316                 :            :  *  ncolors                     - number of colors in colorInfo
     317                 :            :  *  states          - hash of states of resulting graph
     318                 :            :  *  initState       - pointer to initial state of resulting graph
     319                 :            :  *  arcsCount       - total number of arcs of resulting graph (for resource
     320                 :            :  *                    limiting)
     321                 :            :  *  colorTrgms      - array of all color trigrams presented in graph
     322                 :            :  *  colorTrgmsCount - count of that color trigrams
     323                 :            :  *  totalTrgmCount  - total count of extracted simple trigrams
     324                 :            :  *  queue           - queue for CFNA-like graph construction
     325                 :            :  *  overflowed      - if set, we have exceeded resource limit for transformation
     326                 :            :  */
     327                 :            : typedef struct
     328                 :            : {
     329                 :            :         /*
     330                 :            :          * Source CNFA of the regexp, and color information extracted from it
     331                 :            :          * (stage 1)
     332                 :            :          */
     333                 :            :         struct cnfa *cnfa;
     334                 :            :         ColorInfo  *colorInfo;
     335                 :            :         int                     ncolors;
     336                 :            : 
     337                 :            :         /* Transformed graph (stage 2) */
     338                 :            :         HTAB       *states;
     339                 :            :         TrgmState  *initState;
     340                 :            :         int                     arcsCount;
     341                 :            :         List       *queue;
     342                 :            :         bool            overflowed;
     343                 :            : 
     344                 :            :         /* Information about distinct color trigrams in the graph (stage 3) */
     345                 :            :         ColorTrgmInfo *colorTrgms;
     346                 :            : 
     347                 :            :         int                     colorTrgmsCount;
     348                 :            :         int                     totalTrgmCount;
     349                 :            : } TrgmCNFA;
     350                 :            : 
     351                 :            : 
     352                 :            : /*
     353                 :            :  * Final, compact representation of CNFA-like graph.
     354                 :            :  */
     355                 :            : typedef struct
     356                 :            : {
     357                 :            :         int                      targetState;
     358                 :            :         int                      colorTrgm;
     359                 :            : } PackedArc;
     360                 :            : 
     361                 :            : typedef struct
     362                 :            : {
     363                 :            :         int                      arcsCount;
     364                 :            :         PackedArc       *arcs;
     365                 :            : } PackedState;
     366                 :            : 
     367                 :            : struct PackedGraph
     368                 :            : {
     369                 :            :         /*
     370                 :            :          * colorTrigramsCount and colorTrigramsGroups contain information
     371                 :            :          * about how trigrams are grouped into color trigrams. "colorTrigramsCount"
     372                 :            :          * represents total count of color trigrams and "colorTrigramGroups" contain
     373                 :            :          * number of simple trigrams in each color trigram.
     374                 :            :          */
     375                 :            :         int                      colorTrigramsCount;
     376                 :            :         int                     *colorTrigramGroups;
     377                 :            : 
     378                 :            :         /*
     379                 :            :          * "states" points to definition of "statesCount" states. 0 state is
     380                 :            :          * always initial state and 1 state is always final state. Each state's
     381                 :            :          * "arcs" points to "arcsCount" description of arcs. Each arc describe by
     382                 :            :          * number of color trigram and number of target state (both are zero-based).
     383                 :            :          */
     384                 :            :         int                      statesCount;
     385                 :            :         PackedState *states;
     386                 :            : 
     387                 :            :         /* Temporary work space for trigramsMatchGraph() */
     388                 :            :         bool            *colorTrigramsActive;
     389                 :            :         bool            *statesActive;
     390                 :            : };
     391                 :            : 
     392                 :            : 
     393                 :            : /* prototypes for private functions */
     394                 :            : static bool activateState(PackedGraph *graph, int stateno);
     395                 :            : static ColorInfo *getColorInfo(regex_t *regex, int *ncolors);
     396                 :            : static TrgmState *getState(TrgmCNFA *trgmCNFA, TrgmStateKey *key);
     397                 :            : static void transformGraph(TrgmCNFA *trgmCNFA);
     398                 :            : static bool selectColorTrigrams(TrgmCNFA *trgmCNFA);
     399                 :            : static TRGM *expandColorTrigrams(TrgmCNFA *trgmCNFA);
     400                 :            : static PackedGraph *packGraph(TrgmCNFA *trgmCNFA, MemoryContext context);
     401                 :            : 
     402                 :            : #ifdef TRGM_REGEXP_DEBUG
     403                 :            : static void printSourceCNFA(struct cnfa *cnfa, ColorInfo *colors, int ncolors);
     404                 :            : static void printTrgmCNFA(TrgmCNFA *trgmCNFA);
     405                 :            : static void printPackedGraph(PackedGraph *packedGraph, TRGM *trigrams);
     406                 :            : #endif
     407                 :            : 
     408                 :            : 
     409                 :            : /*
     410                 :            :  * Main entry point to process a regular expression. Returns a packed graph
     411                 :            :  * representation of the regular expression, or NULL if the regular expression
     412                 :            :  * was too complex.
     413                 :            :  */
     414                 :            : TRGM *
     415                 :         14 : createTrgmCNFA(text *text_re, MemoryContext context, PackedGraph **graph)
     416                 :            : {
     417                 :            :         struct guts   *g;
     418                 :            :         TrgmCNFA           trgmCNFA;
     419                 :            :         regex_t           *regex;
     420                 :            :         TRGM              *trg;
     421                 :            : 
     422                 :            :         /*
     423                 :            :          * Stage 1: Compile the regexp into a CNFA, using the PostgreSQL regexp
     424                 :            :          * library.
     425                 :            :          */
     426                 :            : #ifdef IGNORECASE
     427                 :         14 :         regex = RE_compile_and_cache(text_re, REG_ADVANCED | REG_ICASE, DEFAULT_COLLATION_OID);
     428                 :            : #else
     429                 :            :         regex = RE_compile_and_cache(text_re, REG_ADVANCED, DEFAULT_COLLATION_OID);
     430                 :            : #endif
     431                 :         14 :         g = (struct guts *) regex->re_guts;
     432                 :         14 :         trgmCNFA.cnfa = &g->search;
     433                 :            : 
     434                 :            :         /* Collect color information from the CNFA */
     435                 :         14 :         trgmCNFA.colorInfo = getColorInfo(regex, &trgmCNFA.ncolors);
     436                 :            : 
     437                 :            : #ifdef TRGM_REGEXP_DEBUG
     438                 :         14 :         printSourceCNFA(&g->search, trgmCNFA.colorInfo, trgmCNFA.ncolors);
     439                 :            : #endif
     440                 :            : 
     441                 :            :         /*
     442                 :            :          * Stage 2: Create a transformed graph from the source CNFA.
     443                 :            :          */
     444                 :         14 :         transformGraph(&trgmCNFA);
     445                 :            : 
     446                 :            : #ifdef TRGM_REGEXP_DEBUG
     447                 :         14 :         printTrgmCNFA(&trgmCNFA);
     448                 :            : #endif
     449                 :            : 
     450         [ +  - ]:         14 :         if (trgmCNFA.initState->fin)
     451                 :            :                 return NULL;
     452                 :            : 
     453                 :            :         /*
     454                 :            :          * Stage 3: Select color trigrams to expand.
     455                 :            :          */
     456         [ +  - ]:         14 :         if (!selectColorTrigrams(&trgmCNFA))
     457                 :            :                 return NULL;
     458                 :            : 
     459                 :            :         /*
     460                 :            :          * Stage 4: Expand color trigrams and pack graph into final representation.
     461                 :            :          */
     462                 :         14 :         trg = expandColorTrigrams(&trgmCNFA);
     463                 :            : 
     464                 :         14 :         *graph = packGraph(&trgmCNFA, context);
     465                 :            : 
     466                 :            : #ifdef TRGM_REGEXP_DEBUG
     467                 :         14 :         printPackedGraph(*graph, trg);
     468                 :            : #endif
     469                 :            : 
     470                 :            :         return trg;
     471                 :            : }
     472                 :            : 
     473                 :            : /*
     474                 :            :  * Main entry point for evaluating a graph.
     475                 :            :  */
     476                 :            : bool
     477                 :          7 : trigramsMatchGraph(PackedGraph *graph, bool *check)
     478                 :            : {
     479                 :            :         int                     i,
     480                 :            :                                 j,
     481                 :            :                                 k;
     482                 :            : 
     483                 :            :         /*
     484                 :            :          * Reset temporary working areas.
     485                 :            :          */
     486                 :          7 :         memset(graph->colorTrigramsActive, 0,
     487                 :          7 :                    sizeof(bool) * graph->colorTrigramsCount);
     488                 :          7 :         memset(graph->statesActive, 0, sizeof(bool) * graph->statesCount);
     489                 :            : 
     490                 :            :         /* Check which color trigrams were matched. */
     491                 :          7 :         j = 0;
     492         [ +  + ]:         20 :         for (i = 0; i < graph->colorTrigramsCount; i++)
     493                 :            :         {
     494                 :         13 :                 int cnt = graph->colorTrigramGroups[i];
     495         [ +  + ]:         74 :                 for (k = j; k < j + cnt; k++)
     496                 :            :                 {
     497         [ +  + ]:         72 :                         if (check[k])
     498                 :            :                         {
     499                 :            :                                 /*
     500                 :            :                                  * Found one matched trigram in the group. Can skip the rest
     501                 :            :                                  * of them and go to the next group.
     502                 :            :                                  */
     503                 :         11 :                                 graph->colorTrigramsActive[i] = true;
     504                 :         11 :                                 break;
     505                 :            :                         }
     506                 :            :                 }
     507                 :         13 :                 j = j + cnt;
     508                 :            :         }
     509                 :            : 
     510                 :            :         /* Recursively evaluate graph, starting from initial state */
     511                 :          7 :         return activateState(graph, 0);
     512                 :            : }
     513                 :            : 
     514                 :            : /*
     515                 :            :  * Recursive function for graph state activation. Returns true if state
     516                 :            :  * activation leads to activation of final state.
     517                 :            :  */
     518                 :            : static bool
     519                 :         11 : activateState(PackedGraph *graph, int stateno)
     520                 :            : {
     521                 :         11 :         PackedState *state = &graph->states[stateno];
     522                 :         11 :         int                     cnt = state->arcsCount;
     523                 :            :         int                     i;
     524                 :            : 
     525                 :         11 :         graph->statesActive[stateno] = true;
     526                 :            : 
     527                 :            :         /* Loop over arcs */
     528         [ +  - ]:         22 :         for (i = 0; i < cnt; i++)
     529                 :            :         {
     530                 :         11 :                 PackedArc *arc = (PackedArc *) &state->arcs[i];
     531                 :            :                 /*
     532                 :            :                  * If corresponding color trigram is present then activate the
     533                 :            :                  * corresponding state.
     534                 :            :                  */
     535         [ +  - ]:         11 :                 if (graph->colorTrigramsActive[arc->colorTrgm])
     536                 :            :                 {
     537         [ +  + ]:         11 :                         if (arc->targetState == 1)
     538                 :            :                                 return true; /* reached final state */
     539         [ +  - ]:          4 :                         if (!graph->statesActive[arc->targetState])
     540                 :            :                         {
     541         [ -  + ]:          4 :                                 if (activateState(graph, arc->targetState))
     542                 :            :                                         return true;
     543                 :            :                         }
     544                 :            :                 }
     545                 :            :         }
     546                 :            :         return false;
     547                 :            : }
     548                 :            : 
     549                 :            : /*---------------------
     550                 :            :  * Subroutines for pre-processing the color map.
     551                 :            :  *---------------------
     552                 :            :  */
     553                 :            : 
     554                 :            : /*
     555                 :            :  * Convert pg_wchar to multibyte character.
     556                 :            :  */
     557                 :            : static mb_char
     558                 :         92 : convertPgWchar(pg_wchar c)
     559                 :            : {
     560                 :            :         /*
     561                 :            :          * "s" has enough of space for a multibyte character of 4 bytes, and
     562                 :            :          * a zero-byte at the end.
     563                 :            :          */
     564                 :            :         char            s[5];
     565                 :            :         char       *lowerCased;
     566                 :            :         mb_char         result;
     567                 :            : 
     568         [ +  - ]:         92 :         if (c == 0)
     569                 :            :                 return 0;
     570                 :            : 
     571                 :         92 :         MemSet(s, 0, sizeof(s));
     572                 :         92 :         pg_wchar2mb_with_len(&c, s, 1);
     573                 :            : 
     574                 :            :         /* Convert to lowercase if needed */
     575                 :            : #ifdef IGNORECASE
     576                 :         92 :         lowerCased = lowerstr(s);
     577         [ +  + ]:         92 :         if (strncmp(lowerCased, s, 4))
     578                 :            :                 return 0;
     579                 :            : #else
     580                 :            :         lowerCased = s;
     581                 :            : #endif
     582                 :         46 :         strncpy((char *) &result, lowerCased, 4);
     583                 :         92 :         return result;
     584                 :            : }
     585                 :            : 
     586                 :            : /*
     587                 :            :  * Add new character into color information
     588                 :            :  */
     589                 :            : static void
     590                 :         46 : addCharToColor(ColorInfo *colorInfo, pg_wchar c)
     591                 :            : {
     592         [ +  + ]:         46 :         if (!colorInfo->alphaChars)
     593                 :            :         {
     594                 :         32 :                 colorInfo->alphaCharsCountAllocated = 16;
     595                 :         32 :                 colorInfo->alphaChars = (pg_wchar *) palloc(sizeof(pg_wchar) *
     596                 :            :                                                                                 colorInfo->alphaCharsCountAllocated);
     597                 :            :         }
     598         [ -  + ]:         46 :         if (colorInfo->alphaCharsCount >= colorInfo->alphaCharsCountAllocated)
     599                 :            :         {
     600                 :          0 :                 colorInfo->alphaCharsCountAllocated *= 2;
     601                 :          0 :                 colorInfo->alphaChars = (pg_wchar *) repalloc(colorInfo->alphaChars,
     602                 :            :                                                 sizeof(pg_wchar) * colorInfo->alphaCharsCountAllocated);
     603                 :            :         }
     604                 :         46 :         colorInfo->alphaChars[colorInfo->alphaCharsCount++] = c;
     605                 :         46 : }
     606                 :            : 
     607                 :            : /*
     608                 :            :  * Recursive function which scans colormap and updates color attributes in
     609                 :            :  * ColorInfo structure. Colormap is a tree which maps character to colors.
     610                 :            :  * The tree contains 4 levels. Each level corresponding to byte of character.
     611                 :            :  * Non-leaf nodes of tree contain pointers to descending tree nodes for each
     612                 :            :  * byte value. Leaf nodes of tree contain color numbers for each byte value.
     613                 :            :  * Potentially colormap could be very large, but most part of the colormap
     614                 :            :  * points to zero colors. That's tree nodes which corresponds to only zero
     615                 :            :  * color can be reused.
     616                 :            :  */
     617                 :            : static void
     618                 :         56 : scanColorMap(union tree tree[NBYTS], union tree *t, ColorInfo *colorInfos,
     619                 :            :                          int level, pg_wchar p)
     620                 :            : {
     621                 :            :         int                     i;
     622                 :            : 
     623                 :         56 :         check_stack_depth();
     624                 :            : 
     625         [ +  + ]:         56 :         if (level < NBYTS - 1)
     626                 :            :         {
     627                 :            :                 /* non-leaf node */
     628         [ +  + ]:      10794 :                 for (i = 0; i < BYTTAB; i++)
     629                 :            :                 {
     630                 :            :                         /*
     631                 :            :                          * This condition checks if all underlying levels express zero
     632                 :            :                          * color. Zero color uses multiple links to same tree node. So,
     633                 :            :                          * avoid scanning it because it's expensive.
     634                 :            :                          */
     635         [ +  + ]:      10752 :                         if (t->tptr[i] == &tree[level + 1])
     636                 :      10710 :                                 continue;
     637                 :            :                         /* Recursive scanning of next level color table */
     638                 :         42 :                         scanColorMap(tree, t->tptr[i], colorInfos, level + 1, (p << 8) | i);
     639                 :            :                 }
     640                 :            :         }
     641                 :            :         else
     642                 :            :         {
     643                 :            :                 /* leaf node */
     644                 :         14 :                 p <<= 8;
     645         [ +  + ]:       3598 :                 for (i = 0; i < BYTTAB; i++)
     646                 :            :                 {
     647                 :       3584 :                         ColorInfo *colorInfo = &colorInfos[t->tcolor[i]];
     648                 :            :                         pg_wchar c;
     649                 :            : 
     650         [ +  + ]:       3584 :                         if (!colorInfo->expandable)
     651                 :       3492 :                                 continue;
     652                 :            : 
     653                 :            :                         /* Convert to multibyte character */
     654                 :         92 :                         c = convertPgWchar(p | i);
     655                 :            : 
     656         [ +  + ]:         92 :                         if (!c)
     657                 :         46 :                                 continue;
     658                 :            : 
     659                 :            :                         /* Update color attributes according to next character */
     660 [ -  + ][ #  # ]:         46 :                         if (ISWORDCHR((char *)&c))
     661                 :         46 :                                 addCharToColor(colorInfo, c);
     662                 :            :                         else
     663                 :       3584 :                                 colorInfo->containNonAlpha = true;
     664                 :            :                 }
     665                 :            :         }
     666                 :         56 : }
     667                 :            : 
     668                 :            : /*
     669                 :            :  * Fill ColorInfo structure for each color by scanning colormap.
     670                 :            :  */
     671                 :            : static ColorInfo *
     672                 :         14 : getColorInfo(regex_t *regex, int *ncolors)
     673                 :            : {
     674                 :            :         struct guts *g;
     675                 :            :         struct colormap *cm;
     676                 :            :         ColorInfo  *result;
     677                 :            :         int                     colorsCount;
     678                 :            :         int                     i;
     679                 :            : 
     680                 :         14 :         g = (struct guts *) regex->re_guts;
     681                 :         14 :         cm = &g->cmap;
     682                 :         14 :         *ncolors = colorsCount = cm->max + 1;
     683                 :            : 
     684                 :         14 :         result = (ColorInfo *) palloc0(colorsCount * sizeof(ColorInfo));
     685                 :            : 
     686                 :            :         /*
     687                 :            :          * Zero color is a default color which contains all characters that aren't
     688                 :            :          * in explicitly expressed classes. Mark that we can expect everything
     689                 :            :          * from it.
     690                 :            :          */
     691                 :         14 :         result[0].expandable = false;
     692         [ +  + ]:        102 :         for (i = 1; i < colorsCount; i++)
     693                 :         88 :                 result[i].expandable = true;
     694                 :            : 
     695                 :         14 :         scanColorMap(cm->tree, &cm->tree[0], result, 0, 0);
     696                 :            : 
     697                 :         14 :         return result;
     698                 :            : }
     699                 :            : 
     700                 :            : /*---------------------
     701                 :            :  * Subroutines for transforming original CNFA graph into a color trigram
     702                 :            :  * graph.
     703                 :            :  *---------------------
     704                 :            :  */
     705                 :            : 
     706                 :            : /*
     707                 :            :  * Check if prefix1 "contains" prefix2. "contains" mean that any exact prefix
     708                 :            :  * (which no ambiguity) which satisfy to prefix2 also satisfy to prefix1.
     709                 :            :  */
     710                 :            : static bool
     711                 :        128 : prefixContains(TrgmPrefix *prefix1, TrgmPrefix *prefix2)
     712                 :            : {
     713         [ +  + ]:        128 :         if (prefix1->s[1] == UNKNOWN_COLOR)
     714                 :            :         {
     715                 :            :                 /* Fully ambiguous prefix contains everything */
     716                 :            :                 return true;
     717                 :            :         }
     718         [ +  + ]:         16 :         else if (prefix1->s[0] == UNKNOWN_COLOR)
     719                 :            :         {
     720                 :            :                 /*
     721                 :            :                  * Prefix with only first unknown color contains every prefix with same
     722                 :            :                  * second color.
     723                 :            :                  */
     724         [ +  + ]:          8 :                 if (prefix1->s[1] == prefix2->s[1])
     725                 :            :                         return true;
     726                 :            :                 else
     727                 :          6 :                         return false;
     728                 :            :         }
     729                 :            :         else
     730                 :            :         {
     731                 :            :                 /* Exact prefix contains only exactly same prefix */
     732 [ +  + ][ -  + ]:          8 :                 if (prefix1->s[0] == prefix2->s[0] && prefix1->s[1] == prefix2->s[1])
     733                 :            :                         return true;
     734                 :            :                 else
     735                 :        128 :                         return false;
     736                 :            :         }
     737                 :            : }
     738                 :            : 
     739                 :            : /*
     740                 :            :  * Add all keys that can be reached without reading any color trigram to
     741                 :            :  * state of CNFA-like graph on color trigrams.
     742                 :            :  */
     743                 :            : static void
     744                 :        206 : addKeys(TrgmCNFA *trgmCNFA, TrgmState *state, TrgmStateKey *key)
     745                 :            : {
     746                 :            :         struct carc *s;
     747                 :            :         TrgmStateKey destKey;
     748                 :            :         ListCell *cell, *prev, *next;
     749                 :            :         TrgmStateKey *keyCopy;
     750                 :            : 
     751 [ +  - ][ +  + ]:        412 :         MemSet(&destKey, 0, sizeof(TrgmStateKey));
     752                 :            : 
     753                 :            :         /* Adjust list of keys with new one */
     754                 :        206 :         prev = NULL;
     755                 :        412 :         cell = list_head(state->keys);
     756         [ +  + ]:        390 :         while (cell)
     757                 :            :         {
     758                 :        296 :                 TrgmStateKey *existingKey = (TrgmStateKey *) lfirst(cell);
     759                 :        296 :                 next = lnext(cell);
     760         [ +  + ]:        296 :                 if (existingKey->nstate == key->nstate)
     761                 :            :                 {
     762         [ +  + ]:        116 :                         if (prefixContains(&existingKey->prefix, &key->prefix))
     763                 :            :                         {
     764                 :            :                                 /* This old key already covers the new key. Nothing to do */
     765                 :            :                                 return;
     766                 :            :                         }
     767         [ +  + ]:          4 :                         if (prefixContains(&key->prefix, &existingKey->prefix))
     768                 :            :                         {
     769                 :            :                                 /*
     770                 :            :                                  * The new key covers this old key. Remove the old key, it's
     771                 :            :                                  * no longer needed once we add this key to the list.
     772                 :            :                                  */
     773                 :          2 :                                 state->keys = list_delete_cell(state->keys, cell, prev);
     774                 :            :                         }
     775                 :            :                         else
     776                 :            :                                 prev = cell;
     777                 :            :                 }
     778                 :            :                 else
     779                 :            :                         prev = cell;
     780                 :        184 :                 cell = next;
     781                 :            :         }
     782                 :            : 
     783                 :         94 :         keyCopy = (TrgmStateKey *) palloc(sizeof(TrgmStateKey));
     784                 :         94 :         memcpy(keyCopy, key, sizeof(TrgmStateKey));
     785                 :         94 :         state->keys = lappend(state->keys, keyCopy);
     786                 :            : 
     787                 :            :         /* Mark final state */
     788         [ +  + ]:         94 :         if (key->nstate == trgmCNFA->cnfa->post)
     789                 :            :         {
     790                 :         14 :                 state->fin = true;
     791                 :            :                 return;
     792                 :            :         }
     793                 :            : 
     794                 :            :         /*
     795                 :            :          * Loop through all outgoing arcs from the corresponding state in the
     796                 :            :          * original CNFA.
     797                 :            :          */
     798                 :         80 :         s = trgmCNFA->cnfa->states[key->nstate];
     799         [ +  + ]:        428 :         while (s->co != COLORLESS)
     800                 :            :         {
     801                 :            :                 ColorInfo *colorInfo;
     802                 :            : 
     803         [ +  + ]:        222 :                 if (s->co == trgmCNFA->cnfa->bos[1])
     804                 :            :                 {
     805                 :            :                         /* Start of line (^) */
     806                 :         26 :                         destKey.nstate = s->to;
     807                 :            : 
     808                 :            :                         /* Mark prefix as start of new color trigram */
     809                 :         26 :                         destKey.prefix.s[0] = EMPTY_COLOR;
     810                 :         26 :                         destKey.prefix.s[1] = EMPTY_COLOR;
     811                 :            : 
     812                 :            :                         /* Add key to this state */
     813                 :         26 :                         addKeys(trgmCNFA, state, &destKey);
     814         [ +  - ]:         26 :                         if (state->fin)
     815                 :            :                                 return;
     816                 :            :                 }
     817         [ +  + ]:        196 :                 else if (s->co == trgmCNFA->cnfa->eos[1])
     818                 :            :                 {
     819                 :            :                         /* End of string ($) */
     820 [ +  - ][ -  + ]:          2 :                         if (key->prefix.s[0] == UNKNOWN_COLOR ||
     821                 :          2 :                                 key->prefix.s[1] == EMPTY_COLOR)
     822                 :            :                         {
     823                 :          0 :                                 destKey.nstate = s->to;
     824                 :            : 
     825                 :            :                                 /*
     826                 :            :                                  * Let's think prefix to become ambiguous (in order to prevent
     827                 :            :                                  * latter fiddling around with keys).
     828                 :            :                                  */
     829                 :          0 :                                 destKey.prefix.s[1] = UNKNOWN_COLOR;
     830                 :          0 :                                 destKey.prefix.s[0] = UNKNOWN_COLOR;
     831                 :            : 
     832                 :            :                                 /* Prefix is ambiguous, add key to the same state */
     833                 :          0 :                                 addKeys(trgmCNFA, state, &destKey);
     834         [ #  # ]:          0 :                                 if (state->fin)
     835                 :            :                                         return;
     836                 :            :                         }
     837                 :            :                 }
     838                 :            :                 else
     839                 :            :                 {
     840                 :            :                         /* Regular color */
     841                 :        194 :                         colorInfo = &trgmCNFA->colorInfo[s->co];
     842                 :            : 
     843         [ +  + ]:        194 :                         if (colorInfo->expandable)
     844                 :            :                         {
     845 [ -  + ][ #  # ]:        154 :                                 if (colorInfo->containNonAlpha &&
     846         [ #  # ]:          0 :                                         (key->prefix.s[0] == UNKNOWN_COLOR ||
     847                 :          0 :                                          key->prefix.s[1] == EMPTY_COLOR))
     848                 :            :                                 {
     849                 :            :                                         /*
     850                 :            :                                          * When color contain non-alphanumeric character we should
     851                 :            :                                          * add empty key with empty prefix.
     852                 :            :                                          */
     853                 :          0 :                                         destKey.prefix.s[0] = EMPTY_COLOR;
     854                 :          0 :                                         destKey.prefix.s[1] = EMPTY_COLOR;
     855                 :          0 :                                         destKey.nstate = s->to;
     856                 :          0 :                                         addKeys(trgmCNFA, state, &destKey);
     857         [ #  # ]:          0 :                                         if (state->fin)
     858                 :            :                                                 return;
     859                 :            :                                 }
     860                 :            : 
     861 [ +  + ][ +  + ]:        154 :                                 if (colorInfo->alphaCharsCount > 0 &&
     862                 :        130 :                                         key->prefix.s[0] == UNKNOWN_COLOR)
     863                 :            :                                 {
     864                 :            :                                         /* Add corresponding key when prefix was unknown */
     865                 :        102 :                                         destKey.prefix.s[0] = key->prefix.s[1];
     866                 :        102 :                                         destKey.prefix.s[1] = s->co;
     867                 :        102 :                                         destKey.nstate = s->to;
     868                 :        102 :                                         addKeys(trgmCNFA, state, &destKey);
     869         [ +  - ]:        102 :                                         if (state->fin)
     870                 :            :                                                 return;
     871                 :            :                                 }
     872                 :            :                         }
     873                 :            :                         else
     874                 :            :                         {
     875                 :            :                                 /*
     876                 :            :                                  * Unexpandable color. Add corresponding key to this state.
     877                 :            :                                  */
     878                 :         40 :                                 destKey.nstate = s->to;
     879                 :         40 :                                 destKey.prefix.s[0] = UNKNOWN_COLOR;
     880                 :         40 :                                 destKey.prefix.s[1] = UNKNOWN_COLOR;
     881                 :         40 :                                 addKeys(trgmCNFA, state, &destKey);
     882         [ +  + ]:         40 :                                 if (state->fin)
     883                 :            :                                         return;
     884                 :            :                         }
     885                 :            :                 }
     886                 :        210 :                 s++;
     887                 :            :         }
     888                 :            : }
     889                 :            : 
     890                 :            : /*
     891                 :            :  * Add outgoing arc from state if needed.
     892                 :            :  */
     893                 :            : static void
     894                 :        124 : addArc(TrgmCNFA *trgmCNFA, TrgmState *state, TrgmStateKey *key,
     895                 :            :            TrgmStateKey *destKey, color co)
     896                 :            : {
     897                 :            :         TrgmArc *arc;
     898                 :            :         ListCell *cell2;
     899                 :            : 
     900                 :            :         /* Check whether we actually can add the arc */
     901 [ +  + ][ +  + ]:        124 :         if (key->prefix.s[0] == UNKNOWN_COLOR ||
     902         [ +  - ]:          2 :                 (key->prefix.s[1] == EMPTY_COLOR && co == EMPTY_COLOR))
     903                 :            :                 return;
     904                 :            : 
     905                 :            :         /* If we have the same key here, we don't need to add new arc */
     906         [ +  + ]:        116 :         foreach(cell2, state->keys)
     907                 :            :         {
     908                 :         88 :                 TrgmStateKey *key2 = (TrgmStateKey *) lfirst(cell2);
     909   [ +  +  +  + ]:         96 :                 if (key2->nstate == destKey->nstate &&
     910                 :          8 :                         prefixContains(&key2->prefix, &destKey->prefix))
     911                 :            :                 {
     912                 :            :                         return;
     913                 :            :                 }
     914                 :            :         }
     915                 :            : 
     916                 :            :         /* Not found, add new arc */
     917                 :         28 :         arc = (TrgmArc *) palloc(sizeof(TrgmArc));
     918                 :         28 :         arc->target = getState(trgmCNFA, destKey);
     919                 :         28 :         arc->trgm[0] = key->prefix.s[0];
     920                 :         28 :         arc->trgm[1] = key->prefix.s[1];
     921                 :         28 :         arc->trgm[2] = co;
     922                 :         28 :         state->arcs = lappend(state->arcs, arc);
     923                 :        124 :         trgmCNFA->arcsCount++;
     924                 :            : }
     925                 :            : 
     926                 :            : /*
     927                 :            :  * Add outgoing arcs from given state.
     928                 :            :  */
     929                 :            : static void
     930                 :         24 : addArcs(TrgmCNFA *trgmCNFA, TrgmState *state)
     931                 :            : {
     932                 :            :         struct carc *s;
     933                 :            :         TrgmStateKey destKey;
     934                 :            :         ListCell *cell;
     935                 :            : 
     936 [ +  - ][ +  + ]:         48 :         MemSet(&destKey, 0, sizeof(TrgmStateKey));
     937                 :            : 
     938                 :            :         /*
     939                 :            :          * Iterate over keys associated with this output state.
     940                 :            :          */
     941         [ +  + ]:         90 :         foreach(cell, state->keys)
     942                 :            :         {
     943                 :         66 :                 TrgmStateKey *key = (TrgmStateKey *) lfirst(cell);
     944                 :         66 :                 s = trgmCNFA->cnfa->states[key->nstate];
     945         [ +  + ]:        266 :                 while (s->co != COLORLESS)
     946                 :            :                 {
     947                 :            :                         ColorInfo *colorInfo;
     948         [ +  + ]:        200 :                         if (s->co == trgmCNFA->cnfa->bos[1])
     949                 :            :                         {
     950                 :            :                                 /* Should be already handled by addKeys. */
     951                 :            :                         }
     952         [ +  + ]:        174 :                         else if (s->co == trgmCNFA->cnfa->eos[1])
     953                 :            :                         {
     954                 :            :                                 /* End of the string ($) */
     955                 :          2 :                                 destKey.nstate = s->to;
     956                 :            : 
     957                 :            :                                 /* Assume prefix to become ambiguous after end of the string */
     958                 :          2 :                                 destKey.prefix.s[1] = UNKNOWN_COLOR;
     959                 :          2 :                                 destKey.prefix.s[0] = UNKNOWN_COLOR;
     960                 :            : 
     961                 :          2 :                                 addArc(trgmCNFA, state, key, &destKey, EMPTY_COLOR);
     962                 :            :                         }
     963                 :            :                         else
     964                 :            :                         {
     965                 :            :                                 /* Regular color: try to add outgoing arcs */
     966                 :        172 :                                 colorInfo = &trgmCNFA->colorInfo[s->co];
     967                 :            : 
     968         [ +  + ]:        172 :                                 if (colorInfo->expandable)
     969                 :            :                                 {
     970         [ -  + ]:        146 :                                         if (colorInfo->containNonAlpha)
     971                 :            :                                         {
     972                 :          0 :                                                 destKey.prefix.s[0] = EMPTY_COLOR;
     973                 :          0 :                                                 destKey.prefix.s[1] = EMPTY_COLOR;
     974                 :          0 :                                                 destKey.nstate = s->to;
     975                 :          0 :                                                 addArc(trgmCNFA, state, key, &destKey, EMPTY_COLOR);
     976                 :            :                                         }
     977                 :            : 
     978         [ +  + ]:        146 :                                         if (colorInfo->alphaCharsCount > 0)
     979                 :            :                                         {
     980                 :        122 :                                                 destKey.prefix.s[0] = key->prefix.s[1];
     981                 :        122 :                                                 destKey.prefix.s[1] = s->co;
     982                 :        122 :                                                 destKey.nstate = s->to;
     983                 :        122 :                                                 addArc(trgmCNFA, state, key, &destKey, s->co);
     984                 :            :                                         }
     985                 :            :                                 }
     986                 :            :                         }
     987                 :        200 :                         s++;
     988                 :            :                 }
     989                 :            :         }
     990                 :         24 : }
     991                 :            : 
     992                 :            : /*
     993                 :            :  * Get state of trigram CNFA-like graph of given enter key and queue it for
     994                 :            :  * processing if needed.
     995                 :            :  */
     996                 :            : static TrgmState *
     997                 :         42 : getState(TrgmCNFA *trgmCNFA, TrgmStateKey *key)
     998                 :            : {
     999                 :            :         bool            found;
    1000                 :            :         TrgmState  *state;
    1001                 :            : 
    1002                 :         42 :         state = hash_search(trgmCNFA->states, key, HASH_ENTER, &found);
    1003         [ +  + ]:         42 :         if (!found)
    1004                 :            :         {
    1005                 :            :                 /* New state: initialize and queue it */
    1006                 :         38 :                 state->arcs = NIL;
    1007                 :         38 :                 state->keys = NIL;
    1008                 :         38 :                 state->init = false;
    1009                 :         38 :                 state->fin = false;
    1010                 :         38 :                 state->children = NIL;
    1011                 :         38 :                 state->parent = NULL;
    1012                 :         38 :                 state->number = -1;
    1013                 :            : 
    1014                 :         38 :                 state->queued = true;
    1015                 :         38 :                 trgmCNFA->queue = lappend(trgmCNFA->queue, state);
    1016                 :            :         }
    1017                 :         42 :         return state;
    1018                 :            : }
    1019                 :            : 
    1020                 :            : /*
    1021                 :            :  * Process the state: add keys and then add outgoing arcs.
    1022                 :            :  */
    1023                 :            : static void
    1024                 :         38 : processState(TrgmCNFA *trgmCNFA, TrgmState *state)
    1025                 :            : {
    1026                 :         38 :         addKeys(trgmCNFA, state, &state->enterKey);
    1027                 :            :         /*
    1028                 :            :          * Add outgoing arc only if state isn't final (we have no interest in arc
    1029                 :            :          * if we already match)
    1030                 :            :          */
    1031         [ +  + ]:         38 :         if (!state->fin)
    1032                 :         24 :                 addArcs(trgmCNFA, state);
    1033                 :            : 
    1034                 :         38 :         state->queued = false;
    1035                 :         38 : }
    1036                 :            : 
    1037                 :            : /*
    1038                 :            :  * Process queue of CFNA-like graph states until all the states are processed.
    1039                 :            :  * This algorithm may be stopped due to resources limitation. In this case we
    1040                 :            :  * assume every unprocessed branch to immediately finish with matching (this
    1041                 :            :  * can give us more false positives but no false negatives) by marking all
    1042                 :            :  * unprocessed states as final.
    1043                 :            :  */
    1044                 :            : static void
    1045                 :         14 : transformGraph(TrgmCNFA *trgmCNFA)
    1046                 :            : {
    1047                 :            :         HASHCTL            hashCtl;
    1048                 :            :         TrgmStateKey   initkey;
    1049                 :            :         TrgmState         *initstate;
    1050                 :            : 
    1051                 :         14 :         trgmCNFA->queue = NIL;
    1052                 :         14 :         trgmCNFA->arcsCount = 0;
    1053                 :         14 :         trgmCNFA->overflowed = false;
    1054                 :            : 
    1055                 :            :         /* Initialize hash of states */
    1056                 :         14 :         hashCtl.keysize = sizeof(TrgmStateKey);
    1057                 :         14 :         hashCtl.entrysize = sizeof(TrgmState);
    1058                 :         14 :         hashCtl.hcxt = CurrentMemoryContext;
    1059                 :         14 :         hashCtl.hash = tag_hash;
    1060                 :         14 :         hashCtl.match = memcmp;
    1061                 :         14 :         trgmCNFA->states =
    1062                 :         14 :                 hash_create("Trigram CNFA",
    1063                 :            :                                         1024,
    1064                 :            :                                         &hashCtl,
    1065                 :            :                                         HASH_ELEM | HASH_CONTEXT | HASH_FUNCTION | HASH_COMPARE);
    1066                 :            : 
    1067                 :            :         /* Create initial state */
    1068 [ +  - ][ +  + ]:         28 :         MemSet(&initkey, 0, sizeof(TrgmStateKey));
    1069                 :         14 :         initkey.prefix.s[0] = UNKNOWN_COLOR;
    1070                 :         14 :         initkey.prefix.s[1] = UNKNOWN_COLOR;
    1071                 :         14 :         initkey.nstate = trgmCNFA->cnfa->pre;
    1072                 :            : 
    1073                 :         14 :         initstate = getState(trgmCNFA, &initkey);
    1074                 :         14 :         initstate->init = true;
    1075                 :         14 :         trgmCNFA->initState = initstate;
    1076                 :            : 
    1077                 :            :         /*
    1078                 :            :          * Recursively build the transformed graph by processing queue of
    1079                 :            :          * states (Breadth-first search).
    1080                 :            :          */
    1081         [ +  + ]:         52 :         while (trgmCNFA->queue != NIL)
    1082                 :            :         {
    1083                 :         76 :                 TrgmState *state = (TrgmState *) linitial(trgmCNFA->queue);
    1084                 :            : 
    1085                 :         38 :                 state->queued = false;
    1086                 :         38 :                 trgmCNFA->queue = list_delete_first(trgmCNFA->queue);
    1087                 :            : 
    1088                 :            :                 /*
    1089                 :            :                  * If we overflow then just mark state as final. Otherwise do actual
    1090                 :            :                  * processing.
    1091                 :            :                  */
    1092         [ -  + ]:         38 :                 if (trgmCNFA->overflowed)
    1093                 :          0 :                         state->fin = true;
    1094                 :            :                 else
    1095                 :         38 :                         processState(trgmCNFA, state);
    1096                 :            : 
    1097                 :            :                 /* Did we overflow? */
    1098   [ +  -  -  + ]:         76 :                 if (trgmCNFA->arcsCount > MAX_RESULT_ARCS ||
    1099                 :         38 :                         hash_get_num_entries(trgmCNFA->states) > MAX_RESULT_STATES)
    1100                 :            :                 {
    1101                 :         38 :                         trgmCNFA->overflowed = true;
    1102                 :            :                 }
    1103                 :            :         }
    1104                 :         14 : }
    1105                 :            : 
    1106                 :            : 
    1107                 :            : /*---------------------
    1108                 :            :  * Subroutines for expanding color trigrams into regular trigrams.
    1109                 :            :  *---------------------
    1110                 :            :  */
    1111                 :            : 
    1112                 :            : /*
    1113                 :            :  * Compare function for sorting of color trigrams by their colors.
    1114                 :            :  */
    1115                 :            : static int
    1116                 :         84 : colorTrgmInfoCmp(const void *p1, const void *p2)
    1117                 :            : {
    1118                 :         84 :         const ColorTrgmInfo *c1 = (const ColorTrgmInfo *)p1;
    1119                 :         84 :         const ColorTrgmInfo *c2 = (const ColorTrgmInfo *)p2;
    1120                 :         84 :         return memcmp(c1->trgm, c2->trgm, sizeof(ColorTrgm));
    1121                 :            : }
    1122                 :            : 
    1123                 :            : /*
    1124                 :            :  * Compare function for sorting color trigrams by descending of their simple
    1125                 :            :  * trigrams counts.
    1126                 :            :  */
    1127                 :            : static int
    1128                 :         16 : colorTrgmInfoCountCmp(const void *p1, const void *p2)
    1129                 :            : {
    1130                 :         16 :         const ColorTrgmInfo *c1 = (const ColorTrgmInfo *)p1;
    1131                 :         16 :         const ColorTrgmInfo *c2 = (const ColorTrgmInfo *)p2;
    1132         [ +  + ]:         16 :         if (c1->count < c2->count)
    1133                 :            :                 return 1;
    1134         [ +  + ]:         10 :         else if (c1->count == c2->count)
    1135                 :            :                 return 0;
    1136                 :            :         else
    1137                 :         16 :                 return -1;
    1138                 :            : }
    1139                 :            : 
    1140                 :            : /*
    1141                 :            :  * Convert trigram into trgm datatype.
    1142                 :            :  */
    1143                 :            : static void
    1144                 :        314 : fillTrgm(trgm *ptrgm, mb_char s[3])
    1145                 :            : {
    1146                 :            :         char            text[14],
    1147                 :            :                            *p;
    1148                 :            :         int                     i;
    1149                 :            : 
    1150                 :            :         /* Write multibyte string into "text". */
    1151                 :        314 :         p = text;
    1152         [ +  + ]:       1256 :         for (i = 0; i < 3; i++)
    1153                 :            :         {
    1154                 :            :                 int len;
    1155         [ +  + ]:        942 :                 if (s[i] != 0)
    1156                 :            :                 {
    1157                 :        936 :                         len = strnlen((char *) &s[i], 4);
    1158                 :        936 :                         memcpy(p, &s[i], len);
    1159                 :        936 :                         p += len;
    1160                 :            :                 }
    1161                 :            :                 else
    1162                 :            :                 {
    1163                 :          6 :                         *p++ = ' ';
    1164                 :            :                 }
    1165                 :            :         }
    1166                 :        314 :         *p = 0;
    1167                 :            : 
    1168                 :            :         /* Extract trigrams from "text" */
    1169                 :        314 :         cnt_trigram(ptrgm, text, p - text);
    1170                 :        314 : }
    1171                 :            : 
    1172                 :            : /*
    1173                 :            :  * Merge states of graph.
    1174                 :            :  */
    1175                 :            : static void
    1176                 :          0 : mergeStates(TrgmState *state1, TrgmState *state2)
    1177                 :            : {
    1178                 :            :         ListCell *cell;
    1179                 :            : 
    1180 [ #  # ][ #  # ]:          0 :         Assert(!state1->parent);
    1181 [ #  # ][ #  # ]:          0 :         Assert(!state2->parent);
    1182 [ #  # ][ #  # ]:          0 :         Assert(state1 != state2);
    1183                 :            : 
    1184         [ #  # ]:          0 :         foreach(cell, state2->children)
    1185                 :            :         {
    1186                 :          0 :                 TrgmState *state = (TrgmState *) lfirst(cell);
    1187                 :          0 :                 state->parent = state1;
    1188                 :            :         }
    1189 [ #  # ][ #  # ]:          0 :         state1->init = state1->init || state2->init;
    1190 [ #  # ][ #  # ]:          0 :         state1->fin = state1->fin || state2->fin;
    1191                 :          0 :         state2->parent = state1;
    1192                 :          0 :         state1->children = list_concat(state1->children, state2->children);
    1193                 :          0 :         state1->children = lappend(state1->children, state2);
    1194                 :          0 :         state2->children = NIL;
    1195                 :          0 : }
    1196                 :            : 
    1197                 :            : /*
    1198                 :            :  * Get vector of all color trigrams in graph and select which of them to expand
    1199                 :            :  * into simple trigrams.
    1200                 :            :  */
    1201                 :            : static bool
    1202                 :         14 : selectColorTrigrams(TrgmCNFA *trgmCNFA)
    1203                 :            : {
    1204                 :            :         HASH_SEQ_STATUS scan_status;
    1205                 :         14 :         int                     arcsCount = trgmCNFA->arcsCount,
    1206                 :            :                                 i;
    1207                 :            :         TrgmState  *state;
    1208                 :            :         ColorTrgmInfo *p1, *p2, *colorTrgms;
    1209                 :            :         int                     totalTrgmCount, number;
    1210                 :            : 
    1211                 :            :         /* Collect color trigrams from all arcs */
    1212                 :         14 :         colorTrgms = (ColorTrgmInfo *) palloc(sizeof(ColorTrgmInfo) * arcsCount);
    1213                 :         14 :         i = 0;
    1214                 :         14 :         hash_seq_init(&scan_status, trgmCNFA->states);
    1215         [ +  + ]:         52 :         while ((state = (TrgmState *) hash_seq_search(&scan_status)) != NULL)
    1216                 :            :         {
    1217                 :            :                 ListCell *cell;
    1218         [ +  + ]:         66 :                 foreach(cell, state->arcs)
    1219                 :            :                 {
    1220                 :         28 :                         TrgmArc *arc = (TrgmArc *) lfirst(cell);
    1221                 :         28 :                         ArcInfo *arcInfo = (ArcInfo *) palloc(sizeof(ArcInfo));
    1222                 :         28 :                         arcInfo->source = state;
    1223                 :         28 :                         arcInfo->target = arc->target;
    1224                 :         28 :                         colorTrgms[i].arcs = list_make1(arcInfo);
    1225                 :         28 :                         colorTrgms[i].expanded = true;
    1226                 :         28 :                         memcpy(&colorTrgms[i].trgm, &arc->trgm, sizeof(ColorTrgm));
    1227                 :         28 :                         i++;
    1228                 :            :                 }
    1229                 :            :         }
    1230 [ +  - ][ -  + ]:         14 :         Assert(i == arcsCount);
    1231                 :            : 
    1232                 :            :         /* Remove duplicates and merge arcs lists */
    1233         [ +  + ]:         14 :         if (arcsCount >= 2)
    1234                 :            :         {
    1235                 :          4 :                 qsort(colorTrgms, arcsCount, sizeof(ColorTrgmInfo), colorTrgmInfoCmp);
    1236                 :          4 :                 p1 = colorTrgms + 1;
    1237                 :          4 :                 p2 = colorTrgms;
    1238         [ +  + ]:         18 :                 while (p1 - colorTrgms < arcsCount)
    1239                 :            :                 {
    1240         [ +  + ]:         14 :                         if (memcmp(p1->trgm, p2->trgm, sizeof(ColorTrgm)) != 0)
    1241                 :            :                         {
    1242                 :         12 :                                 p2++;
    1243                 :         12 :                                 *p2 = *p1;
    1244                 :            :                         }
    1245                 :            :                         else
    1246                 :            :                         {
    1247                 :          2 :                                 p2->arcs = list_concat(p2->arcs, p1->arcs);
    1248                 :            :                         }
    1249                 :         14 :                         p1++;
    1250                 :            :                 }
    1251                 :         14 :                 trgmCNFA->colorTrgmsCount = p2 + 1 - colorTrgms;
    1252                 :            :         }
    1253                 :            :         else
    1254                 :            :         {
    1255                 :         10 :                 trgmCNFA->colorTrgmsCount = arcsCount;
    1256                 :            :         }
    1257                 :            : 
    1258                 :            :         /* Count number of simple trigrams in each color trigram */
    1259                 :            :         totalTrgmCount = 0;
    1260         [ +  + ]:         40 :         for (i = 0; i < trgmCNFA->colorTrgmsCount; i++)
    1261                 :            :         {
    1262                 :         26 :                 int j, count = 1;
    1263                 :         26 :                 ColorTrgmInfo *trgmInfo = &colorTrgms[i];
    1264         [ +  + ]:        104 :                 for (j = 0; j < 3; j++)
    1265                 :            :                 {
    1266                 :            :                         /* FIXME: count could overflow */
    1267         [ +  + ]:         78 :                         if (trgmInfo->trgm[j] != EMPTY_COLOR)
    1268                 :         72 :                                 count *= trgmCNFA->colorInfo[trgmInfo->trgm[j]].alphaCharsCount;
    1269                 :            :                 }
    1270                 :         26 :                 trgmInfo->count = count;
    1271                 :         26 :                 totalTrgmCount += count;
    1272                 :            :         }
    1273                 :            : 
    1274                 :            :         /* Sort color trigrams by descending of their simple trigram counts */
    1275                 :         14 :         qsort(colorTrgms, trgmCNFA->colorTrgmsCount, sizeof(ColorTrgmInfo),
    1276                 :            :                                                                                                                 colorTrgmInfoCountCmp);
    1277                 :            :         /*
    1278                 :            :          * Remove color trigrams from the graph until total number of simple
    1279                 :            :          * trigrams is below MAX_TRGM_COUNT. We start removing from largest color
    1280                 :            :          * trigrams which are most promising for reduce total number of simple
    1281                 :            :          * trigrams. When removing color trigram we have to merge states connected
    1282                 :            :          * by corresponding arcs. It's necessary to not merge initial and final
    1283                 :            :          * states, because our graph becomes useless in this case.
    1284                 :            :          */
    1285         [ +  - ]:         14 :         for (i = 0;
    1286         [ -  + ]:         14 :                  (i < trgmCNFA->colorTrgmsCount) && (totalTrgmCount > MAX_TRGM_COUNT);
    1287                 :          0 :                  i++)
    1288                 :            :         {
    1289                 :          0 :                 ColorTrgmInfo *trgmInfo = &colorTrgms[i];
    1290                 :          0 :                 bool canRemove = true;
    1291                 :            :                 ListCell *cell;
    1292                 :            : 
    1293                 :            :                 /*
    1294                 :            :                  * Does any arc of with this color trigram connect initial and final
    1295                 :            :                  * states?
    1296                 :            :                  */
    1297         [ #  # ]:          0 :                 foreach(cell, trgmInfo->arcs)
    1298                 :            :                 {
    1299                 :          0 :                         ArcInfo *arcInfo = (ArcInfo *) lfirst(cell);
    1300                 :          0 :                         TrgmState *source = arcInfo->source, *target = arcInfo->target;
    1301         [ #  # ]:          0 :                         while (source->parent)
    1302                 :            :                                 source = source->parent;
    1303         [ #  # ]:          0 :                         while (target->parent)
    1304                 :            :                                 target = target->parent;
    1305 [ #  # ][ #  # ]:          0 :                         if ((source->init || target->init) && (source->fin || target->fin))
         [ #  # ][ #  # ]
    1306                 :            :                         {
    1307                 :            :                                 canRemove = false;
    1308                 :            :                                 break;
    1309                 :            :                         }
    1310                 :            :                 }
    1311         [ #  # ]:          0 :                 if (!canRemove)
    1312                 :          0 :                         continue;
    1313                 :            : 
    1314                 :            :                 /* Merge states */
    1315         [ #  # ]:          0 :                 foreach(cell, trgmInfo->arcs)
    1316                 :            :                 {
    1317                 :          0 :                         ArcInfo *arcInfo = (ArcInfo *) lfirst(cell);
    1318                 :          0 :                         TrgmState *source = arcInfo->source, *target = arcInfo->target;
    1319         [ #  # ]:          0 :                         while (source->parent)
    1320                 :            :                                 source = source->parent;
    1321         [ #  # ]:          0 :                         while (target->parent)
    1322                 :            :                                 target = target->parent;
    1323         [ #  # ]:          0 :                         if (source != target)
    1324                 :          0 :                                 mergeStates(source, target);
    1325                 :            :                 }
    1326                 :            : 
    1327                 :          0 :                 trgmInfo->expanded = false;
    1328                 :          0 :                 totalTrgmCount -= trgmInfo->count;
    1329                 :            :         }
    1330                 :         14 :         trgmCNFA->totalTrgmCount = totalTrgmCount;
    1331                 :            : 
    1332                 :            :         /* Did we succeed in fitting into MAX_TRGM_COUNT? */
    1333         [ +  - ]:         14 :         if (totalTrgmCount > MAX_TRGM_COUNT)
    1334                 :            :                 return false;
    1335                 :            : 
    1336                 :            :         /*
    1337                 :            :          * Sort color trigrams by colors (will be useful for bsearch) and enumerate
    1338                 :            :          * expanded color trigrams.
    1339                 :            :          */
    1340                 :         14 :         number = 0;
    1341                 :         14 :         qsort(colorTrgms, trgmCNFA->colorTrgmsCount, sizeof(ColorTrgmInfo),
    1342                 :            :                                                                                                                         colorTrgmInfoCmp);
    1343         [ +  + ]:         40 :         for (i = 0; i < trgmCNFA->colorTrgmsCount; i++)
    1344                 :            :         {
    1345         [ +  - ]:         26 :                 if (colorTrgms[i].expanded)
    1346                 :            :                 {
    1347                 :         26 :                         colorTrgms[i].number = number;
    1348                 :         26 :                         number++;
    1349                 :            :                 }
    1350                 :            :         }
    1351                 :            : 
    1352                 :         14 :         trgmCNFA->colorTrgms = colorTrgms;
    1353                 :            :         return true;
    1354                 :            : }
    1355                 :            : 
    1356                 :            : /*
    1357                 :            :  * Expand selected color trigrams into regular trigrams.
    1358                 :            :  */
    1359                 :            : static TRGM *
    1360                 :         14 : expandColorTrigrams(TrgmCNFA *trgmCNFA)
    1361                 :            : {
    1362                 :            :         TRGM       *trg;
    1363                 :            :         trgm       *p;
    1364                 :            :         int                     i;
    1365                 :            :         ColorInfo       emptyColor;
    1366                 :         14 :         mb_char         emptyChar = 0;
    1367                 :            : 
    1368                 :            :         /* Virtual "empty" color structure for representing zero character */
    1369                 :         14 :         emptyColor.alphaCharsCount = 1;
    1370                 :         14 :         emptyColor.alphaChars = &emptyChar;
    1371                 :            : 
    1372                 :         14 :         trg = (TRGM *) palloc0(TRGMHDRSIZE +
    1373                 :            :                                                                         trgmCNFA->totalTrgmCount * sizeof(trgm));
    1374                 :         14 :         trg->flag = ARRKEY;
    1375                 :         14 :         SET_VARSIZE(trg, CALCGTSIZE(ARRKEY, trgmCNFA->totalTrgmCount));
    1376                 :         14 :         p = GETARR(trg);
    1377         [ +  + ]:         40 :         for (i = 0; i < trgmCNFA->colorTrgmsCount; i++)
    1378                 :            :         {
    1379                 :            :                 mb_char s[3];
    1380                 :         26 :                 ColorTrgmInfo *colorTrgm = &trgmCNFA->colorTrgms[i];
    1381                 :            :                 ColorInfo *c[3];
    1382                 :            :                 int j, i1, i2, i3;
    1383                 :            : 
    1384         [ +  - ]:         26 :                 if (!colorTrgm->expanded)
    1385                 :          0 :                         continue;
    1386                 :            : 
    1387                 :            :                 /* Get colors */
    1388         [ +  + ]:        104 :                 for (j = 0; j < 3; j++)
    1389                 :            :                 {
    1390         [ +  + ]:         78 :                         if (colorTrgm->trgm[j] != EMPTY_COLOR)
    1391                 :         72 :                                 c[j] = &trgmCNFA->colorInfo[colorTrgm->trgm[j]];
    1392                 :            :                         else
    1393                 :          6 :                                 c[j] = &emptyColor;
    1394                 :            :                 }
    1395                 :            : 
    1396                 :            :                 /* Iterate over all possible combinations of color characters */
    1397         [ +  + ]:         76 :                 for (i1 = 0; i1 < c[0]->alphaCharsCount; i1++)
    1398                 :            :                 {
    1399                 :         50 :                         s[0] = c[0]->alphaChars[i1];
    1400         [ +  + ]:        172 :                         for (i2 = 0; i2 < c[1]->alphaCharsCount; i2++)
    1401                 :            :                         {
    1402                 :        122 :                                 s[1] = c[1]->alphaChars[i2];
    1403         [ +  + ]:        436 :                                 for (i3 = 0; i3 < c[2]->alphaCharsCount; i3++)
    1404                 :            :                                 {
    1405                 :        314 :                                         s[2] = c[2]->alphaChars[i3];
    1406                 :        314 :                                         fillTrgm(p, s);
    1407                 :        314 :                                         p++;
    1408                 :            :                                 }
    1409                 :            :                         }
    1410                 :            :                 }
    1411                 :            :         }
    1412                 :         14 :         return trg;
    1413                 :            : }
    1414                 :            : 
    1415                 :            : /*---------------------
    1416                 :            :  * Subroutines for packing the graph into final representation
    1417                 :            :  *---------------------
    1418                 :            :  */
    1419                 :            : 
    1420                 :            : /*
    1421                 :            :  * Temporary structure for representing arc during packaging.
    1422                 :            :  */
    1423                 :            : typedef struct
    1424                 :            : {
    1425                 :            :         int             sourceState;
    1426                 :            :         int             targetState;
    1427                 :            :         int             colorTrgm;
    1428                 :            : } PackArcInfo;
    1429                 :            : 
    1430                 :            : /*
    1431                 :            :  * Comparison function for arcs during comparison. Compares arcs in following
    1432                 :            :  * order: sourceState, colorTrgm, targetState.
    1433                 :            :  */
    1434                 :            : static int
    1435                 :         42 : packArcInfoCmp(const void *a1, const void *a2)
    1436                 :            : {
    1437                 :         42 :         const PackArcInfo *p1 = (const PackArcInfo *) a1;
    1438                 :         42 :         const PackArcInfo *p2 = (const PackArcInfo *) a2;
    1439                 :            : 
    1440         [ +  + ]:         42 :         if (p1->sourceState < p2->sourceState)
    1441                 :            :                 return -1;
    1442         [ +  + ]:         32 :         if (p1->sourceState > p2->sourceState)
    1443                 :            :                 return 1;
    1444         [ +  + ]:         22 :         if (p1->colorTrgm < p2->colorTrgm)
    1445                 :            :                 return -1;
    1446         [ +  + ]:         20 :         if (p1->colorTrgm > p2->colorTrgm)
    1447                 :            :                 return 1;
    1448         [ +  - ]:         18 :         if (p1->targetState < p2->targetState)
    1449                 :            :                 return -1;
    1450         [ +  + ]:         18 :         if (p1->targetState > p2->targetState)
    1451                 :            :                 return 1;
    1452                 :         42 :         return 0;
    1453                 :            : }
    1454                 :            : 
    1455                 :            : /*
    1456                 :            :  * Pack resulting graph into final representation.
    1457                 :            :  */
    1458                 :            : static PackedGraph *
    1459                 :         14 : packGraph(TrgmCNFA *trgmCNFA, MemoryContext context)
    1460                 :            : {
    1461                 :         14 :         int                     number = 2,
    1462                 :            :                                 arcIndex,
    1463                 :            :                                 arcsCount;
    1464                 :            :         HASH_SEQ_STATUS scan_status;
    1465                 :            :         TrgmState  *state;
    1466                 :            :         PackArcInfo *arcs, *p1, *p2;
    1467                 :            :         PackedArc  *packedArcs;
    1468                 :            :         PackedGraph *result;
    1469                 :            :         int                     i,
    1470                 :            :                                 j;
    1471                 :            : 
    1472                 :            :         /* Enumerate states */
    1473                 :         14 :         hash_seq_init(&scan_status, trgmCNFA->states);
    1474         [ +  + ]:         66 :         while ((state = (TrgmState *) hash_seq_search(&scan_status)) != NULL)
    1475                 :            :         {
    1476         [ -  + ]:         38 :                 while (state->parent)
    1477                 :            :                         state = state->parent;
    1478                 :            : 
    1479         [ +  - ]:         38 :                 if (state->number < 0)
    1480                 :            :                 {
    1481         [ +  + ]:         38 :                         if (state->init)
    1482                 :         14 :                                 state->number = 0;
    1483         [ +  + ]:         24 :                         else if (state->fin)
    1484                 :         14 :                                 state->number = 1;
    1485                 :            :                         else
    1486                 :            :                         {
    1487                 :         10 :                                 state->number = number;
    1488                 :         52 :                                 number++;
    1489                 :            :                         }
    1490                 :            :                 }
    1491                 :            :         }
    1492                 :            : 
    1493                 :            :         /* Collect array of all arcs */
    1494                 :         14 :         arcs = (PackArcInfo *) palloc(sizeof(PackArcInfo) * trgmCNFA->arcsCount);
    1495                 :         14 :         arcIndex = 0;
    1496                 :         14 :         hash_seq_init(&scan_status, trgmCNFA->states);
    1497         [ +  + ]:         52 :         while ((state = (TrgmState *) hash_seq_search(&scan_status)) != NULL)
    1498                 :            :         {
    1499                 :            :                 TrgmState *source = state;
    1500                 :            :                 ListCell *cell;
    1501                 :            : 
    1502         [ -  + ]:         38 :                 while (source->parent)
    1503                 :            :                         source = source->parent;
    1504                 :            : 
    1505         [ +  + ]:         66 :                 foreach(cell, state->arcs)
    1506                 :            :                 {
    1507                 :         28 :                         TrgmArc *arc = (TrgmArc *) lfirst(cell);
    1508                 :         28 :                         TrgmState *target = arc->target;
    1509                 :            : 
    1510         [ -  + ]:         28 :                         while (target->parent)
    1511                 :            :                                 target = target->parent;
    1512                 :            : 
    1513         [ +  - ]:         28 :                         if (source->number != target->number)
    1514                 :            :                         {
    1515                 :         28 :                                 arcs[arcIndex].sourceState = source->number;
    1516                 :         28 :                                 arcs[arcIndex].targetState = target->number;
    1517                 :         28 :                                 arcs[arcIndex].colorTrgm = ((ColorTrgmInfo *)bsearch(&arc->trgm,
    1518                 :         56 :                                                                 trgmCNFA->colorTrgms, trgmCNFA->colorTrgmsCount,
    1519                 :         28 :                                                         sizeof(ColorTrgmInfo), colorTrgmInfoCmp))->number;
    1520                 :            : 
    1521                 :         28 :                                 arcIndex++;
    1522                 :            :                         }
    1523                 :            :                 }
    1524                 :            :         }
    1525                 :         14 :         qsort(arcs, arcIndex, sizeof(PackArcInfo), packArcInfoCmp);
    1526                 :            : 
    1527                 :            :         /* We could have duplicates because states were merged. Remove them. */
    1528                 :         14 :         p1 = p2 = arcs;
    1529         [ +  + ]:         42 :         while (p1 < arcs + arcIndex)
    1530                 :            :         {
    1531         [ +  + ]:         28 :                 if (packArcInfoCmp(p1, p2) > 0)
    1532                 :            :                 {
    1533                 :         14 :                         p2++;
    1534                 :         14 :                         *p2 = *p1;
    1535                 :            :                 }
    1536                 :         28 :                 p1++;
    1537                 :            :         }
    1538                 :         14 :         arcsCount = p2 - arcs + 1;
    1539                 :            : 
    1540                 :            :         /* Write packed representation */
    1541                 :         14 :         result = (PackedGraph *) MemoryContextAlloc(context, sizeof(PackedGraph));
    1542                 :            : 
    1543                 :            :         /* Pack color trigrams information */
    1544                 :         14 :         result->colorTrigramsCount = 0;
    1545         [ +  + ]:         40 :         for (i = 0; i < trgmCNFA->colorTrgmsCount; i++)
    1546         [ +  - ]:         26 :                 if (trgmCNFA->colorTrgms[i].expanded)
    1547                 :         26 :                         result->colorTrigramsCount++;
    1548                 :         14 :         result->colorTrigramGroups = (int *) MemoryContextAlloc(context,
    1549                 :         14 :                                                                         sizeof(int) * result->colorTrigramsCount);
    1550                 :         14 :         j = 0;
    1551         [ +  + ]:         40 :         for (i = 0; i < trgmCNFA->colorTrgmsCount; i++)
    1552                 :            :         {
    1553         [ +  - ]:         26 :                 if (trgmCNFA->colorTrgms[i].expanded)
    1554                 :            :                 {
    1555                 :         26 :                         result->colorTrigramGroups[j] = trgmCNFA->colorTrgms[i].count;
    1556                 :         26 :                         j++;
    1557                 :            :                 }
    1558                 :            :         }
    1559                 :            : 
    1560                 :            :         /* Pack states and arcs information */
    1561                 :         14 :         result->states = (PackedState *) MemoryContextAlloc(context,
    1562                 :            :                                                                                                 number * sizeof(PackedState));
    1563                 :         14 :         result->statesCount = number;
    1564                 :         14 :         packedArcs = (PackedArc *) MemoryContextAlloc(context,
    1565                 :            :                                                                                                 arcsCount * sizeof(PackedArc));
    1566                 :         14 :         j = 0;
    1567         [ +  + ]:         52 :         for (i = 0; i < number; i++)
    1568                 :            :         {
    1569                 :         38 :                 int cnt = 0;
    1570                 :         38 :                 result->states[i].arcs = &packedArcs[j];
    1571 [ +  + ][ +  + ]:         66 :                 while (j < arcsCount && arcs[j].sourceState == i)
    1572                 :            :                 {
    1573                 :         28 :                         packedArcs[j].targetState = arcs[j].targetState;
    1574                 :         28 :                         packedArcs[j].colorTrgm = arcs[j].colorTrgm;
    1575                 :         28 :                         cnt++;
    1576                 :         28 :                         j++;
    1577                 :            :                 }
    1578                 :         38 :                 result->states[i].arcsCount = cnt;
    1579                 :            :         }
    1580                 :            : 
    1581                 :            :         /* Allocate memory for evaluation */
    1582                 :         14 :         result->colorTrigramsActive = (bool *) MemoryContextAlloc(context,
    1583                 :         14 :                                                                         sizeof(bool) * result->colorTrigramsCount);
    1584                 :         14 :         result->statesActive = (bool *) MemoryContextAlloc(context,
    1585                 :         14 :                                                                         sizeof(bool) * result->statesCount);
    1586                 :            : 
    1587                 :         14 :         return result;
    1588                 :            : }
    1589                 :            : 
    1590                 :            : 
    1591                 :            : /*---------------------
    1592                 :            :  * Debugging functions
    1593                 :            :  *---------------------
    1594                 :            :  */
    1595                 :            : 
    1596                 :            : #ifdef TRGM_REGEXP_DEBUG
    1597                 :            : /*
    1598                 :            :  * Print initial CNFA, in regexp library's representation
    1599                 :            :  */
    1600                 :            : static void
    1601                 :         14 : printSourceCNFA(struct cnfa *cnfa, ColorInfo *colors, int ncolors)
    1602                 :            : {
    1603                 :            :         int state;
    1604                 :            :         StringInfoData buf;
    1605                 :            :         int i;
    1606                 :            : 
    1607                 :         14 :         initStringInfo(&buf);
    1608                 :            : 
    1609                 :         14 :         appendStringInfo(&buf, "\ndigraph sourceCNFA {\n");
    1610                 :            : 
    1611         [ +  + ]:        100 :         for (state = 0; state < cnfa->nstates; state++)
    1612                 :            :         {
    1613                 :         86 :                 struct carc *s = cnfa->states[state];
    1614                 :            : 
    1615                 :         86 :                 appendStringInfo(&buf, "s%d", state);
    1616         [ +  + ]:         86 :                 if (cnfa->post == state)
    1617                 :         14 :                         appendStringInfo(&buf, " [shape = doublecircle]");
    1618                 :         86 :                 appendStringInfo(&buf, ";\n");
    1619                 :            : 
    1620         [ +  + ]:        338 :                 while (s->co != COLORLESS)
    1621                 :            :                 {
    1622                 :        252 :                         appendStringInfo(&buf, "  s%d -> s%d [label = \"%d\"];\n",
    1623                 :            :                                                          state, s->to, s->co);
    1624                 :        252 :                         s++;
    1625                 :            :                 }
    1626                 :            :         }
    1627                 :            : 
    1628                 :         14 :         appendStringInfo(&buf, " node [shape = point ]; initial;\n");
    1629                 :         14 :         appendStringInfo(&buf, " initial -> s%d;\n", cnfa->pre);
    1630                 :            : 
    1631                 :            :         /* Print colors */
    1632                 :         14 :         appendStringInfo(&buf, " { rank = sink;\n");
    1633                 :         14 :         appendStringInfo(&buf, "  Colors [shape = none, margin=0, label=<\n");
    1634                 :            : 
    1635         [ +  + ]:        116 :         for (i = 0; i < ncolors; i++)
    1636                 :            :         {
    1637                 :        102 :                 ColorInfo *color = &colors[i];
    1638                 :            :                 int             j;
    1639                 :            : 
    1640                 :        102 :                 appendStringInfo(&buf, "<br/>Color %d: ", i);
    1641                 :            : 
    1642         [ +  + ]:        102 :                 if (!color->expandable)
    1643                 :         14 :                         appendStringInfo(&buf, "not expandable");
    1644                 :            :                 else
    1645                 :            :                 {
    1646         [ +  + ]:        134 :                         for (j = 0; j < color->alphaCharsCount; j++)
    1647                 :            :                         {
    1648                 :            :                                 char s[5];
    1649                 :         46 :                                 memcpy(s, (char *) &color->alphaChars[j], 4);
    1650                 :         46 :                                 s[4] = '\0';
    1651                 :         46 :                                 appendStringInfo(&buf, "%s", s);
    1652                 :            :                         }
    1653                 :            :                 }
    1654                 :        102 :                 appendStringInfo(&buf, "\n");
    1655                 :            :         }
    1656                 :            : 
    1657                 :         14 :         appendStringInfo(&buf, "  >];\n");
    1658                 :         14 :         appendStringInfo(&buf, " }\n");
    1659                 :            : 
    1660                 :            : 
    1661                 :         14 :         appendStringInfo(&buf, "}\n");
    1662                 :            : 
    1663                 :            :         {
    1664                 :            :                 /* dot -Tpng -o /tmp/source.png < /tmp/source.dot */
    1665                 :         14 :                 FILE *fp = fopen("/tmp/source.dot", "w");
    1666                 :         14 :                 fprintf(fp, "%s", buf.data);
    1667                 :         14 :                 fclose(fp);
    1668                 :            :         }
    1669                 :            : 
    1670                 :         14 :         pfree(buf.data);
    1671                 :         14 : }
    1672                 :            : 
    1673                 :            : /*
    1674                 :            :  * Print trigram-based CNFA-like graph.
    1675                 :            :  */
    1676                 :            : static void
    1677                 :         14 : printTrgmCNFA(TrgmCNFA *trgmCNFA)
    1678                 :            : {
    1679                 :            :         StringInfoData buf;
    1680                 :            :         HASH_SEQ_STATUS scan_status;
    1681                 :            :         TrgmState *state;
    1682                 :         14 :         TrgmState *initstate = NULL;
    1683                 :            : 
    1684                 :         14 :         initStringInfo(&buf);
    1685                 :            : 
    1686                 :         14 :         appendStringInfo(&buf, "\ndigraph transformedCNFA {\n");
    1687                 :            : 
    1688                 :         14 :         hash_seq_init(&scan_status, trgmCNFA->states);
    1689         [ +  + ]:         52 :         while ((state = (TrgmState *) hash_seq_search(&scan_status)) != NULL)
    1690                 :            :         {
    1691                 :            :                 ListCell *cell;
    1692                 :            : 
    1693                 :         38 :                 appendStringInfo(&buf, "s%p", state);
    1694         [ +  + ]:         38 :                 if (state->fin)
    1695                 :         14 :                         appendStringInfo(&buf, " [shape = doublecircle]");
    1696         [ +  + ]:         38 :                 if (state->init)
    1697                 :         14 :                         initstate = state;
    1698                 :         38 :                 appendStringInfo(&buf, " [label = \"%d\"]", state->enterKey.nstate);
    1699                 :         38 :                 appendStringInfo(&buf, ";\n");
    1700                 :            : 
    1701         [ +  + ]:         66 :                 foreach(cell, state->arcs)
    1702                 :            :                 {
    1703                 :         28 :                         TrgmArc *arc = (TrgmArc *) lfirst(cell);
    1704                 :            : 
    1705                 :         28 :                         appendStringInfo(&buf, "  s%p -> s%p [label = \"%d %d %d\"];\n",
    1706                 :         28 :                                                          (void *) state, (void *) arc->target,
    1707                 :         84 :                                                          (uint32) arc->trgm[0], (uint32) arc->trgm[1], (uint32) arc->trgm[2]);
    1708                 :            :                 }
    1709                 :            :         }
    1710                 :            : 
    1711         [ +  - ]:         14 :         if (initstate)
    1712                 :            :         {
    1713                 :         14 :                 appendStringInfo(&buf, " node [shape = point ]; initial;\n");
    1714                 :         14 :                 appendStringInfo(&buf, " initial -> s%p;\n", (void *) initstate);
    1715                 :            :         }
    1716                 :         14 :         appendStringInfo(&buf, "}\n");
    1717                 :            : 
    1718                 :            :         {
    1719                 :            :                 /* dot -Tpng -o /tmp/transformed.png < /tmp/transformed.dot */
    1720                 :         14 :                 FILE *fp = fopen("/tmp/transformed.dot", "w");
    1721                 :         14 :                 fprintf(fp, "%s", buf.data);
    1722                 :         14 :                 fclose(fp);
    1723                 :            :         }
    1724                 :            : 
    1725                 :         14 :         pfree(buf.data);
    1726                 :         14 : }
    1727                 :            : 
    1728                 :            : /*
    1729                 :            :  * Print final packed representation of resulting trigram-based CNFA-like graph.
    1730                 :            :  */
    1731                 :            : static void
    1732                 :         14 : printPackedGraph(PackedGraph *packedGraph, TRGM *trigrams)
    1733                 :            : {
    1734                 :            :         int                     i;
    1735                 :            :         trgm       *p;
    1736                 :            :         StringInfoData buf;
    1737                 :         14 :         initStringInfo(&buf);
    1738                 :            : 
    1739                 :         14 :         appendStringInfo(&buf, "\ndigraph packedGraph {\n");
    1740                 :            : 
    1741         [ +  + ]:         52 :         for (i = 0; i < packedGraph->statesCount; i++)
    1742                 :            :         {
    1743                 :         38 :                 PackedState *state = &packedGraph->states[i];
    1744                 :            :                 int             j;
    1745                 :            : 
    1746                 :         38 :                 appendStringInfo(&buf, " s%d", i);
    1747         [ +  + ]:         38 :                 if (i == 1)
    1748                 :         14 :                         appendStringInfo(&buf, " [shape = doublecircle]");
    1749                 :            : 
    1750                 :         38 :                 appendStringInfo(&buf, " [label = <s%d>];\n", i);
    1751                 :            : 
    1752         [ +  + ]:         66 :                 for (j = 0; j < state->arcsCount; j++)
    1753                 :            :                 {
    1754                 :         28 :                         PackedArc *arc = &state->arcs[j];
    1755                 :         28 :                         appendStringInfo(&buf, "  s%d -> s%d [label = \"trigram %d\"];\n",
    1756                 :            :                                                          i, arc->targetState, arc->colorTrgm);
    1757                 :            :                 }
    1758                 :            :         }
    1759                 :            : 
    1760                 :         14 :         appendStringInfo(&buf, " node [shape = point ]; initial;\n");
    1761                 :         14 :         appendStringInfo(&buf, " initial -> s%d;\n", 0);
    1762                 :            : 
    1763                 :            :         /* Print trigrams */
    1764                 :         14 :         appendStringInfo(&buf, " { rank = sink;\n");
    1765                 :         14 :         appendStringInfo(&buf, "  Trigrams [shape = none, margin=0, label=<\n");
    1766                 :            : 
    1767                 :         14 :         p = GETARR(trigrams);
    1768         [ +  + ]:         40 :         for (i = 0; i < packedGraph->colorTrigramsCount; i++)
    1769                 :            :         {
    1770                 :         26 :                 int count = packedGraph->colorTrigramGroups[i];
    1771                 :            :                 int j;
    1772                 :            : 
    1773                 :         26 :                 appendStringInfo(&buf, "<br/>Trigram %d: ", i);
    1774                 :            : 
    1775         [ +  + ]:        340 :                 for (j = 0; j < count; j++)
    1776                 :            :                 {
    1777         [ +  + ]:        314 :                         if (j > 0)
    1778                 :        288 :                                 appendStringInfo(&buf, ", ");
    1779                 :        314 :                         appendStringInfo(&buf, "\"%c%c%c\"", (*p)[0], (*p)[1], (*p)[2]);
    1780                 :        314 :                         p++;
    1781                 :            :                 }
    1782                 :            :         }
    1783                 :         14 :         appendStringInfo(&buf, "  >];\n");
    1784                 :         14 :         appendStringInfo(&buf, " }\n");
    1785                 :            : 
    1786                 :         14 :         appendStringInfo(&buf, "}\n");
    1787                 :            : 
    1788                 :            :         {
    1789                 :            :                 /* dot -Tpng -o /tmp/packed.png < /tmp/packed.dot */
    1790                 :         14 :                 FILE *fp = fopen("/tmp/packed.dot", "w");
    1791                 :         14 :                 fprintf(fp, "%s", buf.data);
    1792                 :         14 :                 fclose(fp);
    1793                 :            :         }
    1794                 :            : 
    1795                 :         14 :         pfree(buf.data);
    1796                 :         14 : }
    1797                 :            : #endif

Generated by: LCOV version 1.9