123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071 |
- #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;
- }
- } // namespace NStringSplitPrivate
- 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);
- } // namespace NSplitTargetHasPushBack
- 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() {
- 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));
- }
- } // namespace NStringSplitPrivate
- 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));
- }
|