#pragma once // Some of these includes are just a legacy from previous implementation. // We don't need them here, but removing them is tricky because it breaks all // kinds of builds downstream #include "mem_copy.h" #include "ptr.h" #include "utility.h" #include <util/charset/unidata.h> #include <util/system/platform.h> #include <util/system/yassert.h> #include <contrib/libs/libc_compat/string.h> #include <cctype> #include <cstring> #include <string> #include <string_view> namespace NStringPrivate { template <class TCharType> size_t GetStringLengthWithLimit(const TCharType* s, size_t maxlen) { Y_ASSERT(s); size_t i = 0; for (; i != maxlen && s[i]; ++i) ; return i; } inline size_t GetStringLengthWithLimit(const char* s, size_t maxlen) { Y_ASSERT(s); return strnlen(s, maxlen); } } template <typename TDerived, typename TCharType, typename TTraitsType = std::char_traits<TCharType>> class TStringBase { using TStringView = std::basic_string_view<TCharType>; using TStringViewWithTraits = std::basic_string_view<TCharType, TTraitsType>; public: using TChar = TCharType; using TTraits = TTraitsType; using TSelf = TStringBase<TDerived, TChar, TTraits>; using size_type = size_t; using difference_type = ptrdiff_t; static constexpr size_t npos = size_t(-1); using const_iterator = const TCharType*; using const_reference = const TCharType&; using const_reverse_iterator = std::reverse_iterator<const_iterator>; static constexpr size_t StrLen(const TCharType* s) noexcept { if (Y_LIKELY(s)) { return TTraits::length(s); } return 0; } template <class TCharTraits> inline constexpr operator std::basic_string_view<TCharType, TCharTraits>() const { return std::basic_string_view<TCharType, TCharTraits>(data(), size()); } template <class TCharTraits, class Allocator> inline explicit operator std::basic_string<TCharType, TCharTraits, Allocator>() const { return std::basic_string<TCharType, TCharTraits, Allocator>(Ptr(), Len()); } /** * @param Pointer to character inside the string, or nullptr. * @return Offset from string beginning (in chars), or npos on nullptr. */ inline size_t off(const TCharType* ret) const noexcept { return ret ? (size_t)(ret - Ptr()) : npos; } inline size_t IterOff(const_iterator it) const noexcept { return begin() <= it && end() > it ? size_t(it - begin()) : npos; } constexpr const_iterator begin() const noexcept { return Ptr(); } constexpr const_iterator end() const noexcept { return Ptr() + size(); } constexpr const_iterator cbegin() const noexcept { return begin(); } constexpr const_iterator cend() const noexcept { return end(); } constexpr const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(Ptr() + size()); } constexpr const_reverse_iterator rend() const noexcept { return const_reverse_iterator(Ptr()); } constexpr const_reverse_iterator crbegin() const noexcept { return rbegin(); } constexpr const_reverse_iterator crend() const noexcept { return rend(); } inline TCharType back() const noexcept { Y_ASSERT(!this->empty()); return Ptr()[Len() - 1]; } inline TCharType front() const noexcept { Y_ASSERT(!empty()); return Ptr()[0]; } constexpr const TCharType* data() const noexcept { return Ptr(); } constexpr inline size_t size() const noexcept { return Len(); } constexpr inline bool is_null() const noexcept { return *Ptr() == 0; } Y_PURE_FUNCTION constexpr inline bool empty() const noexcept { return Len() == 0; } constexpr inline explicit operator bool() const noexcept { return !empty(); } public: // style-guide compliant methods constexpr const TCharType* Data() const noexcept { return Ptr(); } constexpr size_t Size() const noexcept { return Len(); } Y_PURE_FUNCTION constexpr bool Empty() const noexcept { return 0 == Len(); } private: static constexpr TStringView LegacySubString(const TStringView view, size_t p, size_t n) noexcept { p = Min(p, view.length()); return view.substr(p, n); } public: // ~~~ Comparison ~~~ : FAMILY0(int, compare) static constexpr int compare(const TSelf& s1, const TSelf& s2) noexcept { return s1.AsStringView().compare(s2.AsStringView()); } static constexpr int compare(const TCharType* p, const TSelf& s2) noexcept { TCharType null{0}; return TStringViewWithTraits(p ? p : &null).compare(s2.AsStringView()); } static constexpr int compare(const TSelf& s1, const TCharType* p) noexcept { TCharType null{0}; return s1.AsStringView().compare(p ? p : &null); } static constexpr int compare(const TStringView s1, const TStringView s2) noexcept { return TStringViewWithTraits(s1.data(), s1.size()).compare(TStringViewWithTraits(s2.data(), s2.size())); } template <class T> constexpr int compare(const T& t) const noexcept { return compare(*this, t); } constexpr int compare(size_t p, size_t n, const TStringView t) const noexcept { return compare(LegacySubString(*this, p, n), t); } constexpr int compare(size_t p, size_t n, const TStringView t, size_t p1, size_t n1) const noexcept { return compare(LegacySubString(*this, p, n), LegacySubString(t, p1, n1)); } constexpr int compare(size_t p, size_t n, const TStringView t, size_t n1) const noexcept { return compare(LegacySubString(*this, p, n), LegacySubString(t, 0, n1)); } constexpr int compare(const TCharType* p, size_t len) const noexcept { return compare(*this, TStringView(p, len)); } static constexpr bool equal(const TSelf& s1, const TSelf& s2) noexcept { return s1.AsStringView() == s2.AsStringView(); } static constexpr bool equal(const TSelf& s1, const TCharType* p) noexcept { if (p == nullptr) { return s1.Len() == 0; } return s1.AsStringView() == p; } static constexpr bool equal(const TCharType* p, const TSelf& s2) noexcept { return equal(s2, p); } static constexpr bool equal(const TStringView s1, const TStringView s2) noexcept { return TStringViewWithTraits{s1.data(), s1.size()} == TStringViewWithTraits{s2.data(), s2.size()}; } template <class T> constexpr bool equal(const T& t) const noexcept { return equal(*this, t); } constexpr bool equal(size_t p, size_t n, const TStringView t) const noexcept { return equal(LegacySubString(*this, p, n), t); } constexpr bool equal(size_t p, size_t n, const TStringView t, size_t p1, size_t n1) const noexcept { return equal(LegacySubString(*this, p, n), LegacySubString(t, p1, n1)); } constexpr bool equal(size_t p, size_t n, const TStringView t, size_t n1) const noexcept { return equal(LegacySubString(*this, p, n), LegacySubString(t, 0, n1)); } static constexpr bool StartsWith(const TCharType* what, size_t whatLen, const TCharType* with, size_t withLen) noexcept { return withLen <= whatLen && TStringViewWithTraits(what, withLen) == TStringViewWithTraits(with, withLen); } static constexpr bool EndsWith(const TCharType* what, size_t whatLen, const TCharType* with, size_t withLen) noexcept { return withLen <= whatLen && TStringViewWithTraits(what + whatLen - withLen, withLen) == TStringViewWithTraits(with, withLen); } constexpr bool StartsWith(const TCharType* s, size_t n) const noexcept { return StartsWith(Ptr(), Len(), s, n); } constexpr bool StartsWith(const TStringView s) const noexcept { return StartsWith(s.data(), s.length()); } constexpr bool StartsWith(TCharType ch) const noexcept { return !empty() && TTraits::eq(*Ptr(), ch); } constexpr bool EndsWith(const TCharType* s, size_t n) const noexcept { return EndsWith(Ptr(), Len(), s, n); } constexpr bool EndsWith(const TStringView s) const noexcept { return EndsWith(s.data(), s.length()); } constexpr bool EndsWith(TCharType ch) const noexcept { return !empty() && TTraits::eq(Ptr()[Len() - 1], ch); } template <typename TDerived2, typename TTraits2> constexpr bool operator==(const TStringBase<TDerived2, TChar, TTraits2>& s2) const noexcept { return equal(*this, s2); } constexpr bool operator==(TStringView s2) const noexcept { return equal(*this, s2); } constexpr bool operator==(const TCharType* pc) const noexcept { return equal(*this, pc); } #ifndef __cpp_impl_three_way_comparison friend constexpr bool operator==(const TCharType* pc, const TSelf& s) noexcept { return equal(pc, s); } template <typename TDerived2, typename TTraits2> friend constexpr bool operator!=(const TSelf& s1, const TStringBase<TDerived2, TChar, TTraits2>& s2) noexcept { return !(s1 == s2); } friend constexpr bool operator!=(const TSelf& s1, TStringView s2) noexcept { return !(s1 == s2); } friend constexpr bool operator!=(const TSelf& s, const TCharType* pc) noexcept { return !(s == pc); } friend constexpr bool operator!=(const TCharType* pc, const TSelf& s) noexcept { return !(pc == s); } #endif template <typename TDerived2, typename TTraits2> friend constexpr bool operator<(const TSelf& s1, const TStringBase<TDerived2, TChar, TTraits2>& s2) noexcept { return compare(s1, s2) < 0; } friend constexpr bool operator<(const TSelf& s1, TStringView s2) noexcept { return compare(s1, s2) < 0; } friend constexpr bool operator<(const TSelf& s, const TCharType* pc) noexcept { return compare(s, pc) < 0; } friend constexpr bool operator<(const TCharType* pc, const TSelf& s) noexcept { return compare(pc, s) < 0; } template <typename TDerived2, typename TTraits2> friend constexpr bool operator<=(const TSelf& s1, const TStringBase<TDerived2, TChar, TTraits2>& s2) noexcept { return compare(s1, s2) <= 0; } friend constexpr bool operator<=(const TSelf& s1, TStringView s2) noexcept { return compare(s1, s2) <= 0; } friend constexpr bool operator<=(const TSelf& s, const TCharType* pc) noexcept { return compare(s, pc) <= 0; } friend constexpr bool operator<=(const TCharType* pc, const TSelf& s) noexcept { return compare(pc, s) <= 0; } template <typename TDerived2, typename TTraits2> friend constexpr bool operator>(const TSelf& s1, const TStringBase<TDerived2, TChar, TTraits2>& s2) noexcept { return compare(s1, s2) > 0; } friend constexpr bool operator>(const TSelf& s1, TStringView s2) noexcept { return compare(s1, s2) > 0; } friend constexpr bool operator>(const TSelf& s, const TCharType* pc) noexcept { return compare(s, pc) > 0; } friend constexpr bool operator>(const TCharType* pc, const TSelf& s) noexcept { return compare(pc, s) > 0; } template <typename TDerived2, typename TTraits2> friend constexpr bool operator>=(const TSelf& s1, const TStringBase<TDerived2, TChar, TTraits2>& s2) noexcept { return compare(s1, s2) >= 0; } friend constexpr bool operator>=(const TSelf& s1, TStringView s2) noexcept { return compare(s1, s2) >= 0; } friend constexpr bool operator>=(const TSelf& s, const TCharType* pc) noexcept { return compare(s, pc) >= 0; } friend constexpr bool operator>=(const TCharType* pc, const TSelf& s) noexcept { return compare(pc, s) >= 0; } // ~~ Read access ~~ inline TCharType at(size_t pos) const noexcept { if (Y_LIKELY(pos < Len())) { return (Ptr())[pos]; } return 0; } inline TCharType operator[](size_t pos) const noexcept { Y_ASSERT(pos < this->size()); return Ptr()[pos]; } //~~~~Search~~~~ /** * @return Position of the substring inside this string, or `npos` if not found. */ inline size_t find(const TStringView s, size_t pos = 0) const noexcept { return find(s.data(), pos, s.size()); } inline size_t find(const TCharType* s, size_t pos, size_t count) const noexcept { return AsStringView().find(s, pos, count); } inline size_t find(TCharType c, size_t pos = 0) const noexcept { return AsStringView().find(c, pos); } inline size_t rfind(TCharType c) const noexcept { return AsStringView().rfind(c); } inline size_t rfind(TCharType c, size_t pos) const noexcept { if (pos == 0) { return npos; } return AsStringView().rfind(c, pos - 1); } inline size_t rfind(const TStringView str, size_t pos = npos) const { return AsStringView().rfind(str.data(), pos, str.size()); } //~~~~Contains~~~~ /** * @returns Whether this string contains the provided substring. */ inline bool Contains(const TStringView s, size_t pos = 0) const noexcept { return !s.length() || find(s, pos) != npos; } inline bool Contains(TChar c, size_t pos = 0) const noexcept { return find(c, pos) != npos; } inline void Contains(std::enable_if<std::is_unsigned<TCharType>::value, char> c, size_t pos = 0) const noexcept { return find(ui8(c), pos) != npos; } //~~~~Character Set Search~~~ inline size_t find_first_of(TCharType c) const noexcept { return find_first_of(c, 0); } inline size_t find_first_of(TCharType c, size_t pos) const noexcept { return find(c, pos); } inline size_t find_first_of(const TStringView set) const noexcept { return find_first_of(set, 0); } inline size_t find_first_of(const TStringView set, size_t pos) const noexcept { return AsStringView().find_first_of(set.data(), pos, set.size()); } inline size_t find_first_not_of(TCharType c) const noexcept { return find_first_not_of(c, 0); } inline size_t find_first_not_of(TCharType c, size_t pos) const noexcept { return find_first_not_of(TStringView(&c, 1), pos); } inline size_t find_first_not_of(const TStringView set) const noexcept { return find_first_not_of(set, 0); } inline size_t find_first_not_of(const TStringView set, size_t pos) const noexcept { return AsStringView().find_first_not_of(set.data(), pos, set.size()); } inline size_t find_last_of(TCharType c, size_t pos = npos) const noexcept { return find_last_of(&c, pos, 1); } inline size_t find_last_of(const TStringView set, size_t pos = npos) const noexcept { return find_last_of(set.data(), pos, set.length()); } inline size_t find_last_of(const TCharType* set, size_t pos, size_t n) const noexcept { return AsStringView().find_last_of(set, pos, n); } inline size_t find_last_not_of(TCharType c, size_t pos = npos) const noexcept { return AsStringView().find_last_not_of(c, pos); } inline size_t find_last_not_of(const TStringView set, size_t pos = npos) const noexcept { return find_last_not_of(set.data(), pos, set.length()); } inline size_t find_last_not_of(const TCharType* set, size_t pos, size_t n) const noexcept { return AsStringView().find_last_not_of(set, pos, n); } inline size_t copy(TCharType* pc, size_t n, size_t pos) const { if (pos > Len()) { throw std::out_of_range("TStringBase::copy"); } return CopyImpl(pc, n, pos); } inline size_t copy(TCharType* pc, size_t n) const noexcept { return CopyImpl(pc, n, 0); } inline size_t strcpy(TCharType* pc, size_t n) const noexcept { if (n) { n = copy(pc, n - 1); pc[n] = 0; } return n; } inline TDerived copy() const Y_WARN_UNUSED_RESULT { return TDerived(Ptr(), Len()); } // ~~~ Partial copy ~~~~ TDerived substr(size_t pos, size_t n = npos) const Y_WARN_UNUSED_RESULT { return TDerived(*This(), pos, n); } private: using GenericFinder = const TCharType* (*)(const TCharType*, size_t, const TCharType*, size_t); constexpr TStringViewWithTraits AsStringView() const { return static_cast<TStringViewWithTraits>(*this); } constexpr inline const TCharType* Ptr() const noexcept { return This()->data(); } constexpr inline size_t Len() const noexcept { return This()->length(); } constexpr inline const TDerived* This() const noexcept { return static_cast<const TDerived*>(this); } inline size_t CopyImpl(TCharType* pc, size_t n, size_t pos) const noexcept { const size_t toCopy = Min(Len() - pos, n); TTraits::copy(pc, Ptr() + pos, toCopy); return toCopy; } };