12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103 |
- #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));
- }
|