Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 84 additions & 69 deletions module/common/include/oops/str.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,86 +10,101 @@ constexpr std::string_view SPACE{" \t\n\r\v\f"};
std::string Repeat(const std::string &str, std::size_t n);
std::string SplitBack(const std::string &str, const std::string &delim);

template <typename Iter>
void Split(std::string_view s, std::string_view delim, bool skip_empty, Iter iter) {
std::size_t begin{0}; // 每个分割字串的头部
while (begin < s.size()) {
std::size_t end{s.find(delim, begin)};
if (end == std::string::npos) {
break;
class SplitView {
public:
class Iterator {
public:
using iterator_category = std::forward_iterator_tag;
using value_type = std::string_view;
using difference_type = std::ptrdiff_t;
using pointer = const std::string_view *;
using reference = const std::string_view &;

Iterator() = default;
Iterator(std::string_view s, std::string_view delim) : s_{s}, delim_{delim} { NextToken(); }
Iterator(std::string_view s, std::string_view delim, bool any_of_delims, bool skip_empty)
: s_{s}, delim_{delim}, any_of_delims_{any_of_delims}, skip_empty_{skip_empty} {
NextToken();
}
if (!skip_empty || end > begin) {
// TODO(resserops): 支持基于lexical_cast的转换
*(iter++) = s.substr(begin, end - begin);

std::string_view operator*() const noexcept { return token_; }
Iterator &operator++() {
NextToken();
return *this;
}
begin = end + delim.size();
}
if (!skip_empty || s.size() > begin) {
*(iter++) = s.substr(begin);
}
}

template <typename Iter>
void Split(std::string_view s, std::string_view delim, Iter iter) {
Split(s, delim, false, iter);
}
bool operator==(const Iterator &other) const {
// 短路和迭代器的比较
if ((s_.data() == nullptr) ^ (other.s_.data() == nullptr)) {
return false; // 一个迭代器是尾迭代器,另一个不是
}
if (s_.data() == nullptr) {
return true; // 均是尾迭代器
}
// 非尾迭代器的严格匹配
return (s_.data() == other.s_.data()) && (s_.size() == other.s_.size()) &&
(token_.data() == other.token_.data()) && (token_.size() == other.token_.size()) &&
(delim_ == other.delim_) && (any_of_delims_ == other.any_of_delims_) &&
(skip_empty_ == other.skip_empty_);
}
bool operator!=(const Iterator &other) const { return !operator==(other); }

template <typename Iter>
void Split(std::string_view s, const char *delim, Iter iter) {
Split(s, delim, false, iter);
}
private:
void NextToken();

template <typename Iter>
void Split(std::string_view s, char delim, Iter iter) {
Split(s, std::string_view(&delim, 1), false, iter);
}
std::string_view s_;
std::string_view token_;
const std::string_view delim_;
const bool any_of_delims_{};
const bool skip_empty_{};
};

template <typename Iter>
void SplitMultiDelim(std::string_view s, std::string_view delims, bool skip_empty, Iter iter) {
std::size_t begin{0};
while (begin < s.size()) {
std::size_t end{s.find_first_of(delims, begin)};
if (end == std::string::npos) {
break;
}
if (!skip_empty || end > begin) {
*(iter++) = s.substr(begin, end - begin);
}
begin = end + 1;
}
if (!skip_empty || s.size() > begin) {
*(iter++) = s.substr(begin);
}
}
using value_type = std::string_view;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using const_iterator = Iterator;
using iterator = Iterator;

template <typename Iter>
void SplitMultiDelim(std::string_view s, std::string_view delims, Iter iter) {
SplitMultiDelim(s, delims, false, iter);
}
SplitView(std::string_view s) : s_{s}, delim_{SPACE}, any_of_delims_{true}, skip_empty_{true} {}
SplitView(std::string_view s, char delim) : s_{s}, delim_(&c_, 1), c_{delim} {}
SplitView(std::string_view s, std::string_view delim) : s_{s}, delim_{delim} {}

template <typename Iter>
void Split(std::string_view s, bool skip_empty, Iter iter) {
return SplitMultiDelim(s, SPACE, skip_empty, iter);
}
Iterator begin() const { return Iterator(s_, delim_, any_of_delims_, skip_empty_); }
Iterator end() const { return Iterator(); }

template <typename Iter>
void Split(std::string_view s, Iter iter) {
return SplitMultiDelim(s, SPACE, true, iter);
}
std::string_view Front() const { return *begin(); }

template <typename Container, typename... Args>
Container Split(Args &&...args) {
Container container;
Split(std::forward<Args>(args)..., std::back_inserter(container));
return container;
}
SplitView &AnyOfDelims(bool b = true) {
any_of_delims_ = b;
return *this;
}

template <typename Container, typename... Args>
Container SplitMultiDelim(Args &&...args) {
Container container;
SplitMultiDelim(std::forward<Args>(args)..., std::back_inserter(container));
return container;
}
SplitView &SkipEmpty(bool b = true) {
skip_empty_ = b;
return *this;
}

template <typename T>
T To() const {
return T(begin(), end());
}

template <template <typename...> typename TT>
auto To() const {
return To<TT<std::string_view>>();
}

private:
const std::string_view s_;
const std::string_view delim_;
bool any_of_delims_{};
bool skip_empty_{};
const char c_{}; // 支持单字符delim
};

inline SplitView Split(std::string_view s) { return SplitView(s); }
inline SplitView Split(std::string_view s, char delim) { return SplitView(s, delim); }
inline SplitView Split(std::string_view s, std::string_view delim) { return SplitView(s, delim); }

constexpr bool IsUpper(char c) noexcept { return 'A' <= c && c <= 'Z'; }
constexpr bool IsLower(char c) noexcept { return 'a' <= c && c <= 'z'; }
Expand Down
34 changes: 34 additions & 0 deletions module/common/src/str.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,40 @@ std::string SplitBack(const std::string &str, const std::string &delim) {
return str.substr(str.rfind(delim) + delim.size());
}

void SplitView::Iterator::NextToken() {
if (s_.data() == nullptr) {
return;
}

do {
if (s_.empty()) {
s_ = {};
return;
}

std::size_t pos;
if (any_of_delims_) {
pos = s_.find_first_of(delim_);
} else if (delim_.empty()) {
pos = 1;
} else {
pos = s_.find(delim_);
}

if (pos == std::string_view::npos) {
token_ = s_;
s_.remove_prefix(s_.size());
} else {
token_ = s_.substr(0, pos);
if (any_of_delims_) {
s_.remove_prefix(pos + 1);
} else {
s_.remove_prefix(pos + delim_.size());
}
}
} while (skip_empty_ && token_.empty() && s_.data() != nullptr);
}

std::string ToLower(std::string_view s) noexcept {
std::string res;
res.reserve(s.size());
Expand Down
27 changes: 11 additions & 16 deletions module/common/test/test_str.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "oops/str.h"
#include "oops/view.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"

template <auto Value>
Expand All @@ -11,6 +12,8 @@ constexpr auto CONSTEXPR_VALUE{Value};

#define TEST_STATIC(suite, case) constexpr void TestStatic_##suite_##case ()

using namespace testing;

TEST(CommonStr, IsUpper) {
using namespace oops;
EXPECT_TRUE(CEXPR(IsUpper('A')));
Expand Down Expand Up @@ -216,26 +219,18 @@ TEST(CommonStr, SplitBack) {
}

TEST(CommonStr, Split) {
using namespace oops;
std::vector<std::string_view> res;
Split("abc defg hi", std::back_inserter(res));
EXPECT_EQ(res, (std::vector<std::string_view>{"abc", "defg", "hi"}));

res.clear();
EXPECT_TRUE(res.empty());
Split(" abc defg hi ", std::back_inserter(res));
EXPECT_EQ(res, (std::vector<std::string_view>{"abc", "defg", "hi"}));

res.clear();
Split("\nabc \t defg\fhi ", std::back_inserter(res));
EXPECT_EQ(res, (std::vector<std::string_view>{"abc", "defg", "hi"}));
EXPECT_THAT(oops::Split(""), ElementsAre());
EXPECT_THAT(oops::Split(" "), ElementsAre());
EXPECT_THAT(oops::Split(" "), ElementsAre());
EXPECT_THAT(oops::Split("abc"), ElementsAre("abc"));
EXPECT_THAT(oops::Split("abc defg hi"), ElementsAre("abc", "defg", "hi"));
EXPECT_THAT(oops::Split(" abc defg hi "), ElementsAre("abc", "defg", "hi"));
EXPECT_THAT(oops::Split("\nabc \t defg\fhi "), ElementsAre("abc", "defg", "hi"));
}

TEST(CommonStr, SplitDelim) {
using namespace oops;
std::vector<std::string_view> res;
Split("abc:defg:hi", ":", std::back_inserter(res));
EXPECT_EQ(res, (std::vector<std::string_view>{"abc", "defg", "hi"}));
EXPECT_THAT(Split("abc:defg:hi", ":"), ElementsAre("abc", "defg", "hi"));
}

TEST(CommonStr, StartsWith) {
Expand Down
2 changes: 1 addition & 1 deletion module/matrix/src/matrix_market_io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ AnyCoo ReadMatrixMarket(std::istream &is) {
throw std::runtime_error("bad istream");
}

auto tokens{Split<std::vector<std::string_view>>(buf)};
auto tokens{Split(buf).To<std::vector>()};
if (tokens.size() != 5) {
throw std::runtime_error(std::string{"unexpected header tokens number: "} + std::to_string(tokens.size()));
}
Expand Down
2 changes: 1 addition & 1 deletion module/profiling/src/system_info.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ constexpr VmFlagsEntry VM_FLAGS_TABLE[]{ENTRY(rd), ENTRY(wr), ENTRY(ex), ENTRY(s
template <>
bool ParseField(std::string_view s, proc::smaps::VmaExt::VmFlags &vm_flags, std::string_view) {
bool failed{false};
for (auto token : Split<std::vector<std::string_view>>(s)) {
for (auto token : Split(s)) {
auto it{std::find_if(
std::begin(VM_FLAGS_TABLE), std::end(VM_FLAGS_TABLE), [token](auto &entry) { return entry.key == token; })};
if (it != std::end(VM_FLAGS_TABLE)) {
Expand Down
12 changes: 6 additions & 6 deletions module/profiling/tool/monitor/monitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -323,17 +323,17 @@ void ParseRawData(std::istream &is, RawDataEntry &entry) {
}

case Step::HEADER_ROW: {
auto tokens{oops::Split<std::vector<std::string_view>>(line, ", ")};
metric_num = tokens.size();
auto tokens{oops::Split(line, ", ")};
for (auto token : tokens) {
entry.metric_data_table.emplace_back(oops::Strip(token));
}
metric_num = entry.metric_data_table.size();
step = Step::DATA_ROW;
break;
}

case Step::DATA_ROW: {
auto tokens{oops::Split<std::vector<std::string_view>>(line, ", ")};
auto tokens{oops::Split(line, ", ").To<std::vector>()};
if (metric_num == 0) {
throw std::runtime_error("bad metric num");
}
Expand Down Expand Up @@ -513,7 +513,7 @@ void ParseArgs(int argc, char *argv[]) {

// --metric
std::string metric{measure.get<std::string>("--metric")};
auto metric_tokens{oops::Split<std::vector<std::string_view>>(metric, ',')};
auto metric_tokens{oops::Split(metric, ',')};

for (auto token : metric_tokens) {
bool matched{false};
Expand All @@ -540,14 +540,14 @@ void ParseArgs(int argc, char *argv[]) {

// input
std::string input{report.get<std::string>("input")};
auto input_tokens{oops::Split<std::vector<std::string_view>>(input, ',')};
auto input_tokens{oops::Split(input, ',')};
for (auto token : input_tokens) {
ARGS.report.input.emplace_back(token);
}

// --dir
std::string dir{report.get<std::string>("--dir")};
auto tokens{oops::Split<std::vector<std::string_view>>(dir, ',')};
auto tokens{oops::Split(dir, ',')};
for (auto token : tokens) {
fs::path dir_path{token};
if (!fs::is_directory(dir_path)) {
Expand Down
Loading