Ensure O(n) worst-case complexity of mbscasestr.
[gnulib.git] / lib / mbscasestr.c
1 /* Case-insensitive searching in a string.
2    Copyright (C) 2005-2007 Free Software Foundation, Inc.
3    Written by Bruno Haible <bruno@clisp.org>, 2005.
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2, or (at your option)
8    any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software Foundation,
17    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
18
19 #include <config.h>
20
21 /* Specification.  */
22 #include <string.h>
23
24 #include <ctype.h>
25 #include <stdbool.h>
26 #include <stddef.h>  /* for NULL, in case a nonstandard string.h lacks it */
27
28 #if HAVE_MBRTOWC
29 # include "mbuiter.h"
30 #endif
31
32 #define TOLOWER(Ch) (isupper (Ch) ? tolower (Ch) : (Ch))
33
34 /* Knuth-Morris-Pratt algorithm.
35    See http://en.wikipedia.org/wiki/Knuth-Morris-Pratt_algorithm
36    Return a boolean indicating success.  */
37
38 static bool
39 knuth_morris_pratt_unibyte (const char *haystack, const char *needle,
40                             const char **resultp)
41 {
42   size_t m = strlen (needle);
43
44   /* Allocate the table.  */
45   size_t *table = (size_t *) malloc (m * sizeof (size_t));
46   if (table == NULL)
47     return false;
48   /* Fill the table.
49      For 0 < i < m:
50        0 < table[i] <= i is defined such that
51        rhaystack[0..i-1] == needle[0..i-1] and rhaystack[i] != needle[i]
52        implies
53        forall 0 <= x < table[i]: rhaystack[x..x+m-1] != needle[0..m-1],
54        and table[i] is as large as possible with this property.
55      table[0] remains uninitialized.  */
56   {
57     size_t i, j;
58
59     table[1] = 1;
60     j = 0;
61     for (i = 2; i < m; i++)
62       {
63         unsigned char b = TOLOWER ((unsigned char) needle[i - 1]);
64
65         for (;;)
66           {
67             if (b == TOLOWER ((unsigned char) needle[j]))
68               {
69                 table[i] = i - ++j;
70                 break;
71               }
72             if (j == 0)
73               {
74                 table[i] = i;
75                 break;
76               }
77             j = j - table[j];
78           }
79       }
80   }
81
82   /* Search, using the table to accelerate the processing.  */
83   {
84     size_t j;
85     const char *rhaystack;
86     const char *phaystack;
87
88     *resultp = NULL;
89     j = 0;
90     rhaystack = haystack;
91     phaystack = haystack;
92     /* Invariant: phaystack = rhaystack + j.  */
93     while (*phaystack != '\0')
94       if (TOLOWER ((unsigned char) needle[j])
95           == TOLOWER ((unsigned char) *phaystack))
96         {
97           j++;
98           phaystack++;
99           if (j == m)
100             {
101               /* The entire needle has been found.  */
102               *resultp = rhaystack;
103               break;
104             }
105         }
106       else if (j > 0)
107         {
108           /* Found a match of needle[0..j-1], mismatch at needle[j].  */
109           rhaystack += table[j];
110           j -= table[j];
111         }
112       else
113         {
114           /* Found a mismatch at needle[0] already.  */
115           rhaystack++;
116           phaystack++;
117         }
118   }
119
120   free (table);
121   return true;
122 }
123
124 #if HAVE_MBRTOWC
125 static bool
126 knuth_morris_pratt_multibyte (const char *haystack, const char *needle,
127                               const char **resultp)
128 {
129   size_t m = mbslen (needle);
130   mbchar_t *needle_mbchars;
131   size_t *table;
132
133   /* Allocate room for needle_mbchars and the table.  */
134   char *memory = (char *) malloc (m * (sizeof (mbchar_t) + sizeof (size_t)));
135   if (memory == NULL)
136     return false;
137   needle_mbchars = (mbchar_t *) memory;
138   table = (size_t *) (memory + m * sizeof (mbchar_t));
139
140   /* Fill needle_mbchars.  */
141   {
142     mbui_iterator_t iter;
143     size_t j;
144
145     j = 0;
146     for (mbui_init (iter, needle); mbui_avail (iter); mbui_advance (iter), j++)
147       {
148         mb_copy (&needle_mbchars[j], &mbui_cur (iter));
149         if (needle_mbchars[j].wc_valid)
150           needle_mbchars[j].wc = towlower (needle_mbchars[j].wc);
151       }
152   }
153
154   /* Fill the table.
155      For 0 < i < m:
156        0 < table[i] <= i is defined such that
157        rhaystack[0..i-1] == needle[0..i-1] and rhaystack[i] != needle[i]
158        implies
159        forall 0 <= x < table[i]: rhaystack[x..x+m-1] != needle[0..m-1],
160        and table[i] is as large as possible with this property.
161      table[0] remains uninitialized.  */
162   {
163     size_t i, j;
164
165     table[1] = 1;
166     j = 0;
167     for (i = 2; i < m; i++)
168       {
169         mbchar_t *b = &needle_mbchars[i - 1];
170
171         for (;;)
172           {
173             if (mb_equal (*b, needle_mbchars[j]))
174               {
175                 table[i] = i - ++j;
176                 break;
177               }
178             if (j == 0)
179               {
180                 table[i] = i;
181                 break;
182               }
183             j = j - table[j];
184           }
185       }
186   }
187
188   /* Search, using the table to accelerate the processing.  */
189   {
190     size_t j;
191     mbui_iterator_t rhaystack;
192     mbui_iterator_t phaystack;
193
194     *resultp = NULL;
195     j = 0;
196     mbui_init (rhaystack, haystack);
197     mbui_init (phaystack, haystack);
198     /* Invariant: phaystack = rhaystack + j.  */
199     while (mbui_avail (phaystack))
200       {
201         mbchar_t c;
202
203         mb_copy (&c, &mbui_cur (phaystack));
204         if (c.wc_valid)
205           c.wc = towlower (c.wc);
206         if (mb_equal (needle_mbchars[j], c))
207           {
208             j++;
209             mbui_advance (phaystack);
210             if (j == m)
211               {
212                 /* The entire needle has been found.  */
213                 *resultp = mbui_cur_ptr (rhaystack);
214                 break;
215               }
216           }
217         else if (j > 0)
218           {
219             /* Found a match of needle[0..j-1], mismatch at needle[j].  */
220             size_t count = table[j];
221             j -= count;
222             for (; count > 0; count--)
223               {
224                 if (!mbui_avail (rhaystack))
225                   abort ();
226                 mbui_advance (rhaystack);
227               }
228           }
229         else
230           {
231             /* Found a mismatch at needle[0] already.  */
232             if (!mbui_avail (rhaystack))
233               abort ();
234             mbui_advance (rhaystack);
235             mbui_advance (phaystack);
236           }
237       }
238   }
239
240   free (memory);
241   return true;
242 }
243 #endif
244
245 /* Find the first occurrence of the character string NEEDLE in the character
246    string HAYSTACK, using case-insensitive comparison.
247    Note: This function may, in multibyte locales, return success even if
248    strlen (haystack) < strlen (needle) !  */
249 char *
250 mbscasestr (const char *haystack, const char *needle)
251 {
252   /* Be careful not to look at the entire extent of haystack or needle
253      until needed.  This is useful because of these two cases:
254        - haystack may be very long, and a match of needle found early,
255        - needle may be very long, and not even a short initial segment of
256          needle may be found in haystack.  */
257 #if HAVE_MBRTOWC
258   if (MB_CUR_MAX > 1)
259     {
260       mbui_iterator_t iter_needle;
261
262       mbui_init (iter_needle, needle);
263       if (mbui_avail (iter_needle))
264         {
265           /* Minimizing the worst-case complexity:
266              Let n = mbslen(haystack), m = mbslen(needle).
267              The naïve algorithm is O(n*m) worst-case.
268              The Knuth-Morris-Pratt algorithm is O(n) worst-case but it needs a
269              memory allocation.
270              To achieve linear complexity and yet amortize the cost of the
271              memory allocation, we activate the Knuth-Morris-Pratt algorithm
272              only once the naïve algorithm has already run for some time; more
273              precisely, when
274                - the outer loop count is >= 10,
275                - the average number of comparisons per outer loop is >= 5,
276                - the total number of comparisons is >= m.
277              But we try it only once.  If the memory allocation attempt failed,
278              we don't retry it.  */
279           bool try_kmp = true;
280           size_t outer_loop_count = 0;
281           size_t comparison_count = 0;
282           size_t last_ccount = 0;                  /* last comparison count */
283           mbui_iterator_t iter_needle_last_ccount; /* = needle + last_ccount */
284
285           mbchar_t b;
286           mbui_iterator_t iter_haystack;
287
288           mbui_init (iter_needle_last_ccount, needle);
289
290           mb_copy (&b, &mbui_cur (iter_needle));
291           if (b.wc_valid)
292             b.wc = towlower (b.wc);
293
294           mbui_init (iter_haystack, haystack);
295           for (;; mbui_advance (iter_haystack))
296             {
297               mbchar_t c;
298
299               if (!mbui_avail (iter_haystack))
300                 /* No match.  */
301                 return NULL;
302
303               /* See whether it's advisable to use an asymptotically faster
304                  algorithm.  */
305               if (try_kmp
306                   && outer_loop_count >= 10
307                   && comparison_count >= 5 * outer_loop_count)
308                 {
309                   /* See if needle + comparison_count now reaches the end of
310                      needle.  */
311                   size_t count = comparison_count - last_ccount;
312                   for (;
313                        count > 0 && mbui_avail (iter_needle_last_ccount);
314                        count--)
315                     mbui_advance (iter_needle_last_ccount);
316                   last_ccount = comparison_count;
317                   if (!mbui_avail (iter_needle_last_ccount))
318                     {
319                       /* Try the Knuth-Morris-Pratt algorithm.  */
320                       const char *result;
321                       bool success =
322                         knuth_morris_pratt_multibyte (haystack, needle,
323                                                       &result);
324                       if (success)
325                         return (char *) result;
326                       try_kmp = false;
327                     }
328                 }
329
330               outer_loop_count++;
331               comparison_count++;
332               mb_copy (&c, &mbui_cur (iter_haystack));
333               if (c.wc_valid)
334                 c.wc = towlower (c.wc);
335               if (mb_equal (c, b))
336                 /* The first character matches.  */
337                 {
338                   mbui_iterator_t rhaystack;
339                   mbui_iterator_t rneedle;
340
341                   memcpy (&rhaystack, &iter_haystack, sizeof (mbui_iterator_t));
342                   mbui_advance (rhaystack);
343
344                   mbui_init (rneedle, needle);
345                   if (!mbui_avail (rneedle))
346                     abort ();
347                   mbui_advance (rneedle);
348
349                   for (;; mbui_advance (rhaystack), mbui_advance (rneedle))
350                     {
351                       if (!mbui_avail (rneedle))
352                         /* Found a match.  */
353                         return (char *) mbui_cur_ptr (iter_haystack);
354                       if (!mbui_avail (rhaystack))
355                         /* No match.  */
356                         return NULL;
357                       comparison_count++;
358                       if (!mb_caseequal (mbui_cur (rhaystack),
359                                          mbui_cur (rneedle)))
360                         /* Nothing in this round.  */
361                         break;
362                     }
363                 }
364             }
365         }
366       else
367         return (char *) haystack;
368     }
369   else
370 #endif
371     {
372       if (*needle != '\0')
373         {
374           /* Minimizing the worst-case complexity:
375              Let n = strlen(haystack), m = strlen(needle).
376              The naïve algorithm is O(n*m) worst-case.
377              The Knuth-Morris-Pratt algorithm is O(n) worst-case but it needs a
378              memory allocation.
379              To achieve linear complexity and yet amortize the cost of the
380              memory allocation, we activate the Knuth-Morris-Pratt algorithm
381              only once the naïve algorithm has already run for some time; more
382              precisely, when
383                - the outer loop count is >= 10,
384                - the average number of comparisons per outer loop is >= 5,
385                - the total number of comparisons is >= m.
386              But we try it only once.  If the memory allocation attempt failed,
387              we don't retry it.  */
388           bool try_kmp = true;
389           size_t outer_loop_count = 0;
390           size_t comparison_count = 0;
391           size_t last_ccount = 0;                  /* last comparison count */
392           const char *needle_last_ccount = needle; /* = needle + last_ccount */
393
394           /* Speed up the following searches of needle by caching its first
395              character.  */
396           unsigned char b = TOLOWER ((unsigned char) *needle);
397
398           needle++;
399           for (;; haystack++)
400             {
401               if (*haystack == '\0')
402                 /* No match.  */
403                 return NULL;
404
405               /* See whether it's advisable to use an asymptotically faster
406                  algorithm.  */
407               if (try_kmp
408                   && outer_loop_count >= 10
409                   && comparison_count >= 5 * outer_loop_count)
410                 {
411                   /* See if needle + comparison_count now reaches the end of
412                      needle.  */
413                   if (needle_last_ccount != NULL)
414                     {
415                       needle_last_ccount +=
416                         strnlen (needle_last_ccount,
417                                  comparison_count - last_ccount);
418                       if (*needle_last_ccount == '\0')
419                         needle_last_ccount = NULL;
420                       last_ccount = comparison_count;
421                     }
422                   if (needle_last_ccount == NULL)
423                     {
424                       /* Try the Knuth-Morris-Pratt algorithm.  */
425                       const char *result;
426                       bool success =
427                         knuth_morris_pratt_unibyte (haystack, needle - 1,
428                                                     &result);
429                       if (success)
430                         return (char *) result;
431                       try_kmp = false;
432                     }
433                 }
434
435               outer_loop_count++;
436               comparison_count++;
437               if (TOLOWER ((unsigned char) *haystack) == b)
438                 /* The first character matches.  */
439                 {
440                   const char *rhaystack = haystack + 1;
441                   const char *rneedle = needle;
442
443                   for (;; rhaystack++, rneedle++)
444                     {
445                       if (*rneedle == '\0')
446                         /* Found a match.  */
447                         return (char *) haystack;
448                       if (*rhaystack == '\0')
449                         /* No match.  */
450                         return NULL;
451                       comparison_count++;
452                       if (TOLOWER ((unsigned char) *rhaystack)
453                           != TOLOWER ((unsigned char) *rneedle))
454                         /* Nothing in this round.  */
455                         break;
456                     }
457                 }
458             }
459         }
460       else
461         return (char *) haystack;
462     }
463 }