#include <stdlib.h>
#include "malloca.h"
-#include "mbuiter.h"
+
+#if GNULIB_MCEL_PREFER
+# include "mcel.h"
+typedef mcel_t mbchar_t;
+static bool mb_equal (mcel_t a, mcel_t b) { return mcel_cmp (a, b) == 0; }
+#else
+# include "mbuiter.h"
+#endif
/* Knuth-Morris-Pratt algorithm. */
#define UNIT unsigned char
{
size_t m = mbslen (needle);
mbchar_t *needle_mbchars;
- size_t *table;
+ size_t extra_align = (alignof (mbchar_t) < alignof (size_t)
+ ? alignof (size_t) - alignof (mbchar_t)
+ : 0);
/* Allocate room for needle_mbchars and the table. */
- void *memory = nmalloca (m, sizeof (mbchar_t) + sizeof (size_t));
+ void *memory = nmalloca (m + !!extra_align,
+ sizeof (mbchar_t) + sizeof (size_t));
void *table_memory;
if (memory == NULL)
return false;
needle_mbchars = memory;
table_memory = needle_mbchars + m;
- table = table_memory;
+ char *aligned = table_memory;
+ aligned += extra_align;
+ aligned -= (uintptr_t) aligned % alignof (size_t);
+ size_t *table = table_memory = aligned;
/* Fill needle_mbchars. */
{
+#if GNULIB_MCEL_PREFER
+ for (size_t j = 0; *needle; needle += needle_mbchars[j++].len)
+ needle_mbchars[j] = mcel_scanz (needle);
+#else
mbui_iterator_t iter;
size_t j;
j = 0;
for (mbui_init (iter, needle); mbui_avail (iter); mbui_advance (iter), j++)
mb_copy (&needle_mbchars[j], &mbui_cur (iter));
+#endif
}
/* Fill the table.
/* Search, using the table to accelerate the processing. */
{
+#if GNULIB_MCEL_PREFER
+ size_t j;
+ char const *rhaystack = haystack;
+ char const *phaystack = haystack;
+
+ j = 0;
+ /* Invariant: phaystack = rhaystack + j. */
+ for (;;)
+ {
+ if (!*phaystack)
+ {
+ rhaystack = NULL;
+ break;
+ }
+ mcel_t g = mcel_scanz (phaystack);
+ if (mcel_cmp (needle_mbchars[j], g) == 0)
+ {
+ j++;
+ /* Exit loop successfully if the entire needle has been found. */
+ if (j == m)
+ break;
+ phaystack += g.len;
+ }
+ else if (j == 0)
+ {
+ /* Found a mismatch at needle[0] already. */
+ rhaystack += mcel_scanz (rhaystack).len;
+ phaystack += g.len;
+ }
+ else
+ {
+ /* Found a match of needle[0..j-1], mismatch at needle[j]. */
+ size_t count = table[j];
+ j -= count;
+ for (; count != 0; count--)
+ rhaystack += mcel_scanz (rhaystack).len;
+ }
+ }
+ *resultp = rhaystack;
+#else
size_t j;
mbui_iterator_t rhaystack;
mbui_iterator_t phaystack;
mbui_advance (rhaystack);
mbui_advance (phaystack);
}
+#endif
}
freea (memory);
needle may be found in haystack. */
if (MB_CUR_MAX > 1)
{
+#if GNULIB_MCEL_PREFER
+ if (!*needle)
+ return (char *) haystack;
+
+ mcel_t ng = mcel_scanz (needle);
+
+ /* Minimizing the worst-case complexity:
+ Let n = mbslen(haystack), m = mbslen(needle).
+ The naïve algorithm is O(n*m) worst-case.
+ The Knuth-Morris-Pratt algorithm is O(n) worst-case but it needs a
+ memory allocation.
+ To achieve linear complexity and yet amortize the cost of the
+ memory allocation, we activate the Knuth-Morris-Pratt algorithm
+ only once the naïve algorithm has already run for some time; more
+ precisely, when
+ - the outer loop count is >= 10,
+ - the average number of comparisons per outer loop is >= 5,
+ - the total number of comparisons is >= m.
+ But we try it only once. If the memory allocation attempt failed,
+ we don't retry it. */
+ bool try_kmp = true;
+ size_t outer_loop_count = 0;
+ size_t comparison_count = 0;
+
+ /* Last comparison count, and needle + last_ccount. */
+ size_t last_ccount = 0;
+ char const *iter_needle_last_ccount = needle;
+
+ char const *iter_haystack = haystack;
+
+ for (mcel_t hg; *iter_haystack; iter_haystack += hg.len)
+ {
+ /* See whether it's advisable to use an asymptotically faster
+ algorithm. */
+ if (try_kmp
+ && outer_loop_count >= 10
+ && comparison_count >= 5 * outer_loop_count)
+ {
+ /* See if needle + comparison_count now reaches the end of
+ needle. */
+ size_t count = comparison_count - last_ccount;
+ for (;
+ count > 0 && *iter_needle_last_ccount;
+ count--)
+ iter_needle_last_ccount
+ += mcel_scanz (iter_needle_last_ccount).len;
+ last_ccount = comparison_count;
+ if (!*iter_needle_last_ccount)
+ {
+ char const *result;
+ if (knuth_morris_pratt_multibyte (haystack, needle,
+ &result))
+ return (char *) result;
+ try_kmp = false;
+ }
+ }
+
+ outer_loop_count++;
+ comparison_count++;
+ hg = mcel_scanz (iter_haystack);
+ if (mcel_cmp (hg, ng) == 0)
+ /* The first character matches. */
+ {
+ char const *rhaystack = iter_haystack + hg.len;
+ char const *rneedle = needle + ng.len;
+ mcel_t rhg, rng;
+ do
+ {
+ if (!*rneedle)
+ return (char *) iter_haystack;
+ if (!*rhaystack)
+ return NULL;
+ rhg = mcel_scanz (rhaystack); rhaystack += rhg.len;
+ rng = mcel_scanz (rneedle); rneedle += rng.len;
+ comparison_count++;
+ }
+ while (mcel_cmp (rhg, rng) == 0);
+ }
+ }
+
+ return NULL;
+#else
mbui_iterator_t iter_needle;
mbui_init (iter_needle, needle);
}
else
return (char *) haystack;
+#endif
}
else
{