aboutsummaryrefslogblamecommitdiffstats
path: root/util/string/split.h
blob: bc46d9e64c63e16533010f7fcf0096727e290971 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
            
                   
                 
 
                                   
                             


                                        
                                
                                    
                                
                                 
                                 
 
                  
 
                                                                                  
 
                               
                                             
                                           









                                                                       









                                               
 
                                                 
                                                                   
                                                       







                                                 
                                                                   
                                                  






                                             
                             
                                                      
                                                
               
                                                      




                  
                                                              













                                                                                      

     
                     
                         
                                                 
                      
                                                    

     
                                                             

                      
     
                                                         
                                                                                    
 


                                        



                       
                                                
 
                                   


                   
                

                     
                     
                       
                                           


                
                                                         
                                                                         
 


                                        



                       
                                                








                        
            
  
                                          
                       
                            
                                         

     
                                                                  






                                                
                 
  
                     
                     
                                  


                  
                                                               
                                 
                                                     



                      
                                                      

                                                                     
     
              

           
                                                        



                                                       
                                        
     
  
 

                                                        
 
                                                               
 






                        
                                                            








                         
                                   
                                      
 
                                
 
                  
                                                                                                         
                                                                 



                        
                                                                                                          
                                                                 



                               
                           
                                            




                                               
                                                                            





                    
                                     
                                                      




                                               
                                                                                                    





                    
                           
                                                           























                                            
                                              
















                                           
                                             































                                                  
                                                      
                   

                  
                                   
                                    
                                    
 
                                                                     
                                                                                               
                                      
                                                                             

                                                                                        
                                                                                                           
                
                                                                              
                                                                
                      
 
                           
                                                
                                                                                                                

                                








                                                        
           
                                                                         
                                      

                                                   
                                                                                                              
 
                                                      
                                                                    
                          
                                  
 
































                                                                                      
                               






                                                                             
                                   


                                                        
                      


                                                             



                                                                                 
                                   


                                                                                         
                      



                                                                                     




                                                                                                         












                                                                         
                                  



                                            
                                                        


                                                                                              
                                                        
























                                                                                         
                                                        




                                                                                              
                                                        












                                                                                         
                                                             














                                                                               
                           
                                                                 
                                              


                                                                   
                                                       


                                                                                                                         
                                              


                                                                       
                           


                                               
                                             
      
                           
                                                               
                           



                                                    
                                                    
                                             
                                                 

                                               

         
                  
                           
                                                                   


                                                                      
                                               
                                 

                                               
                               

                                               
                         

                                               
                                                                              

            
                                                                                                        
         
                                                 
         


                                   

                         
                                                                                  
























                                                                                                                                                                  
                                                                                                                            






















































                                                                                                                  
                        




































                                                                                      
                                                                       








                                                                            
                                                         


                                           
                                                

                                   

                                                                                                      





                                  
                                    

                                           
                                          





















                                                                 
                                       





                              
                                      








                                                                                           
                               


                                            
                                        




















































                                                               
                                                                                      







                                 
                                            








                                                                                          
                                      

                                                    
                                      





                                                                                    
                                    






                                                                                    
                                 

                                                                                              
                                 

                                                                                                              
                                  

                                                                                                                         
                                                     


                                                                                                        
                                




                            
                           





                                                                                         
                                                                                           


                                                         
                                                                                        


                                                    
                                                                                        


                                      
                                                                                 


                                                                                                            
                                                                                                                      


                                                                                                            
                                                                   
 
#pragma once

#include "strspn.h"
#include "cast.h"

#include <util/generic/algorithm.h>
#include <util/generic/fwd.h>
#include <util/generic/iterator.h>
#include <util/generic/iterator_range.h>
#include <util/generic/store_policy.h>
#include <util/generic/strbuf.h>
#include <util/generic/string.h>
#include <util/generic/typetraits.h>
#include <util/generic/vector.h>
#include <util/generic/ylimits.h>
#include <util/system/compat.h>
#include <util/system/defaults.h>

#include <utility>
#include <stlfwd>

// NOTE: Check StringSplitter below to get more convenient split string interface.

