1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105 |
- #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>;
- template <typename S = String, std::enable_if_t<THasData<S>::value, int> = 0>
- TIterState(const String& string) noexcept
- : TStringBufType()
- , DelimiterEnd_(string.data())
- , OriginEnd_(string.data() + string.size())
- {
- }
- template <typename S = String, std::enable_if_t<!THasData<S>::value, int> = 0>
- 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_;
- }
- void MarkExhausted() noexcept {
- UpdateParentBuf(OriginEnd_, OriginEnd_);
- DelimiterEnd_ = OriginEnd_;
- }
- 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)...)
- {
- }
- TSplitRangeBase(const TSplitRangeBase& other)
- : String_(other.String_)
- , State_(String_)
- , Delimiter_(other.Delimiter_)
- {
- }
- TSplitRangeBase(TSplitRangeBase&& other)
- : String_(std::move(other.String_))
- , State_(String_)
- , Delimiter_(std::move(other.Delimiter_))
- {
- other.State_.MarkExhausted();
- }
- TSplitRangeBase& operator=(const TSplitRangeBase& other) = delete;
- TSplitRangeBase& operator=(TSplitRangeBase&& other) = delete;
- 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));
- }
|