#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 // 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 *() { 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; } 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) { 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(); } public: 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(); } 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; bool startswith(XString x) const; bool endswith(XString x) const; }; /// 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 char *iterator; typedef _iterator const_iterator; typedef std::reverse_iterator reverse_iterator; typedef std::reverse_iterator const_reverse_iterator; private: std::string _hack; public: template MString(char (&s)[n]) = delete; template MString(const char (&s)[n]) : _hack(s) {} template 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(); } 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()); } }; /// 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 { const std::string _hack; public: FString() : _hack() {} FString(const MString& s) : _hack(s.begin(), s.end()) {} template FString(char (&s)[n]) = delete; template FString(const char (&s)[n]) : _hack(s) {} template FString(It b, It e) : _hack(b, e) {} iterator begin() const { return &*_hack.begin(); } iterator end() const { return &*_hack.end(); } 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; SString olslice(size_t o, size_t l) const; SString opslice(size_t b, size_t 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) {} iterator begin() const { return &_s.begin()[_o]; } iterator end() const { return &*_s.end(); } 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; SString olslice(size_t o, size_t l) const; SString opslice(size_t b, size_t e) const; operator FString() { 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), _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]; } 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 olslice(size_t o, size_t l) const; SString opslice(size_t b, size_t e) const; operator FString() { if (_b == 0 && _e == _s.size()) return _s; else return FString(begin(), end()); } operator TString() { 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; 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()) {} enum { really_construct_from_a_pointer }; ZString() { *this = ZString(""); } // no MString ZString(const FString& s) : _b(&*s.begin()), _e(&*s.end()) {} ZString(const TString& s) : _b(&*s.begin()), _e(&*s.end()) {} // no SString ZString(decltype(really_construct_from_a_pointer), const char *s) : _b(s), _e(s + strlen(s)) {} template ZString(char (&s)[n]) = delete; template ZString(const char (&s)[n]) : _b(s), _e(s + strlen(s)) {} iterator begin() const { return _b; } iterator end() const { return _e; } 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; XString olslice(size_t o, size_t l) const; XString opslice(size_t b, size_t e) const; }; /// 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; public: // do I really want this? XString() : _b(nullptr), _e(nullptr) {} XString(std::nullptr_t) = delete; // no MString XString(const FString& s) : _b(&*s.begin()), _e(&*s.end()) {} XString(const TString& s) : _b(&*s.begin()), _e(&*s.end()) {} XString(const SString& s) : _b(&*s.begin()), _e(&*s.end()) {} XString(const ZString& s) : _b(&*s.begin()), _e(&*s.end()) {} template XString(char (&s)[n]) = delete; template XString(const char (&s)[n]) : _b(s), _e(s + strlen(s)) {} // mostly internal XString(const char *b, const char *e) : _b(b), _e(e) {} iterator begin() const { return _b; } iterator end() const { return _e; } 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 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); } }; template class VString : public _crtp_string> { char _data[n]; unsigned char _special; typedef typename _crtp_string>::iterator iterator; public: static_assert(n & 1, "Size should probably be odd."); 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 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(ZString(e, s)); } VString() { *this = XString(); } // hopefully this is obvious iterator begin() const { return std::begin(_data); } iterator end() const { return std::end(_data) - _special; } 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 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); } operator XString() const { return XString(&*begin(), &*end()); } }; // not really intended for public use inline int xstr_compare(XString l, XString r) { return std::lexicographical_compare( l.begin(), l.end(), r.begin(), r.end()); } template bool operator == (const L& l, const R& r) { return xstr_compare(l, r) == 0; } template bool operator != (const L& l, const R& r) { return xstr_compare(l, r) != 0; } template bool operator < (const L& l, const R& r) { return xstr_compare(l, r) < 0; } template bool operator <= (const L& l, const R& r) { return xstr_compare(l, r) <= 0; } template bool operator > (const L& l, const R& r) { return xstr_compare(l, r) > 0; } template bool operator >= (const L& l, const R& r) { return xstr_compare(l, r) >= 0; } // sadness typedef MString MS; typedef FString FS; typedef SString SS; typedef TString TS; typedef ZString ZS; typedef XString XS; // _crtp_string template XS _crtp_string::xslice_t(size_t o) const { return XS(&begin()[o], &*end()); } template XS _crtp_string::xslice_h(size_t o) const { return XS(&*begin(), &begin()[o]); } template XS _crtp_string::xrslice_t(size_t no) const { return XS(&end()[-no], &*end()); } template XS _crtp_string::xrslice_h(size_t no) const { return XS(&*begin(), &end()[-no]); } template XS _crtp_string::xlslice(size_t o, size_t l) const { return XS(&begin()[o], &begin()[o + l]); } template XS _crtp_string::xpslice(size_t b, size_t e) const { return XS(&begin()[b], &begin()[e]); } 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; } // 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 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); } // 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 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); } // 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::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); } // ZString inline ZS ZS::oslice_t(size_t o) const { return ZS(really_construct_from_a_pointer, &begin()[o]); } inline XS ZS::oslice_h(size_t o) const { return XS(&*begin(), &begin()[o]); } inline ZS ZS::orslice_t(size_t no) const { return ZS(really_construct_from_a_pointer, &end()[-no]); } inline XS ZS::orslice_h(size_t no) const { return XS(&*begin(), &end()[-no]); } inline XS ZS::olslice(size_t o, size_t l) const { return XS(&begin()[o], &begin()[o + l]); } inline XS ZS::opslice(size_t b, size_t e) const { return XS(&begin()[b], &begin()[e]); } // cxxstdio helpers // I think the conversion will happen automatically. TODO test this. //const char *convert_for_printf(const FString& fs) { return fs.c_str(); } //const char *convert_for_printf(const TString& ts) { return ts.c_str(); } inline const char *convert_for_printf(const ZString& zs) { return zs.c_str(); } } // 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