namespace NStringSplitPrivate {

    template <class T, class I, class = void>
    struct TIsConsumer: std::false_type {};

    template <class T, class I>
    struct TIsConsumer<
        T, I,
        TVoidT<decltype(std::declval<T>().Consume(
            std::declval<I>(), std::declval<I>(), std::declval<I>()))>>
        : std::true_type {};

    template <class T, class I>
    constexpr bool TIsConsumerV = TIsConsumer<T, I>::value;

    template <class T>
    T* Find(T* str, std::common_type_t<T> ch) {
        for (; *str; ++str) {
            if (*str == ch) {
                return str;
            }
        }

        return nullptr;
    }

}

template <class I, class TDelim, class TConsumer>
std::enable_if_t<::NStringSplitPrivate::TIsConsumerV<TConsumer, I>>
SplitString(I b, I e, const TDelim& d, TConsumer&& c) {
    I l, i;

    do {
        l = b;
        i = d.Find(b, e);
    } while (c.Consume(l, i, b) && (b != i));
}

template <class I, class TDelim, class TConsumer>
std::enable_if_t<::NStringSplitPrivate::TIsConsumerV<TConsumer, I>>
SplitString(I b, const TDelim& d, TConsumer&& c) {
    I l, i;

    do {
        l = b;
        i = d.Find(b);
    } while (c.Consume(l, i, b) && (b != i));
}

template <class I1, class I2>
static inline I1* FastStrChr(I1* str, I2 f) noexcept {
    I1* ret = NStringSplitPrivate::Find(str, f);

    if (!ret) {
        ret = str + std::char_traits<I1>::length(str);
    }

    return ret;
}

template <class I>
static inline I* FastStrStr(I* str, I* f, size_t l) noexcept {
    std::basic_string_view<I> strView(str);
    const auto ret = strView.find(*f);

    if (ret != std::string::npos) {
        std::basic_string_view<I> fView(f, l);
        strView = strView.substr(ret);
        for (; strView.size() >= l; strView = strView.substr(1)) {
            if (strView.substr(0, l) == fView) {
                break;
            }
        }

        return strView.size() >= l ? strView.data() : strView.data() + strView.size();
    } else {
        return strView.data() + strView.size();
    }
}

template <class Char>
struct TStringDelimiter {
    inline TStringDelimiter(Char* delim) noexcept
        : Delim(delim)
        , Len(std::char_traits<Char>::length(delim))
    {
    }

    inline TStringDelimiter(Char* delim, size_t len) noexcept
        : Delim(delim)
        , Len(len)
    {
    }

    inline Char* Find(Char*& b, Char* e) const noexcept {
        const auto ret = std::basic_string_view<Char>(b, e - b).find(Delim, 0, Len);

        if (ret != std::string::npos) {
            const auto result = b + ret;
            b = result + Len;
            return result;
        }

        return (b = e);
    }

    inline Char* Find(Char*& b) const noexcept {
        Char* ret = FastStrStr(b, Delim, Len);

        b = *ret ? ret + Len : ret;

        return ret;
    }

    Char* Delim;
    const size_t Len;
};

template <class Char>
struct TCharDelimiter {
    inline TCharDelimiter(Char ch) noexcept
        : Ch(ch)
    {
    }

    inline Char* Find(Char*& b, Char* e) const noexcept {
        const auto ret = std::basic_string_view<Char>(b, e - b).find(Ch);

        if (ret != std::string::npos) {
            const auto result = b + ret;
            b = result + 1;
            return result;
        }

        return (b = e);
    }

    inline Char* Find(Char*& b) const noexcept {
        Char* ret = FastStrChr(b, Ch);

        if (*ret) {
            b = ret + 1;
        } else {
            b = ret;
        }

        return ret;
    }

    Char Ch;
};

template <class Iterator, class Condition>
struct TFuncDelimiter {
public:
    template <class... Args>
    TFuncDelimiter(Args&&... args)
        : Fn(std::forward<Args>(args)...)
    {
    }

    inline Iterator Find(Iterator& b, Iterator e) const noexcept {
        if ((b = std::find_if(b, e, Fn)) != e) {
            return b++;
        }

        return b;
    }

private:
    Condition Fn;
};

