Skip to content

Commit c1db56d

Browse files
chadaustinfacebook-github-bot
authored andcommitted
add a polyfill for std::string_view::{starts,ends}_with
Summary: We won't be on C++20 for a while and we'd like to cut dependencies on folly::Range where possible. starts_with and ends_with are too useful, so add a polyfill. Reviewed By: xavierd Differential Revision: D41097519 fbshipit-source-id: 298f65b15b8beb6f56ab1b6b9896fc6b1225f0fb
1 parent 2fdbba3 commit c1db56d

File tree

3 files changed

+155
-7
lines changed

3 files changed

+155
-7
lines changed

eden/fs/service/EdenServiceHandler.cpp

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
#include "eden/fs/utils/ProcUtil.h"
7676
#include "eden/fs/utils/SourceLocation.h"
7777
#include "eden/fs/utils/StatTimes.h"
78+
#include "eden/fs/utils/String.h"
7879
#include "eden/fs/utils/UnboundedQueueExecutor.h"
7980

8081
#ifdef EDEN_HAVE_USAGE_SERVICE
@@ -2398,12 +2399,6 @@ ImmediateFuture<folly::Unit> detachIfBackgrounded(
23982399
}
23992400
}
24002401

2401-
// string_view::starts_with() is not available until C++20
2402-
// As a workaround, we provide our own implementation of starts_with().
2403-
bool string_view_starts_with(std::string_view str, std::string_view prefix) {
2404-
return prefix == str.substr(0, prefix.size());
2405-
}
2406-
24072402
void maybeLogExpensiveGlob(
24082403
const std::vector<std::string>& globs,
24092404
const folly::StringPiece searchRoot,
@@ -2414,7 +2409,7 @@ void maybeLogExpensiveGlob(
24142409

24152410
if (searchRoot.empty()) {
24162411
for (const auto& glob : globs) {
2417-
if (string_view_starts_with(glob, "**")) {
2412+
if (string_view{glob}.starts_with("**")) {
24182413
shouldLogExpensiveGlob = true;
24192414
}
24202415
}

eden/fs/utils/String.h

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This software may be used and distributed according to the terms of the
5+
* GNU General Public License version 2.
6+
*/
7+
8+
#pragma once
9+
10+
#include <string_view>
11+
12+
namespace facebook::eden {
13+
14+
constexpr inline bool starts_with(
15+
std::string_view haystack,
16+
std::string_view needle) noexcept {
17+
return needle == haystack.substr(0, needle.size());
18+
}
19+
20+
constexpr inline bool starts_with(
21+
std::string_view haystack,
22+
char needle) noexcept {
23+
const char* data = haystack.data();
24+
size_t size = haystack.size();
25+
return size > 0 && *data == needle;
26+
}
27+
28+
constexpr inline bool starts_with(
29+
std::string_view haystack,
30+
const char* needle) noexcept {
31+
return starts_with(haystack, std::string_view{needle});
32+
}
33+
34+
constexpr bool ends_with(
35+
std::string_view haystack,
36+
std::string_view needle) noexcept {
37+
size_t haystack_size = haystack.size();
38+
size_t needle_size = needle.size();
39+
return haystack_size >= needle_size &&
40+
needle == haystack.substr(haystack_size - needle_size);
41+
}
42+
43+
constexpr bool ends_with(std::string_view haystack, char needle) noexcept {
44+
const char* data = haystack.data();
45+
size_t size = haystack.size();
46+
return size > 0 && data[size - 1] == needle;
47+
}
48+
49+
constexpr bool ends_with(
50+
std::string_view haystack,
51+
const char* needle) noexcept {
52+
return ends_with(haystack, std::string_view{needle});
53+
}
54+
55+
struct string_view : std::string_view {
56+
#if __cplusplus <= 202002L
57+
constexpr bool starts_with(std::string_view sv) const noexcept {
58+
return eden::starts_with(*this, sv);
59+
}
60+
61+
constexpr bool starts_with(char c) const noexcept {
62+
return eden::starts_with(*this, c);
63+
}
64+
65+
constexpr bool starts_with(const char* s) const {
66+
return eden::starts_with(*this, s);
67+
}
68+
69+
constexpr bool ends_with(std::string_view sv) const noexcept {
70+
return eden::ends_with(*this, sv);
71+
}
72+
73+
constexpr bool ends_with(char c) const noexcept {
74+
return eden::ends_with(*this, c);
75+
}
76+
77+
constexpr bool ends_with(const char* s) const {
78+
return eden::ends_with(*this, s);
79+
}
80+
#endif
81+
};
82+
83+
} // namespace facebook::eden

eden/fs/utils/test/StringTest.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This software may be used and distributed according to the terms of the
5+
* GNU General Public License version 2.
6+
*/
7+
8+
#include "eden/fs/utils/String.h"
9+
10+
#include <folly/portability/GTest.h>
11+
12+
namespace facebook::eden {
13+
namespace {
14+
15+
// It's okay in the future if we kill `eden::string_view` and consistently use
16+
// `std::string_view` everywhere. In the meantime, however, to avoid
17+
// platform-specific compiler errors, it makes sense to ensure they're different
18+
// types.
19+
static_assert(!std::is_same_v<std::string_view, string_view>);
20+
21+
struct TestCase {
22+
std::string_view haystack;
23+
std::string_view needle;
24+
bool result;
25+
};
26+
27+
const TestCase startsWithTests[] = {
28+
{"haystack", "hay", true},
29+
{"haystack", "ay", false},
30+
{"haystack", "", true},
31+
{"", "", true},
32+
{"", "x", false},
33+
{"haystack", "haystackhaystack", false},
34+
};
35+
36+
TEST(String, starts_with) {
37+
for (auto& tc : startsWithTests) {
38+
EXPECT_EQ(tc.result, starts_with(tc.haystack, tc.needle))
39+
<< "starts_with: haystack=" << tc.haystack << " needle=" << tc.needle;
40+
EXPECT_EQ(tc.result, string_view{tc.haystack}.starts_with(tc.needle))
41+
<< "starts_with: haystack=" << tc.haystack << " needle=" << tc.needle;
42+
}
43+
44+
EXPECT_TRUE(string_view{"haystack"}.starts_with('h'));
45+
EXPECT_FALSE(string_view{"haystack"}.starts_with('k'));
46+
}
47+
48+
const TestCase endsWithTests[] = {
49+
{"haystack", "hay", false},
50+
{"haystack", "ack", true},
51+
{"haystack", "", true},
52+
{"", "", true},
53+
{"", "x", false},
54+
{"haystack", "haystackhaystack", false},
55+
};
56+
57+
TEST(String, ends_with) {
58+
for (auto& tc : endsWithTests) {
59+
EXPECT_EQ(tc.result, ends_with(tc.haystack, tc.needle))
60+
<< "ends_with: haystack=" << tc.haystack << " needle=" << tc.needle;
61+
EXPECT_EQ(tc.result, string_view{tc.haystack}.ends_with(tc.needle))
62+
<< "ends_with: haystack=" << tc.haystack << " needle=" << tc.needle;
63+
}
64+
65+
EXPECT_TRUE(string_view{"haystack"}.ends_with('k'));
66+
EXPECT_FALSE(string_view{"haystack"}.ends_with('h'));
67+
}
68+
69+
} // namespace
70+
} // namespace facebook::eden

0 commit comments

Comments
 (0)