text: #pragma once
  
  #include "fwd.h"
  #include "string.h"
  #include "utility.h"
  #include "typetraits.h"
  
  #include <string_view>
  
  template <typename TChar, typename TTraits>
  class TBasicStringBuf: public TFixedString<TChar, TTraits>, public TStringBase<TBasicStringBuf<TChar, TTraits>, TChar, TTraits> {
      using TdSelf = TBasicStringBuf;
      using TBaseStr = TFixedString<TChar, TTraits>;
      using TBase = TStringBase<TdSelf, TChar, TTraits>;
  
  public:
      using char_type = TChar; // TODO: DROP
      using value_type = TChar;
      using traits_type = TTraits;
  
      constexpr inline TBasicStringBuf(const TChar* data, size_t len) noexcept
          : TBaseStr(data, len)
      {
      }
  
      inline TBasicStringBuf(const TChar* data) noexcept
          : TBaseStr(data, TBase::StrLen(data))
      {
      }
  
      inline TBasicStringBuf(const TChar* beg, const TChar* end) noexcept
          : TBaseStr(beg, end)
      {
          Y_ASSERT(beg <= end);
      }
  
      template <typename D, typename T>
      inline TBasicStringBuf(const TStringBase<D, TChar, T>& str) noexcept
          : TBaseStr(str)
      {
      }
  
      template <typename T, typename A>
      inline TBasicStringBuf(const std::basic_string<TChar, T, A>& str) noexcept
          : TBaseStr(str)
      {
      }
  
      template <typename TCharTraits>
      constexpr TBasicStringBuf(std::basic_string_view<TChar, TCharTraits> view) noexcept
          : TBaseStr(view.data(), view.size())
      {
      }
  
      constexpr TBasicStringBuf() noexcept
          : TBaseStr()
      {
      }
  
      constexpr inline TBasicStringBuf(const TBaseStr& src) noexcept
          : TBaseStr(src)
      {
      }
  
      inline TBasicStringBuf(const TBaseStr& src, size_t pos, size_t n) noexcept
          : TBaseStr(src)
      {
          Skip(pos).Trunc(n);
      }
  
      inline TBasicStringBuf(const TBaseStr& src, size_t pos) noexcept
          : TBasicStringBuf(src, pos, TBase::npos)
      {
      }
  
      template<typename TCharTraits>
      constexpr operator std::basic_string_view<TChar, TCharTraits>() const noexcept {
          return {data(), length()};
      }
  
  public: // required by TStringBase
      constexpr inline const TChar* data() const noexcept {
          return Start;
      }
  
      constexpr inline size_t length() const noexcept {
          return Length;
      }
  
  public:
      void Clear() {
          *this = TdSelf();
      }
  
      constexpr bool IsInited() const noexcept {
          return nullptr != Start;
      }
  
  public:
      /**
       * Tries to split string in two parts using given delimiter character.
       * Searches for the delimiter, scanning string from the beginning.
       * The delimiter is excluded from the result. Both out parameters are
       * left unmodified if there was no delimiter character in string.
       *
       * @param[in] delim                 Delimiter character.
       * @param[out] l                    The first part of split result.
       * @param[out] r                    The second part of split result.
       * @returns                         Whether the split was actually performed.
       */
      inline bool TrySplit(TChar delim, TdSelf& l, TdSelf& r) const noexcept {
          return TrySplitOn(TBase::find(delim), l, r);
      }
  
      /**
       * Tries to split string in two parts using given delimiter character.
       * Searches for the delimiter, scanning string from the end.
       * The delimiter is excluded from the result. Both out parameters are
       * left unmodified if there was no delimiter character in string.
       *
       * @param[in] delim                 Delimiter character.
       * @param[out] l                    The first part of split result.
       * @param[out] r                    The second part of split result.
       * @returns                         Whether the split was actually performed.
       */
      inline bool TryRSplit(TChar delim, TdSelf& l, TdSelf& r) const noexcept {
          return TrySplitOn(TBase::rfind(delim), l, r);
      }
  
      /**
       * Tries to split string in two parts using given delimiter sequence.
       * Searches for the delimiter, scanning string from the beginning.
       * The delimiter sequence is excluded from the result. Both out parameters
       * are left unmodified if there was no delimiter character in string.
       *
       * @param[in] delim                 Delimiter sequence.
       * @param[out] l                    The first part of split result.
       * @param[out] r                    The second part of split result.
       * @returns                         Whether the split was actually performed.
       */
      inline bool TrySplit(TdSelf delim, TdSelf& l, TdSelf& r) const noexcept {
          return TrySplitOn(TBase::find(delim), l, r, delim.size());
      }
  
      /**
       * Tries to split string in two parts using given delimiter sequence.
       * Searches for the delimiter, scanning string from the end.
       * The delimiter sequence is excluded from the result. Both out parameters
       * are left unmodified if there was no delimiter character in string.
       *
       * @param[in] delim                 Delimiter sequence.
       * @param[out] l                    The first part of split result.
       * @param[out] r                    The second part of split result.
       * @returns                         Whether the split was actually performed.
       */
      inline bool TryRSplit(TdSelf delim, TdSelf& l, TdSelf& r) const noexcept {
          return TrySplitOn(TBase::rfind(delim), l, r, delim.size());
      }
  
      inline void Split(TChar delim, TdSelf& l, TdSelf& r) const noexcept {
          SplitTemplate(delim, l, r);
      }
  
      inline void RSplit(TChar delim, TdSelf& l, TdSelf& r) const noexcept {
          RSplitTemplate(delim, l, r);
      }
  
      inline void Split(TdSelf delim, TdSelf& l, TdSelf& r) const noexcept {
          SplitTemplate(delim, l, r);
      }
  
      inline void RSplit(TdSelf delim, TdSelf& l, TdSelf& r) const noexcept {
          RSplitTemplate(delim, l, r);
      }
  
  private:
      // splits on a delimiter at a given position; delimiter is excluded
      void DoSplitOn(size_t pos, TdSelf& l, TdSelf& r, size_t len) const noexcept {
          Y_ASSERT(pos != TBase::npos);
  
          // make a copy in case one of l/r is really *this
          const TdSelf tok = SubStr(pos + len);
          l = Head(pos);
          r = tok;
      }
  
  public:
      // In all methods below with @pos parameter, @pos is supposed to be
      // a result of string find()/rfind()/find_first() or other similiar functions,
      // returning either position within string length [0..Length) or npos.
      // For all other @pos values (out of string index range) the behaviour isn't well defined
      // For example, for TStringBuf s("abc"):
      // s.TrySplitOn(s.find('z'), ...) is false, but s.TrySplitOn(100500, ...) is true.
  
      bool TrySplitOn(size_t pos, TdSelf& l, TdSelf& r, size_t len = 1) const noexcept {
          if (TBase::npos == pos)
              return false;
  
          DoSplitOn(pos, l, r, len);
          return true;
      }
  
      void SplitOn(size_t pos, TdSelf& l, TdSelf& r, size_t len = 1) const noexcept {
          if (!TrySplitOn(pos, l, r, len)) {
              l = *this;
              r = TdSelf();
          }
      }
  
      bool TrySplitAt(size_t pos, TdSelf& l, TdSelf& r) const noexcept {
          return TrySplitOn(pos, l, r, 0);
      }
  
      void SplitAt(size_t pos, TdSelf& l, TdSelf& r) const noexcept {
          SplitOn(pos, l, r, 0);
      }
  
      /*
      // Not implemented intentionally, use TrySplitOn() instead
      void RSplitOn(size_t pos, TdSelf& l, TdSelf& r) const noexcept;
      void RSplitAt(size_t pos, TdSelf& l, TdSelf& r) const noexcept;
  */
  
  public:
      Y_PURE_FUNCTION
      inline TdSelf After(TChar c) const noexcept {
          TdSelf l, r;
          return TrySplit(c, l, r) ? r : *this;
      }
  
      Y_PURE_FUNCTION
      inline TdSelf Before(TChar c) const noexcept {
          TdSelf l, r;
          return TrySplit(c, l, r) ? l : *this;
      }
  
      Y_PURE_FUNCTION
      inline TdSelf RAfter(TChar c) const noexcept {
          TdSelf l, r;
          return TryRSplit(c, l, r) ? r : *this;
      }
  
      Y_PURE_FUNCTION
      inline TdSelf RBefore(TChar c) const noexcept {
          TdSelf l, r;
          return TryRSplit(c, l, r) ? l : *this;
      }
  
  public:
      inline bool AfterPrefix(const TdSelf& prefix, TdSelf& result) const noexcept {
          if (this->StartsWith(prefix)) {
              result = Tail(prefix.Length);
              return true;
          }
          return false;
      }
  
      inline bool BeforeSuffix(const TdSelf& suffix, TdSelf& result) const noexcept {
          if (this->EndsWith(suffix)) {
              result = Head(Length - suffix.Length);
              return true;
          }
          return false;
      }
  
      inline bool SkipPrefix(const TdSelf& prefix) noexcept {
          return AfterPrefix(prefix, *this);
      }
  
      inline bool ChopSuffix(const TdSelf& suffix) noexcept {
          return BeforeSuffix(suffix, *this);
      }
  
  public:
      // returns tail, including pos
      TdSelf SplitOffAt(size_t pos) {
          const TdSelf tok = SubStr(pos);
          Trunc(pos);
          return tok;
      }
  
      // returns head, tail includes pos
      TdSelf NextTokAt(size_t pos) {
          const TdSelf tok = Head(pos);
          Skip(pos);
          return tok;
      }
  
      TdSelf SplitOffOn(size_t pos) {
          TdSelf tok;
          SplitOn(pos, *this, tok);
          return tok;
      }
  
      TdSelf NextTokOn(size_t pos) {
          TdSelf tok;
          SplitOn(pos, tok, *this);
          return tok;
      }
      /*
      // See comment on RSplitOn() above
      TdSelf RSplitOffOn(size_t pos);
      TdSelf RNextTokOn(size_t pos);
  */
  
  public:
      TdSelf SplitOff(TChar delim) {
          TdSelf tok;
          Split(delim, *this, tok);
          return tok;
      }
  
      TdSelf RSplitOff(TChar delim) {
          TdSelf tok;
          RSplit(delim, tok, *this);
          return tok;
      }
  
      bool NextTok(TChar delim, TdSelf& tok) {
          return NextTokTemplate(delim, tok);
      }
  
      bool NextTok(TdSelf delim, TdSelf& tok) {
          return NextTokTemplate(delim, tok);
      }
  
      bool RNextTok(TChar delim, TdSelf& tok) {
          return RNextTokTemplate(delim, tok);
      }
  
      bool RNextTok(TdSelf delim, TdSelf& tok) {
          return RNextTokTemplate(delim, tok);
      }
  
      bool ReadLine(TdSelf& tok) {
          if (NextTok('\n', tok)) {
              while (!tok.empty() && tok.back() == '\r') {
                  --tok.Length;
              }
  
              return true;
          }
  
          return false;
      }
  
      TdSelf NextTok(TChar delim) {
          return NextTokTemplate(delim);
      }
  
      TdSelf RNextTok(TChar delim) {
          return RNextTokTemplate(delim);
      }
  
      TdSelf NextTok(TdSelf delim) {
          return NextTokTemplate(delim);
      }
  
      TdSelf RNextTok(TdSelf delim) {
          return RNextTokTemplate(delim);
      }
  
  public: // string subsequences
      /// Cut last @c shift characters (or less if length is less than @c shift)
      inline TdSelf& Chop(size_t shift) noexcept {
          ChopImpl(shift);
  
          return *this;
      }
  
      /// Cut first @c shift characters (or less if length is less than @c shift)
      inline TdSelf& Skip(size_t shift) noexcept {
          Start += ChopImpl(shift);
  
          return *this;
      }
  
      /// Sets the start pointer to a position relative to the end
      inline TdSelf& RSeek(size_t len) noexcept {
          if (Length > len) {
              Start += Length - len;
              Length = len;
          }
  
          return *this;
      }
  
      inline TdSelf& Trunc(size_t len) noexcept {
          if (Length > len) {
              Length = len;
          }
  
          return *this;
      }
  
      Y_PURE_FUNCTION
      inline TdSelf SubStr(size_t beg) const noexcept {
          return TdSelf(*this).Skip(beg);
      }
  
      Y_PURE_FUNCTION
      inline TdSelf SubStr(size_t beg, size_t len) const noexcept {
          return SubStr(beg).Trunc(len);
      }
  
      Y_PURE_FUNCTION
      inline TdSelf Head(size_t pos) const noexcept {
          return TdSelf(*this).Trunc(pos);
      }
  
      Y_PURE_FUNCTION
      inline TdSelf Tail(size_t pos) const noexcept {
          return SubStr(pos);
      }
  
      Y_PURE_FUNCTION
      inline TdSelf Last(size_t len) const noexcept {
          return TdSelf(*this).RSeek(len);
      }
  
      TGenericString<TChar> ToString() const {
          return {Start, Length};
      }
  
      TGenericString<TChar> Quote() const {
          return ToString().Quote();
      }
  
  private:
      inline size_t ChopImpl(size_t shift) noexcept {
          if (shift > length())
              shift = length();
          Length -= shift;
          return shift;
      }
  
      template <typename TDelimiterType>
      TdSelf NextTokTemplate(TDelimiterType delim) {
          TdSelf tok;
          Split(delim, tok, *this);
          return tok;
      }
  
      template <typename TDelimiterType>
      TdSelf RNextTokTemplate(TDelimiterType delim) {
          TdSelf tok;
          RSplit(delim, *this, tok);
          return tok;
      }
  
      template <typename TDelimiterType>
      bool NextTokTemplate(TDelimiterType delim, TdSelf& tok) {
          if (!this->empty()) {
              tok = NextTokTemplate(delim);
              return true;
          }
          return false;
      }
  
      template <typename TDelimiterType>
      bool RNextTokTemplate(TDelimiterType delim, TdSelf& tok) {
          if (!this->empty()) {
              tok = RNextTokTemplate(delim);
              return true;
          }
          return false;
      }
  
      template <typename TDelimiterType>
      inline void SplitTemplate(TDelimiterType delim, TdSelf& l, TdSelf& r) const noexcept {
          if (!TrySplit(delim, l, r)) {
              l = *this;
              r = TdSelf();
          }
      }
  
      template <typename TDelimiterType>
      inline void RSplitTemplate(TDelimiterType delim, TdSelf& l, TdSelf& r) const noexcept {
          if (!TryRSplit(delim, l, r)) {
              r = *this;
              l = TdSelf();
          }
      }
  
  private:
      using TBaseStr::Length;
      using TBaseStr::Start;
  };
  
  std::ostream& operator<< (std::ostream& os, TStringBuf buf);
  
  static inline TString ToString(const TStringBuf str) {
      return TString(str);
  }
  
  static inline TUtf16String ToWtring(const TWtringBuf wtr) {
      return TUtf16String(wtr);
  }
  
  static inline TUtf32String ToUtf32String(const TUtf32String wtr) {
      return TUtf32String(wtr);
  }
  
  template <typename TChar, size_t size>
  constexpr inline TBasicStringBuf<TChar> AsStringBuf(const TChar (&str)[size]) noexcept {
      return TBasicStringBuf<TChar>(str, size - 1);
  }
  