template <class Char>
struct TFindFirstOf {
    inline TFindFirstOf(Char* set)
        : Set(set)
    {
    }

    inline Char* FindFirstOf(Char* b, Char* e) const noexcept {
        Char* ret = b;
        for (; ret != e; ++ret) {
            if (NStringSplitPrivate::Find(Set, *ret))
                break;
        }
        return ret;
    }

    inline Char* FindFirstOf(Char* b) const noexcept {
        const std::basic_string_view<Char> bView(b);
        const auto ret = bView.find_first_of(Set);
        return ret != std::string::npos ? b + ret : b + bView.size();
    }

    Char* Set;
};

template <>
struct TFindFirstOf<const char>: public TCompactStrSpn {
    inline TFindFirstOf(const char* set, const char* e)
        : TCompactStrSpn(set, e)
    {
    }

    inline TFindFirstOf(const char* set)
        : TCompactStrSpn(set)
    {
    }
};

template <class Char>
struct TSetDelimiter: private TFindFirstOf<const Char> {
    using TFindFirstOf<const Char>::TFindFirstOf;

    inline Char* Find(Char*& b, Char* e) const noexcept {
        Char* ret = const_cast<Char*>(this->FindFirstOf(b, e));

        if (ret != e) {
            b = ret + 1;
            return ret;
        }

        return (b = e);
    }

    inline Char* Find(Char*& b) const noexcept {
        Char* ret = const_cast<Char*>(this->FindFirstOf(b));

        if (*ret) {
            b = ret + 1;
            return ret;
        }

        return (b = ret);
    }
};

namespace NSplitTargetHasPushBack {
    Y_HAS_MEMBER(push_back, PushBack);
}

template <class T, class = void>
struct TConsumerBackInserter;

template <class T>
struct TConsumerBackInserter<T, std::enable_if_t<NSplitTargetHasPushBack::TClassHasPushBack<T>::value>> {
    static void DoInsert(T* C, const typename T::value_type& i) {
        C->push_back(i);
    }
};

template <class T>
struct TConsumerBackInserter<T, std::enable_if_t<!NSplitTargetHasPushBack::TClassHasPushBack<T>::value>> {
    static void DoInsert(T* C, const typename T::value_type& i) {
        C->insert(C->end(), i);
    }
};

template <class T>
struct TContainerConsumer {
    inline TContainerConsumer(T* c) noexcept
        : C(c)
    {
    }

    template <class I>
    inline bool Consume(I* b, I* d, I* /*e*/) {
        TConsumerBackInserter<T>::DoInsert(C, typename T::value_type(b, d));

        return true;
    }

    T* C;
};

template <class T>
struct TContainerConvertingConsumer {
    inline TContainerConvertingConsumer(T* c) noexcept
        : C(c)
    {
    }

    template <class I>
    inline bool Consume(I* b, I* d, I* /*e*/) {
        TConsumerBackInserter<T>::DoInsert(C, FromString<typename T::value_type>(TStringBuf(b, d)));

        return true;
    }

    T* C;
};

template <class S, class I>
struct TLimitingConsumer {
    inline TLimitingConsumer(size_t cnt, S* slave) noexcept
        : Cnt(cnt ? cnt - 1 : Max<size_t>())
        , Slave(slave)
        , Last(nullptr)
    {
    }

    inline bool Consume(I* b, I* d, I* e) {
        if (!Cnt) {
            Last = b;

            return false;
        }

        --Cnt;

        return Slave->Consume(b, d, e);
    }

    size_t Cnt;
    S* Slave;
    I* Last;
};

template <class S>
struct TSkipEmptyTokens {
    inline TSkipEmptyTokens(S* slave) noexcept
        : Slave(slave)
    {
    }

    template <class I>
    inline bool Consume(I* b, I* d, I* e) {
        if (b != d) {
            return Slave->Consume(b, d, e);
        }

        return true;
    }

    S* Slave;
};

template <class S>
struct TKeepDelimiters {
    inline TKeepDelimiters(S* slave) noexcept
        : Slave(slave)
    {
    }

