Commit c8972cd5 authored by Yedidya Feldblum's avatar Yedidya Feldblum Committed by Facebook GitHub Bot

Re-port memrchr

Summary:
[Folly] Re-port memrchr, avoiding build-system detection.

Proper detection at build-system time is challenging.
* Some platforms do not define `memrchr`.
* Some platforms define it as a C function.
* Some platforms define it as a pair of C++ functions.

Proper porting requires proper build-system detection.

Rather than going down that rabbit-hole, we can wrap `memrchr` and do compile-time detection using SFINAE.

This change adds a new strategy. A subsequent change may remove the old strategy.

Reviewed By: Orvid

Differential Revision: D22125127

fbshipit-source-id: 2da7cca571de335b2cff43ff7846ab04d90136ef
parent 190ff15c
...@@ -21,9 +21,9 @@ ...@@ -21,9 +21,9 @@
#include <folly/Portability.h> #include <folly/Portability.h>
#include <folly/hash/SpookyHashV2.h> #include <folly/hash/SpookyHashV2.h>
#include <folly/lang/CString.h>
#include <folly/lang/Exception.h> #include <folly/lang/Exception.h>
#include <folly/portability/Constexpr.h> #include <folly/portability/Constexpr.h>
#include <folly/portability/String.h>
#include <algorithm> #include <algorithm>
#include <array> #include <array>
...@@ -1436,7 +1436,7 @@ inline size_t rfind(const Range<const char*>& haystack, const char& needle) { ...@@ -1436,7 +1436,7 @@ inline size_t rfind(const Range<const char*>& haystack, const char& needle) {
return std::string::npos; return std::string::npos;
} }
auto pos = static_cast<const char*>( auto pos = static_cast<const char*>(
::memrchr(haystack.data(), needle, haystack.size())); memrchr(haystack.data(), needle, haystack.size()));
return pos == nullptr ? std::string::npos : pos - haystack.data(); return pos == nullptr ? std::string::npos : pos - haystack.data();
} }
...@@ -1463,7 +1463,7 @@ inline size_t rfind( ...@@ -1463,7 +1463,7 @@ inline size_t rfind(
return std::string::npos; return std::string::npos;
} }
auto pos = static_cast<const unsigned char*>( auto pos = static_cast<const unsigned char*>(
::memrchr(haystack.data(), needle, haystack.size())); memrchr(haystack.data(), needle, haystack.size()));
return pos == nullptr ? std::string::npos : pos - haystack.data(); return pos == nullptr ? std::string::npos : pos - haystack.data();
} }
......
...@@ -17,9 +17,79 @@ ...@@ -17,9 +17,79 @@
#include <folly/lang/CString.h> #include <folly/lang/CString.h>
#include <algorithm> #include <algorithm>
#include <cstring>
#include <type_traits>
#include <folly/CppAttributes.h>
#include <folly/functional/Invoke.h>
namespace {
struct poison {};
FOLLY_MAYBE_UNUSED FOLLY_ERASE void memrchr(poison) noexcept {}
} // namespace
namespace folly { namespace folly {
namespace detail {
void* memrchr_fallback(void* s, int c, std::size_t len) noexcept {
return const_cast<void*>(
memrchr_fallback(const_cast<void const*>(s), c, len));
}
void const* memrchr_fallback(void const* s, int c, std::size_t len) noexcept {
auto const ss = static_cast<unsigned char const*>(s);
for (auto it = ss + len - 1; it >= ss; --it) {
if (*it == static_cast<unsigned char>(c)) {
return it;
}
}
return nullptr;
}
} // namespace detail
namespace c_string_detail {
struct invoke_fallback_memrchr_fn {
template <typename... A>
FOLLY_MAYBE_UNUSED FOLLY_ERASE auto operator()(A... a) const noexcept
-> decltype(detail::memrchr_fallback(a...)) {
return detail::memrchr_fallback(a...);
}
};
struct invoke_primary_memrchr_fn {
template <typename... A>
FOLLY_MAYBE_UNUSED FOLLY_ERASE auto operator()(A... a) const noexcept
-> decltype(::memrchr(a...)) {
return ::memrchr(a...);
}
};
} // namespace c_string_detail
namespace {
template <typename... A>
using invoke_memrchr_fn = conditional_t<
is_invocable_v<c_string_detail::invoke_primary_memrchr_fn, A...>,
c_string_detail::invoke_primary_memrchr_fn,
c_string_detail::invoke_fallback_memrchr_fn>;
template <typename... A>
constexpr invoke_memrchr_fn<A...> invoke_memrchr{};
} // namespace
void* memrchr(void* s, int c, std::size_t len) noexcept {
return invoke_memrchr<void*, int, std::size_t>(s, c, len);
}
void const* memrchr(void const* s, int c, std::size_t len) noexcept {
return invoke_memrchr<void const*, int, std::size_t>(s, c, len);
}
std::size_t std::size_t
strlcpy(char* const dest, char const* const src, std::size_t const size) { strlcpy(char* const dest, char const* const src, std::size_t const size) {
std::size_t const len = std::strlen(src); std::size_t const len = std::strlen(src);
......
...@@ -21,6 +21,19 @@ ...@@ -21,6 +21,19 @@
namespace folly { namespace folly {
namespace detail {
void* memrchr_fallback(void* s, int c, std::size_t len) noexcept;
void const* memrchr_fallback(void const* s, int c, std::size_t len) noexcept;
} // namespace detail
// memrchr
//
// mimic: memrchr, glibc++
void* memrchr(void* s, int c, std::size_t len) noexcept;
void const* memrchr(void const* s, int c, std::size_t len) noexcept;
// strlcpy // strlcpy
// //
// mimic: strlcpy, libbsd // mimic: strlcpy, libbsd
......
...@@ -20,6 +20,23 @@ ...@@ -20,6 +20,23 @@
class CStringTest : public testing::Test {}; class CStringTest : public testing::Test {};
TEST_F(CStringTest, memrchr) {
const char* const cdata = "foobar";
char mdata[] = "foobar";
EXPECT_EQ(cdata + 3, folly::memrchr(cdata, 'b', 6));
EXPECT_EQ(mdata + 3, folly::memrchr(mdata, 'b', 6));
EXPECT_EQ(nullptr, folly::memrchr(cdata, 'x', 6));
EXPECT_EQ(nullptr, folly::memrchr(mdata, 'x', 6));
EXPECT_EQ(cdata + 3, folly::detail::memrchr_fallback(cdata, 'b', 6));
EXPECT_EQ(mdata + 3, folly::detail::memrchr_fallback(mdata, 'b', 6));
EXPECT_EQ(nullptr, folly::detail::memrchr_fallback(cdata, 'x', 6));
EXPECT_EQ(nullptr, folly::detail::memrchr_fallback(mdata, 'x', 6));
}
TEST_F(CStringTest, strlcpy) { TEST_F(CStringTest, strlcpy) {
char buf[6]; char buf[6];
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment