From da96c1ec7937994152ca3fad09af95b21ffe4b18 Mon Sep 17 00:00:00 2001 From: Jason Rhinelander Date: Sat, 22 Feb 2020 21:57:32 -0400 Subject: [PATCH] Make string_view a full std::string_view implementation Adds the missing pieces plus adds a test script. --- lokimq/string_view.h | 134 ++++++++++++++++++++++++++++++++++++++++++- tests/CMakeLists.txt | 1 + 2 files changed, 133 insertions(+), 2 deletions(-) diff --git a/lokimq/string_view.h b/lokimq/string_view.h index 25ee885..e981693 100644 --- a/lokimq/string_view.h +++ b/lokimq/string_view.h @@ -38,26 +38,50 @@ namespace lokimq { using string_view = std::string_view; } #else #include +#include namespace lokimq { -/// Basic implementation of a subset of std::string_view (enough for what we need in lokimq) +/// Basic implementation of std::string_view (except for std::hash support). class simple_string_view { const char *data_; size_t size_; public: + using traits_type = std::char_traits; + using value_type = char; + using pointer = char*; + using const_pointer = const char*; + using reference = char&; + using const_reference = const char&; + using const_iterator = const_pointer; + using iterator = const_iterator; + using const_reverse_iterator = std::reverse_iterator; + using reverse_iterator = const_reverse_iterator; + using size_type = std::size_t; + using different_type = std::ptrdiff_t; + + static constexpr auto& npos = std::string::npos; + constexpr simple_string_view() noexcept : data_{nullptr}, size_{0} {} constexpr simple_string_view(const simple_string_view&) noexcept = default; simple_string_view(const std::string& str) : data_{str.data()}, size_{str.size()} {} constexpr simple_string_view(const char* data, size_t size) noexcept : data_{data}, size_{size} {} - simple_string_view(const char* data) : data_{data}, size_{std::char_traits::length(data)} {} + simple_string_view(const char* data) : data_{data}, size_{traits_type::length(data)} {} simple_string_view& operator=(const simple_string_view&) = default; constexpr const char* data() const noexcept { return data_; } constexpr size_t size() const noexcept { return size_; } + constexpr size_t length() const noexcept { return size_; } + constexpr size_t max_size() const noexcept { return std::numeric_limits::max(); } constexpr bool empty() const noexcept { return size_ == 0; } operator std::string() const { return {data_, size_}; } const char* begin() const noexcept { return data_; } + const char* cbegin() const noexcept { return data_; } const char* end() const noexcept { return data_ + size_; } + const char* cend() const noexcept { return data_ + size_; } + reverse_iterator rbegin() const { return reverse_iterator{end()}; } + reverse_iterator crbegin() const { return reverse_iterator{end()}; } + reverse_iterator rend() const { return reverse_iterator{begin()}; } + reverse_iterator crend() const { return reverse_iterator{begin()}; } constexpr const char& operator[](size_t pos) const { return data_[pos]; } constexpr const char& front() const { return *data_; } constexpr const char& back() const { return data_[size_ - 1]; } @@ -65,6 +89,112 @@ public: constexpr void remove_prefix(size_t n) { data_ += n; size_ -= n; } constexpr void remove_suffix(size_t n) { size_ -= n; } void swap(simple_string_view &s) noexcept { std::swap(data_, s.data_); std::swap(size_, s.size_); } + + constexpr const char& at(size_t pos) const { + if (pos >= size()) + throw std::out_of_range{"invalid string_view index"}; + return data_[pos]; + }; + + size_t copy(char* dest, size_t count, size_t pos = 0) const { + if (pos > size()) throw std::out_of_range{"invalid copy pos"}; + size_t rcount = std::min(count, size_ - pos); + traits_type::copy(dest, data_ + pos, rcount); + return rcount; + } + + constexpr simple_string_view substr(size_t pos = 0, size_t count = npos) const { + if (pos > size()) throw std::out_of_range{"invalid substr range"}; + simple_string_view result = *this; + if (pos > 0) result.remove_prefix(pos); + if (count < result.size()) result.remove_suffix(result.size() - count); + return result; + } + + constexpr size_t find(simple_string_view v, size_t pos = 0) const noexcept { + if (pos > size_ || v.size_ > size_) return npos; + for (const size_t max_pos = size_ - v.size_; pos <= max_pos; ++pos) { + if (0 == traits_type::compare(v.data_, data_ + pos, v.size_)) + return pos; + } + return npos; + } + constexpr size_t find(char c, size_t pos = 0) const noexcept { return find({&c, 1}, pos); } + constexpr size_t find(const char* c, size_t pos, size_t count) const { return find({c, count}, pos); } + size_t find(const char* c, size_t pos = 0) const { return find(simple_string_view(c), pos); } + + constexpr size_t rfind(simple_string_view v, size_t pos = npos) const noexcept { + if (v.size_ > size_) return npos; + const size_t max_pos = size_ - v.size_; + for (pos = std::min(pos, max_pos); pos <= max_pos; --pos) { + if (0 == traits_type::compare(v.data_, data_ + pos, v.size_)) + return pos; + } + return npos; + } + constexpr size_t rfind(char c, size_t pos = npos) const noexcept { return rfind({&c, 1}, pos); } + constexpr size_t rfind(const char* c, size_t pos, size_t count) const { return rfind({c, count}, pos); } + size_t rfind(const char* c, size_t pos = npos) const { return rfind(simple_string_view(c), pos); } + + constexpr size_t find_first_of(simple_string_view v, size_t pos = 0) const noexcept { + for (; pos < size_; ++pos) + for (char c : v) + if (data_[pos] == c) + return pos; + return npos; + } + constexpr size_t find_first_of(char c, size_t pos = 0) const noexcept { return find_first_of({&c, 1}, pos); } + constexpr size_t find_first_of(const char* c, size_t pos, size_t count) const { return find_first_of({c, count}, pos); } + size_t find_first_of(const char* c, size_t pos = 0) const { return find_first_of(simple_string_view(c), pos); } + + constexpr size_t find_last_of(simple_string_view v, const size_t pos = npos) const noexcept { + if (size_ == 0) return npos; + const size_t last_pos = std::min(pos, size_-1); + for (size_t i = last_pos; i <= last_pos; --i) + for (char c : v) + if (data_[i] == c) + return i; + return npos; + } + constexpr size_t find_last_of(char c, size_t pos = npos) const noexcept { return find_last_of({&c, 1}, pos); } + constexpr size_t find_last_of(const char* c, size_t pos, size_t count) const { return find_last_of({c, count}, pos); } + size_t find_last_of(const char* c, size_t pos = npos) const { return find_last_of(simple_string_view(c), pos); } + + constexpr size_t find_first_not_of(simple_string_view v, size_t pos = 0) const noexcept { + for (; pos < size_; ++pos) { + bool none = true; + for (char c : v) { + if (data_[pos] == c) { + none = false; + break; + } + } + if (none) return pos; + } + return npos; + } + constexpr size_t find_first_not_of(char c, size_t pos = 0) const noexcept { return find_first_not_of({&c, 1}, pos); } + constexpr size_t find_first_not_of(const char* c, size_t pos, size_t count) const { return find_first_not_of({c, count}, pos); } + size_t find_first_not_of(const char* c, size_t pos = 0) const { return find_first_not_of(simple_string_view(c), pos); } + + constexpr size_t find_last_not_of(simple_string_view v, const size_t pos = npos) const noexcept { + if (size_ == 0) return npos; + const size_t last_pos = std::min(pos, size_-1); + for (size_t i = last_pos; i <= last_pos; --i) { + bool none = true; + for (char c : v) { + if (data_[i] == c) { + none = false; + break; + } + } + if (none) return i; + } + return npos; + } + constexpr size_t find_last_not_of(char c, size_t pos = npos) const noexcept { return find_last_not_of({&c, 1}, pos); } + constexpr size_t find_last_not_of(const char* c, size_t pos, size_t count) const { return find_last_not_of({c, count}, pos); } + size_t find_last_not_of(const char* c, size_t pos = npos) const { return find_last_not_of(simple_string_view(c), pos); } }; inline bool operator==(simple_string_view lhs, simple_string_view rhs) { return lhs.size() == rhs.size() && 0 == std::char_traits::compare(lhs.data(), rhs.data(), lhs.size()); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c3d7497..6a22a45 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -7,6 +7,7 @@ set(LMQ_TEST_SRC test_connect.cpp test_commands.cpp test_requests.cpp + test_string_view.cpp ) add_executable(tests ${LMQ_TEST_SRC})