    template <class I>
    inline bool Consume(I* b, I* d, I* e) {
        if (Slave->Consume(b, d, d)) {
            if (d != e) {
                return Slave->Consume(d, e, e);
            }

            return true;
        }

        return false;
    }

    S* Slave;
};

template <class T>
struct TSimplePusher {
    inline bool Consume(char* b, char* d, char*) {
        *d = 0;
        C->push_back(b);

        return true;
    }

    T* C;
};

template <class T>
static inline void Split(char* buf, char ch, T* res) {
    res->resize(0);
    if (*buf == 0)
        return;

    TCharDelimiter<char> delim(ch);
    TSimplePusher<T> pusher = {res};

    SplitString(buf, delim, pusher);
}

/// Split string into res vector. Res vector is cleared before split.
/// Old good slow split function.
/// Field delimter is any number of symbols specified in delim (no empty strings in res vector)
/// @return number of elements created
size_t Split(const char* in, const char* delim, TVector<TString>& res);
size_t Split(const TString& in, const TString& delim, TVector<TString>& res);

/// Old split reimplemented for TStringBuf using the new code
/// Note that delim can be constructed from char* automatically (it is not cheap though)
inline size_t Split(const TStringBuf s, const TSetDelimiter<const char>& delim, TVector<TStringBuf>& res) {
    res.clear();
    TContainerConsumer<TVector<TStringBuf>> res1(&res);
    TSkipEmptyTokens<TContainerConsumer<TVector<TStringBuf>>> consumer(&res1);
    SplitString(s.data(), s.data() + s.size(), delim, consumer);
    return res.size();
}

template <class P, class D>
void GetNext(TStringBuf& s, D delim, P& param) {
    TStringBuf next = s.NextTok(delim);
    Y_ENSURE(next.IsInited(), TStringBuf("Split: number of fields less than number of Split output arguments"));
    param = FromString<P>(next);
}

template <class P, class D>
void GetNext(TStringBuf& s, D delim, TMaybe<P>& param) {
    TStringBuf next = s.NextTok(delim);
    if (next.IsInited()) {
        param = FromString<P>(next);
    } else {
        param.Clear();
    }
}

// example:
// Split(TStringBuf("Sherlock,2014,36.6"), ',', name, year, temperature);
template <class D, class P1, class P2>
void Split(TStringBuf s, D delim, P1& p1, P2& p2) {
    GetNext(s, delim, p1);
    GetNext(s, delim, p2);
    Y_ENSURE(!s.IsInited(), TStringBuf("Split: number of fields more than number of Split output arguments"));
}

template <class D, class P1, class P2, class... Other>
void Split(TStringBuf s, D delim, P1& p1, P2& p2, Other&... other) {
    GetNext(s, delim, p1);
    Split(s, delim, p2, other...);
}

/**
 * \fn auto StringSplitter(...)
 *
 * Creates a string splitter object. The only use for it is to call one of its
 * `Split*` methods, and then do something with the resulting proxy range.
 *
 * Some examples:
 * \code
 * TVector<TStringBuf> values = StringSplitter("1\t2\t3").Split('\t');
 *
 * for(TStringBuf part: StringSplitter("1::2::::3").SplitByString("::").SkipEmpty()) {
 *     Cerr << part;
 * }
 *
 * TVector<TString> firstTwoValues = StringSplitter("1\t2\t3").Split('\t').Take(2);
 * \endcode
 *
 * Use `Collect` or `AddTo` to store split results into an existing container:
 * \code
 * TVector<TStringBuf> values = {"0"};
 * StringSplitter("1\t2\t3").Split('\t').AddTo(&values);
 * \endcode
 * Note that `Collect` clears target container, while `AddTo` just inserts values.
 * You can use these methods with any container that has `emplace` / `emplace_back`.
 *
 * Use `ParseInto` to also perform string conversions before inserting values
 * into target container:
 * \code
 * TSet<int> values;
 * StringSplitter("1\t2\t3").Split('\t').ParseInto(&values);
 * \endcode
 */

