diff options
author | Ben Longbons <b.r.longbons@gmail.com> | 2013-06-29 23:23:43 -0700 |
---|---|---|
committer | Ben Longbons <b.r.longbons@gmail.com> | 2013-08-01 15:19:45 -0700 |
commit | 3b98f3439e33b15bba2036c402f9925340fdb2b9 (patch) | |
tree | f6a59330bb747d9cc64f5f83d06e7f76dc01d540 /src/common/strings.hpp | |
parent | 8d1480c1be7c9741876d89008277a2b3629a4d01 (diff) | |
download | tmwa-3b98f3439e33b15bba2036c402f9925340fdb2b9.tar.gz tmwa-3b98f3439e33b15bba2036c402f9925340fdb2b9.tar.bz2 tmwa-3b98f3439e33b15bba2036c402f9925340fdb2b9.tar.xz tmwa-3b98f3439e33b15bba2036c402f9925340fdb2b9.zip |
Poison std::string and use the various string classes
Diffstat (limited to 'src/common/strings.hpp')
-rw-r--r-- | src/common/strings.hpp | 695 |
1 files changed, 589 insertions, 106 deletions
diff --git a/src/common/strings.hpp b/src/common/strings.hpp index 8562ec4..ead3f52 100644 --- a/src/common/strings.hpp +++ b/src/common/strings.hpp @@ -22,10 +22,17 @@ #include "sanity.hpp" #include <cassert> +#include <cstdarg> #include <cstring> +#include <algorithm> +#include <deque> #include <iterator> -#include <string> +#include <memory> +#include <type_traits> +#include <vector> + +#include "utils2.hpp" // It is a common mistake to assume that one string class for everything. // Because C++ and TMWA have a C legacy, there are a few more here @@ -64,13 +71,13 @@ namespace strings _iterator(const char *p=nullptr) : _ptr(p) {} // iterator - reference operator *() { return *_ptr; } + reference operator *() const { return *_ptr; } X& operator ++() { ++_ptr; return *this; } // equality comparable friend bool operator == (X l, X r) { return l._ptr == r._ptr; } // input iterator friend bool operator != (X l, X r) { return !(l == r); } - pointer operator->() { return _ptr; } + pointer operator->() const { return _ptr; } X operator++ (int) { X out = *this; ++*this; return out; } // forward iterator is mostly semantical, and the ctor is above // bidirectional iterator @@ -83,8 +90,8 @@ namespace strings X& operator -= (difference_type n) { _ptr -= n; return *this; } friend X operator - (X a, difference_type n) { return a -= n; } friend difference_type operator - (X b, X a) { return b._ptr - a._ptr; } - reference operator[](difference_type n) { return _ptr[n]; } - friend bool operator < (X a, X b) { return a._ptr - b._ptr; } + reference operator[](difference_type n) const { return _ptr[n]; } + friend bool operator < (X a, X b) { return a._ptr < b._ptr; } friend bool operator > (X a, X b) { return b < a; } friend bool operator >= (X a, X b) { return !(a < b); } friend bool operator <= (X a, X b) { return !(a > b); } @@ -92,7 +99,7 @@ namespace strings /// A helper class that implements all the interesting stuff that can /// be done on any constant string, in terms of .begin() and .end(). - template<class T> + template<class T, class O, class Z, class X=XString> class _crtp_string { public: @@ -108,22 +115,67 @@ namespace strings size_t size() const { return end() - begin(); } reverse_iterator rbegin() const { return reverse_iterator(end()); } reverse_iterator rend() const { return reverse_iterator(begin()); } - operator bool() { return size(); } - bool operator !() { return !size(); } + explicit + operator bool() const { return size(); } + bool operator !() const { return !size(); } char operator[](size_t i) const { return begin()[i]; } char front() const { return *begin(); } char back() const { return end()[-1]; } const char *data() { return &*begin(); } - XString xslice_t(size_t o) const; - XString xslice_h(size_t o) const; - XString xrslice_t(size_t no) const; - XString xrslice_h(size_t no) const; - XString xlslice(size_t o, size_t l) const; - XString xpslice(size_t b, size_t e) const; + Z xslice_t(size_t o) const; + X xslice_h(size_t o) const; + Z xrslice_t(size_t no) const; + X xrslice_h(size_t no) const; + Z xislice_t(iterator it) const; + X xislice_h(iterator it) const; + X xlslice(size_t o, size_t l) const; + X xpslice(size_t b, size_t e) const; + X xislice(iterator b, iterator e) const; + Z lstrip() const; + X rstrip() const; + X strip() const; + bool startswith(XString x) const; bool endswith(XString x) const; + bool startswith(char c) const; + bool endswith(char c) const; + + bool contains(char c) const; + bool contains_seq(XString s) const; + bool contains_any(XString s) const; + + bool has_print() const; + bool is_print() const; + __attribute__((deprecated)) + O to_print() const; + + bool is_graph() const; + bool has_graph() const; + + bool has_lower() const; + bool is_lower() const; + O to_lower() const; + + bool has_upper() const; + bool is_upper() const; + O to_upper() const; + + bool has_alpha() const; // equivalent to has_lower || has_upper + bool is_alpha() const; // NOT equivalent to is_lower || is_upper + + bool has_digit2() const; + bool is_digit2() const; + bool has_digit8() const; + bool is_digit8() const; + bool has_digit10() const; + bool is_digit10() const; + bool has_digit16() const; + bool is_digit16() const; + + bool has_alnum() const; // equivalent to has_alpha || has_digit10 + bool is_alnum() const; // NOT equivalent to is_alpha || is_digit10 }; @@ -134,53 +186,112 @@ namespace strings class MString { public: - typedef char *iterator; - typedef _iterator<MString> const_iterator; + typedef std::deque<char>::iterator iterator; + typedef std::deque<char>::const_iterator const_iterator; typedef std::reverse_iterator<iterator> reverse_iterator; typedef std::reverse_iterator<const_iterator> const_reverse_iterator; private: - std::string _hack; + std::deque<char> _hack; public: - template<size_t n> - MString(char (&s)[n]) = delete; - template<size_t n> - MString(const char (&s)[n]) : _hack(s) {} - template<class It> - MString(It b, It e) : _hack(b, e) {} - - iterator begin() { return &*_hack.begin(); } - iterator end() { return &*_hack.end(); } - const_iterator begin() const { return &*_hack.begin(); } - const_iterator end() const { return &*_hack.end(); } + iterator begin() { return _hack.begin(); } + iterator end() { return _hack.end(); } + const_iterator begin() const { return _hack.begin(); } + const_iterator end() const { return _hack.end(); } reverse_iterator rbegin() { return reverse_iterator(end()); } reverse_iterator rend() { return reverse_iterator(begin()); } const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } + + size_t size() const { return _hack.size(); } + explicit + operator bool() const { return size(); } + bool operator !() const { return !size(); } + + MString& operator += (MString rhs) + { + _hack.insert(_hack.end(), rhs.begin(), rhs.end()); + return *this; + } + MString& operator += (char c) + { + _hack.push_back(c); + return *this; + } + MString& operator += (XString xs); + + void pop_back(size_t n=1) + { + while (n--) + _hack.pop_back(); + } + char& front() + { + return _hack.front(); + } + char& back() + { + return _hack.back(); + } }; /// An owning string that has reached its final contents. /// The storage is NUL-terminated /// TODO implement a special one, that guarantees refcounting. - class FString : public _crtp_string<FString> + class FString : public _crtp_string<FString, FString, ZString, XString> { - /*const*/ std::string _hack; + std::shared_ptr<std::vector<char>> _hack2; + + template<class It> + void _assign(It b, It e) + { + if (b == e) + { + // TODO use a special empty object + // return; + } + if (!std::is_base_of<std::forward_iterator_tag, typename std::iterator_traits<It>::iterator_category>::value) + { + // can't use std::distance + _hack2 = std::make_shared<std::vector<char>>(); + for (; b != e; ++b) + _hack2->push_back(*b); + _hack2->push_back('\0'); + _hack2->shrink_to_fit(); + } + size_t diff = std::distance(b, e); + _hack2 = std::make_shared<std::vector<char>>(diff + 1, '\0'); + std::copy(b, e, _hack2->begin()); + } public: -#ifndef __clang__ - __attribute__((warning("This should be removed in the next diff"))) -#endif - FString(std::string s) : _hack(std::move(s)) {} + FString() + { + const char *sadness = ""; + _assign(sadness, sadness); + } + + explicit FString(const MString& s) + { + _assign(s.begin(), s.end()); + } - FString() : _hack() {} - FString(const MString& s) : _hack(s.begin(), s.end()) {} template<size_t n> FString(char (&s)[n]) = delete; + template<size_t n> - FString(const char (&s)[n]) : _hack(s) {} + FString(const char (&s)[n]) + { + _assign(s, s + strlen(s)); + } + template<class It> - FString(It b, It e) : _hack(b, e) {} + FString(It b, It e) + { + _assign(b, e); + } + - iterator begin() const { return &*_hack.begin(); } - iterator end() const { return &*_hack.end(); } + iterator begin() const { return &_hack2->begin()[0]; } + iterator end() const { return &_hack2->end()[-1]; } const FString *base() const { return this; } const char *c_str() const { return &*begin(); } @@ -188,13 +299,16 @@ namespace strings SString oslice_h(size_t o) const; TString orslice_t(size_t no) const; SString orslice_h(size_t no) const; + TString oislice_t(iterator it) const; + SString oislice_h(iterator it) const; SString olslice(size_t o, size_t l) const; SString opslice(size_t b, size_t e) const; + SString oislice(iterator b, iterator e) const; }; /// An owning string that represents a tail slice of an FString. /// Guaranteed to be NUL-terminated. - class TString : public _crtp_string<TString> + class TString : public _crtp_string<TString, TString, ZString, XString> { friend class SString; FString _s; @@ -206,6 +320,8 @@ namespace strings TString(char (&s)[n]) = delete; template<size_t n> TString(const char (&s)[n]) : _s(s), _o(0) {} + //template<class It> + //TString(It b, It e) : _s(b, e), _o(0) {} iterator begin() const { return &_s.begin()[_o]; } iterator end() const { return &*_s.end(); } @@ -216,28 +332,32 @@ namespace strings SString oslice_h(size_t o) const; TString orslice_t(size_t no) const; SString orslice_h(size_t no) const; + TString oislice_t(iterator it) const; + SString oislice_h(iterator it) const; SString olslice(size_t o, size_t l) const; SString opslice(size_t b, size_t e) const; + SString oislice(iterator b, iterator e) const; - operator FString() + operator FString() const { if (_o) return FString(begin(), end()); else return _s; } }; /// An owning string that represents a arbitrary slice of an FString. /// Not guaranteed to be NUL-terminated. - class SString : public _crtp_string<SString> + class SString : public _crtp_string<SString, SString, XString, XString> { FString _s; size_t _b, _e; public: SString() : _s(), _b(), _e() {} SString(FString f) : _s(std::move(f)), _b(), _e(_s.size()) {} - SString(TString t) : _s(t._s), _e(_s.size()) {} + SString(TString t) : _s(t._s), _b(0), _e(_s.size()) {} template<size_t n> SString(char (&s)[n]) = delete; template<size_t n> SString(const char (&s)[n]) : _s(s), _b(0), _e(_s.size()) {} - + //template<class It> + //SString(It b, It e) : _s(b, e), _b(0), _e(_s.size()) {} SString(FString f, size_t b, size_t e) : _s(std::move(f)), _b(b), _e(e) {} iterator begin() const { return &_s.begin()[_b]; } @@ -248,34 +368,34 @@ namespace strings SString oslice_h(size_t o) const; SString orslice_t(size_t no) const; SString orslice_h(size_t no) const; + SString oislice_t(iterator it) const; + SString oislice_h(iterator it) const; SString olslice(size_t o, size_t l) const; SString opslice(size_t b, size_t e) const; + SString oislice(iterator b, iterator e) const; - operator FString() + operator FString() const { if (_b == 0 && _e == _s.size()) return _s; else return FString(begin(), end()); } - operator TString() + operator TString() const { if (_e == _s.size()) return TString(_s, _b); else return FString(begin(), end()); } }; /// A non-owning string that is guaranteed to be NUL-terminated. /// This should be only used as a parameter. - class ZString : public _crtp_string<ZString> + class ZString : public _crtp_string<ZString, FString, ZString, XString> { iterator _b, _e; // optional const FString *_base; public: -#ifndef __clang__ - __attribute__((warning("This should be removed in the next diff"))) -#endif - ZString(const std::string& s) : _b(&*s.begin()), _e(&*s.end()), _base(nullptr) {} - enum { really_construct_from_a_pointer }; ZString() { *this = ZString(""); } // no MString ZString(const FString& s) : _b(&*s.begin()), _e(&*s.end()), _base(s.base()) {} ZString(const TString& s) : _b(&*s.begin()), _e(&*s.end()), _base(s.base()) {} ZString(const SString&) = delete; + // dangerous + ZString(const char *b, const char *e, const FString *base_) : _b(b), _e(e), _base(base_) {} ZString(decltype(really_construct_from_a_pointer), const char *s, const FString *base_) : _b(s), _e(s + strlen(s)), _base(base_) {} template<size_t n> ZString(char (&s)[n]) = delete; @@ -291,27 +411,30 @@ namespace strings XString oslice_h(size_t o) const; ZString orslice_t(size_t no) const; XString orslice_h(size_t no) const; + ZString oislice_t(iterator it) const; + XString oislice_h(iterator it) const; XString olslice(size_t o, size_t l) const; XString opslice(size_t b, size_t e) const; + XString oislice(iterator b, iterator e) const; - operator FString() + operator FString() const { if (_base) return SString(*_base, &*_b - &*_base->begin(), &*_e - &*_base->begin()); else return FString(_b, _e); } - operator TString() + operator TString() const { if (_base) return SString(*_base, &*_b - &*_base->begin(), &*_e - &*_base->begin()); else return FString(_b, _e); } - operator SString() + operator SString() const { if (_base) return SString(*_base, &*_b - &*_base->begin(), &*_e - &*_base->begin()); else return FString(_b, _e); } }; /// A non-owning string that is not guaranteed to be NUL-terminated. /// This should be only used as a parameter. - class XString : public _crtp_string<XString> + class XString : public _crtp_string<XString, FString, XString, XString> { iterator _b, _e; // optional const FString *_base; public: // do I really want this? - XString() : _b(nullptr), _e(nullptr) {} + XString() : _b(""), _e(_b), _base() {} XString(std::nullptr_t) = delete; // no MString XString(const FString& s) : _b(&*s.begin()), _e(&*s.end()), _base(s.base()) {} @@ -331,32 +454,34 @@ namespace strings iterator begin() const { return _b; } iterator end() const { return _e; } - const FString *base() const { return _base; }; + const FString *base() const { return _base; } XString oslice_t(size_t o) const { return xslice_t(o); } XString oslice_h(size_t o) const { return xslice_h(o); } XString orslice_t(size_t no) const { return xrslice_t(no); } XString orslice_h(size_t no) const { return xrslice_h(no); } + XString oislice_t(iterator it) const { return xislice_t(it); } + XString oislice_h(iterator it) const { return xislice_h(it); } XString olslice(size_t o, size_t l) const { return xlslice(o, l); } XString opslice(size_t b, size_t e) const { return xpslice(b, e); } + XString oislice(iterator b, iterator e) const { return xislice(b, e); } - operator FString() + operator FString() const { if (_base) return SString(*_base, &*_b - &*_base->begin(), &*_e - &*_base->begin()); else return FString(_b, _e); } - operator TString() + operator TString() const { if (_base) return SString(*_base, &*_b - &*_base->begin(), &*_e - &*_base->begin()); else return FString(_b, _e); } - operator SString() + operator SString() const { if (_base) return SString(*_base, &*_b - &*_base->begin(), &*_e - &*_base->begin()); else return FString(_b, _e); } - operator ZString() = delete; + operator ZString() const = delete; }; template<uint8_t n> - class VString : public _crtp_string<VString<n>> + class VString : public _crtp_string<VString<n>, VString<n>, ZString, XString> { char _data[n]; unsigned char _special; - typedef typename _crtp_string<VString<n>>::iterator iterator; public: - static_assert(n & 1, "Size should probably be odd."); + typedef typename _crtp_string<VString<n>, VString<n>, ZString, XString>::iterator iterator; VString(XString x) : _data(), _special() { if (x.size() > n) @@ -397,71 +522,181 @@ namespace strings { *this = XString(e, s, nullptr); } + VString(char c) + { + *this = XString(&c, &c + 1, nullptr); + } VString() { *this = XString(); } + // hopefully this is obvious iterator begin() const { return std::begin(_data); } iterator end() const { return std::end(_data) - _special; } - const FString *base() const { return nullptr; }; + const FString *base() const { return nullptr; } const char *c_str() const { return &*begin(); } VString oslice_t(size_t o) const { return this->xslice_t(o); } VString oslice_h(size_t o) const { return this->xslice_h(o); } VString orslice_t(size_t no) const { return this->xrslice_t(no); } VString orslice_h(size_t no) const { return this->xrslice_h(no); } + VString oislice_t(iterator it) const { return this->xislice_t(it); } + VString oislice_h(iterator it) const { return this->xislice_h(it); } VString olslice(size_t o, size_t l) const { return this->xlslice(o, l); } VString opslice(size_t b, size_t e) const { return this->xpslice(b, e); } + VString oislice(iterator b, iterator e) const { return this->xislice(b, e); } operator FString() const { return FString(begin(), end()); } operator TString() const { return FString(begin(), end()); } operator SString() const { return FString(begin(), end()); } operator ZString() const { return ZString(_data); } operator XString() const { return XString(&*begin(), &*end(), nullptr); } - }; + template<uint8_t m> + operator VString<m>() const + { + static_assert(m > n, "can only grow"); + XString x = *this; + return VString<m>(XString(x)); + } + }; // not really intended for public use inline int xstr_compare(XString l, XString r) { - return std::lexicographical_compare( + bool less = std::lexicographical_compare( l.begin(), l.end(), r.begin(), r.end()); + bool greater = std::lexicographical_compare( + r.begin(), r.end(), + l.begin(), l.end()); + return greater - less; } + template<class L, class R> - bool operator == (const L& l, const R& r) + class string_comparison_allowed + { + constexpr static bool l_is_vstring_exact = std::is_same<VString<sizeof(L) - 1>, L>::value; + constexpr static bool l_is_vstring_approx = std::is_base_of<VString<sizeof(L) - 1>, L>::value; + constexpr static bool r_is_vstring_exact = std::is_same<VString<sizeof(R) - 1>, R>::value; + constexpr static bool r_is_vstring_approx = std::is_base_of<VString<sizeof(R) - 1>, R>::value; + + constexpr static bool l_is_restricted = l_is_vstring_approx && !l_is_vstring_exact; + constexpr static bool r_is_restricted = r_is_vstring_approx && !r_is_vstring_exact; + public: + constexpr static bool value = std::is_same<L, R>::value || (!l_is_restricted && !r_is_restricted); + }; + + struct _test : VString<1> {}; + struct _test2 : VString<1> {}; + + static_assert(string_comparison_allowed<_test, _test>::value, "tt"); + static_assert(string_comparison_allowed<VString<1>, VString<1>>::value, "vv"); + static_assert(!string_comparison_allowed<_test, XString>::value, "tx"); + static_assert(!string_comparison_allowed<_test, VString<1>>::value, "tv"); + static_assert(!string_comparison_allowed<_test, _test2>::value, "t2"); + static_assert(string_comparison_allowed<VString<1>, XString>::value, "vx"); + static_assert(string_comparison_allowed<XString, XString>::value, "xx"); + static_assert(string_comparison_allowed<XString, FString>::value, "xf"); + + template<class L, class R, typename=typename std::enable_if<string_comparison_allowed<L, R>::value>::type> + auto operator == (const L& l, const R& r) -> decltype((xstr_compare(l, r), true)) { return xstr_compare(l, r) == 0; } - template<class L, class R> - bool operator != (const L& l, const R& r) + template<class L, class R, typename=typename std::enable_if<string_comparison_allowed<L, R>::value>::type> + auto operator != (const L& l, const R& r) -> decltype((xstr_compare(l, r), true)) { return xstr_compare(l, r) != 0; } - template<class L, class R> - bool operator < (const L& l, const R& r) + template<class L, class R, typename=typename std::enable_if<string_comparison_allowed<L, R>::value>::type> + auto operator < (const L& l, const R& r) -> decltype((xstr_compare(l, r), true)) { return xstr_compare(l, r) < 0; } - template<class L, class R> - bool operator <= (const L& l, const R& r) + template<class L, class R, typename=typename std::enable_if<string_comparison_allowed<L, R>::value>::type> + auto operator <= (const L& l, const R& r) -> decltype((xstr_compare(l, r), true)) { return xstr_compare(l, r) <= 0; } - template<class L, class R> - bool operator > (const L& l, const R& r) + template<class L, class R, typename=typename std::enable_if<string_comparison_allowed<L, R>::value>::type> + auto operator > (const L& l, const R& r) -> decltype((xstr_compare(l, r), true)) { return xstr_compare(l, r) > 0; } - template<class L, class R> - bool operator >= (const L& l, const R& r) + template<class L, class R, typename=typename std::enable_if<string_comparison_allowed<L, R>::value>::type> + auto operator >= (const L& l, const R& r) -> decltype((xstr_compare(l, r), true)) { return xstr_compare(l, r) >= 0; } + namespace detail + { + constexpr + bool is_print(char c) + { + return ' ' <= c && c <= '~'; + } + constexpr + bool is_graph(char c) + { + return is_print(c) && c != ' '; + } + constexpr + bool is_lower(char c) + { + return 'a' <= c && c <= 'z'; + } + constexpr + bool is_upper(char c) + { + return 'A' <= c && c <= 'Z'; + } + constexpr + bool is_alpha(char c) + { + return is_lower(c) || is_upper(c); + } + constexpr + bool is_digit2(char c) + { + return '0' <= c && c <= '1'; + } + constexpr + bool is_digit8(char c) + { + return '0' <= c && c <= '7'; + } + constexpr + bool is_digit10(char c) + { + return '0' <= c && c <= '9'; + } + constexpr + bool is_digit16(char c) + { + return ('0' <= c && c <= '9') || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f'); + } + constexpr + bool is_alnum(char c) + { + return is_alpha(c) || is_digit10(c); + } + + constexpr + char to_lower(char c) + { + return is_upper(c) ? c | ' ' : c; + } + constexpr + char to_upper(char c) + { + return is_lower(c) ? c & ~' ' : c; + } + } // sadness typedef MString MS; @@ -472,30 +707,176 @@ namespace strings typedef XString XS; // _crtp_string - template<class T> - XS _crtp_string<T>::xslice_t(size_t o) const - { return XS(&begin()[o], &*end(), base()); } - template<class T> - XS _crtp_string<T>::xslice_h(size_t o) const - { return XS(&*begin(), &begin()[o], base()); } - template<class T> - XS _crtp_string<T>::xrslice_t(size_t no) const - { return XS(&end()[-no], &*end(), base()); } - template<class T> - XS _crtp_string<T>::xrslice_h(size_t no) const - { return XS(&*begin(), &end()[-no], base()); } - template<class T> - XS _crtp_string<T>::xlslice(size_t o, size_t l) const - { return XS(&begin()[o], &begin()[o + l], base()); } - template<class T> - XS _crtp_string<T>::xpslice(size_t b, size_t e) const - { return XS(&begin()[b], &begin()[e], base()); } - template<class T> - bool _crtp_string<T>::startswith(XS x) const - { return size() > x.size() && xslice_h(x.size()) == x; } - template<class T> - bool _crtp_string<T>::endswith(XS x) const + template<class T, class O, class Z, class X> + Z _crtp_string<T, O, Z, X>::xslice_t(size_t o) const + { return Z(&begin()[o], &*end(), base()); } + template<class T, class O, class Z, class X> + X _crtp_string<T, O, Z, X>::xslice_h(size_t o) const + { return X(&*begin(), &begin()[o], base()); } + template<class T, class O, class Z, class X> + Z _crtp_string<T, O, Z, X>::xrslice_t(size_t no) const + { return Z(&end()[-no], &*end(), base()); } + template<class T, class O, class Z, class X> + X _crtp_string<T, O, Z, X>::xrslice_h(size_t no) const + { return X(&*begin(), &end()[-no], base()); } + template<class T, class O, class Z, class X> + Z _crtp_string<T, O, Z, X>::xislice_t(iterator it) const + { return Z(&*it, &*end(), base()); } + template<class T, class O, class Z, class X> + X _crtp_string<T, O, Z, X>::xislice_h(iterator it) const + { return X(&*begin(), &*it, base()); } + template<class T, class O, class Z, class X> + X _crtp_string<T, O, Z, X>::xlslice(size_t o, size_t l) const + { return X(&begin()[o], &begin()[o + l], base()); } + template<class T, class O, class Z, class X> + X _crtp_string<T, O, Z, X>::xpslice(size_t b, size_t e) const + { return X(&begin()[b], &begin()[e], base()); } + template<class T, class O, class Z, class X> + X _crtp_string<T, O, Z, X>::xislice(iterator b, iterator e) const + { return X(&*b, &*e, base()); } + template<class T, class O, class Z, class X> + Z _crtp_string<T, O, Z, X>::lstrip() const + { + Z z = _ref(); + while (z.startswith(' ')) + z = z.xslice_t(1); + return z; + } + template<class T, class O, class Z, class X> + X _crtp_string<T, O, Z, X>::rstrip() const + { + X x = _ref(); + while (x.endswith(' ')) + x = x.xrslice_h(1); + return x; + } + template<class T, class O, class Z, class X> + X _crtp_string<T, O, Z, X>::strip() const + { return lstrip().rstrip(); } + + template<class T, class O, class Z, class X> + bool _crtp_string<T, O, Z, X>::startswith(XS x) const + { return size() >= x.size() && xslice_h(x.size()) == x; } + template<class T, class O, class Z, class X> + bool _crtp_string<T, O, Z, X>::endswith(XS x) const { return size() > x.size() && xrslice_t(x.size()) == x; } + template<class T, class O, class Z, class X> + bool _crtp_string<T, O, Z, X>::startswith(char c) const + { return size() && front() == c; } + template<class T, class O, class Z, class X> + bool _crtp_string<T, O, Z, X>::endswith(char c) const + { return size() && back() == c; } + template<class T, class O, class Z, class X> + bool _crtp_string<T, O, Z, X>::contains(char c) const + { return std::find(begin(), end(), c) != end(); } + template<class T, class O, class Z, class X> + bool _crtp_string<T, O, Z, X>::contains_seq(XString s) const + { return std::search(begin(), end(), s.begin(), s.end()) != end(); } + template<class T, class O, class Z, class X> + bool _crtp_string<T, O, Z, X>::contains_any(XString s) const + { return std::find_if(begin(), end(), [s](char c) { return s.contains(c); }) != end(); } + + template<class T, class O, class Z, class X> + bool _crtp_string<T, O, Z, X>::has_print() const + { return std::find_if(begin(), end(), detail::is_print) != end(); } + template<class T, class O, class Z, class X> + bool _crtp_string<T, O, Z, X>::is_print() const + { return std::find_if_not(begin(), end(), detail::is_print) == end(); } + template<class T, class O, class Z, class X> + O _crtp_string<T, O, Z, X>::to_print() const + { + if (is_print()) return _ref(); + char buf[size()]; + char *const b = buf; + char *const e = std::transform(begin(), end(), b, [](char c) { return detail::is_print(c) ? c : '_'; }); + return XString(b, e, nullptr); + } + + template<class T, class O, class Z, class X> + bool _crtp_string<T, O, Z, X>::has_graph() const + { return std::find_if(begin(), end(), detail::is_graph) != end(); } + template<class T, class O, class Z, class X> + bool _crtp_string<T, O, Z, X>::is_graph() const + { return std::find_if_not(begin(), end(), detail::is_graph) == end(); } + + template<class T, class O, class Z, class X> + bool _crtp_string<T, O, Z, X>::has_lower() const + { return std::find_if(begin(), end(), detail::is_lower) != end(); } + template<class T, class O, class Z, class X> + bool _crtp_string<T, O, Z, X>::is_lower() const + { return std::find_if_not(begin(), end(), detail::is_lower) == end(); } + template<class T, class O, class Z, class X> + O _crtp_string<T, O, Z, X>::to_lower() const + { + if (!has_upper()) return _ref(); + char buf[size()]; + char *const b = buf; + char *const e = std::transform(begin(), end(), b, detail::to_lower); + return XString(b, e, nullptr); + } + + template<class T, class O, class Z, class X> + bool _crtp_string<T, O, Z, X>::has_upper() const + { return std::find_if(begin(), end(), detail::is_upper) != end(); } + template<class T, class O, class Z, class X> + bool _crtp_string<T, O, Z, X>::is_upper() const + { return std::find_if_not(begin(), end(), detail::is_upper) == end(); } + template<class T, class O, class Z, class X> + O _crtp_string<T, O, Z, X>::to_upper() const + { + if (!has_lower()) return _ref(); + char buf[size()]; + char *const b = buf; + char *const e = std::transform(begin(), end(), b, detail::to_upper); + return XString(b, e, nullptr); + } + + template<class T, class O, class Z, class X> + bool _crtp_string<T, O, Z, X>::has_alpha() const + { return std::find_if(begin(), end(), detail::is_alpha) != end(); } + template<class T, class O, class Z, class X> + bool _crtp_string<T, O, Z, X>::is_alpha() const + { return std::find_if_not(begin(), end(), detail::is_alpha) == end(); } + + template<class T, class O, class Z, class X> + bool _crtp_string<T, O, Z, X>::has_digit2() const + { return std::find_if(begin(), end(), detail::is_digit2) != end(); } + template<class T, class O, class Z, class X> + bool _crtp_string<T, O, Z, X>::is_digit2() const + { return std::find_if_not(begin(), end(), detail::is_digit2) == end(); } + template<class T, class O, class Z, class X> + bool _crtp_string<T, O, Z, X>::has_digit8() const + { return std::find_if(begin(), end(), detail::is_digit8) != end(); } + template<class T, class O, class Z, class X> + bool _crtp_string<T, O, Z, X>::is_digit8() const + { return std::find_if_not(begin(), end(), detail::is_digit8) == end(); } + template<class T, class O, class Z, class X> + bool _crtp_string<T, O, Z, X>::has_digit10() const + { return std::find_if(begin(), end(), detail::is_digit10) != end(); } + template<class T, class O, class Z, class X> + bool _crtp_string<T, O, Z, X>::is_digit10() const + { return std::find_if_not(begin(), end(), detail::is_digit10) == end(); } + template<class T, class O, class Z, class X> + bool _crtp_string<T, O, Z, X>::has_digit16() const + { return std::find_if(begin(), end(), detail::is_digit16) != end(); } + template<class T, class O, class Z, class X> + bool _crtp_string<T, O, Z, X>::is_digit16() const + { return std::find_if_not(begin(), end(), detail::is_digit16) == end(); } + + template<class T, class O, class Z, class X> + bool _crtp_string<T, O, Z, X>::has_alnum() const + { return std::find_if(begin(), end(), detail::is_alnum) != end(); } + template<class T, class O, class Z, class X> + bool _crtp_string<T, O, Z, X>::is_alnum() const + { return std::find_if_not(begin(), end(), detail::is_alnum) == end(); } + + // MString + inline + MS& MS::operator += (XS x) + { + _hack.insert(_hack.end(), x.begin(), x.end()); + return *this; + } // FString inline @@ -511,11 +892,20 @@ namespace strings SS FS::orslice_h(size_t no) const { return SS(*this, 0, size() - no); } inline + TS FS::oislice_t(iterator it) const + { return TS(*this, it - begin()); } + inline + SS FS::oislice_h(iterator it) const + { return SS(*this, 0, it - begin()); } + inline SS FS::olslice(size_t o, size_t l) const { return SS(*this, o, o + l); } inline SS FS::opslice(size_t b, size_t e) const { return SS(*this, b, e); } + inline + SS FS::oislice(iterator b, iterator e) const + { return SS(*this, b - begin(), e - begin()); } // TString inline @@ -531,11 +921,20 @@ namespace strings SS TS::orslice_h(size_t no) const { return SS(_s, _o, _s.size() - no); } inline + TS TS::oislice_t(iterator it) const + { return TS(_s, _o + it - begin()); } + inline + SS TS::oislice_h(iterator it) const + { return SS(_s, _o, _o + it - begin()); } + inline SS TS::olslice(size_t o, size_t l) const { return SS(_s, _o + o, _o + o + l); } inline SS TS::opslice(size_t b, size_t e) const { return SS(_s, _o + b, _o + e); } + inline + SS TS::oislice(iterator b, iterator e) const + { return SS(_s, _o + b - begin(), _o + e - begin()); } // SString inline @@ -551,11 +950,20 @@ namespace strings SS SS::orslice_h(size_t no) const { return SS(_s, _b, _e - no); } inline + SS SS::oislice_t(iterator it) const + { return SS(_s, _b + it - begin(), _e); } + inline + SS SS::oislice_h(iterator it) const + { return SS(_s, _b, _b + it - begin()); } + inline SS SS::olslice(size_t o, size_t l) const { return SS(_s, _b + o, _b + o + l); } inline SS SS::opslice(size_t b, size_t e) const { return SS(_s, _b + b, _b + e); } + inline + SS SS::oislice(iterator b, iterator e) const + { return SS(_s, _b + b - begin(), _b + e - begin()); } // ZString inline @@ -571,22 +979,97 @@ namespace strings XS ZS::orslice_h(size_t no) const { return XS(&*begin(), &end()[-no], base()); } inline + ZS ZS::oislice_t(iterator it) const + { return ZS(really_construct_from_a_pointer, &*it, base()); } + inline + XS ZS::oislice_h(iterator it) const + { return XS(&*begin(), &*it, base()); } + inline XS ZS::olslice(size_t o, size_t l) const { return XS(&begin()[o], &begin()[o + l], base()); } inline XS ZS::opslice(size_t b, size_t e) const { return XS(&begin()[b], &begin()[e], base()); } + inline + XS ZS::oislice(iterator b, iterator e) const + { return XS(&*b, &*e, base()); } // cxxstdio helpers // I think the conversion will happen automatically. TODO test this. // Nope, it doesn't, since there's a template + // Actually, it might now. + inline + const char *decay_for_printf(const FString& fs) { return fs.c_str(); } inline - const char *convert_for_printf(const FString& fs) { return fs.c_str(); } + const char *decay_for_printf(const TString& ts) { return ts.c_str(); } inline - const char *convert_for_printf(const TString& ts) { return ts.c_str(); } + const char *decay_for_printf(const ZString& zs) { return zs.c_str(); } + template<uint8_t n> inline - const char *convert_for_printf(const ZString& zs) { return zs.c_str(); } + const char *decay_for_printf(const VString<n>& vs) { return vs.c_str(); } + + template<uint8_t len> + inline __attribute__((format(printf, 2, 0))) + int do_vprint(VString<len>& out, const char *fmt, va_list ap) + { + char buffer[len + 1]; + vsnprintf(buffer, len + 1, fmt, ap); + + out = const_(buffer); + return len; + } + + inline __attribute__((format(printf, 2, 0))) + int do_vprint(FString& out, const char *fmt, va_list ap) + { + int len; + { + va_list ap2; + va_copy(ap2, ap); + len = vsnprintf(nullptr, 0, fmt, ap2); + va_end(ap2); + } + char buffer[len + 1]; + vsnprintf(buffer, len + 1, fmt, ap); + + out = FString(buffer, buffer + len); + return len; + } + + inline __attribute__((format(scanf, 2, 0))) + int do_vscan(ZString in, const char *fmt, va_list ap) + { + return vsscanf(in.c_str(), fmt, ap); + } + + class StringConverter + { + FString& out; + char *mid; + public: + StringConverter(FString& s) + : out(s), mid(nullptr) + {} + ~StringConverter() + { + if (mid) + { + out = ZString(ZString::really_construct_from_a_pointer, mid, nullptr); + free(mid); + } + } + char **operator &() + { + return ∣ + } + }; + + inline + StringConverter convert_for_scanf(FString& s) + { + return StringConverter(s); + } } // namespace strings // owning |