1 /* Copyright (C) 1991-2022 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) any later version.
8
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
15 License along with the GNU C Library; if not, see
16 <https://www.gnu.org/licenses/>. */
17
18 #ifndef _LIBC
19 # include <config.h>
20 #endif
21
22 #include <string.h>
23
24 #ifndef _LIBC
25 # define __memmem memmem
26 #endif
27
28 #define RETURN_TYPE void *
29 #define AVAILABLE(h, h_l, j, n_l) ((j) <= (h_l) - (n_l))
30 #define FASTSEARCH(S,C,N) (void*) memchr ((void *)(S), (C), (N))
31 #include "str-two-way.h"
32
33 #undef memmem
34
35 /* Hash character pairs so a small shift table can be used. All bits of
36 p[0] are included, but not all bits from p[-1]. So if two equal hashes
37 match on p[-1], p[0] matches too. Hash collisions are harmless and result
38 in smaller shifts. */
39 #define hash2(p) (((size_t)(p)[0] - ((size_t)(p)[-1] << 3)) % sizeof (shift))
40
41 /* Fast memmem algorithm with guaranteed linear-time performance.
42 Small needles up to size 2 use a dedicated linear search. Longer needles
43 up to size 256 use a novel modified Horspool algorithm. It hashes pairs
44 of characters to quickly skip past mismatches. The main search loop only
45 exits if the last 2 characters match, avoiding unnecessary calls to memcmp
46 and allowing for a larger skip if there is no match. A self-adapting
47 filtering check is used to quickly detect mismatches in long needles.
48 By limiting the needle length to 256, the shift table can be reduced to 8
49 bits per entry, lowering preprocessing overhead and minimizing cache effects.
50 The limit also implies worst-case performance is linear.
51 Needles larger than 256 characters use the linear-time Two-Way algorithm. */
52 void *
__memmem(const void * haystack,size_t hs_len,const void * needle,size_t ne_len)53 __memmem (const void *haystack, size_t hs_len,
54 const void *needle, size_t ne_len)
55 {
56 const unsigned char *hs = (const unsigned char *) haystack;
57 const unsigned char *ne = (const unsigned char *) needle;
58
59 if (ne_len == 0)
60 return (void *) hs;
61 if (ne_len == 1)
62 return (void *) memchr (hs, ne[0], hs_len);
63
64 /* Ensure haystack length is >= needle length. */
65 if (hs_len < ne_len)
66 return NULL;
67
68 const unsigned char *end = hs + hs_len - ne_len;
69
70 if (ne_len == 2)
71 {
72 uint32_t nw = ne[0] << 16 | ne[1], hw = hs[0] << 16 | hs[1];
73 for (hs++; hs <= end && hw != nw; )
74 hw = hw << 16 | *++hs;
75 return hw == nw ? (void *)hs - 1 : NULL;
76 }
77
78 /* Use Two-Way algorithm for very long needles. */
79 if (__builtin_expect (ne_len > 256, 0))
80 return two_way_long_needle (hs, hs_len, ne, ne_len);
81
82 uint8_t shift[256];
83 size_t tmp, shift1;
84 size_t m1 = ne_len - 1;
85 size_t offset = 0;
86
87 memset (shift, 0, sizeof (shift));
88 for (int i = 1; i < m1; i++)
89 shift[hash2 (ne + i)] = i;
90 /* Shift1 is the amount we can skip after matching the hash of the
91 needle end but not the full needle. */
92 shift1 = m1 - shift[hash2 (ne + m1)];
93 shift[hash2 (ne + m1)] = m1;
94
95 for ( ; hs <= end; )
96 {
97 /* Skip past character pairs not in the needle. */
98 do
99 {
100 hs += m1;
101 tmp = shift[hash2 (hs)];
102 }
103 while (tmp == 0 && hs <= end);
104
105 /* If the match is not at the end of the needle, shift to the end
106 and continue until we match the hash of the needle end. */
107 hs -= tmp;
108 if (tmp < m1)
109 continue;
110
111 /* Hash of the last 2 characters matches. If the needle is long,
112 try to quickly filter out mismatches. */
113 if (m1 < 15 || memcmp (hs + offset, ne + offset, 8) == 0)
114 {
115 if (memcmp (hs, ne, m1) == 0)
116 return (void *) hs;
117
118 /* Adjust filter offset when it doesn't find the mismatch. */
119 offset = (offset >= 8 ? offset : m1) - 8;
120 }
121
122 /* Skip based on matching the hash of the needle end. */
123 hs += shift1;
124 }
125 return NULL;
126 }
127 libc_hidden_def (__memmem)
128 weak_alias (__memmem, memmem)
129 libc_hidden_weak (memmem)
130