namespace NStringSplitPrivate {
    Y_HAS_MEMBER(push_back, PushBack);
    Y_HAS_MEMBER(insert, Insert);
    Y_HAS_MEMBER(data, Data);

    /**
     * This one is needed here so that `std::string_view -> std::string_view`
     * conversion works.
     */
    template <class Src, class Dst>
    inline void DoFromString(const Src& src, Dst* dst) {
        *dst = ::FromString<Dst>(src);
    }

    template <class T>
    inline void DoFromString(const T& src, T* dst) noexcept {
        *dst = src;
    }

    template <class T>
    inline void DoFromString(const T& src, decltype(std::ignore)* dst) noexcept {
        *dst = src;
    }

    template <class Src, class Dst>
    inline Y_WARN_UNUSED_RESULT bool TryDoFromString(const Src& src, Dst* dst) noexcept {
        return ::TryFromString(src, *dst);
    }

    template <class T>
    inline Y_WARN_UNUSED_RESULT bool TryDoFromString(const T& src, T* dst) noexcept {
        *dst = src;
        return true;
    }

    template <class T>
    inline Y_WARN_UNUSED_RESULT bool TryDoFromString(const T& src, decltype(std::ignore)* dst) noexcept {
        *dst = src;
        return true;
    }

    /**
     * Consumer that places provided elements into a container. Not using
     * `emplace(iterator)` for efficiency.
     */
    template <class Container>
    struct TContainerConsumer {
        using value_type = typename Container::value_type;

        TContainerConsumer(Container* c)
            : C_(c)
        {
        }

        // TODO: return bool (continue)
        template <class StringBuf>
        void operator()(StringBuf e) const {
            this->operator()(C_, e);
        }

    private:
        template <class OtherContainer, class StringBuf>
        auto operator()(OtherContainer* c, StringBuf e) const -> decltype(c->emplace_back()) {
            return c->emplace_back(value_type(e));
        }

        template <class OtherContainer, class StringBuf>
        auto operator()(OtherContainer* c, StringBuf e) const -> decltype(c->emplace()) {
            return c->emplace(value_type(e));
        }

        Container* C_;
    };

    /**
     * Consumer that converts provided elements via `FromString` and places them
     * into a container.
     */
    template <class Container>
    struct TContainerConvertingConsumer {
        using value_type = typename Container::value_type;

        TContainerConvertingConsumer(Container* c)
            : C_(c)
        {
        }

        template <class StringBuf>
        void operator()(StringBuf e) const {
            this->operator()(C_, e);
        }

    private:
        template <class OtherContainer, class StringBuf>
        auto operator()(OtherContainer* c, StringBuf e) const -> decltype(c->emplace_back()) {
            value_type v;
            DoFromString(e, &v);
            return c->emplace_back(std::move(v));
        }

        template <class OtherContainer, class StringBuf>
        auto operator()(OtherContainer* c, StringBuf e) const -> decltype(c->emplace()) {
            value_type v;
            DoFromString(e, &v);
            return c->emplace(std::move(v));
        }

        Container* C_;
    };

    template <class String>
    struct TStringBufOfImpl {
        using type = std::conditional_t<
            THasData<String>::value,
            TBasicStringBuf<typename String::value_type>,
            TIteratorRange<typename String::const_iterator>>;
    };

    template <class Char, class Traits, class Allocator>
    struct TStringBufOfImpl<std::basic_string<Char, Traits, Allocator>> {
        using type = std::basic_string_view<Char, Traits>;
    };

    template <class Char, class Traits>
    struct TStringBufOfImpl<std::basic_string_view<Char, Traits>> {
        using type = std::basic_string_view<Char, Traits>;
    };

    /**
     * Metafunction that returns a string buffer for the given type. This is to
     * make sure that splitting `std::string` returns `std::string_view`.
     */
    template <class String>
    using TStringBufOf = typename TStringBufOfImpl<String>::type;

    template <class StringBuf, class Iterator>
    StringBuf DoMakeStringBuf(Iterator b, Iterator e, StringBuf*) {
        return StringBuf(b, e);
    }

    template <class Char, class Traits, class Iterator>
    std::basic_string_view<Char, Traits> DoMakeStringBuf(Iterator b, Iterator e, std::basic_string_view<Char, Traits>*) {
        return std::basic_string_view<Char, Traits>(b, e - b);
    }

