diff --git a/lokimq/hex.h b/lokimq/hex.h index 6de5d3e..6e45afd 100644 --- a/lokimq/hex.h +++ b/lokimq/hex.h @@ -75,6 +75,13 @@ inline std::string to_hex(string_view s) { return hex; } +inline std::string to_hex(ustring_view s) { + std::string hex; + hex.reserve(s.size() * 2); + to_hex(s.begin(), s.end(), std::back_inserter(hex)); + return hex; +} + /// Returns true if all elements in the range are hex characters template constexpr bool is_hex(It begin, It end) { @@ -87,6 +94,7 @@ constexpr bool is_hex(It begin, It end) { /// Returns true if all elements in the string-like value are hex characters constexpr bool is_hex(string_view s) { return is_hex(s.begin(), s.end()); } +constexpr bool is_hex(ustring_view s) { return is_hex(s.begin(), s.end()); } /// Convert a hex digit into its numeric (0-15) value constexpr char from_hex_digit(unsigned char x) noexcept { @@ -119,4 +127,11 @@ inline std::string from_hex(string_view s) { return bytes; } +inline std::string from_hex(ustring_view s) { + std::string bytes; + bytes.reserve(s.size() / 2); + from_hex(s.begin(), s.end(), std::back_inserter(bytes)); + return bytes; +} + } diff --git a/lokimq/string_view.h b/lokimq/string_view.h index 57f9390..5baa163 100644 --- a/lokimq/string_view.h +++ b/lokimq/string_view.h @@ -33,7 +33,10 @@ #ifdef __cpp_lib_string_view #include -namespace lokimq { using string_view = std::string_view; } +namespace lokimq { +using string_view = std::string_view; +using ustring_view = std::basic_string_view; +} #else @@ -43,16 +46,17 @@ namespace lokimq { using string_view = std::string_view; } namespace lokimq { /// Basic implementation of std::string_view (except for std::hash support). +template class simple_string_view { - const char *data_; + const CharT *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 traits_type = std::char_traits; + using value_type = CharT; + using pointer = CharT*; + using const_pointer = const CharT*; + using reference = CharT&; + using const_reference = const CharT&; using const_iterator = const_pointer; using iterator = const_iterator; using const_reverse_iterator = std::reverse_iterator; @@ -64,27 +68,27 @@ public: 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_{traits_type::length(data)} {} + simple_string_view(const std::basic_string& str) : data_{str.data()}, size_{str.size()} {} + constexpr simple_string_view(const CharT* data, size_t size) noexcept : data_{data}, size_{size} {} + simple_string_view(const CharT* 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 const CharT* 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; } - explicit operator std::string() const { return {data_, size_}; } - constexpr const char* begin() const noexcept { return data_; } - constexpr const char* cbegin() const noexcept { return data_; } - constexpr const char* end() const noexcept { return data_ + size_; } - constexpr const char* cend() const noexcept { return data_ + size_; } + explicit operator std::basic_string() const { return {data_, size_}; } + constexpr const CharT* begin() const noexcept { return data_; } + constexpr const CharT* cbegin() const noexcept { return data_; } + constexpr const CharT* end() const noexcept { return data_ + size_; } + constexpr const CharT* 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]; } + constexpr const CharT& operator[](size_t pos) const { return data_[pos]; } + constexpr const CharT& front() const { return *data_; } + constexpr const CharT& back() const { return data_[size_ - 1]; } int compare(simple_string_view s) const; constexpr void remove_prefix(size_t n) { data_ += n; size_ -= n; } constexpr void remove_suffix(size_t n) { size_ -= n; } @@ -93,13 +97,13 @@ public: #if defined(__clang__) || !defined(__GNUG__) || __GNUC__ >= 6 constexpr // GCC 5.x is buggy wrt constexpr throwing #endif - const char& at(size_t pos) const { + const CharT& 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 { + size_t copy(CharT* 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); @@ -125,9 +129,9 @@ public: } return npos; } - size_t find(char c, size_t pos = 0) const { return find({&c, 1}, pos); } - 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); } + size_t find(CharT c, size_t pos = 0) const { return find({&c, 1}, pos); } + size_t find(const CharT* c, size_t pos, size_t count) const { return find({c, count}, pos); } + size_t find(const CharT* c, size_t pos = 0) const { return find(simple_string_view(c), pos); } size_t rfind(simple_string_view v, size_t pos = npos) const { if (v.size_ > size_) return npos; @@ -138,38 +142,38 @@ public: } return npos; } - size_t rfind(char c, size_t pos = npos) const { return rfind({&c, 1}, pos); } - 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); } + size_t rfind(CharT c, size_t pos = npos) const { return rfind({&c, 1}, pos); } + size_t rfind(const CharT* c, size_t pos, size_t count) const { return rfind({c, count}, pos); } + size_t rfind(const CharT* 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) + for (CharT 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_first_of(CharT c, size_t pos = 0) const noexcept { return find_first_of({&c, 1}, pos); } + constexpr size_t find_first_of(const CharT* c, size_t pos, size_t count) const { return find_first_of({c, count}, pos); } + size_t find_first_of(const CharT* 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) + for (CharT 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_last_of(CharT c, size_t pos = npos) const noexcept { return find_last_of({&c, 1}, pos); } + constexpr size_t find_last_of(const CharT* c, size_t pos, size_t count) const { return find_last_of({c, count}, pos); } + size_t find_last_of(const CharT* 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) { + for (CharT c : v) { if (data_[pos] == c) { none = false; break; @@ -179,16 +183,16 @@ public: } 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_first_not_of(CharT c, size_t pos = 0) const noexcept { return find_first_not_of({&c, 1}, pos); } + constexpr size_t find_first_not_of(const CharT* c, size_t pos, size_t count) const { return find_first_not_of({c, count}, pos); } + size_t find_first_not_of(const CharT* 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) { + for (CharT c : v) { if (data_[i] == c) { none = false; break; @@ -198,47 +202,106 @@ public: } 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); } + constexpr size_t find_last_not_of(CharT c, size_t pos = npos) const noexcept { return find_last_not_of({&c, 1}, pos); } + constexpr size_t find_last_not_of(const CharT* c, size_t pos, size_t count) const { return find_last_not_of({c, count}, pos); } + size_t find_last_not_of(const CharT* 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()); +/// We have three of each of these: one with two string views, one with RHS argument deduction, and +/// one with LHS argument deduction, so that you can do (sv == sv), (sv == "foo"), and ("foo" == sv) +template +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()); }; -inline bool operator!=(simple_string_view lhs, simple_string_view rhs) { +template +inline bool operator==(simple_string_view lhs, std::common_type_t> rhs) { + return lhs.size() == rhs.size() && 0 == std::char_traits::compare(lhs.data(), rhs.data(), lhs.size()); +}; +template +inline bool operator==(std::common_type_t> lhs, simple_string_view rhs) { + return lhs.size() == rhs.size() && 0 == std::char_traits::compare(lhs.data(), rhs.data(), lhs.size()); +}; +template +inline bool operator!=(simple_string_view lhs, simple_string_view rhs) { return !(lhs == rhs); } -inline int simple_string_view::compare(simple_string_view s) const { - int cmp = std::char_traits::compare(data_, s.data(), std::min(size_, s.size())); +template +inline bool operator!=(simple_string_view lhs, std::common_type_t> rhs) { + return !(lhs == rhs); +} +template +inline bool operator!=(std::common_type_t> lhs, simple_string_view rhs) { + return !(lhs == rhs); +} +template +inline int simple_string_view::compare(simple_string_view s) const { + int cmp = std::char_traits::compare(data_, s.data(), std::min(size_, s.size())); if (cmp) return cmp; if (size_ < s.size()) return -1; else if (size_ > s.size()) return 1; return 0; } -inline bool operator<(simple_string_view lhs, simple_string_view rhs) { +template +inline bool operator<(simple_string_view lhs, simple_string_view rhs) { return lhs.compare(rhs) < 0; }; -inline bool operator<=(simple_string_view lhs, simple_string_view rhs) { +template +inline bool operator<(simple_string_view lhs, std::common_type_t> rhs) { + return lhs.compare(rhs) < 0; +}; +template +inline bool operator<(std::common_type_t> lhs, simple_string_view rhs) { + return lhs.compare(rhs) < 0; +}; +template +inline bool operator<=(simple_string_view lhs, simple_string_view rhs) { return lhs.compare(rhs) <= 0; }; -inline bool operator>(simple_string_view lhs, simple_string_view rhs) { +template +inline bool operator<=(simple_string_view lhs, std::common_type_t> rhs) { + return lhs.compare(rhs) <= 0; +}; +template +inline bool operator<=(std::common_type_t> lhs, simple_string_view rhs) { + return lhs.compare(rhs) <= 0; +}; +template +inline bool operator>(simple_string_view lhs, simple_string_view rhs) { return lhs.compare(rhs) > 0; }; -inline bool operator>=(simple_string_view lhs, simple_string_view rhs) { +template +inline bool operator>(simple_string_view lhs, std::common_type_t> rhs) { + return lhs.compare(rhs) > 0; +}; +template +inline bool operator>(std::common_type_t> lhs, simple_string_view rhs) { + return lhs.compare(rhs) > 0; +}; +template +inline bool operator>=(simple_string_view lhs, simple_string_view rhs) { return lhs.compare(rhs) >= 0; }; -inline std::ostream& operator<<(std::ostream& os, const simple_string_view& s) { +template +inline bool operator>=(simple_string_view lhs, std::common_type_t> rhs) { + return lhs.compare(rhs) >= 0; +}; +template +inline bool operator>=(std::common_type_t> lhs, simple_string_view rhs) { + return lhs.compare(rhs) >= 0; +}; +template +inline std::basic_ostream& operator<<(std::basic_ostream& os, const simple_string_view& s) { os.write(s.data(), s.size()); return os; } -using string_view = simple_string_view; +using string_view = simple_string_view; +using ustring_view = simple_string_view; } #endif -// Add a "foo"_sv literal that works exactly like the C++17 "foo"sv literal, but works with out +// Add a "foo"_sv literal that works exactly like the C++17 "foo"sv literal, but works with our // implementation in pre-C++17. namespace lokimq { inline namespace literals {