1
0
mirror of https://https.git.savannah.gnu.org/git/gnulib.git synced 2026-09-01 02:34:55 +00:00

mbsstr: support GNULIB_MCEL_PREFER

Support mcel API for apps that prefer it.
* lib/mbsstr.c [GNULIB_MCEL_PREFER]: Include mcel.h not mbuiter.h.
(mbchar_t, mb_equal) [GNULIB_MCEL_PREFER]: New type and function,
to make it easier to use common code.
(knuth_morris_pratt_multibyte): Don't assume mbchar_t's alignment
is at least that of size_t.
(knuth_morris_pratt_multibyte, mbsstr) [GNULIB_MCEL_PREFER]:
Use mcel API.
* modules/mbsstr (Depends-on): Add alignasof.
This commit is contained in:
Paul Eggert
2023-09-09 17:14:15 -07:00
parent aab8f27073
commit f039d18ef9
3 changed files with 158 additions and 4 deletions
+11
View File
@@ -1,5 +1,16 @@
2023-09-09 Paul Eggert <eggert@cs.ucla.edu>
mbsstr: support GNULIB_MCEL_PREFER
Support mcel API for apps that prefer it.
* lib/mbsstr.c [GNULIB_MCEL_PREFER]: Include mcel.h not mbuiter.h.
(mbchar_t, mb_equal) [GNULIB_MCEL_PREFER]: New type and function,
to make it easier to use common code.
(knuth_morris_pratt_multibyte): Don't assume mbchar_t's alignment
is at least that of size_t.
(knuth_morris_pratt_multibyte, mbsstr) [GNULIB_MCEL_PREFER]:
Use mcel API.
* modules/mbsstr (Depends-on): Add alignasof.
mbslen: support GNULIB_MCEL_PREFER
Support mcel API for apps that prefer it.
The following changes are in effect only if GNULIB_MCEL_PREFER.
+146 -4
View File
@@ -24,7 +24,14 @@
#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
@@ -42,25 +49,36 @@ knuth_morris_pratt_multibyte (const char *haystack, const char *needle,
{
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.
@@ -132,6 +150,46 @@ knuth_morris_pratt_multibyte (const char *haystack, const char *needle,
/* 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;
@@ -173,6 +231,7 @@ knuth_morris_pratt_multibyte (const char *haystack, const char *needle,
mbui_advance (rhaystack);
mbui_advance (phaystack);
}
#endif
}
freea (memory);
@@ -191,6 +250,88 @@ mbsstr (const char *haystack, const char *needle)
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);
@@ -287,6 +428,7 @@ mbsstr (const char *haystack, const char *needle)
}
else
return (char *) haystack;
#endif
}
else
{
+1
View File
@@ -6,6 +6,7 @@ lib/mbsstr.c
lib/str-kmp.h
Depends-on:
alignasof
mbuiter
stdbool
string