    template <class StringBuf, class Iterator>
    StringBuf MakeStringBuf(Iterator b, Iterator e) {
        return DoMakeStringBuf(b, e, static_cast<StringBuf*>(nullptr));
    }

    template <class String>
    struct TIteratorOfImpl {
        using type = std::conditional_t<
            THasData<String>::value,
            const typename String::value_type*,
            typename String::const_iterator>;
    };

    template <class String>
    using TIteratorOf = typename TIteratorOfImpl<String>::type;

    template <class String>
    class TStringSplitter;

    template <class String>
    struct TIterState: public TStringBufOf<String> {
    public:
        using TStringBufType = TStringBufOf<String>;
        using TIterator = TIteratorOf<String>;
        friend class TStringSplitter<String>;

        TIterState(const String& string) noexcept
            : TStringBufType()
            , DelimiterEnd_(std::begin(string))
            , OriginEnd_(std::end(string))
        {
        }

        template <
            typename Other,
            typename = std::enable_if_t<
                std::is_convertible<Other, TStringBufType>::value>>
        bool operator==(const Other& toCompare) const {
            return TStringBufType(*this) == TStringBufType(toCompare);
        }

        TIterator TokenStart() const noexcept {
            return this->begin();
        }

        TIterator TokenDelim() const noexcept {
            return this->end();
        }

        TStringBufType Token() const noexcept {
            return *this;
        }

        TStringBufType Delim() const noexcept {
            return MakeStringBuf<TStringBufType>(TokenDelim(), DelimiterEnd_);
        }

    private:
        void UpdateParentBuf(TIterator tokenStart, TIterator tokenDelim) noexcept {
            *static_cast<TStringBufType*>(this) = MakeStringBuf<TStringBufType>(tokenStart, tokenDelim);
        }

        bool DelimiterIsEmpty() const noexcept {
            return TokenDelim() == DelimiterEnd_;
        }

    private:
        TIterator DelimiterEnd_;
        const TIterator OriginEnd_;
    };

    template <class Base>
    class TSplitRange: public Base, public TInputRangeAdaptor<TSplitRange<Base>> {
        using TStringBufType = decltype(std::declval<Base>().Next()->Token());

    public:
        template <typename... Args>
        inline TSplitRange(Args&&... args)
            : Base(std::forward<Args>(args)...)
        {
        }

        template <class Consumer, std::enable_if_t<std::is_same<decltype(std::declval<Consumer>()(std::declval<TStringBufType>())), void>::value, int>* = nullptr>
        inline void Consume(Consumer&& f) {
            for (auto&& it : *this) {
                f(it.Token());
            }
        }

        template <class Consumer, std::enable_if_t<std::is_same<decltype(std::declval<Consumer>()(std::declval<TStringBufType>())), bool>::value, int>* = nullptr>
        inline bool Consume(Consumer&& f) {
            for (auto&& it : *this) {
                if (!f(it.Token())) {
                    return false;
                }
            }
            return true;
        }

        template <class Container, class = std::enable_if_t<THasInsert<Container>::value || THasPushBack<Container>::value>>
        operator Container() {
            Container result;
            AddTo(&result);
            return result;
        }

        template <class S>
        inline TVector<S> ToList() {
            TVector<S> result;
            for (auto&& it : *this) {
                result.push_back(S(it.Token()));
            }
            return result;
        }

        template <class Container>
        inline void Collect(Container* c) {
            Y_ASSERT(c);
            c->clear();
            AddTo(c);
        }

        template <class Container>
        inline void AddTo(Container* c) {
            Y_ASSERT(c);
            TContainerConsumer<Container> consumer(c);
            Consume(consumer);
        }

        template <class Container>
        inline void ParseInto(Container* c) {
            Y_ASSERT(c);
            TContainerConvertingConsumer<Container> consumer(c);
            Consume(consumer);
        }

