Skip to content

Commit 5c3f854

Browse files
committed
Fix strcasestr()
Fixes #1323
1 parent ad0a7c6 commit 5c3f854

File tree

2 files changed

+61
-60
lines changed

2 files changed

+61
-60
lines changed

libc/str/strcasestr.c

Lines changed: 56 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -17,76 +17,72 @@
1717
│ PERFORMANCE OF THIS SOFTWARE. │
1818
╚─────────────────────────────────────────────────────────────────────────────*/
1919
#include "libc/str/str.h"
20-
#include "libc/dce.h"
20+
#include "libc/mem/alloca.h"
21+
#include "libc/runtime/stack.h"
2122
#include "libc/str/tab.h"
2223

23-
typedef char xmm_t __attribute__((__vector_size__(16), __aligned__(16)));
24+
static void computeLPS(const char *pattern, long M, long *lps) {
25+
long len = 0;
26+
lps[0] = 0;
27+
long i = 1;
28+
while (i < M) {
29+
if (kToLower[pattern[i] & 255] == kToLower[pattern[len] & 255]) {
30+
len++;
31+
lps[i] = len;
32+
i++;
33+
} else {
34+
if (len != 0) {
35+
len = lps[len - 1];
36+
} else {
37+
lps[i] = 0;
38+
i++;
39+
}
40+
}
41+
}
42+
}
43+
44+
static char *kmp(const char *s, size_t n, const char *ss, size_t m) {
45+
if (!m)
46+
return (char *)s;
47+
if (n < m)
48+
return NULL;
49+
#pragma GCC push_options
50+
#pragma GCC diagnostic ignored "-Walloca-larger-than="
51+
#pragma GCC diagnostic ignored "-Wanalyzer-out-of-bounds"
52+
long need = sizeof(long) * m;
53+
long *lps = (long *)alloca(need);
54+
CheckLargeStackAllocation(lps, need);
55+
#pragma GCC pop_options
56+
computeLPS(ss, m, lps);
57+
long i = 0;
58+
long j = 0;
59+
while (i < n) {
60+
if (kToLower[ss[j] & 255] == kToLower[s[i] & 255]) {
61+
i++;
62+
j++;
63+
}
64+
if (j == m) {
65+
return (char *)(s + i - j);
66+
} else if (i < n && kToLower[ss[j] & 255] != kToLower[s[i] & 255]) {
67+
if (j != 0) {
68+
j = lps[j - 1];
69+
} else {
70+
i++;
71+
}
72+
}
73+
}
74+
return NULL;
75+
}
2476

2577
/**
2678
* Searches for substring case-insensitively.
2779
*
2880
* @param haystack is the search area, as a NUL-terminated string
2981
* @param needle is the desired substring, also NUL-terminated
3082
* @return pointer to first substring within haystack, or NULL
31-
* @note this implementation goes fast in practice but isn't hardened
32-
* against pathological cases, and therefore shouldn't be used on
33-
* untrustworthy data
3483
* @asyncsignalsafe
3584
* @see strstr()
3685
*/
37-
__vex char *strcasestr(const char *haystack, const char *needle) {
38-
#if defined(__x86_64__) && !defined(__chibicc__)
39-
char c;
40-
size_t i;
41-
unsigned k, m;
42-
const xmm_t *p;
43-
xmm_t v, n1, n2, z = {0};
44-
if (haystack == needle || !*needle)
45-
return (char *)haystack;
46-
c = *needle;
47-
n1 = (xmm_t){c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c};
48-
c = kToLower[c & 255];
49-
n2 = (xmm_t){c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c};
50-
for (;;) {
51-
k = (uintptr_t)haystack & 15;
52-
p = (const xmm_t *)((uintptr_t)haystack & -16);
53-
v = *p;
54-
m = __builtin_ia32_pmovmskb128((v == z) | (v == n1) | (v == n2));
55-
m >>= k;
56-
m <<= k;
57-
while (!m) {
58-
v = *++p;
59-
m = __builtin_ia32_pmovmskb128((v == z) | (v == n1) | (v == n2));
60-
}
61-
haystack = (const char *)p + __builtin_ctzl(m);
62-
for (i = 0;; ++i) {
63-
if (!needle[i])
64-
return (/*unconst*/ char *)haystack;
65-
if (!haystack[i])
66-
break;
67-
if (kToLower[needle[i] & 255] != kToLower[haystack[i] & 255])
68-
break;
69-
}
70-
if (!*haystack++)
71-
break;
72-
}
73-
return 0;
74-
#else
75-
size_t i;
76-
if (haystack == needle || !*needle)
77-
return (void *)haystack;
78-
for (;;) {
79-
for (i = 0;; ++i) {
80-
if (!needle[i])
81-
return (/*unconst*/ char *)haystack;
82-
if (!haystack[i])
83-
break;
84-
if (kToLower[needle[i] & 255] != kToLower[haystack[i] & 255])
85-
break;
86-
}
87-
if (!*haystack++)
88-
break;
89-
}
90-
return 0;
91-
#endif
86+
char *strcasestr(const char *haystack, const char *needle) {
87+
return kmp(haystack, strlen(haystack), needle, strlen(needle));
9288
}

test/libc/str/strcasestr_test.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ char *strcasestr_naive(const char *haystack, const char *needle) {
4949
return 0;
5050
}
5151

52+
TEST(strcasestr, tester) {
53+
const char *haystack = "Windows";
54+
ASSERT_STREQ(haystack, strcasestr(haystack, "win"));
55+
}
56+
5257
TEST(strcasestr, test_emptyString_isFoundAtBeginning) {
5358
MAKESTRING(haystack, "abc123def");
5459
ASSERT_STREQ(&haystack[0], strcasestr(haystack, gc(strdup(""))));

0 commit comments

Comments
 (0)