#ifndef TMWA_COMMON_STRINGS_HPP #define TMWA_COMMON_STRINGS_HPP // strings.hpp - All the string classes you'll ever need. // // Copyright © 2013 Ben Longbons // // This file is part of The Mana World (Athena server) // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #include "sanity.hpp" #include #include #include #include #include #include #include #include #include #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 // than would probably be necessary in an ideal language. namespace strings { // owning class MString; class FString; class TString; // C legacy version of SString class SString; // is this one really worth it? // non-owning class ZString; // C legacy version of XString class XString; // semi-owning template class VString; // simple pointer-wrapping iterator that can be used to get distinct // types for different containers. template class _iterator { typedef _iterator X; const char *_ptr; public: typedef ptrdiff_t difference_type; typedef char value_type; typedef const char *pointer; typedef const char& reference; typedef std::random_access_iterator_tag iterator_category; _iterator(const char *p=nullptr) : _ptr(p) {} // iterator 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->() const { return _ptr; } X operator++ (int) { X out = *this; ++*this; return out; } // forward iterator is mostly semantical, and the ctor is above // bidirectional iterator X& operator --() { --_ptr; return *this; } X operator-- (int) { X out = *this; --*this; return out; } // random access iterator X& operator += (difference_type n) { _ptr += n; return *this; } friend X operator + (X a, difference_type n) { return a += n; } friend X operator + (difference_type n, X a) { return a += n; } 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) 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); } }; /// 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 _crtp_string { public: // this will have to be changed if MString decides to join in. typedef _iterator iterator; typedef std::reverse_iterator reverse_iterator; private: const T& _ref() const { return static_cast(*this); } iterator begin() const { return _ref().begin(); } iterator end() const { return _ref().end(); } const FString *base() const { return _ref().base(); } public: size_t size() const { return end() - begin(); } reverse_iterator rbegin() const { return reverse_iterator(end()); } reverse_iterator rend() const { return reverse_iterator(begin()); } 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(); } 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 }; /// An owning string that is still expected to change. /// The storage might not be contiguous, but it still offers /// random-access iterators. /// TODO implement a special one, to avoid quirks of std::string. class MString { public: typedef std::deque::iterator iterator; typedef std::deque::const_iterator const_iterator; typedef std::reverse_iterator reverse_iterator; typedef std::reverse_iterator const_reverse_iterator; private: std::deque _hack; public: 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 { std::shared_ptr> _hack2; template void _assign(It b, It e) { if (b == e) { // TODO use a special empty object // return; } if (!std::is_base_of::iterator_category>::value) { // can't use std::distance _hack2 = std::make_shared>(); 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>(diff + 1, '\0'); std::copy(b, e, _hack2->begin()); } public: FString() { const char *sadness = ""; _assign(sadness, sadness); } explicit FString(const MString& s) { _assign(s.begin(), s.end()); } template FString(char (&s)[n]) = delete; template FString(const char (&s)[n]) { _assign(s, s + strlen(s)); } template FString(It b, It e) { _assign(b, e); } 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(); } TString oslice_t(size_t o) const; 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 { friend class SString; FString _s; size_t _o; public: TString() : _s(), _o() {} TString(FString b, size_t i=0) : _s(std::move(b)), _o(i) {} template TString(char (&s)[n]) = delete; template TString(const char (&s)[n]) : _s(s), _o(0) {} //template //TString(It b, It e) : _s(b, e), _o(0) {} iterator begin() const { return &_s.begin()[_o]; } iterator end() const { return &*_s.end(); } const FString *base() const { return &_s; } const char *c_str() const { return &*begin(); } TString oslice_t(size_t o) const; 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() 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 { 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), _b(0), _e(_s.size()) {} template SString(char (&s)[n]) = delete; template SString(const char (&s)[n]) : _s(s), _b(0), _e(_s.size()) {} //template //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]; } iterator end() const { return &_s.begin()[_e]; } const FString *base() const { return &_s; } SString oslice_t(size_t o) const; 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() const { if (_b == 0 && _e == _s.size()) return _s; else return FString(begin(), end()); } 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 { iterator _b, _e; // optional const FString *_base; public: 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 ZString(char (&s)[n]) = delete; template ZString(const char (&s)[n], const FString *base_=nullptr) : _b(s), _e(s + strlen(s)), _base(base_) {} iterator begin() const { return _b; } iterator end() const { return _e; } const FString *base() const { return _base; } const char *c_str() const { return &*begin(); } ZString oslice_t(size_t o) const; 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() const { if (_base) return SString(*_base, &*_b - &*_base->begin(), &*_e - &*_base->begin()); else return FString(_b, _e); } operator TString() const { if (_base) return SString(*_base, &*_b - &*_base->begin(), &*_e - &*_base->begin()); else return FString(_b, _e); } 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 { iterator _b, _e; // optional const FString *_base; public: // do I really want this? 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()) {} XString(const TString& s) : _b(&*s.begin()), _e(&*s.end()), _base(s.base()) {} XString(const SString& s) : _b(&*s.begin()), _e(&*s.end()), _base(s.base()) {} XString(const ZString& s) : _b(&*s.begin()), _e(&*s.end()), _base(s.base()) {} template XString(char (&s)[n]) = delete; template XString(const char (&s)[n]) : _b(s), _e(s + strlen(s)), _base(nullptr) {} // mostly internal XString(const char *b, const char *e, const FString *base_) : _b(b), _e(e), _base(base_) {} XString(decltype(ZString::really_construct_from_a_pointer) e, const char *s, const FString *base_) { *this = ZString(e, s, base_); } iterator begin() const { return _b; } iterator end() const { return _e; } 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() const { if (_base) return SString(*_base, &*_b - &*_base->begin(), &*_e - &*_base->begin()); else return FString(_b, _e); } operator TString() const { if (_base) return SString(*_base, &*_b - &*_base->begin(), &*_e - &*_base->begin()); else return FString(_b, _e); } operator SString() const { if (_base) return SString(*_base, &*_b - &*_base->begin(), &*_e - &*_base->begin()); else return FString(_b, _e); } operator ZString() const = delete; }; template class VString : public _crtp_string, VString, ZString, XString> { char _data[n]; unsigned char _special; public: typedef typename _crtp_string, VString, ZString, XString>::iterator iterator; VString(XString x) : _data(), _special() { if (x.size() > n) // we're hoping this doesn't happen // hopefully there will be few enough users of this class x = x.xslice_h(n); char *e = std::copy(x.begin(), x.end(), std::begin(_data)); _special = std::end(_data) - e; assert (_special == n - x.size()); // 0 when it needs to be } // poor man's delegated constructors // needed for gcc 4.6 compatibility VString(FString f) { *this = XString(f); } VString(TString t) { *this = XString(t); } VString(SString s) { *this = XString(s); } VString(ZString z) { *this = XString(z); } template VString(char (&s)[m]) = delete; template VString(const char (&s)[m]) { static_assert(m <= n + 1, "string would truncate"); *this = XString(s); } VString(decltype(ZString::really_construct_from_a_pointer) e, const char *s) { *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 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 operator VString() const { static_assert(m > n, "can only grow"); XString x = *this; return VString(XString(x)); } }; // not really intended for public use inline int xstr_compare(XString l, XString r) { 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 string_comparison_allowed { constexpr static bool l_is_vstring_exact = std::is_same, L>::value; constexpr static bool l_is_vstring_approx = std::is_base_of, L>::value; constexpr static bool r_is_vstring_exact = std::is_same, R>::value; constexpr static bool r_is_vstring_approx = std::is_base_of, 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::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>>::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, XString>::value, "vx"); static_assert(string_comparison_allowed::value, "xx"); static_assert(string_comparison_allowed::value, "xf"); template::value>::type> auto operator == (const L& l, const R& r) -> decltype((xstr_compare(l, r), true)) { return xstr_compare(l, r) == 0; } template::value>::type> auto operator != (const L& l, const R& r) -> decltype((xstr_compare(l, r), true)) { return xstr_compare(l, r) != 0; } template::value>::type> auto operator < (const L& l, const R& r) -> decltype((xstr_compare(l, r), true)) { return xstr_compare(l, r) < 0; } template::value>::type> auto operator <= (const L& l, const R& r) -> decltype((xstr_compare(l, r), true)) { return xstr_compare(l, r) <= 0; } template::value>::type> auto operator > (const L& l, const R& r) -> decltype((xstr_compare(l, r), true)) { return xstr_compare(l, r) > 0; } template::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; typedef FString FS; typedef SString SS; typedef TString TS; typedef ZString ZS; typedef XString XS; // _crtp_string template Z _crtp_string::xslice_t(size_t o) const { return Z(&begin()[o], &*end(), base()); } template X _crtp_string::xslice_h(size_t o) const { return X(&*begin(), &begin()[o], base()); } template Z _crtp_string::xrslice_t(size_t no) const { return Z(&end()[-no], &*end(), base()); } template X _crtp_string::xrslice_h(size_t no) const { return X(&*begin(), &end()[-no], base()); } template Z _crtp_string::xislice_t(iterator it) const { return Z(&*it, &*end(), base()); } template X _crtp_string::xislice_h(iterator it) const { return X(&*begin(), &*it, base()); } template X _crtp_string::xlslice(size_t o, size_t l) const { return X(&begin()[o], &begin()[o + l], base()); } template X _crtp_string::xpslice(size_t b, size_t e) const { return X(&begin()[b], &begin()[e], base()); } template X _crtp_string::xislice(iterator b, iterator e) const { return X(&*b, &*e, base()); } template Z _crtp_string::lstrip() const { Z z = _ref(); while (z.startswith(' ')) z = z.xslice_t(1); return z; } template X _crtp_string::rstrip() const { X x = _ref(); while (x.endswith(' ')) x = x.xrslice_h(1); return x; } template X _crtp_string::strip() const { return lstrip().rstrip(); } template bool _crtp_string::startswith(XS x) const { return size() >= x.size() && xslice_h(x.size()) == x; } template bool _crtp_string::endswith(XS x) const { return size() > x.size() && xrslice_t(x.size()) == x; } template bool _crtp_string::startswith(char c) const { return size() && front() == c; } template bool _crtp_string::endswith(char c) const { return size() && back() == c; } template bool _crtp_string::contains(char c) const { return std::find(begin(), end(), c) != end(); } template bool _crtp_string::contains_seq(XString s) const { return std::search(begin(), end(), s.begin(), s.end()) != end(); } template bool _crtp_string::contains_any(XString s) const { return std::find_if(begin(), end(), [s](char c) { return s.contains(c); }) != end(); } template bool _crtp_string::has_print() const { return std::find_if(begin(), end(), detail::is_print) != end(); } template bool _crtp_string::is_print() const { return std::find_if_not(begin(), end(), detail::is_print) == end(); } template O _crtp_string::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 bool _crtp_string::has_graph() const { return std::find_if(begin(), end(), detail::is_graph) != end(); } template bool _crtp_string::is_graph() const { return std::find_if_not(begin(), end(), detail::is_graph) == end(); } template bool _crtp_string::has_lower() const { return std::find_if(begin(), end(), detail::is_lower) != end(); } template bool _crtp_string::is_lower() const { return std::find_if_not(begin(), end(), detail::is_lower) == end(); } template O _crtp_string::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 bool _crtp_string::has_upper() const { return std::find_if(begin(), end(), detail::is_upper) != end(); } template bool _crtp_string::is_upper() const { return std::find_if_not(begin(), end(), detail::is_upper) == end(); } template O _crtp_string::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 bool _crtp_string::has_alpha() const { return std::find_if(begin(), end(), detail::is_alpha) != end(); } template bool _crtp_string::is_alpha() const { return std::find_if_not(begin(), end(), detail::is_alpha) == end(); } template bool _crtp_string::has_digit2() const { return std::find_if(begin(), end(), detail::is_digit2) != end(); } template bool _crtp_string::is_digit2() const { return std::find_if_not(begin(), end(), detail::is_digit2) == end(); } template bool _crtp_string::has_digit8() const { return std::find_if(begin(), end(), detail::is_digit8) != end(); } template bool _crtp_string::is_digit8() const { return std::find_if_not(begin(), end(), detail::is_digit8) == end(); } template bool _crtp_string::has_digit10() const { return std::find_if(begin(), end(), detail::is_digit10) != end(); } template bool _crtp_string::is_digit10() const { return std::find_if_not(begin(), end(), detail::is_digit10) == end(); } template bool _crtp_string::has_digit16() const { return std::find_if(begin(), end(), detail::is_digit16) != end(); } template bool _crtp_string::is_digit16() const { return std::find_if_not(begin(), end(), detail::is_digit16) == end(); } template bool _crtp_string::has_alnum() const { return std::find_if(begin(), end(), detail::is_alnum) != end(); } template bool _crtp_string::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 TS FS::oslice_t(size_t o) const { return TS(*this, o); } inline SS FS::oslice_h(size_t o) const { return SS(*this, 0, o); } inline TS FS::orslice_t(size_t no) const { return TS(*this, size() - no); } inline 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 TS TS::oslice_t(size_t o) const { return TS(_s, _o + o); } inline SS TS::oslice_h(size_t o) const { return SS(_s, _o, _o + o); } inline TS TS::orslice_t(size_t no) const { return TS(_s, _s.size() - no); } inline 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 SS SS::oslice_t(size_t o) const { return SS(_s, _b + o, _e); } inline SS SS::oslice_h(size_t o) const { return SS(_s, _b, _b + o); } inline SS SS::orslice_t(size_t no) const { return SS(_s, _e - no, _e); } inline 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 ZS ZS::oslice_t(size_t o) const { return ZS(really_construct_from_a_pointer, &begin()[o], base()); } inline XS ZS::oslice_h(size_t o) const { return XS(&*begin(), &begin()[o], base()); } inline ZS ZS::orslice_t(size_t no) const { return ZS(really_construct_from_a_pointer, &end()[-no], base()); } inline 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 *decay_for_printf(const TString& ts) { return ts.c_str(); } inline const char *decay_for_printf(const ZString& zs) { return zs.c_str(); } template inline const char *decay_for_printf(const VString& vs) { return vs.c_str(); } template inline __attribute__((format(printf, 2, 0))) int do_vprint(VString& 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 using strings::MString; using strings::FString; using strings::SString; using strings::TString; // non-owning using strings::ZString; using strings::XString; // semi-owning using strings::VString; #endif // TMWA_COMMON_STRINGS_HPP