        // TODO: this is actually TryParseInto
        /**
         * Same as `CollectInto`, just doesn't throw.
         *
         * \param[out] args                 Output arguments.
         * \returns                         Whether parsing was successful.
         */
        template <typename... Args>
        inline bool TryCollectInto(Args*... args) noexcept {
            size_t successfullyFilled = 0;
            auto it = this->begin();

            //FIXME: actually, some kind of TryApplyToMany is needed in order to stop iteration upon first failure
            ApplyToMany([&](auto&& arg) {
                if (it != this->end()) {
                    if (TryDoFromString(it->Token(), arg)) {
                        ++successfullyFilled;
                    }
                    ++it;
                }
            }, args...);

            return successfullyFilled == sizeof...(args) && it == this->end();
        }

        // TODO: this is actually ParseInto
        /**
         * Splits and parses everything that's in this splitter into `args`.
         *
         * Example usage:
         * \code
         * int l, r;
         * StringSplitter("100*200").Split('*').CollectInto(&l, &r);
         * \endcode
         *
         * \param[out] args                 Output arguments.
         * \throws                          If not all items were parsed, or
         *                                  if there were too many items in the split.
         */
        template <typename... Args>
        inline void CollectInto(Args*... args) {
            Y_ENSURE(TryCollectInto<Args...>(args...));
        }

        inline size_t Count() const {
            size_t cnt = 0;
            for (auto&& it : *this) {
                Y_UNUSED(it);
                ++cnt;
            }
            return cnt;
        }
    };

    template <class String>
    class TStringSplitter {
        using TStringType = String;
        using TChar = typename TStringType::value_type;
        using TIteratorState = TIterState<TStringType>;
        using TStringBufType = typename TIteratorState::TStringBufType;
        using TIterator = typename TIteratorState::TIterator;

        /**
         * Base class for all split ranges that actually does the splitting.
         */
        template <class DelimStorage>
        struct TSplitRangeBase {
            template <class OtherString, class... Args>
            inline TSplitRangeBase(OtherString&& s, Args&&... args)
                : String_(std::forward<OtherString>(s))
                , State_(String_)
                , Delimiter_(std::forward<Args>(args)...)
            {
            }

            inline TIteratorState* Next() {
                if (State_.DelimiterIsEmpty()) {
                    return nullptr;
                }

                const auto tokenBegin = State_.DelimiterEnd_;
                const auto tokenEnd = Delimiter_.Ptr()->Find(State_.DelimiterEnd_, State_.OriginEnd_);
                State_.UpdateParentBuf(tokenBegin, tokenEnd);

                return &State_;
            }

        private:
            TStringType String_;
            TIteratorState State_;
            DelimStorage Delimiter_;
        };

        template <class Base, class Filter>
        struct TFilterRange: public Base {
            template <class... Args>
            inline TFilterRange(const Base& base, Args&&... args)
                : Base(base)
                , Filter_(std::forward<Args>(args)...)
            {
            }

            inline TIteratorState* Next() {
                TIteratorState* ret;

                do {
                    ret = Base::Next();
                } while (ret && !Filter_.Accept(ret));

                return ret;
            }

            Filter Filter_;
        };

        struct TNonEmptyFilter {
            template <class TToken>
            inline bool Accept(const TToken* token) noexcept {
                return !token->empty();
            }
        };

        template <class TIter>
        struct TStopIteration;

        template <class Base>
        struct TFilters: public Base {
            template <class TFilter>
            using TIt = TSplitRange<TStopIteration<TFilters<TFilterRange<Base, TFilter>>>>;

            template <typename... Args>
            inline TFilters(Args&&... args)
                : Base(std::forward<Args>(args)...)
            {
            }

            inline TIt<TNonEmptyFilter> SkipEmpty() const {
                return {*this};
            }
        };

        template <class Base, class Stopper>
        struct TStopRange: public Base {
            template <typename... Args>
            inline TStopRange(const Base& base, Args&&... args)
                : Base(base)
                , Stopper_(std::forward<Args>(args)...)
            {
            }

            inline TIteratorState* Next() {
                TIteratorState* ret = Base::Next();
                if (!ret || Stopper_.Stop(ret)) {
                    return nullptr;
                }
                return ret;
            }

            Stopper Stopper_;
        };

