]>
git.saurik.com Git - apple/libc.git/blob - regex/TRE/lib/tre-match-parallel.c
2 tre-match-parallel.c - TRE parallel regex matching engine
4 This software is released under a BSD-style license.
5 See the file LICENSE for details and copyright.
10 This algorithm searches for matches basically by reading characters
11 in the searched string one by one, starting at the beginning. All
12 matching paths in the TNFA are traversed in parallel. When two or
13 more paths reach the same state, exactly one is chosen according to
14 tag ordering rules; if returning submatches is not required it does
15 not matter which path is chosen.
17 The worst case time required for finding the leftmost and longest
18 match, or determining that there is no match, is always linearly
19 dependent on the length of the text being searched.
21 This algorithm cannot handle TNFAs with back referencing nodes.
22 See `tre-match-backtrack.c'.
28 #endif /* HAVE_CONFIG_H */
30 /* Unset TRE_USE_ALLOCA to avoid using the stack to hold all the state
35 /* AIX requires this to be the first thing in the file. */
43 # ifndef alloca /* predefined by HP cc +Olibcalls */
49 #endif /* TRE_USE_ALLOCA */
56 #endif /* HAVE_WCHAR_H */
59 #endif /* HAVE_WCTYPE_H */
62 #endif /* !TRE_WCHAR */
65 #endif /* HAVE_MALLOC_H */
67 #include "tre-internal.h"
68 #include "tre-match-utils.h"
75 tre_tnfa_transition_t
*state
;
87 tre_print_reach1(tre_tnfa_transition_t
*state
, tre_tag_t
*tags
, int num_tags
)
89 DPRINT((" %p", (void *)state
));
93 tre_print_tags(tags
, num_tags
);
98 tre_print_reach(const tre_tnfa_t
*tnfa
, tre_tnfa_reach_t
*reach
, int num_tags
)
100 while (reach
->state
!= NULL
)
102 tre_print_reach1(reach
->state
, reach
->tags
, num_tags
);
108 #endif /* TRE_DEBUG */
111 tre_tnfa_run_parallel(const tre_tnfa_t
*tnfa
, const void *string
, int len
,
112 tre_str_type_t type
, tre_tag_t
*match_tags
, int eflags
,
115 /* State variables required by GET_NEXT_WCHAR. */
116 tre_char_t prev_c
= 0, next_c
= 0;
117 const char *str_byte
= string
;
119 unsigned int pos_add_next
= 1;
121 const wchar_t *str_wide
= string
;
124 #endif /* TRE_MBSTATE */
125 #endif /* TRE_WCHAR */
126 int reg_notbol
= eflags
& REG_NOTBOL
;
127 int reg_noteol
= eflags
& REG_NOTEOL
;
128 int reg_newline
= tnfa
->cflags
& REG_NEWLINE
;
130 int str_user_end
= 0;
131 #endif /* TRE_STR_USER */
134 tre_tnfa_transition_t
*trans_i
;
135 tre_tnfa_reach_t
*reach
, *reach_next
, *reach_i
, *reach_next_i
;
136 tre_reach_pos_t
*reach_pos
;
140 int match_eo
= -1; /* end offset of match (-1 if no match found yet) */
143 #endif /* TRE_DEBUG */
144 tre_tag_t
*tmp_tags
= NULL
;
150 memset(&mbstate
, '\0', sizeof(mbstate
));
151 #endif /* TRE_MBSTATE */
153 DPRINT(("tre_tnfa_run_parallel, input type %d\n", type
));
158 num_tags
= tnfa
->num_tags
;
160 /* Allocate memory for temporary data required for matching. This needs to
161 be done for every matching operation to be thread safe. This allocates
162 everything in a single large block from the stack frame using alloca()
163 or with malloc() if alloca is unavailable. */
165 int rbytes
, pbytes
, total_bytes
;
167 /* Compute the length of the block we need. */
168 tbytes
= sizeof(*tmp_tags
) * num_tags
;
169 rbytes
= sizeof(*reach_next
) * (tnfa
->num_states
+ 1);
170 pbytes
= sizeof(*reach_pos
) * tnfa
->num_states
;
172 (sizeof(long) - 1) * 4 /* for alignment paddings */
173 + (rbytes
+ tbytes
* tnfa
->num_states
) * 2 + tbytes
+ pbytes
;
175 DPRINT(("tre_tnfa_run_parallel, allocate %d bytes\n", total_bytes
));
176 /* Allocate the memory. */
177 #ifdef TRE_USE_ALLOCA
178 buf
= alloca(total_bytes
);
179 #else /* !TRE_USE_ALLOCA */
180 buf
= xmalloc((unsigned)total_bytes
);
181 #endif /* !TRE_USE_ALLOCA */
184 memset(buf
, 0, (size_t)total_bytes
);
186 /* Get the various pointers within tmp_buf (properly aligned). */
187 tmp_tags
= (void *)buf
;
188 tmp_buf
= buf
+ tbytes
;
189 tmp_buf
+= ALIGN(tmp_buf
, long);
190 reach_next
= (void *)tmp_buf
;
192 tmp_buf
+= ALIGN(tmp_buf
, long);
193 reach
= (void *)tmp_buf
;
195 tmp_buf
+= ALIGN(tmp_buf
, long);
196 reach_pos
= (void *)tmp_buf
;
198 tmp_buf
+= ALIGN(tmp_buf
, long);
199 for (i
= 0; i
< tnfa
->num_states
; i
++)
201 reach
[i
].tags
= (void *)tmp_buf
;
203 reach_next
[i
].tags
= (void *)tmp_buf
;
208 for (i
= 0; i
< tnfa
->num_states
; i
++)
209 reach_pos
[i
].pos
= -1;
211 /* If only one character can start a match, find it first. */
212 if (tnfa
->first_char
>= 0 && type
== STR_BYTE
&& str_byte
)
214 const char *orig_str
= str_byte
;
215 int first
= tnfa
->first_char
;
218 str_byte
= memchr(orig_str
, first
, (size_t)len
);
220 str_byte
= strchr(orig_str
, first
);
221 if (str_byte
== NULL
)
223 #ifndef TRE_USE_ALLOCA
226 #endif /* !TRE_USE_ALLOCA */
229 DPRINT(("skipped %lu chars\n", (unsigned long)(str_byte
- orig_str
)));
230 if (str_byte
>= orig_str
+ 1)
231 prev_c
= (unsigned char)*(str_byte
- 1);
232 next_c
= (unsigned char)*str_byte
;
233 pos
= str_byte
- orig_str
;
234 if (len
< 0 || pos
< len
)
244 /* Skip over characters that cannot possibly be the first character
246 if (tnfa
->firstpos_chars
!= NULL
)
248 char *chars
= tnfa
->firstpos_chars
;
252 const char *orig_str
= str_byte
;
253 /* XXX - use strpbrk() and wcspbrk() because they might be
254 optimized for the target architecture. Try also strcspn()
255 and wcscspn() and compare the speeds. */
256 while (next_c
!= L
'\0' && !chars
[next_c
])
258 next_c
= *str_byte
++;
260 prev_c
= *(str_byte
- 2);
261 pos
+= str_byte
- orig_str
;
262 DPRINT(("skipped %d chars\n", str_byte
- orig_str
));
266 while (pos
<= len
&& !chars
[next_c
])
269 next_c
= (unsigned char)(*str_byte
++);
276 DPRINT(("length: %d\n", len
));
277 DPRINT(("pos:chr/code | states and tags\n"));
278 DPRINT(("-------------+------------------------------------------------\n"));
280 reach_next_i
= reach_next
;
281 while (/*CONSTCOND*/1)
283 /* If no match found yet, add the initial states to `reach_next'. */
287 trans_i
= tnfa
->initial
;
288 while (trans_i
->state
!= NULL
)
290 if (reach_pos
[trans_i
->state_id
].pos
< pos
)
292 if (trans_i
->assertions
293 && CHECK_ASSERTIONS(trans_i
->assertions
))
295 DPRINT(("assertion failed\n"));
300 DPRINT((" %p", (void *)trans_i
->state
));
301 reach_next_i
->state
= trans_i
->state
;
302 memset(reach_next_i
->tags
, 0, tbytes
);
303 tag_i
= trans_i
->tags
;
308 if (*tag_i
< num_tags
)
309 tre_tag_set(reach_next_i
->tags
, *tag_i
, pos
, touch
);
314 if (reach_next_i
->state
== tnfa
->final
)
316 DPRINT((" found empty match\n"));
318 memcpy(match_tags
, reach_next_i
->tags
, tbytes
);
320 reach_pos
[trans_i
->state_id
].pos
= pos
;
321 reach_pos
[trans_i
->state_id
].tags
= &reach_next_i
->tags
;
327 reach_next_i
->state
= NULL
;
331 if (num_tags
== 0 || reach_next_i
== reach_next
)
332 /* We have found a match. */
336 /* Check for end of string. */
340 if (type
== STR_USER
)
346 #endif /* TRE_STR_USER */
359 DPRINT(("%3d:%2lc/%05d |", pos
- 1, (tre_cint_t
)prev_c
, (int)prev_c
));
360 tre_print_reach(tnfa
, reach_next
, num_tags
);
361 //DPRINT(("%3d:%2lc/%05d |", pos, (tre_cint_t)next_c, (int)next_c));
362 //tre_print_reach(tnfa, reach_next, num_tags);
363 #endif /* TRE_DEBUG */
365 /* Swap `reach' and `reach_next'. */
368 reach_next
= reach_i
;
372 #endif /* TRE_DEBUG */
374 /* For each state in `reach' see if there is a transition leaving with
375 the current input symbol to a state not yet in `reach_next', and
376 add the destination states to `reach_next'. */
377 reach_next_i
= reach_next
;
378 for (reach_i
= reach
; reach_i
->state
; reach_i
++)
380 for (trans_i
= reach_i
->state
; trans_i
->state
; trans_i
++)
382 /* Does this transition match the input symbol? */
383 if (trans_i
->code_min
<= (tre_cint_t
)prev_c
&&
384 trans_i
->code_max
>= (tre_cint_t
)prev_c
)
386 if (trans_i
->assertions
387 && (CHECK_ASSERTIONS(trans_i
->assertions
)
388 || CHECK_CHAR_CLASSES(trans_i
, tnfa
, eflags
)))
390 DPRINT(("assertion failed\n"));
394 /* Compute the tags after this transition. */
395 memcpy(tmp_tags
, reach_i
->tags
, tbytes
);
396 tag_i
= trans_i
->tags
;
401 if (*tag_i
< num_tags
)
402 tre_tag_set(tmp_tags
, *tag_i
, pos
, touch
);
408 /* For each new transition, weed out those that don't
409 fulfill the minimal matching conditions. */
410 if (tnfa
->num_minimals
&& match_eo
>= 0)
416 DPRINT(("Checking minimal conditions: match_eo=%d "
417 "match_tags=", match_eo
));
418 tre_print_tags(match_tags
, num_tags
);
422 #endif /* TRE_DEBUG */
423 for (i
= 0; tnfa
->minimal_tags
[i
] >= 0; i
+= 2)
425 int end
= tnfa
->minimal_tags
[i
];
426 int start
= tnfa
->minimal_tags
[i
+ 1];
427 DPRINT((" Minimal start %d, end %d\n", start
, end
));
428 if (tre_minimal_tag_order(start
, end
, match_tags
,
438 DPRINT((" Throwing out"));
439 tre_print_reach1(reach_i
->state
, tmp_tags
,
442 #endif /* TRE_DEBUG */
447 if (reach_pos
[trans_i
->state_id
].pos
< pos
)
449 /* Found an unvisited node. */
450 reach_next_i
->state
= trans_i
->state
;
451 tmp_iptr
= reach_next_i
->tags
;
452 reach_next_i
->tags
= tmp_tags
;
454 reach_pos
[trans_i
->state_id
].pos
= pos
;
455 reach_pos
[trans_i
->state_id
].tags
= &reach_next_i
->tags
;
457 if (reach_next_i
->state
== tnfa
->final
460 && tre_tag_get(reach_next_i
->tags
, 0) <=
461 tre_tag_get(match_tags
, 0))))
464 DPRINT((" found match"));
465 tre_print_reach1(trans_i
->state
, reach_next_i
->tags
, num_tags
);
467 #endif /* TRE_DEBUG */
469 memcpy(match_tags
, reach_next_i
->tags
, tbytes
);
476 assert(reach_pos
[trans_i
->state_id
].pos
== pos
);
477 /* Another path has also reached this state. We choose
478 the winner by examining the tag values for both
480 if (tre_tag_order(num_tags
, tnfa
->tag_directions
,
482 *reach_pos
[trans_i
->state_id
].tags
))
484 /* The new path wins. */
485 tmp_iptr
= *reach_pos
[trans_i
->state_id
].tags
;
486 *reach_pos
[trans_i
->state_id
].tags
= tmp_tags
;
487 if (trans_i
->state
== tnfa
->final
)
490 DPRINT((" found better match"));
491 tre_print_reach1(trans_i
->state
, tmp_tags
, num_tags
);
493 #endif /* TRE_DEBUG */
495 memcpy(match_tags
, tmp_tags
, tbytes
);
503 reach_next_i
->state
= NULL
;
506 DPRINT(("match end offset = %d\n", match_eo
));
508 *match_end_ofs
= match_eo
;
512 DPRINT(("Winning tags="));
513 tre_print_tags_all(match_tags
, num_tags
);
514 DPRINT((" touch=%d\n", touch
));
516 #endif /* TRE_DEBUG */
518 #ifndef TRE_USE_ALLOCA
521 #endif /* !TRE_USE_ALLOCA */
523 return match_eo
>= 0 ? REG_OK
: REG_NOMATCH
;