diff options
author | Ben Longbons <b.r.longbons@gmail.com> | 2013-06-22 22:30:13 -0700 |
---|---|---|
committer | Ben Longbons <b.r.longbons@gmail.com> | 2013-06-23 22:07:50 -0700 |
commit | dbbfda0e96037da4f208ff8f00d181a5294484ae (patch) | |
tree | 7a7b9982c6d98ddc2271aade72040ea9233a4a11 /src/common/strings.hpp | |
parent | 83db3bbee4e19e7426a32ee89ad6c2d8e48260f2 (diff) | |
download | tmwa-dbbfda0e96037da4f208ff8f00d181a5294484ae.tar.gz tmwa-dbbfda0e96037da4f208ff8f00d181a5294484ae.tar.bz2 tmwa-dbbfda0e96037da4f208ff8f00d181a5294484ae.tar.xz tmwa-dbbfda0e96037da4f208ff8f00d181a5294484ae.zip |
add new stuff stuff (with tests!), poison memcmp and strncpy
Diffstat (limited to 'src/common/strings.hpp')
-rw-r--r-- | src/common/strings.hpp | 537 |
1 files changed, 537 insertions, 0 deletions
diff --git a/src/common/strings.hpp b/src/common/strings.hpp new file mode 100644 index 0000000..2d34d57 --- /dev/null +++ b/src/common/strings.hpp @@ -0,0 +1,537 @@ +#ifndef TMWA_COMMON_STRINGS_HPP +#define TMWA_COMMON_STRINGS_HPP +// strings.hpp - All the string classes you'll ever need. +// +// Copyright © 2013 Ben Longbons <b.r.longbons@gmail.com> +// +// 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 <http://www.gnu.org/licenses/>. + +#include "sanity.hpp" + +#include <cassert> +#include <cstring> + +#include <iterator> +#include <string> + +// 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<uint8_t len> + class VString; + + // simple pointer-wrapping iterator that can be used to get distinct + // types for different containers. + template<class Tag> + 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 T> + class _crtp_string + { + public: + // this will have to be changed if MString decides to join in. + typedef _iterator<T> iterator; + typedef std::reverse_iterator<iterator> reverse_iterator; + private: + const T& _ref() const { return static_cast<const T&>(*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<MString> const_iterator; + typedef std::reverse_iterator<iterator> reverse_iterator; + typedef std::reverse_iterator<const_iterator> const_reverse_iterator; + private: + std::string _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(); } + 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<FString> + { + const std::string _hack; + public: + 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) {} + template<class It> + 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<TString> + { + 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<SString> + { + 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<ZString> + { + 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<size_t n> + ZString(char (&s)[n]) = delete; + template<size_t n> + 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<XString> + { + 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<size_t n> + XString(char (&s)[n]) = delete; + template<size_t n> + 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<uint8_t n> + class VString : public _crtp_string<VString<n>> + { + char _data[n]; + unsigned char _special; + typedef typename _crtp_string<VString<n>>::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<size_t m> + VString(char (&s)[m]) = delete; + template<size_t m> + 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<class L, class R> + bool operator == (const L& l, const R& r) + { + return xstr_compare(l, r) == 0; + } + template<class L, class R> + bool operator != (const L& l, const R& r) + { + return xstr_compare(l, r) != 0; + } + template<class L, class R> + bool operator < (const L& l, const R& r) + { + return xstr_compare(l, r) < 0; + } + template<class L, class R> + bool operator <= (const L& l, const R& r) + { + return xstr_compare(l, r) <= 0; + } + template<class L, class R> + bool operator > (const L& l, const R& r) + { + return xstr_compare(l, r) > 0; + } + template<class L, class R> + 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<class T> + XS _crtp_string<T>::xslice_t(size_t o) const + { return XS(&begin()[o], &*end()); } + template<class T> + XS _crtp_string<T>::xslice_h(size_t o) const + { return XS(&*begin(), &begin()[o]); } + template<class T> + XS _crtp_string<T>::xrslice_t(size_t no) const + { return XS(&end()[-no], &*end()); } + template<class T> + XS _crtp_string<T>::xrslice_h(size_t no) const + { return XS(&*begin(), &end()[-no]); } + template<class T> + XS _crtp_string<T>::xlslice(size_t o, size_t l) const + { return XS(&begin()[o], &begin()[o + l]); } + template<class T> + XS _crtp_string<T>::xpslice(size_t b, size_t e) const + { return XS(&begin()[b], &begin()[e]); } + 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 + { 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 |