        struct TTake {
            TTake() = default;

            TTake(size_t count)
                : Count(count)
            {
            }

            template <class TToken>
            inline bool Stop(TToken*) noexcept {
                if (Count > 0) {
                    --Count;
                    return false;
                } else {
                    return true;
                }
            }

            size_t Count = 0;
        };

        struct TLimit {
            TLimit() = default;

            TLimit(size_t count)
                : Count(count)
            {
                Y_ASSERT(Count > 0);
            }

            template <class TToken>
            inline bool Stop(TToken* token) noexcept {
                if (Count > 1) {
                    --Count;
                    return false;
                } else if (Count == 1) {
                    token->DelimiterEnd_ = token->OriginEnd_;
                    token->UpdateParentBuf(token->TokenStart(), token->DelimiterEnd_);
                    return false;
                }
                return true;
            }

            size_t Count = 0;
        };

        template <class Base>
        struct TStopIteration: public Base {
            template <class TStopper>
            using TIt = TSplitRange<TStopIteration<TFilters<TStopRange<Base, TStopper>>>>;

            template <typename... Args>
            inline TStopIteration(Args&&... args)
                : Base(std::forward<Args>(args)...)
            {
            }

            inline TIt<TTake> Take(size_t count) {
                return {*this, count};
            }

            inline TIt<TLimit> Limit(size_t count) {
                return {*this, count};
            }
        };

        template <class TPolicy>
        using TIt = TSplitRange<TStopIteration<TFilters<TSplitRangeBase<TPolicy>>>>;

    public:
        template <class OtherString>
        explicit TStringSplitter(OtherString&& s)
            : String_(std::forward<OtherString>(s))
        {
        }

        //does not own TDelim
        template <class TDelim>
        inline TIt<TPtrPolicy<const TDelim>> Split(const TDelim& d) const noexcept {
            return {String_, &d};
        }

        inline TIt<TEmbedPolicy<TCharDelimiter<const TChar>>> Split(TChar ch) const noexcept {
            return {String_, ch};
        }

        inline TIt<TSimpleRefPolicy<TSetDelimiter<const TChar>>> SplitBySet(const TChar* set) const noexcept {
            return {String_, set};
        }

        inline TIt<TEmbedPolicy<TStringDelimiter<const TChar>>> SplitByString(const TStringBufType& str) const noexcept {
            return {String_, str.data(), str.size()};
        }

        template <class TFunc>
        inline TIt<TEmbedPolicy<TFuncDelimiter<TIterator, TFunc>>> SplitByFunc(TFunc f) const noexcept {
            return {String_, f};
        }

    private:
        TStringType String_;
    };

    template <class String>
    auto MakeStringSplitter(String&& s) {
        return TStringSplitter<std::remove_reference_t<String>>(std::forward<String>(s));
    }
}

template <class Iterator>
auto StringSplitter(Iterator begin, Iterator end) {
    return ::NStringSplitPrivate::MakeStringSplitter(TIteratorRange<Iterator>(begin, end));
}

template <class Char>
auto StringSplitter(const Char* begin, const Char* end) {
    return ::NStringSplitPrivate::MakeStringSplitter(TBasicStringBuf<Char>(begin, end));
}

template <class Char>
auto StringSplitter(const Char* begin, size_t len) {
    return ::NStringSplitPrivate::MakeStringSplitter(TBasicStringBuf<Char>(begin, len));
}

template <class Char>
auto StringSplitter(const Char* str) {
    return ::NStringSplitPrivate::MakeStringSplitter(TBasicStringBuf<Char>(str));
}

template <class String, std::enable_if_t<!std::is_pointer<std::remove_reference_t<String>>::value, int> = 0>
auto StringSplitter(String& s) {
    return ::NStringSplitPrivate::MakeStringSplitter(::NStringSplitPrivate::TStringBufOf<String>(s.data(), s.size()));
}

template <class String, std::enable_if_t<!std::is_pointer<std::remove_reference_t<String>>::value, int> = 0>
auto StringSplitter(String&& s) {
    return ::NStringSplitPrivate::MakeStringSplitter(std::move(s));
}