From dbbfda0e96037da4f208ff8f00d181a5294484ae Mon Sep 17 00:00:00 2001 From: Ben Longbons Date: Sat, 22 Jun 2013 22:30:13 -0700 Subject: add new stuff stuff (with tests!), poison memcmp and strncpy --- src/common/dumb_ptr.hpp | 3 + src/common/md5calc.cpp | 3 +- src/common/mmo.hpp | 9 + src/common/socket.cpp | 2 +- src/common/socket.hpp | 16 +- src/common/strings.hpp | 537 ++++++++++++++++++++++++++++++++++++++++++++ src/common/strings_test.cpp | 99 ++++++++ src/common/utils.hpp | 14 +- src/common/utils2.hpp | 21 ++ 9 files changed, 696 insertions(+), 8 deletions(-) create mode 100644 src/common/strings.hpp create mode 100644 src/common/strings_test.cpp (limited to 'src/common') 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 #include "const_array.hpp" +#include "strings.hpp" // unmanaged new/delete-able pointer // should be replaced by std::unique_ptr @@ -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, FD_SETSIZE> session; inline void RFIFOFLUSH(int fd) { - really_memmove(&session[fd]->rdata[0], static_cast(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 -uint8_t *pod_addressof(T& structure) +uint8_t *pod_addressof_m(T& structure) { static_assert(is_trivially_copyable::value, "Can only byte-copy POD-ish structs"); return &reinterpret_cast(structure); } +template +const uint8_t *pod_addressof_c(const T& structure) +{ + static_assert(is_trivially_copyable::value, "Can only byte-copy POD-ish structs"); + return &reinterpret_cast(structure); +} /// Check how much can be read @@ -141,7 +147,7 @@ uint32_t RFIFOL(int fd, size_t pos) template void RFIFO_STRUCT(int fd, size_t pos, T& structure) { - really_memcpy(pod_addressof(structure), static_cast(RFIFOP(fd, pos)), sizeof(T)); + really_memcpy(pod_addressof_m(structure), static_cast(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 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 void WFIFO_STRUCT(int fd, size_t pos, T& structure) { - really_memcpy(static_cast(WFIFOP(fd, pos)), pod_addressof(structure), sizeof(T)); + really_memcpy(static_cast(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 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 +// +// 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 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 + +template +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 +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 +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 #include #include #include @@ -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 -- cgit v1.2.3-60-g2f50