| /* Searching in a string. -*- coding: utf-8 -*- |
| Copyright (C) 2005-2026 Free Software Foundation, Inc. |
| Written by Bruno Haible <bruno@clisp.org>, 2005. |
| |
| This file is free software: you can redistribute it and/or modify |
| it under the terms of the GNU Lesser General Public License as |
| published by the Free Software Foundation, either version 3 of the |
| License, or (at your option) any later version. |
| |
| This file is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU Lesser General Public License for more details. |
| |
| You should have received a copy of the GNU Lesser General Public License |
| along with this program. If not, see <https://www.gnu.org/licenses/>. */ |
| |
| /* Don't use the const-improved function macros in this compilation unit. */ |
| #define _GL_NO_CONST_GENERICS |
| |
| #include <config.h> |
| |
| /* Specification. */ |
| #include <string.h> |
| |
| #include <stddef.h> /* for NULL, in case a nonstandard string.h lacks it */ |
| #include <stdlib.h> |
| |
| #include "malloca.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_eq (a, b); } |
| #else |
| # include "mbuiter.h" |
| #endif |
| |
| /* Knuth-Morris-Pratt algorithm. */ |
| #define UNIT unsigned char |
| #define CANON_ELEMENT(c) c |
| #include "str-kmp.h" |
| |
| /* Knuth-Morris-Pratt algorithm. |
| See https://en.wikipedia.org/wiki/Knuth-Morris-Pratt_algorithm |
| Return a boolean indicating success: |
| Return true and set *RESULTP if the search was completed. |
| Return false if it was aborted because not enough memory was available. */ |
| static bool |
| knuth_morris_pratt_multibyte (const char *haystack, const char *needle, |
| const char **resultp) |
| { |
| size_t m = mbslen (needle); |
| mbchar_t *needle_mbchars; |
| 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 + !!extra_align, |
| sizeof (mbchar_t) + sizeof (size_t)); |
| if (memory == NULL) |
| return false; |
| needle_mbchars = memory; |
| void *table_memory = needle_mbchars + m; |
| 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 = 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. |
| For 0 < i < m: |
| 0 < table[i] <= i is defined such that |
| forall 0 < x < table[i]: needle[x..i-1] != needle[0..i-1-x], |
| and table[i] is as large as possible with this property. |
| This implies: |
| 1) For 0 < i < m: |
| If table[i] < i, |
| needle[table[i]..i-1] = needle[0..i-1-table[i]]. |
| 2) For 0 < i < m: |
| rhaystack[0..i-1] == needle[0..i-1] |
| and exists h, i <= h < m: rhaystack[h] != needle[h] |
| implies |
| forall 0 <= x < table[i]: rhaystack[x..x+m-1] != needle[0..m-1]. |
| table[0] remains uninitialized. */ |
| { |
| size_t j; |
| |
| /* i = 1: Nothing to verify for x = 0. */ |
| table[1] = 1; |
| j = 0; |
| |
| for (size_t i = 2; i < m; i++) |
| { |
| /* Here: j = i-1 - table[i-1]. |
| The inequality needle[x..i-1] != needle[0..i-1-x] is known to hold |
| for x < table[i-1], by induction. |
| Furthermore, if j>0: needle[i-1-j..i-2] = needle[0..j-1]. */ |
| mbchar_t *b = &needle_mbchars[i - 1]; |
| |
| for (;;) |
| { |
| /* Invariants: The inequality needle[x..i-1] != needle[0..i-1-x] |
| is known to hold for x < i-1-j. |
| Furthermore, if j>0: needle[i-1-j..i-2] = needle[0..j-1]. */ |
| if (mb_equal (*b, needle_mbchars[j])) |
| { |
| /* Set table[i] := i-1-j. */ |
| table[i] = i - ++j; |
| break; |
| } |
| /* The inequality needle[x..i-1] != needle[0..i-1-x] also holds |
| for x = i-1-j, because |
| needle[i-1] != needle[j] = needle[i-1-x]. */ |
| if (j == 0) |
| { |
| /* The inequality holds for all possible x. */ |
| table[i] = i; |
| break; |
| } |
| /* The inequality needle[x..i-1] != needle[0..i-1-x] also holds |
| for i-1-j < x < i-1-j+table[j], because for these x: |
| needle[x..i-2] |
| = needle[x-(i-1-j)..j-1] |
| != needle[0..j-1-(x-(i-1-j))] (by definition of table[j]) |
| = needle[0..i-2-x], |
| hence needle[x..i-1] != needle[0..i-1-x]. |
| Furthermore |
| needle[i-1-j+table[j]..i-2] |
| = needle[table[j]..j-1] |
| = needle[0..j-1-table[j]] (by definition of table[j]). */ |
| j = j - table[j]; |
| } |
| /* Here: j = i - table[i]. */ |
| } |
| } |
| |
| /* Search, using the table to accelerate the processing. */ |
| { |
| #if GNULIB_MCEL_PREFER |
| char const *rhaystack = haystack; |
| char const *phaystack = haystack; |
| |
| size_t j = 0; |
| /* Invariant: phaystack = rhaystack + j. */ |
| for (;;) |
| { |
| if (!*phaystack) |
| { |
| rhaystack = NULL; |
| break; |
| } |
| mcel_t g = mcel_scanz (phaystack); |
| if (mcel_eq (needle_mbchars[j], g)) |
| { |
| 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 |
| *resultp = NULL; |
| size_t j = 0; |
| mbui_iterator_t rhaystack; |
| mbui_init (rhaystack, haystack); |
| mbui_iterator_t phaystack; |
| mbui_init (phaystack, haystack); |
| /* Invariant: phaystack = rhaystack + j. */ |
| while (mbui_avail (phaystack)) |
| if (mb_equal (needle_mbchars[j], mbui_cur (phaystack))) |
| { |
| j++; |
| mbui_advance (phaystack); |
| if (j == m) |
| { |
| /* The entire needle has been found. */ |
| *resultp = mbui_cur_ptr (rhaystack); |
| break; |
| } |
| } |
| else if (j > 0) |
| { |
| /* Found a match of needle[0..j-1], mismatch at needle[j]. */ |
| size_t count = table[j]; |
| j -= count; |
| for (; count > 0; count--) |
| { |
| if (!mbui_avail (rhaystack)) |
| abort (); |
| mbui_advance (rhaystack); |
| } |
| } |
| else |
| { |
| /* Found a mismatch at needle[0] already. */ |
| if (!mbui_avail (rhaystack)) |
| abort (); |
| mbui_advance (rhaystack); |
| mbui_advance (phaystack); |
| } |
| #endif |
| } |
| |
| freea (memory); |
| return true; |
| } |
| |
| /* Find the first occurrence of the character string NEEDLE in the character |
| string HAYSTACK. Return NULL if NEEDLE is not found in HAYSTACK. */ |
| char * |
| mbsstr (const char *haystack, const char *needle) |
| { |
| /* Be careful not to look at the entire extent of haystack or needle |
| until needed. This is useful because of these two cases: |
| - haystack may be very long, and a match of needle found early, |
| - needle may be very long, and not even a short initial segment of |
| 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 (; *iter_haystack; ) |
| { |
| /* 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++; |
| mcel_t hg = mcel_scanz (iter_haystack); |
| if (mcel_eq (hg, ng)) |
| /* 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_eq (rhg, rng)); |
| } |
| |
| iter_haystack += hg.len; |
| } |
| |
| return NULL; |
| #else |
| mbui_iterator_t iter_needle; |
| mbui_init (iter_needle, needle); |
| if (mbui_avail (iter_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; |
| size_t last_ccount = 0; /* last comparison count */ |
| |
| mbui_iterator_t iter_needle_last_ccount; /* = needle + last_ccount */ |
| mbui_init (iter_needle_last_ccount, needle); |
| |
| mbui_iterator_t iter_haystack; |
| mbui_init (iter_haystack, haystack); |
| for (;; mbui_advance (iter_haystack)) |
| { |
| if (!mbui_avail (iter_haystack)) |
| /* No match. */ |
| return NULL; |
| |
| /* 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 && mbui_avail (iter_needle_last_ccount); |
| count--) |
| mbui_advance (iter_needle_last_ccount); |
| last_ccount = comparison_count; |
| if (!mbui_avail (iter_needle_last_ccount)) |
| { |
| /* Try the Knuth-Morris-Pratt algorithm. */ |
| const char *result; |
| bool success = |
| knuth_morris_pratt_multibyte (haystack, needle, |
| &result); |
| if (success) |
| return (char *) result; |
| try_kmp = false; |
| } |
| } |
| |
| outer_loop_count++; |
| comparison_count++; |
| if (mb_equal (mbui_cur (iter_haystack), mbui_cur (iter_needle))) |
| /* The first character matches. */ |
| { |
| mbui_iterator_t rhaystack; |
| memcpy (&rhaystack, &iter_haystack, sizeof (mbui_iterator_t)); |
| mbui_advance (rhaystack); |
| |
| mbui_iterator_t rneedle; |
| mbui_init (rneedle, needle); |
| if (!mbui_avail (rneedle)) |
| abort (); |
| mbui_advance (rneedle); |
| |
| for (;; mbui_advance (rhaystack), mbui_advance (rneedle)) |
| { |
| if (!mbui_avail (rneedle)) |
| /* Found a match. */ |
| return (char *) mbui_cur_ptr (iter_haystack); |
| if (!mbui_avail (rhaystack)) |
| /* No match. */ |
| return NULL; |
| comparison_count++; |
| if (!mb_equal (mbui_cur (rhaystack), mbui_cur (rneedle))) |
| /* Nothing in this round. */ |
| break; |
| } |
| } |
| } |
| } |
| else |
| return (char *) haystack; |
| #endif |
| } |
| else |
| { |
| if (*needle != '\0') |
| { |
| /* Minimizing the worst-case complexity: |
| Let n = strlen(haystack), m = strlen(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; |
| size_t last_ccount = 0; /* last comparison count */ |
| const char *needle_last_ccount = needle; /* = needle + last_ccount */ |
| |
| /* Speed up the following searches of needle by caching its first |
| character. */ |
| char b = *needle++; |
| |
| for (;; haystack++) |
| { |
| if (*haystack == '\0') |
| /* No match. */ |
| return NULL; |
| |
| /* 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. */ |
| if (needle_last_ccount != NULL) |
| { |
| needle_last_ccount += |
| strnlen (needle_last_ccount, |
| comparison_count - last_ccount); |
| if (*needle_last_ccount == '\0') |
| needle_last_ccount = NULL; |
| last_ccount = comparison_count; |
| } |
| if (needle_last_ccount == NULL) |
| { |
| /* Try the Knuth-Morris-Pratt algorithm. */ |
| const unsigned char *result; |
| bool success = |
| knuth_morris_pratt ((const unsigned char *) haystack, |
| (const unsigned char *) (needle - 1), |
| strlen (needle - 1), |
| &result); |
| if (success) |
| return (char *) result; |
| try_kmp = false; |
| } |
| } |
| |
| outer_loop_count++; |
| comparison_count++; |
| if (*haystack == b) |
| /* The first character matches. */ |
| { |
| const char *rhaystack = haystack + 1; |
| const char *rneedle = needle; |
| |
| for (;; rhaystack++, rneedle++) |
| { |
| if (*rneedle == '\0') |
| /* Found a match. */ |
| return (char *) haystack; |
| if (*rhaystack == '\0') |
| /* No match. */ |
| return NULL; |
| comparison_count++; |
| if (*rhaystack != *rneedle) |
| /* Nothing in this round. */ |
| break; |
| } |
| } |
| } |
| } |
| else |
| return (char *) haystack; |
| } |
| } |