PageRenderTime 195ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/tre-0.8.0/lib/tre-match-parallel.c

#
C | 501 lines | 400 code | 46 blank | 55 comment | 91 complexity | 7c6daf483818259b59670921958a317c MD5 | raw file
Possible License(s): BSD-3-Clause
  1. /*
  2. tre-match-parallel.c - TRE parallel regex matching engine
  3. This software is released under a BSD-style license.
  4. See the file LICENSE for details and copyright.
  5. */
  6. /*
  7. This algorithm searches for matches basically by reading characters
  8. in the searched string one by one, starting at the beginning. All
  9. matching paths in the TNFA are traversed in parallel. When two or
  10. more paths reach the same state, exactly one is chosen according to
  11. tag ordering rules; if returning submatches is not required it does
  12. not matter which path is chosen.
  13. The worst case time required for finding the leftmost and longest
  14. match, or determining that there is no match, is always linearly
  15. dependent on the length of the text being searched.
  16. This algorithm cannot handle TNFAs with back referencing nodes.
  17. See `tre-match-backtrack.c'.
  18. */
  19. #ifdef HAVE_CONFIG_H
  20. #include <config.h>
  21. #endif /* HAVE_CONFIG_H */
  22. #ifdef TRE_USE_ALLOCA
  23. /* AIX requires this to be the first thing in the file. */
  24. #ifndef __GNUC__
  25. # if HAVE_ALLOCA_H
  26. # include <alloca.h>
  27. # else
  28. # ifdef _AIX
  29. #pragma alloca
  30. # else
  31. # ifndef alloca /* predefined by HP cc +Olibcalls */
  32. char *alloca ();
  33. # endif
  34. # endif
  35. # endif
  36. #endif
  37. #endif /* TRE_USE_ALLOCA */
  38. #include <assert.h>
  39. #include <stdlib.h>
  40. #include <string.h>
  41. #ifdef HAVE_WCHAR_H
  42. #include <wchar.h>
  43. #endif /* HAVE_WCHAR_H */
  44. #ifdef HAVE_WCTYPE_H
  45. #include <wctype.h>
  46. #endif /* HAVE_WCTYPE_H */
  47. #ifndef TRE_WCHAR
  48. #include <ctype.h>
  49. #endif /* !TRE_WCHAR */
  50. #ifdef HAVE_MALLOC_H
  51. #include <malloc.h>
  52. #endif /* HAVE_MALLOC_H */
  53. #include "tre-internal.h"
  54. #include "tre-match-utils.h"
  55. #include "tre.h"
  56. #include "xmalloc.h"
  57. typedef struct {
  58. tre_tnfa_transition_t *state;
  59. int *tags;
  60. } tre_tnfa_reach_t;
  61. typedef struct {
  62. int pos;
  63. int **tags;
  64. } tre_reach_pos_t;
  65. #ifdef TRE_DEBUG
  66. static void
  67. tre_print_reach(const tre_tnfa_t *tnfa, tre_tnfa_reach_t *reach, int num_tags)
  68. {
  69. int i;
  70. while (reach->state != NULL)
  71. {
  72. DPRINT((" %p", (void *)reach->state));
  73. if (num_tags > 0)
  74. {
  75. DPRINT(("/"));
  76. for (i = 0; i < num_tags; i++)
  77. {
  78. DPRINT(("%d:%d", i, reach->tags[i]));
  79. if (i < (num_tags-1))
  80. DPRINT((","));
  81. }
  82. }
  83. reach++;
  84. }
  85. DPRINT(("\n"));
  86. }
  87. #endif /* TRE_DEBUG */
  88. reg_errcode_t
  89. tre_tnfa_run_parallel(const tre_tnfa_t *tnfa, const void *string, int len,
  90. tre_str_type_t type, int *match_tags, int eflags,
  91. int *match_end_ofs)
  92. {
  93. /* State variables required by GET_NEXT_WCHAR. */
  94. tre_char_t prev_c = 0, next_c = 0;
  95. const char *str_byte = string;
  96. int pos = -1;
  97. unsigned int pos_add_next = 1;
  98. #ifdef TRE_WCHAR
  99. const wchar_t *str_wide = string;
  100. #ifdef TRE_MBSTATE
  101. mbstate_t mbstate;
  102. #endif /* TRE_MBSTATE */
  103. #endif /* TRE_WCHAR */
  104. int reg_notbol = eflags & REG_NOTBOL;
  105. int reg_noteol = eflags & REG_NOTEOL;
  106. int reg_newline = tnfa->cflags & REG_NEWLINE;
  107. int str_user_end = 0;
  108. char *buf;
  109. tre_tnfa_transition_t *trans_i;
  110. tre_tnfa_reach_t *reach, *reach_next, *reach_i, *reach_next_i;
  111. tre_reach_pos_t *reach_pos;
  112. int *tag_i;
  113. int num_tags, i;
  114. int match_eo = -1; /* end offset of match (-1 if no match found yet) */
  115. int new_match = 0;
  116. int *tmp_tags = NULL;
  117. int *tmp_iptr;
  118. #ifdef TRE_MBSTATE
  119. memset(&mbstate, '\0', sizeof(mbstate));
  120. #endif /* TRE_MBSTATE */
  121. DPRINT(("tre_tnfa_run_parallel, input type %d\n", type));
  122. if (!match_tags)
  123. num_tags = 0;
  124. else
  125. num_tags = tnfa->num_tags;
  126. /* Allocate memory for temporary data required for matching. This needs to
  127. be done for every matching operation to be thread safe. This allocates
  128. everything in a single large block from the stack frame using alloca()
  129. or with malloc() if alloca is unavailable. */
  130. {
  131. int tbytes, rbytes, pbytes, xbytes, total_bytes;
  132. char *tmp_buf;
  133. /* Compute the length of the block we need. */
  134. tbytes = sizeof(*tmp_tags) * num_tags;
  135. rbytes = sizeof(*reach_next) * (tnfa->num_states + 1);
  136. pbytes = sizeof(*reach_pos) * tnfa->num_states;
  137. xbytes = sizeof(int) * num_tags;
  138. total_bytes =
  139. (sizeof(long) - 1) * 4 /* for alignment paddings */
  140. + (rbytes + xbytes * tnfa->num_states) * 2 + tbytes + pbytes;
  141. /* Allocate the memory. */
  142. #ifdef TRE_USE_ALLOCA
  143. buf = alloca(total_bytes);
  144. #else /* !TRE_USE_ALLOCA */
  145. buf = xmalloc((unsigned)total_bytes);
  146. #endif /* !TRE_USE_ALLOCA */
  147. if (buf == NULL)
  148. return REG_ESPACE;
  149. memset(buf, 0, (size_t)total_bytes);
  150. /* Get the various pointers within tmp_buf (properly aligned). */
  151. tmp_tags = (void *)buf;
  152. tmp_buf = buf + tbytes;
  153. tmp_buf += ALIGN(tmp_buf, long);
  154. reach_next = (void *)tmp_buf;
  155. tmp_buf += rbytes;
  156. tmp_buf += ALIGN(tmp_buf, long);
  157. reach = (void *)tmp_buf;
  158. tmp_buf += rbytes;
  159. tmp_buf += ALIGN(tmp_buf, long);
  160. reach_pos = (void *)tmp_buf;
  161. tmp_buf += pbytes;
  162. tmp_buf += ALIGN(tmp_buf, long);
  163. for (i = 0; i < tnfa->num_states; i++)
  164. {
  165. reach[i].tags = (void *)tmp_buf;
  166. tmp_buf += xbytes;
  167. reach_next[i].tags = (void *)tmp_buf;
  168. tmp_buf += xbytes;
  169. }
  170. }
  171. for (i = 0; i < tnfa->num_states; i++)
  172. reach_pos[i].pos = -1;
  173. /* If only one character can start a match, find it first. */
  174. if (tnfa->first_char >= 0 && type == STR_BYTE && str_byte)
  175. {
  176. const char *orig_str = str_byte;
  177. int first = tnfa->first_char;
  178. if (len >= 0)
  179. str_byte = memchr(orig_str, first, (size_t)len);
  180. else
  181. str_byte = strchr(orig_str, first);
  182. if (str_byte == NULL)
  183. {
  184. #ifndef TRE_USE_ALLOCA
  185. if (buf)
  186. xfree(buf);
  187. #endif /* !TRE_USE_ALLOCA */
  188. return REG_NOMATCH;
  189. }
  190. DPRINT(("skipped %lu chars\n", (unsigned long)(str_byte - orig_str)));
  191. if (str_byte >= orig_str + 1)
  192. prev_c = (unsigned char)*(str_byte - 1);
  193. next_c = (unsigned char)*str_byte;
  194. pos = str_byte - orig_str;
  195. if (len < 0 || pos < len)
  196. str_byte++;
  197. }
  198. else
  199. {
  200. GET_NEXT_WCHAR();
  201. pos = 0;
  202. }
  203. #if 0
  204. /* Skip over characters that cannot possibly be the first character
  205. of a match. */
  206. if (tnfa->firstpos_chars != NULL)
  207. {
  208. char *chars = tnfa->firstpos_chars;
  209. if (len < 0)
  210. {
  211. const char *orig_str = str_byte;
  212. /* XXX - use strpbrk() and wcspbrk() because they might be
  213. optimized for the target architecture. Try also strcspn()
  214. and wcscspn() and compare the speeds. */
  215. while (next_c != L'\0' && !chars[next_c])
  216. {
  217. next_c = *str_byte++;
  218. }
  219. prev_c = *(str_byte - 2);
  220. pos += str_byte - orig_str;
  221. DPRINT(("skipped %d chars\n", str_byte - orig_str));
  222. }
  223. else
  224. {
  225. while (pos <= len && !chars[next_c])
  226. {
  227. prev_c = next_c;
  228. next_c = (unsigned char)(*str_byte++);
  229. pos++;
  230. }
  231. }
  232. }
  233. #endif
  234. DPRINT(("length: %d\n", len));
  235. DPRINT(("pos:chr/code | states and tags\n"));
  236. DPRINT(("-------------+------------------------------------------------\n"));
  237. reach_next_i = reach_next;
  238. while (/*CONSTCOND*/1)
  239. {
  240. /* If no match found yet, add the initial states to `reach_next'. */
  241. if (match_eo < 0)
  242. {
  243. DPRINT((" init >"));
  244. trans_i = tnfa->initial;
  245. while (trans_i->state != NULL)
  246. {
  247. if (reach_pos[trans_i->state_id].pos < pos)
  248. {
  249. if (trans_i->assertions
  250. && CHECK_ASSERTIONS(trans_i->assertions))
  251. {
  252. DPRINT(("assertion failed\n"));
  253. trans_i++;
  254. continue;
  255. }
  256. DPRINT((" %p", (void *)trans_i->state));
  257. reach_next_i->state = trans_i->state;
  258. for (i = 0; i < num_tags; i++)
  259. reach_next_i->tags[i] = -1;
  260. tag_i = trans_i->tags;
  261. if (tag_i)
  262. while (*tag_i >= 0)
  263. {
  264. if (*tag_i < num_tags)
  265. reach_next_i->tags[*tag_i] = pos;
  266. tag_i++;
  267. }
  268. if (reach_next_i->state == tnfa->final)
  269. {
  270. DPRINT((" found empty match\n"));
  271. match_eo = pos;
  272. new_match = 1;
  273. for (i = 0; i < num_tags; i++)
  274. match_tags[i] = reach_next_i->tags[i];
  275. }
  276. reach_pos[trans_i->state_id].pos = pos;
  277. reach_pos[trans_i->state_id].tags = &reach_next_i->tags;
  278. reach_next_i++;
  279. }
  280. trans_i++;
  281. }
  282. DPRINT(("\n"));
  283. reach_next_i->state = NULL;
  284. }
  285. else
  286. {
  287. if (num_tags == 0 || reach_next_i == reach_next)
  288. /* We have found a match. */
  289. break;
  290. }
  291. /* Check for end of string. */
  292. if (len < 0)
  293. {
  294. if (type == STR_USER)
  295. {
  296. if (str_user_end)
  297. break;
  298. }
  299. else if (next_c == L'\0')
  300. break;
  301. }
  302. else
  303. {
  304. if (pos >= len)
  305. break;
  306. }
  307. GET_NEXT_WCHAR();
  308. #ifdef TRE_DEBUG
  309. DPRINT(("%3d:%2lc/%05d |", pos - 1, (tre_cint_t)prev_c, (int)prev_c));
  310. tre_print_reach(tnfa, reach_next, num_tags);
  311. DPRINT(("%3d:%2lc/%05d |", pos, (tre_cint_t)next_c, (int)next_c));
  312. tre_print_reach(tnfa, reach_next, num_tags);
  313. #endif /* TRE_DEBUG */
  314. /* Swap `reach' and `reach_next'. */
  315. reach_i = reach;
  316. reach = reach_next;
  317. reach_next = reach_i;
  318. /* For each state in `reach', weed out states that don't fulfill the
  319. minimal matching conditions. */
  320. if (tnfa->num_minimals && new_match)
  321. {
  322. new_match = 0;
  323. reach_next_i = reach_next;
  324. for (reach_i = reach; reach_i->state; reach_i++)
  325. {
  326. int skip = 0;
  327. for (i = 0; tnfa->minimal_tags[i] >= 0; i += 2)
  328. {
  329. int end = tnfa->minimal_tags[i];
  330. int start = tnfa->minimal_tags[i + 1];
  331. DPRINT((" Minimal start %d, end %d\n", start, end));
  332. if (end >= num_tags)
  333. {
  334. DPRINT((" Throwing %p out.\n", reach_i->state));
  335. skip = 1;
  336. break;
  337. }
  338. else if (reach_i->tags[start] == match_tags[start]
  339. && reach_i->tags[end] < match_tags[end])
  340. {
  341. DPRINT((" Throwing %p out because t%d < %d\n",
  342. reach_i->state, end, match_tags[end]));
  343. skip = 1;
  344. break;
  345. }
  346. }
  347. if (!skip)
  348. {
  349. reach_next_i->state = reach_i->state;
  350. tmp_iptr = reach_next_i->tags;
  351. reach_next_i->tags = reach_i->tags;
  352. reach_i->tags = tmp_iptr;
  353. reach_next_i++;
  354. }
  355. }
  356. reach_next_i->state = NULL;
  357. /* Swap `reach' and `reach_next'. */
  358. reach_i = reach;
  359. reach = reach_next;
  360. reach_next = reach_i;
  361. }
  362. /* For each state in `reach' see if there is a transition leaving with
  363. the current input symbol to a state not yet in `reach_next', and
  364. add the destination states to `reach_next'. */
  365. reach_next_i = reach_next;
  366. for (reach_i = reach; reach_i->state; reach_i++)
  367. {
  368. for (trans_i = reach_i->state; trans_i->state; trans_i++)
  369. {
  370. /* Does this transition match the input symbol? */
  371. if (trans_i->code_min <= (tre_cint_t)prev_c &&
  372. trans_i->code_max >= (tre_cint_t)prev_c)
  373. {
  374. if (trans_i->assertions
  375. && (CHECK_ASSERTIONS(trans_i->assertions)
  376. || CHECK_CHAR_CLASSES(trans_i, tnfa, eflags)))
  377. {
  378. DPRINT(("assertion failed\n"));
  379. continue;
  380. }
  381. /* Compute the tags after this transition. */
  382. for (i = 0; i < num_tags; i++)
  383. tmp_tags[i] = reach_i->tags[i];
  384. tag_i = trans_i->tags;
  385. if (tag_i != NULL)
  386. while (*tag_i >= 0)
  387. {
  388. if (*tag_i < num_tags)
  389. tmp_tags[*tag_i] = pos;
  390. tag_i++;
  391. }
  392. if (reach_pos[trans_i->state_id].pos < pos)
  393. {
  394. /* Found an unvisited node. */
  395. reach_next_i->state = trans_i->state;
  396. tmp_iptr = reach_next_i->tags;
  397. reach_next_i->tags = tmp_tags;
  398. tmp_tags = tmp_iptr;
  399. reach_pos[trans_i->state_id].pos = pos;
  400. reach_pos[trans_i->state_id].tags = &reach_next_i->tags;
  401. if (reach_next_i->state == tnfa->final
  402. && (match_eo == -1
  403. || (num_tags > 0
  404. && reach_next_i->tags[0] <= match_tags[0])))
  405. {
  406. DPRINT((" found match %p\n", trans_i->state));
  407. match_eo = pos;
  408. new_match = 1;
  409. for (i = 0; i < num_tags; i++)
  410. match_tags[i] = reach_next_i->tags[i];
  411. }
  412. reach_next_i++;
  413. }
  414. else
  415. {
  416. assert(reach_pos[trans_i->state_id].pos == pos);
  417. /* Another path has also reached this state. We choose
  418. the winner by examining the tag values for both
  419. paths. */
  420. if (tre_tag_order(num_tags, tnfa->tag_directions,
  421. tmp_tags,
  422. *reach_pos[trans_i->state_id].tags))
  423. {
  424. /* The new path wins. */
  425. tmp_iptr = *reach_pos[trans_i->state_id].tags;
  426. *reach_pos[trans_i->state_id].tags = tmp_tags;
  427. if (trans_i->state == tnfa->final)
  428. {
  429. DPRINT((" found better match\n"));
  430. match_eo = pos;
  431. new_match = 1;
  432. for (i = 0; i < num_tags; i++)
  433. match_tags[i] = tmp_tags[i];
  434. }
  435. tmp_tags = tmp_iptr;
  436. }
  437. }
  438. }
  439. }
  440. }
  441. reach_next_i->state = NULL;
  442. }
  443. DPRINT(("match end offset = %d\n", match_eo));
  444. #ifndef TRE_USE_ALLOCA
  445. if (buf)
  446. xfree(buf);
  447. #endif /* !TRE_USE_ALLOCA */
  448. *match_end_ofs = match_eo;
  449. return match_eo >= 0 ? REG_OK : REG_NOMATCH;
  450. }
  451. /* EOF */