summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
authorBen Longbons <b.r.longbons@gmail.com>2013-06-22 22:30:13 -0700
committerBen Longbons <b.r.longbons@gmail.com>2013-06-23 22:07:50 -0700
commitdbbfda0e96037da4f208ff8f00d181a5294484ae (patch)
tree7a7b9982c6d98ddc2271aade72040ea9233a4a11 /src/common
parent83db3bbee4e19e7426a32ee89ad6c2d8e48260f2 (diff)
downloadtmwa-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')
-rw-r--r--src/common/dumb_ptr.hpp3
-rw-r--r--src/common/md5calc.cpp3
-rw-r--r--src/common/mmo.hpp9
-rw-r--r--src/common/socket.cpp2
-rw-r--r--src/common/socket.hpp16
-rw-r--r--src/common/strings.hpp537
-rw-r--r--src/common/strings_test.cpp99
-rw-r--r--src/common/utils.hpp14
-rw-r--r--src/common/utils2.hpp21
9 files changed, 696 insertions, 8 deletions
diff --git a/src/common/dumb_ptr.hpp b/src/common/dumb_ptr.hpp
index e10ca3a..ab98b57 100644
--- a/src/common/dumb_ptr.hpp
+++ b/src/common/dumb_ptr.hpp
@@ -26,6 +26,7 @@
#include <algorithm>
#include "const_array.hpp"
+#include "strings.hpp"
// unmanaged new/delete-able pointer
// should be replaced by std::unique_ptr<T>
@@ -261,6 +262,8 @@ struct dumb_string
return !impl;
}
+ operator ZString() { return ZString(ZString::really_construct_from_a_pointer, c_str()); }
+
#if 0
friend bool operator == (dumb_string l, dumb_string r)
{
diff --git a/src/common/md5calc.cpp b/src/common/md5calc.cpp
index f773c9f..582c152 100644
--- a/src/common/md5calc.cpp
+++ b/src/common/md5calc.cpp
@@ -317,7 +317,8 @@ const char *make_salt(void)
bool pass_ok(const char *password, const char *crypted)
{
char buf[40];
- strncpy(buf, crypted, 40);
+ strzcpy(buf, crypted, 40);
+ // crypted is like !salt$hash
char *salt = buf + 1;
*strchr(salt, '$') = '\0';
diff --git a/src/common/mmo.hpp b/src/common/mmo.hpp
index 14b8bac..450aa61 100644
--- a/src/common/mmo.hpp
+++ b/src/common/mmo.hpp
@@ -92,6 +92,15 @@ struct skill_value
{
unsigned short lv;
SkillFlags flags;
+
+ friend bool operator == (const skill_value& l, const skill_value& r)
+ {
+ return l.lv == r.lv && l.flags == r.flags;
+ }
+ friend bool operator != (const skill_value& l, const skill_value& r)
+ {
+ return !(l == r);
+ }
};
struct global_reg
diff --git a/src/common/socket.cpp b/src/common/socket.cpp
index c509825..214fb5a 100644
--- a/src/common/socket.cpp
+++ b/src/common/socket.cpp
@@ -38,7 +38,7 @@ std::array<std::unique_ptr<struct socket_data>, FD_SETSIZE> session;
inline
void RFIFOFLUSH(int fd)
{
- really_memmove(&session[fd]->rdata[0], static_cast<const uint8_t *>(RFIFOP(fd, 0)), RFIFOREST(fd));
+ really_memmove(&session[fd]->rdata[0], &session[fd]->rdata[session[fd]->rdata_pos], RFIFOREST(fd));
session[fd]->rdata_size = RFIFOREST(fd);
session[fd]->rdata_pos = 0;
}
diff --git a/src/common/socket.hpp b/src/common/socket.hpp
index ef7b193..2366373 100644
--- a/src/common/socket.hpp
+++ b/src/common/socket.hpp
@@ -103,12 +103,18 @@ FILE *fopen_(const char *path, const char *mode);
bool free_fds(void);
template<class T>
-uint8_t *pod_addressof(T& structure)
+uint8_t *pod_addressof_m(T& structure)
{
static_assert(is_trivially_copyable<T>::value, "Can only byte-copy POD-ish structs");
return &reinterpret_cast<uint8_t&>(structure);
}
+template<class T>
+const uint8_t *pod_addressof_c(const T& structure)
+{
+ static_assert(is_trivially_copyable<T>::value, "Can only byte-copy POD-ish structs");
+ return &reinterpret_cast<const uint8_t&>(structure);
+}
/// Check how much can be read
@@ -141,7 +147,7 @@ uint32_t RFIFOL(int fd, size_t pos)
template<class T>
void RFIFO_STRUCT(int fd, size_t pos, T& structure)
{
- really_memcpy(pod_addressof(structure), static_cast<const uint8_t *>(RFIFOP(fd, pos)), sizeof(T));
+ really_memcpy(pod_addressof_m(structure), static_cast<const uint8_t *>(RFIFOP(fd, pos)), sizeof(T));
}
inline
void RFIFO_STRING(int fd, size_t pos, char *out, size_t len)
@@ -181,7 +187,7 @@ uint32_t RBUFL(const uint8_t *p, size_t pos)
template<class T>
void RBUF_STRUCT(const uint8_t *p, size_t pos, T& structure)
{
- really_memcpy(pod_addressof(structure), p + pos, sizeof(T));
+ really_memcpy(pod_addressof_m(structure), p + pos, sizeof(T));
}
inline
void RBUF_STRING(const uint8_t *p, size_t pos, char *out, size_t len)
@@ -220,7 +226,7 @@ uint32_t& WFIFOL(int fd, size_t pos)
template<class T>
void WFIFO_STRUCT(int fd, size_t pos, T& structure)
{
- really_memcpy(static_cast<uint8_t *>(WFIFOP(fd, pos)), pod_addressof(structure), sizeof(T));
+ really_memcpy(static_cast<uint8_t *>(WFIFOP(fd, pos)), pod_addressof_c(structure), sizeof(T));
}
inline
void WFIFO_STRING(int fd, size_t pos, const char *s, size_t len)
@@ -267,7 +273,7 @@ uint32_t& WBUFL(uint8_t *p, size_t pos)
template<class T>
void WBUF_STRUCT(uint8_t *p, size_t pos, T& structure)
{
- really_memcpy(p + pos, pod_addressof(structure), sizeof(T));
+ really_memcpy(p + pos, pod_addressof_c(structure), sizeof(T));
}
inline
void WBUF_STRING(uint8_t *p, size_t pos, const char *s, size_t len)
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
diff --git a/src/common/strings_test.cpp b/src/common/strings_test.cpp
new file mode 100644
index 0000000..e83d283
--- /dev/null
+++ b/src/common/strings_test.cpp
@@ -0,0 +1,99 @@
+#include "../../src/common/strings.hpp"
+
+#include <gtest/gtest.h>
+
+template<typename T>
+class StringTest : public ::testing::Test
+{
+};
+TYPED_TEST_CASE_P(StringTest);
+
+TYPED_TEST_P(StringTest, basic)
+{
+ TypeParam hi("Hello");
+ EXPECT_EQ(5, hi.size());
+ EXPECT_EQ(hi, hi);
+ const char hi2[] = "Hello\0random garbage";
+ EXPECT_EQ(hi, hi2);
+ TypeParam hi0;
+ EXPECT_EQ(0, hi0.size());
+}
+
+TYPED_TEST_P(StringTest, iterators)
+{
+ TypeParam hi("Hello");
+ EXPECT_EQ(hi.begin(), hi.begin());
+ EXPECT_NE(hi.begin(), hi.end());
+ EXPECT_EQ(5, std::distance(hi.begin(), hi.end()));
+ const char *hi2 = "Hello";
+ EXPECT_TRUE(std::equal(hi.begin(), hi.end(), hi2));
+}
+
+TYPED_TEST_P(StringTest, xslice)
+{
+ TypeParam hi("Hello, World!");
+ EXPECT_EQ(" World!", hi.xslice_t(6));
+ EXPECT_EQ("Hello,", hi.xslice_h(6));
+ EXPECT_EQ("World!", hi.xrslice_t(6));
+ EXPECT_EQ("Hello, ", hi.xrslice_h(6));
+ EXPECT_EQ("World", hi.xlslice(7, 5));
+ EXPECT_EQ("World", hi.xpslice(7, 12));
+ EXPECT_TRUE(hi.startswith("Hello"));
+ EXPECT_TRUE(hi.endswith("World!"));
+}
+
+TYPED_TEST_P(StringTest, oslice)
+{
+ TypeParam hi("Hello, World!");
+ EXPECT_EQ(" World!", hi.oslice_t(6));
+ EXPECT_EQ("Hello,", hi.oslice_h(6));
+ EXPECT_EQ("World!", hi.orslice_t(6));
+ EXPECT_EQ("Hello, ", hi.orslice_h(6));
+ EXPECT_EQ("World", hi.olslice(7, 5));
+ EXPECT_EQ("World", hi.opslice(7, 12));
+}
+
+REGISTER_TYPED_TEST_CASE_P(StringTest,
+ basic, iterators, xslice, oslice);
+
+typedef ::testing::Types<
+ FString, TString, SString, ZString, XString, VString<255>
+> MostStringTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(StringStuff, StringTest, MostStringTypes);
+
+TEST(VStringTest, basic)
+{
+ VString<5> hi = "Hello";
+ EXPECT_EQ(5, hi.size());
+ EXPECT_EQ(hi, hi);
+ // truncation
+ VString<5> hi2(ZString::really_construct_from_a_pointer, "Hello, world!");
+ EXPECT_EQ(5, hi2.size());
+ EXPECT_EQ(hi, hi2);
+ // short
+ hi = "hi";
+ EXPECT_EQ(2, hi.size());
+ VString<5> hi0;
+ EXPECT_EQ(0, hi0.size());
+}
+
+template<typename T>
+class NulStringTest : public ::testing::Test
+{
+};
+TYPED_TEST_CASE_P(NulStringTest);
+
+TYPED_TEST_P(NulStringTest, basic)
+{
+ TypeParam hi("hello");
+ EXPECT_EQ(hi.size(), strlen(hi.c_str()));
+ EXPECT_STREQ("hello", hi.c_str());
+}
+
+REGISTER_TYPED_TEST_CASE_P(NulStringTest,
+ basic);
+
+typedef ::testing::Types<
+ FString, TString, ZString, VString<255>
+> NulStringTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(NulStringStuff, NulStringTest, NulStringTypes);
diff --git a/src/common/utils.hpp b/src/common/utils.hpp
index cd45aa1..ab32948 100644
--- a/src/common/utils.hpp
+++ b/src/common/utils.hpp
@@ -34,7 +34,8 @@ void strzcpy(char *dest, const char *src, size_t n)
{
if (n)
{
- strncpy(dest, src, n);
+ // hmph
+ strncpy(dest, src, n - 1);
dest[n - 1] = '\0';
}
}
@@ -50,6 +51,11 @@ void really_memmove(uint8_t *dest, const uint8_t *src, size_t n)
{
memmove(dest, src, n);
}
+inline
+bool really_memequal(const uint8_t *a, const uint8_t *b, size_t n)
+{
+ return memcmp(a, b, n) == 0;
+}
inline
void really_memset0(uint8_t *dest, size_t n)
@@ -140,4 +146,10 @@ static_assert(sizeof(TIMESTAMP_DUMMY) == sizeof(timestamp_seconds_buffer),
&t \
)
+template<class T>
+const T& const_(T& t)
+{
+ return t;
+}
+
#endif //UTILS_HPP
diff --git a/src/common/utils2.hpp b/src/common/utils2.hpp
index 2218625..d7d6f8a 100644
--- a/src/common/utils2.hpp
+++ b/src/common/utils2.hpp
@@ -3,6 +3,7 @@
#include "sanity.hpp"
+#include <algorithm>
#include <functional>
#include <iterator>
#include <memory>
@@ -39,6 +40,26 @@ struct earray
{
return _data + size_t(max);
}
+
+ const T *begin() const
+ {
+ return _data;
+ }
+
+ const T *end() const
+ {
+ return _data + size_t(max);
+ }
+
+ friend bool operator == (const earray& l, const earray& r)
+ {
+ return std::equal(l.begin(), l.end(), r.begin());
+ }
+
+ friend bool operator != (const earray& l, const earray& r)
+ {
+ return !(l == r);
+ }
};
template<class T, class E>