Fix memmem behavior with empty needles.
Change-Id: I8b893d80c27b548652d843af9520d7adc8ba8902
diff --git a/libc/Android.bp b/libc/Android.bp
index c706935..6ba8ae6 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -7,7 +7,6 @@
"bionic/getpriority.c",
"bionic/initgroups.c",
"bionic/isatty.c",
- "bionic/memmem.c",
"bionic/pututline.c",
"bionic/sched_cpualloc.c",
"bionic/sched_cpucount.c",
@@ -1245,6 +1244,7 @@
"bionic/mbrtoc16.cpp",
"bionic/mbrtoc32.cpp",
"bionic/mbstate.cpp",
+ "bionic/memmem.cpp",
"bionic/mempcpy.cpp",
"bionic/mkdir.cpp",
"bionic/mkfifo.cpp",
diff --git a/libc/bionic/memmem.c b/libc/bionic/memmem.c
deleted file mode 100644
index e72501b..0000000
--- a/libc/bionic/memmem.c
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-/*
- * This uses the "Not So Naive" algorithm, a very simple but
- * usually effective algorithm, see:
- * http://www-igm.univ-mlv.fr/~lecroq/string/
- */
-#include <string.h>
-
-void *memmem(const void *haystack, size_t n, const void *needle, size_t m)
-{
- if (m > n || !m || !n)
- return NULL;
-
- if (__builtin_expect((m > 1), 1)) {
- const unsigned char* y = (const unsigned char*) haystack;
- const unsigned char* x = (const unsigned char*) needle;
- size_t j = 0;
- size_t k = 1, l = 2;
-
- if (x[0] == x[1]) {
- k = 2;
- l = 1;
- }
- while (j <= n-m) {
- if (x[1] != y[j+1]) {
- j += k;
- } else {
- if (!memcmp(x+2, y+j+2, m-2) && x[0] == y[j])
- return (void*) &y[j];
- j += l;
- }
- }
- } else {
- /* degenerate case */
- return memchr(haystack, ((unsigned char*)needle)[0], n);
- }
- return NULL;
-}
diff --git a/libc/bionic/memmem.cpp b/libc/bionic/memmem.cpp
new file mode 100644
index 0000000..61d681f
--- /dev/null
+++ b/libc/bionic/memmem.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <string.h>
+
+void* memmem(const void* void_haystack, size_t n, const void* void_needle, size_t m) {
+ const unsigned char* haystack = reinterpret_cast<const unsigned char*>(void_haystack);
+ const unsigned char* needle = reinterpret_cast<const unsigned char*>(void_needle);
+
+ if (n < m) return nullptr;
+
+ if (m == 0) return const_cast<void*>(void_haystack);
+ if (m == 1) return memchr(haystack, needle[0], n);
+
+ // This uses the "Not So Naive" algorithm, a very simple but usually effective algorithm.
+ // http://www-igm.univ-mlv.fr/~lecroq/string/
+ const unsigned char* y = haystack;
+ const unsigned char* x = needle;
+ size_t j = 0;
+ size_t k = 1, l = 2;
+
+ if (x[0] == x[1]) {
+ k = 2;
+ l = 1;
+ }
+ while (j <= n-m) {
+ if (x[1] != y[j+1]) {
+ j += k;
+ } else {
+ if (!memcmp(x+2, y+j+2, m-2) && x[0] == y[j]) return const_cast<unsigned char*>(&y[j]);
+ j += l;
+ }
+ }
+ return nullptr;
+}
diff --git a/tests/string_test.cpp b/tests/string_test.cpp
index 763d65c..385fe33 100644
--- a/tests/string_test.cpp
+++ b/tests/string_test.cpp
@@ -1455,3 +1455,32 @@
}
RunSingleBufferAlignTest(MEDIUM, DoMemcpySameTest);
}
+
+TEST(STRING_TEST, memmem_strstr_empty_needle) {
+ const char* some_haystack = "haystack";
+ const char* empty_haystack = "";
+
+ ASSERT_EQ(some_haystack, memmem(some_haystack, 8, "", 0));
+ ASSERT_EQ(empty_haystack, memmem(empty_haystack, 0, "", 0));
+
+ ASSERT_EQ(some_haystack, strstr(some_haystack, ""));
+ ASSERT_EQ(empty_haystack, strstr(empty_haystack, ""));
+}
+
+TEST(STRING_TEST, memmem_smoke) {
+ const char haystack[] = "big\0daddy\0giant\0haystacks";
+ ASSERT_EQ(haystack, memmem(haystack, sizeof(haystack), "", 0));
+ ASSERT_EQ(haystack + 3, memmem(haystack, sizeof(haystack), "", 1));
+ ASSERT_EQ(haystack + 0, memmem(haystack, sizeof(haystack), "b", 1));
+ ASSERT_EQ(haystack + 1, memmem(haystack, sizeof(haystack), "i", 1));
+ ASSERT_EQ(haystack + 4, memmem(haystack, sizeof(haystack), "da", 2));
+ ASSERT_EQ(haystack + 8, memmem(haystack, sizeof(haystack), "y\0g", 3));
+}
+
+TEST(STRING_TEST, strstr_smoke) {
+ const char* haystack = "big daddy/giant haystacks";
+ ASSERT_EQ(haystack, strstr(haystack, ""));
+ ASSERT_EQ(haystack + 0, strstr(haystack, "b"));
+ ASSERT_EQ(haystack + 1, strstr(haystack, "i"));
+ ASSERT_EQ(haystack + 4, strstr(haystack, "da"));
+}