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
|