summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/const_array.hpp51
-rw-r--r--src/common/core.cpp6
-rw-r--r--src/common/core.hpp4
-rw-r--r--src/common/cxxstdio.cpp33
-rw-r--r--src/common/cxxstdio.hpp96
-rw-r--r--src/common/cxxstdio_test.cpp3
-rw-r--r--src/common/dumb_ptr.hpp137
-rw-r--r--src/common/extract.cpp30
-rw-r--r--src/common/extract.hpp123
-rw-r--r--src/common/extract_test.cpp319
-rw-r--r--src/common/human_time_diff.hpp85
-rw-r--r--src/common/human_time_diff_test.cpp84
-rw-r--r--src/common/intern-pool.hpp13
-rw-r--r--src/common/intern-pool_test.cpp18
-rw-r--r--src/common/io.hpp26
-rw-r--r--src/common/lock.cpp20
-rw-r--r--src/common/lock.hpp6
-rw-r--r--src/common/md5calc.cpp92
-rw-r--r--src/common/md5calc.hpp37
-rw-r--r--src/common/md5calc_test.cpp27
-rw-r--r--src/common/mmo.hpp121
-rw-r--r--src/common/sanity.hpp23
-rw-r--r--src/common/socket.cpp47
-rw-r--r--src/common/socket.hpp53
-rw-r--r--src/common/strings.hpp695
-rw-r--r--src/common/strings2_test.cpp118
-rw-r--r--src/common/strings_test.cpp162
-rw-r--r--src/common/timer.cpp4
-rw-r--r--src/common/timer.hpp4
-rw-r--r--src/common/utils.cpp131
-rw-r--r--src/common/utils.hpp39
-rw-r--r--src/common/utils2.hpp29
32 files changed, 1915 insertions, 721 deletions
diff --git a/src/common/const_array.hpp b/src/common/const_array.hpp
index a3a6d58..314eccf 100644
--- a/src/common/const_array.hpp
+++ b/src/common/const_array.hpp
@@ -25,11 +25,11 @@
#include <iterator>
#include <ostream>
-#include <string>
#include <vector>
#ifdef WORKAROUND_GCC46_COMPILER
// constexpr is buggy with templates in this version
+// Is this still needed now that const_string is removed?
# define constexpr /* nothing */
#endif
@@ -125,57 +125,8 @@ public:
}
};
-// subclass just provides a simpler name and some conversions
-// Important note: it must be safe to dereference end, though
-// the value is unspecified.
-class const_string : public const_array<char>
-{
-public:
- // Implicit conversion from C string.
- constexpr
- const_string(const char *z)
- : const_array<char>(z, z ? strlen(z) : 0)
- {}
-
- // Same as parent constructor.
- constexpr
- const_string(const char *s, size_t l)
- : const_array<char>(s, l)
- {}
-
- // Same as parent constructor.
- constexpr
- const_string(const char *b, const char *e)
- : const_array<char>(b, e)
- {}
-
- // Same as parent constructor.
- const_string(const std::vector<char> s)
- : const_array<char>(s)
- {}
-
- // Implicit conversion from C++ string.
- const_string(const std::string& s)
- : const_array<char>(s.data(), s.size())
- {}
-
- // but disallow converion from a temporary.
- const_string(std::string&&) = delete;
-
- // allow being sloppy
- constexpr
- const_string(const_array<char> a)
- : const_array<char>(a)
- {}
-};
#ifdef WORKAROUND_GCC46_COMPILER
# undef constexpr
#endif
-inline
-std::ostream& operator << (std::ostream& o, const_string s)
-{
- return o.write(s.data(), s.size());
-}
-
#endif // CONST_ARRAY_HPP
diff --git a/src/common/core.cpp b/src/common/core.cpp
index 153414d..76aa09c 100644
--- a/src/common/core.cpp
+++ b/src/common/core.cpp
@@ -76,7 +76,11 @@ int main(int argc, char **argv)
{
do_socket();
- do_init(argc, argv);
+ // ZString args[argc]; is (deliberately!) not supported by clang yet
+ ZString *args = static_cast<ZString *>(alloca(argc * sizeof(ZString)));
+ for (int i = 0; i < argc; ++i)
+ args[i] = ZString(ZString::really_construct_from_a_pointer, argv[i], nullptr);
+ do_init(argc, args);
// set up exit handlers *after* the initialization has happened.
// This is because term_func is likely to depend on successful init.
diff --git a/src/common/core.hpp b/src/common/core.hpp
index 0c11efb..f1473ed 100644
--- a/src/common/core.hpp
+++ b/src/common/core.hpp
@@ -3,6 +3,8 @@
#include "sanity.hpp"
+#include "strings.hpp"
+
/// core.c contains a server-independent main() function
/// and then runs a do_sendrecv loop
@@ -12,7 +14,7 @@ extern bool runflag;
/// This is an external function defined by each server
/// This function must register stuff for the parse loop
-extern int do_init(int, char **);
+extern int do_init(int, ZString *);
/// Cleanup function called whenever a signal kills us
/// or when if we manage to exit() gracefully.
diff --git a/src/common/cxxstdio.cpp b/src/common/cxxstdio.cpp
deleted file mode 100644
index 8f4001f..0000000
--- a/src/common/cxxstdio.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
-#include "cxxstdio.hpp"
-// cxxstdio.cpp - pass C++ types through scanf/printf
-//
-// 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 <cstdlib>
-
-namespace cxxstdio
-{
-StringConverter::~StringConverter()
-{
- if (mid)
- {
- out = mid;
- free(mid);
- }
-}
-} // namespace cxxstdio
diff --git a/src/common/cxxstdio.hpp b/src/common/cxxstdio.hpp
index 96c3ca2..89cc5de 100644
--- a/src/common/cxxstdio.hpp
+++ b/src/common/cxxstdio.hpp
@@ -24,37 +24,19 @@
#include <cstdarg>
#include <cstdio>
-#include <string>
-
#include "const_array.hpp"
#include "utils2.hpp"
namespace cxxstdio
{
+ // other implementations of do_vprint or do_vscan are injected by ADL.
inline __attribute__((format(printf, 2, 0)))
int do_vprint(FILE *out, const char *fmt, va_list ap)
{
return vfprintf(out, fmt, ap);
}
- inline __attribute__((format(printf, 2, 0)))
- int do_vprint(std::string& 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 = buffer;
- return len;
- }
-
inline __attribute__((format(scanf, 2, 0)))
int do_vscan(FILE *in, const char *fmt, va_list ap)
{
@@ -72,13 +54,6 @@ namespace cxxstdio
int do_vscan(const char *, const char *, va_list) = delete;
#endif
- inline __attribute__((format(scanf, 2, 0)))
- int do_vscan(const std::string& in, const char *fmt, va_list ap)
- {
- return vsscanf(in.c_str(), fmt, ap);
- }
-
-
template<class T>
inline __attribute__((format(printf, 2, 3)))
int do_print(T&& t, const char *fmt, ...) throw()
@@ -104,13 +79,19 @@ namespace cxxstdio
}
- template<class T>
- typename remove_enum<T>::type convert_for_printf(T v)
+ template<class T, typename=typename std::enable_if<!std::is_class<T>::value>::type>
+ typename remove_enum<T>::type decay_for_printf(T v)
{
typedef typename remove_enum<T>::type repr_type;
return repr_type(v);
}
+ template<class T, typename=decltype(decay_for_printf(std::declval<T&&>()))>
+ T&& convert_for_printf(T&& v)
+ {
+ return std::forward<T>(v);
+ }
+
template<class T, typename = typename std::enable_if<!std::is_enum<T>::value>::type>
T& convert_for_scanf(T& v)
{
@@ -152,7 +133,7 @@ namespace cxxstdio
}
template<class E>
constexpr
- E get_max_value(E)
+ E get_enum_max_value(E)
{
return E::max_value;
}
@@ -199,34 +180,6 @@ namespace cxxstdio
return v;
}
-
- inline
- const char *convert_for_printf(const std::string& s)
- {
- return s.c_str();
- }
-
- class StringConverter
- {
- std::string& out;
- char *mid;
- public:
- StringConverter(std::string& s)
- : out(s), mid(nullptr)
- {}
- ~StringConverter();
- char **operator &()
- {
- return &mid;
- }
- };
-
- inline
- StringConverter convert_for_scanf(std::string& s)
- {
- return StringConverter(s);
- }
-
template<class Format>
class PrintFormatter
{
@@ -238,7 +191,7 @@ namespace cxxstdio
constexpr static
const char *print_format = Format::print_format();
return do_print(std::forward<T>(t), print_format,
- convert_for_printf(std::forward<A>(a))...);
+ decay_for_printf(convert_for_printf(std::forward<A>(a)))...);
}
};
@@ -257,7 +210,7 @@ namespace cxxstdio
}
};
-#define FPRINTF(file, fmt, ...) \
+#define XPRINTF(out, fmt, ...) \
([&]() -> int \
{ \
struct format_impl \
@@ -265,10 +218,10 @@ namespace cxxstdio
constexpr static \
const char *print_format() { return fmt; } \
}; \
- return cxxstdio::PrintFormatter<format_impl>::print(file, ## __VA_ARGS__); \
+ return cxxstdio::PrintFormatter<format_impl>::print(out, ## __VA_ARGS__); \
}())
-#define FSCANF(file, fmt, ...) \
+#define XSCANF(out, fmt, ...) \
([&]() -> int \
{ \
struct format_impl \
@@ -276,22 +229,33 @@ namespace cxxstdio
constexpr static \
const char *scan_format() { return fmt; } \
}; \
- return cxxstdio::ScanFormatter<format_impl>::scan(file, ## __VA_ARGS__); \
+ return cxxstdio::ScanFormatter<format_impl>::scan(out, ## __VA_ARGS__); \
}())
+#define FPRINTF(file, fmt, ...) XPRINTF(no_cast<FILE *>(file), fmt, ## __VA_ARGS__)
+#define FSCANF(file, fmt, ...) XSCANF(no_cast<FILE *>(file), fmt, ## __VA_ARGS__)
#define PRINTF(fmt, ...) FPRINTF(stdout, fmt, ## __VA_ARGS__)
-#define SPRINTF(str, fmt, ...) FPRINTF(str, fmt, ## __VA_ARGS__)
+#define SPRINTF(str, fmt, ...) XPRINTF(base_cast<FString&>(str), fmt, ## __VA_ARGS__)
+#define SNPRINTF(str, n, fmt, ...) XPRINTF(base_cast<VString<n-1>&>(str), fmt, ## __VA_ARGS__)
#define SCANF(fmt, ...) FSCANF(stdin, fmt, ## __VA_ARGS__)
-#define SSCANF(str, fmt, ...) FSCANF(str, fmt, ## __VA_ARGS__)
+#define SSCANF(str, fmt, ...) XSCANF(/*ZString or compatible*/str, fmt, ## __VA_ARGS__)
#define STRPRINTF(fmt, ...) \
- ([&]() -> std::string \
+ ([&]() -> FString \
{ \
- std::string _out_impl; \
+ FString _out_impl; \
SPRINTF(_out_impl, fmt, ## __VA_ARGS__);\
return _out_impl; \
}())
+#define STRNPRINTF(n, fmt, ...) \
+ ([&]() -> VString<n - 1> \
+ { \
+ VString<n - 1> _out_impl; \
+ SNPRINTF(_out_impl, n, fmt, ## __VA_ARGS__);\
+ return _out_impl; \
+ }())
+
} // namespace cxxstdio
#endif // CXXSTDIO_HPP
diff --git a/src/common/cxxstdio_test.cpp b/src/common/cxxstdio_test.cpp
new file mode 100644
index 0000000..9b6eeb2
--- /dev/null
+++ b/src/common/cxxstdio_test.cpp
@@ -0,0 +1,3 @@
+#include "cxxstdio.hpp"
+
+#include <gtest/gtest.h>
diff --git a/src/common/dumb_ptr.hpp b/src/common/dumb_ptr.hpp
index 8863ef2..fe5031a 100644
--- a/src/common/dumb_ptr.hpp
+++ b/src/common/dumb_ptr.hpp
@@ -202,25 +202,25 @@ struct dumb_string
{
return dumb_string::copy(sz, sz + strlen(sz));
}
- static dumb_string copys(const std::string& s)
+ static dumb_string copys(XString s)
{
return dumb_string::copy(&*s.begin(), &*s.end());
}
- static dumb_string copyn(const char *sn, size_t n)
+ static
+#ifndef __clang__
+ __attribute__((warning("shouldn't use this - slice instead")))
+#endif
+ dumb_string copyn(const char *sn, size_t n)
{
return dumb_string::copy(sn, sn + strnlen(sn, n));
}
- static dumb_string copyc(const_string s)
- {
- return dumb_string::copy(s.begin(), s.end());
- }
static
- dumb_string fake(const char *p)
+ dumb_string fake(ZString p)
{
dumb_string rv;
- size_t len = p ? strlen(p) : 0;
- rv.impl = dumb_ptr<char[]>(const_cast<char *>(p), len);
+ size_t len = p.size();
+ rv.impl = dumb_ptr<char[]>(const_cast<char *>(p.c_str()), len);
return rv;
}
@@ -235,17 +235,17 @@ struct dumb_string
const char *c_str() const
{
- return impl ? &impl[0] : "";
+ return &impl[0];
}
- std::string str() const
+ operator ZString() const
{
- return c_str();
+ return ZString(ZString::really_construct_from_a_pointer, c_str(), nullptr);
}
- operator const_string() const
+ FString str() const
{
- return const_string(c_str());
+ return ZString(*this);
}
char& operator[](size_t i) const
@@ -262,117 +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)
- {
- return l.impl == r.impl;
- }
- friend bool operator != (dumb_string l, dumb_string r)
- {
- return !(l == r);
- }
-#endif
};
-namespace operators
-{
- inline
- bool operator == (dumb_string l, dumb_string r)
- {
- return strcmp(l.c_str(), r.c_str()) == 0;
- }
- inline
- bool operator != (dumb_string l, dumb_string r)
- {
- return strcmp(l.c_str(), r.c_str()) != 0;
- }
- inline
- bool operator < (dumb_string l, dumb_string r)
- {
- return strcmp(l.c_str(), r.c_str()) < 0;
- }
- inline
- bool operator <= (dumb_string l, dumb_string r)
- {
- return strcmp(l.c_str(), r.c_str()) <= 0;
- }
- inline
- bool operator > (dumb_string l, dumb_string r)
- {
- return strcmp(l.c_str(), r.c_str()) > 0;
- }
- inline
- bool operator >= (dumb_string l, dumb_string r)
- {
- return strcmp(l.c_str(), r.c_str()) >= 0;
- }
-
- inline
- bool operator == (const char *l, dumb_string r)
- {
- return strcmp(l, r.c_str()) == 0;
- }
- inline
- bool operator != (const char *l, dumb_string r)
- {
- return strcmp(l, r.c_str()) != 0;
- }
- inline
- bool operator < (const char *l, dumb_string r)
- {
- return strcmp(l, r.c_str()) < 0;
- }
- inline
- bool operator <= (const char *l, dumb_string r)
- {
- return strcmp(l, r.c_str()) <= 0;
- }
- inline
- bool operator > (const char *l, dumb_string r)
- {
- return strcmp(l, r.c_str()) > 0;
- }
- inline
- bool operator >= (const char *l, dumb_string r)
- {
- return strcmp(l, r.c_str()) >= 0;
- }
-
- inline
- bool operator == (dumb_string l, const char *r)
- {
- return strcmp(l.c_str(), r) == 0;
- }
- inline
- bool operator != (dumb_string l, const char *r)
- {
- return strcmp(l.c_str(), r) != 0;
- }
- inline
- bool operator < (dumb_string l, const char *r)
- {
- return strcmp(l.c_str(), r) < 0;
- }
- inline
- bool operator <= (dumb_string l, const char *r)
- {
- return strcmp(l.c_str(), r) <= 0;
- }
- inline
- bool operator > (dumb_string l, const char *r)
- {
- return strcmp(l.c_str(), r) > 0;
- }
- inline
- bool operator >= (dumb_string l, const char *r)
- {
- return strcmp(l.c_str(), r) >= 0;
- }
-}
-
inline
const char *convert_for_printf(dumb_string ds)
{
diff --git a/src/common/extract.cpp b/src/common/extract.cpp
index 5e89e19..720e6df 100644
--- a/src/common/extract.cpp
+++ b/src/common/extract.cpp
@@ -18,28 +18,31 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
-bool extract(const_string str, const_string *rv)
+#include "../poison.hpp"
+
+bool extract(XString str, XString *rv)
{
*rv = str;
return true;
}
-bool extract(const_string str, std::string *rv)
+bool extract(XString str, FString *rv)
{
- *rv = std::string(str.begin(), str.end());
+ *rv = str;
return true;
}
-bool extract(const_string str, struct global_reg *var)
+bool extract(XString str, struct global_reg *var)
{
return extract(str,
record<','>(&var->str, &var->value));
}
-bool extract(const_string str, struct item *it)
+bool extract(XString str, struct item *it)
{
+ it->broken = 0;
return extract(str,
- record<','>(
+ record<',', 11>(
&it->id,
&it->nameid,
&it->amount,
@@ -51,18 +54,5 @@ bool extract(const_string str, struct item *it)
&it->card[1],
&it->card[2],
&it->card[3],
- &it->broken))
- || extract(str,
- record<','>(
- &it->id,
- &it->nameid,
- &it->amount,
- &it->equip,
- &it->identify,
- &it->refine,
- &it->attribute,
- &it->card[0],
- &it->card[1],
- &it->card[2],
- &it->card[3]));
+ &it->broken));
}
diff --git a/src/common/extract.hpp b/src/common/extract.hpp
index ae1a74b..3c24693 100644
--- a/src/common/extract.hpp
+++ b/src/common/extract.hpp
@@ -27,14 +27,15 @@
#include "mmo.hpp"
#include "utils.hpp"
-template<class T, typename=typename std::enable_if<std::is_integral<T>::value && !std::is_same<T, char>::value>::type>
-bool extract(const_string str, T *iv)
+template<class T, typename=typename std::enable_if<std::is_integral<T>::value && !std::is_same<T, char>::value && !std::is_same<T, bool>::value>::type>
+bool extract(XString str, T *iv)
{
if (!str || str.size() > 20)
return false;
if (!((str.front() == '-' && std::is_signed<T>::value)
|| ('0' <= str.front() && str.front() <= '9')))
return false;
+ // needs a NUL, but can't always be given one. TODO VString?
char buf[20 + 1];
std::copy(str.begin(), str.end(), buf);
buf[str.size()] = '\0';
@@ -60,14 +61,14 @@ bool extract(const_string str, T *iv)
}
inline
-bool extract(const_string str, TimeT *tv)
+bool extract(XString str, TimeT *tv)
{
return extract(str, &tv->value);
}
// extra typename=void to workaround some duplicate overload rule
template<class T, typename=typename std::enable_if<std::is_enum<T>::value>::type, typename=void>
-bool extract(const_string str, T *iv)
+bool extract(XString str, T *iv)
{
typedef typename underlying_type<T>::type U;
U v;
@@ -79,59 +80,90 @@ bool extract(const_string str, T *iv)
return true;
}
-bool extract(const_string str, const_string *rv);
+bool extract(XString str, XString *rv);
-bool extract(const_string str, std::string *rv);
+bool extract(XString str, FString *rv);
-template<size_t N>
-__attribute__((deprecated))
-bool extract(const_string str, char (*out)[N])
+template<uint8_t N>
+bool extract(XString str, VString<N> *out)
{
- if (str.size() >= N)
+ if (str.size() > N)
return false;
- std::copy(str.begin(), str.end(), *out);
- std::fill(*out + str.size() , *out + N, '\0');
+ *out = str;
return true;
}
+template<class T>
+class LStripper
+{
+public:
+ T impl;
+};
+
+template<class T>
+LStripper<T> lstripping(T v)
+{
+ return {v};
+}
+
+template<class T>
+bool extract(XString str, LStripper<T> out)
+{
+ return extract(str.lstrip(), out.impl);
+}
+
// basically just a std::tuple
// but it provides its data members publically
-template<char split, class... T>
+template<char split, int n, class... T>
class Record;
-template<char split>
-class Record<split>
+template<char split, int n>
+class Record<split, n>
{
};
-template<char split, class F, class... R>
-class Record<split, F, R...>
+template<char split, int n, class F, class... R>
+class Record<split, n, F, R...>
{
public:
F frist;
- Record<split, R...> rest;
+ Record<split, n - 1, R...> rest;
public:
Record(F f, R... r)
: frist(f), rest(r...)
{}
};
template<char split, class... T>
-Record<split, T...> record(T... t)
+Record<split, sizeof...(T), T...> record(T... t)
+{
+ return Record<split, sizeof...(T), T...>(t...);
+}
+template<char split, int n, class... T>
+Record<split, n, T...> record(T... t)
{
- return Record<split, T...>(t...);
+ static_assert(0 < n && n < sizeof...(T), "don't be silly");
+ return Record<split, n, T...>(t...);
}
-template<char split>
-bool extract(const_string str, Record<split>)
+template<char split, int n>
+bool extract(XString str, Record<split, n>)
{
return !str;
}
-template<char split, class F, class... R>
-bool extract(const_string str, Record<split, F, R...> rec)
+
+template<char split, int n, class F, class... R>
+bool extract(XString str, Record<split, n, F, R...> rec)
{
- const char *s = std::find(str.begin(), str.end(), split);
+ XString::iterator s = std::find(str.begin(), str.end(), split);
+ XString::iterator s2 = s;
+ if (s2 != str.end())
+ ++s2;
+ XString head = str.xislice_h(s);
+ XString tail = str.xislice_t(s2);
if (s == str.end())
- return sizeof...(R) == 0 && extract(str, rec.frist);
- return extract(const_string(str.begin(), s), rec.frist)
- && extract(const_string(s + 1, str.end()), rec.rest);
+ return (extract(head, rec.frist) && n <= 1)
+ || (!head && n <= 0);
+
+ return (extract(head, rec.frist) || n <= 0)
+ && extract(tail, rec.rest);
}
template<char split, class T>
@@ -147,20 +179,41 @@ VRecord<split, T> vrec(std::vector<T> *arr)
}
template<char split, class T>
-bool extract(const_string str, VRecord<split, T> rec)
+bool extract(XString str, VRecord<split, T> rec)
{
- if (str.empty())
+ if (!str)
return true;
- const char *s = std::find(str.begin(), str.end(), split);
+ XString::iterator s = std::find(str.begin(), str.end(), split);
rec.arr->emplace_back();
if (s == str.end())
return extract(str, &rec.arr->back());
- return extract(const_string(str.begin(), s), &rec.arr->back())
- && extract(const_string(s + 1, str.end()), rec);
+ return extract(str.xislice_h(s), &rec.arr->back())
+ && extract(str.xislice_t(s + 1), rec);
}
-bool extract(const_string str, struct global_reg *var);
+bool extract(XString str, struct global_reg *var);
+
+bool extract(XString str, struct item *it);
+
+inline
+bool extract(XString str, MapName *m)
+{
+ VString<15> tmp;
+ bool rv = extract(str, &tmp);
+ *m = tmp;
+ return rv;
+}
-bool extract(const_string str, struct item *it);
+inline
+bool extract(XString str, CharName *out)
+{
+ VString<23> tmp;
+ if (extract(str, &tmp))
+ {
+ *out = CharName(tmp);
+ return true;
+ }
+ return false;
+}
#endif // EXTRACT_HPP
diff --git a/src/common/extract_test.cpp b/src/common/extract_test.cpp
new file mode 100644
index 0000000..d8e9ebe
--- /dev/null
+++ b/src/common/extract_test.cpp
@@ -0,0 +1,319 @@
+#include "extract.hpp"
+
+#include <gtest/gtest.h>
+
+TEST(extract, record_int)
+{
+ int x, y, z;
+ x = y = z = 0;
+ EXPECT_FALSE(extract("1 2 3 4 ", record<' '>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(3, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract("1 2 3 4", record<' '>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(3, z);
+ x = y = z = 0;
+ EXPECT_TRUE(extract("1 2 3 ", record<' '>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(3, z);
+ x = y = z = 0;
+ EXPECT_TRUE(extract("1 2 3", record<' '>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(3, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract("1 2 ", record<' '>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract("1 2", record<' '>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract("1 ", record<' '>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(0, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract("1", record<' '>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(0, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract(" ", record<' '>(&x, &y, &z)));
+ EXPECT_EQ(0, x);
+ EXPECT_EQ(0, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract("", record<' '>(&x, &y, &z)));
+ EXPECT_EQ(0, x);
+ EXPECT_EQ(0, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+
+ EXPECT_FALSE(extract("1 2 3 4 ", record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(3, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract("1 2 3 4", record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(3, z);
+ x = y = z = 0;
+ EXPECT_TRUE(extract("1 2 3 ", record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(3, z);
+ x = y = z = 0;
+ EXPECT_TRUE(extract("1 2 3", record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(3, z);
+ x = y = z = 0;
+ EXPECT_TRUE(extract("1 2 ", record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_TRUE(extract("1 2", record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract("1 ", record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(0, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract("1", record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(0, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract(" ", record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ(0, x);
+ EXPECT_EQ(0, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract("", record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ(0, x);
+ EXPECT_EQ(0, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+
+ EXPECT_FALSE(extract("1 2 3 4 ", record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(3, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract("1 2 3 4", record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(3, z);
+ x = y = z = 0;
+ EXPECT_TRUE(extract("1 2 3 ", record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(3, z);
+ x = y = z = 0;
+ EXPECT_TRUE(extract("1 2 3", record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(3, z);
+ x = y = z = 0;
+ EXPECT_TRUE(extract("1 2 ", record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_TRUE(extract("1 2", record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(2, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_TRUE(extract("1 ", record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(0, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_TRUE(extract("1", record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ(1, x);
+ EXPECT_EQ(0, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract(" ", record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ(0, x);
+ EXPECT_EQ(0, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+ EXPECT_FALSE(extract("", record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ(0, x);
+ EXPECT_EQ(0, y);
+ EXPECT_EQ(0, z);
+ x = y = z = 0;
+}
+
+TEST(extract, record_str)
+{
+ XString x, y, z;
+ x = y = z = "";
+ EXPECT_FALSE(extract("1 2 3 4 ", record<' '>(&x, &y, &z)));
+ EXPECT_EQ("1", x);
+ EXPECT_EQ("2", y);
+ EXPECT_EQ("3", z);
+ x = y = z = "";
+ EXPECT_FALSE(extract("1 2 3 4", record<' '>(&x, &y, &z)));
+ EXPECT_EQ("1", x);
+ EXPECT_EQ("2", y);
+ EXPECT_EQ("3", z);
+ x = y = z = "";
+ EXPECT_TRUE(extract("1 2 3 ", record<' '>(&x, &y, &z)));
+ EXPECT_EQ("1", x);
+ EXPECT_EQ("2", y);
+ EXPECT_EQ("3", z);
+ x = y = z = "";
+ EXPECT_TRUE(extract("1 2 3", record<' '>(&x, &y, &z)));
+ EXPECT_EQ("1", x);
+ EXPECT_EQ("2", y);
+ EXPECT_EQ("3", z);
+ x = y = z = "";
+ EXPECT_TRUE(extract("1 2 ", record<' '>(&x, &y, &z)));
+ EXPECT_EQ("1", x);
+ EXPECT_EQ("2", y);
+ EXPECT_EQ("", z);
+ x = y = z = "";
+ EXPECT_FALSE(extract("1 2", record<' '>(&x, &y, &z)));
+ EXPECT_EQ("1", x);
+ EXPECT_EQ("2", y);
+ EXPECT_EQ("", z);
+ x = y = z = "";
+ EXPECT_FALSE(extract("1 ", record<' '>(&x, &y, &z)));
+ EXPECT_EQ("1", x);
+ EXPECT_EQ("", y);
+ EXPECT_EQ("", z);
+ x = y = z = "";
+ EXPECT_FALSE(extract("1", record<' '>(&x, &y, &z)));
+ EXPECT_EQ("1", x);
+ EXPECT_EQ("", y);
+ EXPECT_EQ("", z);
+ x = y = z = "";
+ EXPECT_FALSE(extract(" ", record<' '>(&x, &y, &z)));
+ EXPECT_EQ("", x);
+ EXPECT_EQ("", y);
+ EXPECT_EQ("", z);
+ x = y = z = "";
+ EXPECT_FALSE(extract("", record<' '>(&x, &y, &z)));
+ EXPECT_EQ("", x);
+ EXPECT_EQ("", y);
+ EXPECT_EQ("", z);
+ x = y = z = "";
+
+ EXPECT_FALSE(extract("1 2 3 4 ", record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ("1", x);
+ EXPECT_EQ("2", y);
+ EXPECT_EQ("3", z);
+ x = y = z = "";
+ EXPECT_FALSE(extract("1 2 3 4", record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ("1", x);
+ EXPECT_EQ("2", y);
+ EXPECT_EQ("3", z);
+ x = y = z = "";
+ EXPECT_TRUE(extract("1 2 3 ", record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ("1", x);
+ EXPECT_EQ("2", y);
+ EXPECT_EQ("3", z);
+ x = y = z = "";
+ EXPECT_TRUE(extract("1 2 3", record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ("1", x);
+ EXPECT_EQ("2", y);
+ EXPECT_EQ("3", z);
+ x = y = z = "";
+ EXPECT_TRUE(extract("1 2 ", record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ("1", x);
+ EXPECT_EQ("2", y);
+ EXPECT_EQ("", z);
+ x = y = z = "";
+ EXPECT_TRUE(extract("1 2", record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ("1", x);
+ EXPECT_EQ("2", y);
+ EXPECT_EQ("", z);
+ x = y = z = "";
+ EXPECT_TRUE(extract("1 ", record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ("1", x);
+ EXPECT_EQ("", y);
+ EXPECT_EQ("", z);
+ x = y = z = "";
+ EXPECT_FALSE(extract("1", record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ("1", x);
+ EXPECT_EQ("", y);
+ EXPECT_EQ("", z);
+ x = y = z = "";
+ EXPECT_TRUE(extract(" ", record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ("", x);
+ EXPECT_EQ("", y);
+ EXPECT_EQ("", z);
+ x = y = z = "";
+ EXPECT_FALSE(extract("", record<' ', 2>(&x, &y, &z)));
+ EXPECT_EQ("", x);
+ EXPECT_EQ("", y);
+ EXPECT_EQ("", z);
+ x = y = z = "";
+
+ EXPECT_FALSE(extract("1 2 3 4 ", record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ("1", x);
+ EXPECT_EQ("2", y);
+ EXPECT_EQ("3", z);
+ x = y = z = "";
+ EXPECT_FALSE(extract("1 2 3 4", record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ("1", x);
+ EXPECT_EQ("2", y);
+ EXPECT_EQ("3", z);
+ x = y = z = "";
+ EXPECT_TRUE(extract("1 2 3 ", record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ("1", x);
+ EXPECT_EQ("2", y);
+ EXPECT_EQ("3", z);
+ x = y = z = "";
+ EXPECT_TRUE(extract("1 2 3", record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ("1", x);
+ EXPECT_EQ("2", y);
+ EXPECT_EQ("3", z);
+ x = y = z = "";
+ EXPECT_TRUE(extract("1 2 ", record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ("1", x);
+ EXPECT_EQ("2", y);
+ EXPECT_EQ("", z);
+ x = y = z = "";
+ EXPECT_TRUE(extract("1 2", record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ("1", x);
+ EXPECT_EQ("2", y);
+ EXPECT_EQ("", z);
+ x = y = z = "";
+ EXPECT_TRUE(extract("1 ", record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ("1", x);
+ EXPECT_EQ("", y);
+ EXPECT_EQ("", z);
+ x = y = z = "";
+ EXPECT_TRUE(extract("1", record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ("1", x);
+ EXPECT_EQ("", y);
+ EXPECT_EQ("", z);
+ x = y = z = "";
+ EXPECT_TRUE(extract(" ", record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ("", x);
+ EXPECT_EQ("", y);
+ EXPECT_EQ("", z);
+ x = y = z = "";
+ EXPECT_TRUE(extract("", record<' ', 1>(&x, &y, &z)));
+ EXPECT_EQ("", x);
+ EXPECT_EQ("", y);
+ EXPECT_EQ("", z);
+ x = y = z = "";
+}
diff --git a/src/common/human_time_diff.hpp b/src/common/human_time_diff.hpp
new file mode 100644
index 0000000..3fc0f09
--- /dev/null
+++ b/src/common/human_time_diff.hpp
@@ -0,0 +1,85 @@
+#ifndef TMWA_COMMON_HUMAN_TIME_DIFF_HPP
+#define TMWA_COMMON_HUMAN_TIME_DIFF_HPP
+// human_time_diff.hpp - broken deltas
+//
+// 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 "extract.hpp"
+#include "strings.hpp"
+
+struct HumanTimeDiff
+{
+ short year, month, day, hour, minute, second;
+
+ explicit
+ operator bool()
+ {
+ return year || month || day || hour || minute || second;
+ }
+
+ bool operator !()
+ {
+ return !bool(*this);
+ }
+};
+
+inline
+bool extract(XString str, HumanTimeDiff *iv)
+{
+ // str is a sequence of [-+]?[0-9]+([ay]|m|[jd]|h|mn|s)
+ // there are NO spaces here
+ // parse by counting the number starts
+ auto is_num = [](char c)
+ { return c == '-' || c == '+' || ('0' <= c && c <= '9'); };
+ if (!str || !is_num(str.front()))
+ return false;
+ *iv = HumanTimeDiff{};
+ while (str)
+ {
+ auto it = std::find_if_not(str.begin(), str.end(), is_num);
+ auto it2 = std::find_if(it, str.end(), is_num);
+ XString number = str.xislice_h(it);
+ XString suffix = str.xislice(it, it2);
+ str = str.xislice_t(it2);
+
+ short *ptr = nullptr;
+ if (suffix == "y" || suffix == "a")
+ ptr = &iv->year;
+ else if (suffix == "m")
+ ptr = &iv->month;
+ else if (suffix == "j" || suffix == "d")
+ ptr = &iv->day;
+ else if (suffix == "h")
+ ptr = &iv->hour;
+ else if (suffix == "mn")
+ ptr = &iv->minute;
+ else if (suffix == "s")
+ ptr = &iv->second;
+ else
+ return false;
+ if (number.startswith('+') && !number.startswith("+-"))
+ number = number.xslice_t(1);
+ if (*ptr || !extract(number, ptr))
+ return false;
+ }
+ return true;
+}
+
+#endif // TMWA_COMMON_HUMAN_TIME_DIFF_HPP
diff --git a/src/common/human_time_diff_test.cpp b/src/common/human_time_diff_test.cpp
new file mode 100644
index 0000000..d11a116
--- /dev/null
+++ b/src/common/human_time_diff_test.cpp
@@ -0,0 +1,84 @@
+#include "human_time_diff.hpp"
+
+#include <gtest/gtest.h>
+
+// a sequence of [-+]?[0-9]+([ay]|m|[jd]|h|mn|s)
+
+TEST(humantimediff, single)
+{
+ HumanTimeDiff diff;
+
+ EXPECT_TRUE(extract("42y", &diff));
+ EXPECT_EQ(42, diff.year);
+ EXPECT_EQ(0, diff.month);
+ EXPECT_EQ(0, diff.day);
+ EXPECT_EQ(0, diff.hour);
+ EXPECT_EQ(0, diff.minute);
+ EXPECT_EQ(0, diff.second);
+
+ EXPECT_TRUE(extract("42m", &diff));
+ EXPECT_EQ(0, diff.year);
+ EXPECT_EQ(42, diff.month);
+ EXPECT_EQ(0, diff.day);
+ EXPECT_EQ(0, diff.hour);
+ EXPECT_EQ(0, diff.minute);
+ EXPECT_EQ(0, diff.second);
+
+ EXPECT_TRUE(extract("42d", &diff));
+ EXPECT_EQ(0, diff.year);
+ EXPECT_EQ(0, diff.month);
+ EXPECT_EQ(42, diff.day);
+ EXPECT_EQ(0, diff.hour);
+ EXPECT_EQ(0, diff.minute);
+ EXPECT_EQ(0, diff.second);
+
+ EXPECT_TRUE(extract("42h", &diff));
+ EXPECT_EQ(0, diff.year);
+ EXPECT_EQ(0, diff.month);
+ EXPECT_EQ(0, diff.day);
+ EXPECT_EQ(42, diff.hour);
+ EXPECT_EQ(0, diff.minute);
+ EXPECT_EQ(0, diff.second);
+
+ EXPECT_TRUE(extract("42mn", &diff));
+ EXPECT_EQ(0, diff.year);
+ EXPECT_EQ(0, diff.month);
+ EXPECT_EQ(0, diff.day);
+ EXPECT_EQ(0, diff.hour);
+ EXPECT_EQ(42, diff.minute);
+ EXPECT_EQ(0, diff.second);
+
+ EXPECT_TRUE(extract("42s", &diff));
+ EXPECT_EQ(0, diff.year);
+ EXPECT_EQ(0, diff.month);
+ EXPECT_EQ(0, diff.day);
+ EXPECT_EQ(0, diff.hour);
+ EXPECT_EQ(0, diff.minute);
+ EXPECT_EQ(42, diff.second);
+
+ EXPECT_TRUE(extract("+42y", &diff));
+ EXPECT_EQ(42, diff.year);
+ EXPECT_TRUE(extract("-42y", &diff));
+ EXPECT_EQ(-42, diff.year);
+ EXPECT_FALSE(extract("++42y", &diff));
+ EXPECT_FALSE(extract("+-42y", &diff));
+ EXPECT_FALSE(extract("-+42y", &diff));
+ EXPECT_FALSE(extract("--42y", &diff));
+ EXPECT_FALSE(extract("4+2y", &diff));
+ EXPECT_FALSE(extract("42z", &diff));
+}
+
+TEST(humantimediff, multiple)
+{
+ HumanTimeDiff diff;
+
+ EXPECT_TRUE(extract("42y23m-2d", &diff));
+ EXPECT_EQ(42, diff.year);
+ EXPECT_EQ(23, diff.month);
+ EXPECT_EQ(-2, diff.day);
+ EXPECT_EQ(0, diff.hour);
+ EXPECT_EQ(0, diff.minute);
+ EXPECT_EQ(0, diff.second);
+ EXPECT_FALSE(extract("1y2y", &diff));
+}
+
diff --git a/src/common/intern-pool.hpp b/src/common/intern-pool.hpp
index caa54e4..db840a2 100644
--- a/src/common/intern-pool.hpp
+++ b/src/common/intern-pool.hpp
@@ -4,16 +4,19 @@
#include <cassert>
#include <map>
-#include <string>
#include <vector>
+#include "strings.hpp"
+
class InternPool
{
- std::map<std::string, size_t> known;
- std::vector<std::string> names;
+ std::map<FString, size_t> known;
+ std::vector<FString> names;
public:
- size_t intern(const std::string& name)
+ size_t intern(XString name_)
{
+ FString name = name_;
+ // hm, I could change this to do aliases
auto pair = known.insert({name, known.size()});
if (pair.second)
names.push_back(name);
@@ -21,7 +24,7 @@ public:
return pair.first->second;
}
- const std::string& outtern(size_t sz) const
+ ZString outtern(size_t sz) const
{
return names[sz];
}
diff --git a/src/common/intern-pool_test.cpp b/src/common/intern-pool_test.cpp
new file mode 100644
index 0000000..3bbca7b
--- /dev/null
+++ b/src/common/intern-pool_test.cpp
@@ -0,0 +1,18 @@
+#include "intern-pool.hpp"
+
+#include <gtest/gtest.h>
+
+TEST(InternPool, whydoesthisalwaysneedasecondname)
+{
+ InternPool p;
+ EXPECT_EQ(0, p.size());
+ EXPECT_EQ(0, p.intern("hello"));
+ EXPECT_EQ(0, p.intern("hello"));
+ EXPECT_EQ(1, p.size());
+ EXPECT_EQ(1, p.intern("world"));
+ EXPECT_EQ(0, p.intern("hello"));
+ EXPECT_EQ(1, p.intern("world"));
+ EXPECT_EQ(2, p.size());
+ EXPECT_EQ("hello", p.outtern(0));
+ EXPECT_EQ("world", p.outtern(1));
+}
diff --git a/src/common/io.hpp b/src/common/io.hpp
new file mode 100644
index 0000000..1831651
--- /dev/null
+++ b/src/common/io.hpp
@@ -0,0 +1,26 @@
+#ifndef TMWA_COMMON_IO_HPP
+#define TMWA_COMMON_IO_HPP
+
+#include <istream>
+#include <ostream>
+
+#include "strings.hpp"
+
+namespace io
+{
+ inline
+ std::istream& getline(std::istream& in, FString& line)
+ {
+ std::string s;
+ if (std::getline(in, s))
+ {
+ std::string::const_iterator begin = s.cbegin(), end = s.cend();
+ if (begin != end && end[-1] == '\r')
+ --end;
+ line = FString(begin, end);
+ }
+ return in;
+ }
+} // namespace io
+
+#endif //TMWA_COMMON_IO_HPP
diff --git a/src/common/lock.cpp b/src/common/lock.cpp
index 82856e1..f19cd92 100644
--- a/src/common/lock.cpp
+++ b/src/common/lock.cpp
@@ -17,17 +17,17 @@ const int backup_count = 10;
/// (Until the file is closed, it keeps the old file)
// Start writing a tmpfile
-FILE *lock_fopen(const char *filename, int *info)
+FILE *lock_fopen(ZString filename, int *info)
{
FILE *fp;
int no = getpid();
// Get a filename that doesn't already exist
- std::string newfile;
+ FString newfile;
do
{
newfile = STRPRINTF("%s_%d.tmp", filename, no++);
- fp = fopen_(newfile.c_str(), "wx");
+ fp = fopen(newfile.c_str(), "wx");
}
while (!fp);
*info = --no;
@@ -35,22 +35,22 @@ FILE *lock_fopen(const char *filename, int *info)
}
// Delete the old file and rename the new file
-void lock_fclose(FILE *fp, const char *filename, int *info)
+void lock_fclose(FILE *fp, ZString filename, int *info)
{
if (fp)
{
- fclose_(fp);
+ fclose(fp);
int n = backup_count;
- std::string old_filename = STRPRINTF("%s.%d", filename, n);
+ FString old_filename = STRPRINTF("%s.%d", filename, n);
while (--n)
{
- std::string newer_filename = STRPRINTF("%s.%d", filename, n);
+ FString newer_filename = STRPRINTF("%s.%d", filename, n);
rename(newer_filename.c_str(), old_filename.c_str());
old_filename = std::move(newer_filename);
}
- rename(filename, old_filename.c_str());
+ rename(filename.c_str(), old_filename.c_str());
- std::string tmpfile = STRPRINTF("%s_%d.tmp", filename, *info);
- rename(tmpfile.c_str(), filename);
+ FString tmpfile = STRPRINTF("%s_%d.tmp", filename, *info);
+ rename(tmpfile.c_str(), filename.c_str());
}
}
diff --git a/src/common/lock.hpp b/src/common/lock.hpp
index f7ce2d8..df4d1f8 100644
--- a/src/common/lock.hpp
+++ b/src/common/lock.hpp
@@ -5,11 +5,13 @@
#include <cstdio>
+#include "strings.hpp"
+
// TODO replace with a class
/// Locked FILE I/O
// Changes are made in a separate file until lock_fclose
-FILE *lock_fopen(const char *filename, int *info);
-void lock_fclose(FILE * fp, const char *filename, int *info);
+FILE *lock_fopen(ZString filename, int *info);
+void lock_fclose(FILE *fp, ZString filename, int *info);
#endif // LOCK_HPP
diff --git a/src/common/md5calc.cpp b/src/common/md5calc.cpp
index 582c152..ae134b7 100644
--- a/src/common/md5calc.cpp
+++ b/src/common/md5calc.cpp
@@ -2,6 +2,7 @@
#include <cstring>
+#include "cxxstdio.hpp"
#include "random.hpp"
#include "utils.hpp"
@@ -171,7 +172,7 @@ void MD5_do_block(MD5_state* state, MD5_block block)
#undef d
}
-void MD5_to_bin(MD5_state state, uint8_t out[0x10])
+void MD5_to_bin(MD5_state state, md5_binary& out)
{
for (int i = 0; i < 0x10; i++)
out[i] = state.val[i / 4] >> 8 * (i % 4);
@@ -180,47 +181,47 @@ void MD5_to_bin(MD5_state state, uint8_t out[0x10])
static
const char hex[] = "0123456789abcdef";
-void MD5_to_str(MD5_state state, char out[0x21])
+void MD5_to_str(MD5_state state, md5_string& out_)
{
- uint8_t bin[16];
+ md5_binary bin;
MD5_to_bin(state, bin);
+ char out[0x20];
for (int i = 0; i < 0x10; i++)
out[2 * i] = hex[bin[i] >> 4],
out[2 * i + 1] = hex[bin[i] & 0xf];
- out[0x20] = '\0';
+ out_ = stringish<md5_string>(XString(out, out + 0x20, nullptr));
}
-MD5_state MD5_from_string(const char* msg, const size_t msglen)
+MD5_state MD5_from_string(XString msg)
{
MD5_state state;
MD5_init(&state);
MD5_block block;
- size_t rem = msglen;
- while (rem >= 64)
+ const uint64_t msg_full_len = msg.size();
+ while (msg.size() >= 64)
{
for (int i = 0; i < 0x10; i++)
X[i] = msg[4 * i + 0] | msg[4 * i + 1] << 8 | msg[4 * i + 2] << 16 | msg[4 * i + 3] << 24;
MD5_do_block(&state, block);
- msg += 64;
- rem -= 64;
+ msg = msg.xslice_t(64);
}
// now pad 1-512 bits + the 64-bit length - may be two blocks
uint8_t buf[0x40] = {};
- really_memcpy(buf, reinterpret_cast<const uint8_t *>(msg), rem);
- buf[rem] = 0x80; // a single one bit
- if (64 - rem > 8)
+ really_memcpy(buf, reinterpret_cast<const uint8_t *>(msg.data()), msg.size());
+ buf[msg.size()] = 0x80; // a single one bit
+ if (64 - msg.size() > 8)
{
for (int i = 0; i < 8; i++)
- buf[0x38 + i] = (static_cast<uint64_t>(msglen) * 8) >> (i * 8);
+ buf[0x38 + i] = (msg_full_len * 8) >> (i * 8);
}
for (int i = 0; i < 0x10; i++)
X[i] = buf[4 * i + 0] | buf[4 * i + 1] << 8 | buf[4 * i + 2] << 16 | buf[4 * i + 3] << 24;
MD5_do_block(&state, block);
- if (64 - rem <= 8)
+ if (64 - msg.size() <= 8)
{
really_memset0(buf, 0x38);
for (int i = 0; i < 8; i++)
- buf[0x38 + i] = (static_cast<uint64_t>(msglen) * 8) >> (i * 8);
+ buf[0x38 + i] = (msg_full_len * 8) >> (i * 8);
for (int i = 0; i < 0x10; i++)
X[i] = buf[4 * i + 0] | buf[4 * i + 1] << 8 | buf[4 * i + 2] << 16 | buf[4 * i + 3] << 24;
MD5_do_block(&state, block);
@@ -228,12 +229,8 @@ MD5_state MD5_from_string(const char* msg, const size_t msglen)
return state;
}
-// This could be reimplemented without the strlen()
-MD5_state MD5_from_cstring(const char* msg)
-{
- return MD5_from_string(msg, strlen(msg));
-}
-
+// TODO - refactor MD5 into a stream, and merge the implementations
+// I once implemented an ostream that does it ...
MD5_state MD5_from_FILE(FILE* in)
{
uint64_t total_len = 0;
@@ -286,50 +283,53 @@ MD5_state MD5_from_FILE(FILE* in)
// Hash a password with a salt.
// Whoever wrote this FAILS programming
-const char *MD5_saltcrypt(const char *key, const char *salt)
+AccountCrypt MD5_saltcrypt(AccountPass key, SaltString salt)
{
- char buf[65];
+ char cbuf[64] {};
// hash the key then the salt
// buf ends up as a 64-char NUL-terminated string
- MD5_to_str(MD5_from_cstring(key), buf);
- MD5_to_str(MD5_from_cstring(salt), buf + 32);
+ md5_string tbuf, tbuf2;
+ MD5_to_str(MD5_from_string(key), tbuf);
+ MD5_to_str(MD5_from_string(salt), tbuf2);
+ const auto it = std::copy(tbuf.begin(), tbuf.end(), std::begin(cbuf));
+ auto it2 = std::copy(tbuf2.begin(), tbuf2.end(), it);
+ assert(it2 == std::end(cbuf));
+
+ md5_string tbuf3;
+ MD5_to_str(MD5_from_string(XString(std::begin(cbuf), it2, nullptr)), tbuf3);
- // Hash the buffer back into sbuf - this is stupid
- // (luckily, putting the result into itself is safe)
- MD5_to_str(MD5_from_cstring(buf), buf + 32);
+ VString<31> obuf;
- static char obuf[33];
// This truncates the string, but we have to keep it like that for compatibility
- snprintf(obuf, 32, "!%s$%s", salt, buf + 32);
- return obuf;
+ SNPRINTF(obuf, 32, "!%s$%s", salt, tbuf3);
+ return stringish<AccountCrypt>(obuf);
}
-const char *make_salt(void)
+SaltString make_salt(void)
{
- static char salt[6];
+ char salt[5];
for (int i = 0; i < 5; i++)
// 126 would probably actually be okay
salt[i] = random_::in(48, 125);
- return salt;
+ return stringish<SaltString>(XString(salt + 0, salt + 5, nullptr));
}
-bool pass_ok(const char *password, const char *crypted)
+bool pass_ok(AccountPass password, AccountCrypt crypted)
{
- char buf[40];
- strzcpy(buf, crypted, 40);
// crypted is like !salt$hash
- char *salt = buf + 1;
- *strchr(salt, '$') = '\0';
+ auto begin = crypted.begin() + 1;
+ auto end = std::find(begin, crypted.end(), '$');
+ SaltString salt = stringish<SaltString>(crypted.xislice(begin, end));
- return !strcmp(crypted, MD5_saltcrypt(password, salt));
+ return crypted == MD5_saltcrypt(password, salt);
}
// [M|h]ashes up an IP address and a secret key
// to return a hopefully unique masked IP.
-struct in_addr MD5_ip(char *secret, struct in_addr ip)
+struct in_addr MD5_ip(struct in_addr ip)
{
- uint8_t obuf[16];
+ static SaltString secret = make_salt();
union
{
uint8_t bytes[4];
@@ -337,10 +337,10 @@ struct in_addr MD5_ip(char *secret, struct in_addr ip)
} conv;
// MD5sum a secret + the IP address
- char ipbuf[32] {};
- snprintf(ipbuf, sizeof(ipbuf), "%u%s", ip.s_addr, secret);
- /// TODO stop it from being a cstring
- MD5_to_bin(MD5_from_cstring(ipbuf), obuf);
+ VString<31> ipbuf;
+ SNPRINTF(ipbuf, 32, "%u%s", ip.s_addr, secret);
+ md5_binary obuf;
+ MD5_to_bin(MD5_from_string(ipbuf), obuf);
// Fold the md5sum to 32 bits, pack the bytes to an in_addr
conv.bytes[0] = obuf[0] ^ obuf[1] ^ obuf[8] ^ obuf[9];
diff --git a/src/common/md5calc.hpp b/src/common/md5calc.hpp
index de19e0f..98f44d6 100644
--- a/src/common/md5calc.hpp
+++ b/src/common/md5calc.hpp
@@ -9,44 +9,51 @@
#include <cstddef>
#include <cstdio>
+#include <array>
+
+#include "mmo.hpp"
+#include "strings.hpp"
+
/// The digest state - becomes the output
-typedef struct
+struct MD5_state
{
// classically named {A,B,C,D}
// but use an so we can index
uint32_t val[4];
-} MD5_state;
-typedef struct
+};
+struct MD5_block
{
uint32_t data[16];
-} MD5_block;
+};
+
+struct md5_binary : std::array<uint8_t, 0x10> {};
+struct md5_string : VString<0x20> {};
+struct SaltString : VString<5> {};
// Implementation
-void MD5_init(MD5_state* state);
-void MD5_do_block(MD5_state* state, MD5_block block);
+void MD5_init(MD5_state *state);
+void MD5_do_block(MD5_state *state, MD5_block block);
// Output formatting
-void MD5_to_bin(MD5_state state, uint8_t out[0x10]);
-void MD5_to_str(MD5_state state, char out[0x21]);
+void MD5_to_bin(MD5_state state, md5_binary& out);
+void MD5_to_str(MD5_state state, md5_string& out);
// Convenience
-MD5_state MD5_from_string(const char* msg, const size_t msglen);
-MD5_state MD5_from_cstring(const char* msg);
+MD5_state MD5_from_string(XString msg);
MD5_state MD5_from_FILE(FILE* in);
-// statically-allocated output
// whoever wrote this fails basic understanding of
-const char *MD5_saltcrypt(const char *key, const char *salt);
+AccountCrypt MD5_saltcrypt(AccountPass key, SaltString salt);
/// return some random characters (statically allocated)
// Currently, returns a 5-char string
-const char *make_salt(void);
+SaltString make_salt(void);
/// check plaintext password against saved saltcrypt
-bool pass_ok(const char *password, const char *crypted);
+bool pass_ok(AccountPass password, AccountCrypt crypted);
/// This returns an in_addr because it is configurable whether it gets called at all
-struct in_addr MD5_ip(char *secret, struct in_addr ip);
+struct in_addr MD5_ip(struct in_addr ip);
#endif // MD5CALC_HPP
diff --git a/src/common/md5calc_test.cpp b/src/common/md5calc_test.cpp
new file mode 100644
index 0000000..51b0b68
--- /dev/null
+++ b/src/common/md5calc_test.cpp
@@ -0,0 +1,27 @@
+#include "md5calc.hpp"
+
+#include <gtest/gtest.h>
+
+#include "utils.hpp"
+
+// This should be made part of the main API,
+// but is not yet to keep the diff small.
+// Edit: hack to fix the new strict comparison.
+static
+VString<32> MD5(XString in)
+{
+ md5_string out;
+ MD5_to_str(MD5_from_string(in), out);
+ return out;
+}
+
+TEST(md5calc, rfc1321)
+{
+ EXPECT_EQ("d41d8cd98f00b204e9800998ecf8427e", MD5(""));
+ EXPECT_EQ("0cc175b9c0f1b6a831c399e269772661", MD5("a"));
+ EXPECT_EQ("900150983cd24fb0d6963f7d28e17f72", MD5("abc"));
+ EXPECT_EQ("f96b697d7cb7938d525a2f31aaf161d0", MD5("message digest"));
+ EXPECT_EQ("c3fcd3d76192e4007dfb496cca67e13b", MD5("abcdefghijklmnopqrstuvwxyz"));
+ EXPECT_EQ("d174ab98d277d9f5a5611c2c9f419d9f", MD5("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"));
+ EXPECT_EQ("57edf4a22be3c955ac49da2e2107b67a", MD5("12345678901234567890123456789012345678901234567890123456789012345678901234567890"));
+}
diff --git a/src/common/mmo.hpp b/src/common/mmo.hpp
index 450aa61..89ff50a 100644
--- a/src/common/mmo.hpp
+++ b/src/common/mmo.hpp
@@ -4,7 +4,10 @@
# include "sanity.hpp"
# include "timer.t.hpp"
-# include "utils.hpp"
+# include "utils2.hpp"
+
+// affects CharName
+# define NAME_IGNORING_CASE 1
constexpr int FIFOSIZE_SERVERLINK = 256 * 1024;
@@ -37,6 +40,105 @@ constexpr int MAX_PARTY = 12;
# define CHAR_CONF_NAME "conf/char_athena.conf"
+struct AccountName : VString<23> {};
+struct AccountPass : VString<23> {};
+struct AccountCrypt : VString<39> {};
+struct AccountEmail : VString<39> {};
+struct ServerName : VString<19> {};
+struct PartyName : VString<23> {};
+struct VarName : VString<31> {};
+template<class T>
+T stringish(VString<sizeof(T) - 1> iv)
+{
+ T rv;
+ static_cast<VString<sizeof(T) - 1>&>(rv) = iv;
+ return rv;
+}
+#define DEFAULT_EMAIL stringish<AccountEmail>("a@a.com")
+
+// It is decreed: a mapname shall not contain an extension
+class MapName : public strings::_crtp_string<MapName, MapName, ZString, XString>
+{
+ VString<15> _impl;
+public:
+ MapName() = default;
+ MapName(VString<15> v) : _impl(v.oislice_h(std::find(v.begin(), v.end(), '.'))) {}
+
+ iterator begin() const { return &*_impl.begin(); }
+ iterator end() const { return &*_impl.begin(); }
+ const char *c_str() const { return _impl.c_str(); }
+
+ operator FString() const { return _impl; }
+ operator TString() const { return _impl; }
+ operator SString() const { return _impl; }
+ operator ZString() const { return _impl; }
+ operator XString() const { return _impl; }
+};
+template<>
+inline
+MapName stringish<MapName>(VString<15> iv)
+{
+ return iv;
+}
+inline
+const char *decay_for_printf(const MapName& vs) { return vs.c_str(); }
+
+// It is decreed: a charname is sometimes case sensitive
+struct CharName
+{
+private:
+ VString<23> _impl;
+public:
+ CharName() = default;
+ explicit CharName(VString<23> name)
+ : _impl(name)
+ {}
+
+ VString<23> to__actual() const
+ {
+ return _impl;
+ }
+ VString<23> to__lower() const
+ {
+ return _impl.to_lower();
+ }
+ VString<23> to__upper() const
+ {
+ return _impl.to_upper();
+ }
+ VString<23> to__canonical() const
+ {
+#if NAME_IGNORING_CASE == 0
+ return to__actual();
+#endif
+#if NAME_IGNORING_CASE == 1
+ return to__lower();
+#endif
+ }
+
+ friend bool operator == (const CharName& l, const CharName& r)
+ { return l.to__canonical() == r.to__canonical(); }
+ friend bool operator != (const CharName& l, const CharName& r)
+ { return l.to__canonical() != r.to__canonical(); }
+ friend bool operator < (const CharName& l, const CharName& r)
+ { return l.to__canonical() < r.to__canonical(); }
+ friend bool operator <= (const CharName& l, const CharName& r)
+ { return l.to__canonical() <= r.to__canonical(); }
+ friend bool operator > (const CharName& l, const CharName& r)
+ { return l.to__canonical() > r.to__canonical(); }
+ friend bool operator >= (const CharName& l, const CharName& r)
+ { return l.to__canonical() >= r.to__canonical(); }
+
+ friend
+ VString<23> convert_for_printf(const CharName& vs) { return vs.to__actual(); }
+};
+template<>
+inline
+CharName stringish<CharName>(VString<23> iv)
+{
+ return CharName(iv);
+}
+
namespace e
{
enum class EPOS : uint16_t
@@ -78,7 +180,7 @@ struct item
struct point
{
- char map_[16];
+ MapName map_;
short x, y;
};
@@ -105,7 +207,7 @@ struct skill_value
struct global_reg
{
- char str[32];
+ VarName str;
int value;
};
@@ -182,7 +284,7 @@ struct mmo_charstatus
short shield;
short head_top, head_mid, head_bottom;
- char name[24];
+ CharName name;
unsigned char base_level, job_level;
earray<short, ATTR, ATTR::COUNT> attrs;
unsigned char char_num, sex;
@@ -221,7 +323,8 @@ struct GM_Account
struct party_member
{
int account_id;
- char name[24], map[24];
+ CharName name;
+ MapName map;
int leader, online, lv;
struct map_session_data *sd;
};
@@ -229,16 +332,10 @@ struct party_member
struct party
{
int party_id;
- char name[24];
+ PartyName name;
int exp;
int item;
struct party_member member[MAX_PARTY];
};
-struct square
-{
- int val1[5];
- int val2[5];
-};
-
#endif // MMO_HPP
diff --git a/src/common/sanity.hpp b/src/common/sanity.hpp
index 74e24df..3658f9f 100644
--- a/src/common/sanity.hpp
+++ b/src/common/sanity.hpp
@@ -22,29 +22,6 @@
# if __GNUC_MINOR__ < 6 && !defined(__clang__)
# error "Please upgrade to at least GCC 4.6"
# endif // __GNUC_MINOR__ < 6 && !defined(__clang__)
-// temporary workaround for library issues
-// since __GLIBCXX__ is hard to use
-# if __GNUC_MINOR__ == 6
-# define WORKAROUND_GCC46_COMPILER
-# endif // __GNUC_MINOR__ == 6
-# ifdef __GLIBCXX__
-// versions of libstdc++ from gcc
-// 4.6.0, 4.6.1, 4.6.2, 4.6.3, 4.6.4
-# if __GLIBCXX__ == 20110325 \
- || __GLIBCXX__ == 20110627 \
- || __GLIBCXX__ == 20111026 \
- || __GLIBCXX__ == 20120301 \
- || __GLIBCXX__ == 20130412 \
- /* various Debian snapshots */ \
- || __GLIBCXX__ == 20121127 \
- || __GLIBCXX__ == 20130114
-# define WORKAROUND_GCC46_LIBRARY
-# endif // __GLIBCXX__ == ...
-# endif // defined __GLIBCXX__
-# if defined(WORKAROUND_GCC46_COMPILER) \
- && !defined(WORKAROUND_GCC46_LIBRARY)
-# error "Unknown gcc 4.6.x release"
-# endif // compiler and not library
# endif // __GNUC__ == 4
# if not defined(__i386__) and not defined(__x86_64__)
diff --git a/src/common/socket.cpp b/src/common/socket.cpp
index 214fb5a..2d08171 100644
--- a/src/common/socket.cpp
+++ b/src/common/socket.cpp
@@ -21,8 +21,6 @@
static
fd_set readfds;
int fd_max;
-static
-int currentuse;
static
const uint32_t RFIFO_SIZE = 65536;
@@ -128,16 +126,17 @@ void connect_client(int listen_fd)
perror("accept");
return;
}
- if (fd_max <= fd)
- {
- fd_max = fd + 1;
- }
- if (!free_fds())
+ if (fd >= SOFT_LIMIT)
{
FPRINTF(stderr, "softlimit reached, disconnecting : %d\n", fd);
- delete_session(fd);
+ shutdown(fd, SHUT_RDWR);
+ close(fd);
return;
}
+ if (fd_max <= fd)
+ {
+ fd_max = fd + 1;
+ }
const int yes = 1;
/// Allow to bind() again after the server restarts.
@@ -178,8 +177,6 @@ void connect_client(int listen_fd)
session[fd]->client_addr = client_address;
session[fd]->created = TimeT::now();
session[fd]->connected = 0;
-
- currentuse++;
}
int make_listen_port(uint16_t port)
@@ -237,7 +234,6 @@ int make_listen_port(uint16_t port)
session[fd]->created = TimeT::now();
session[fd]->connected = 1;
- currentuse++;
return fd;
}
@@ -295,7 +291,6 @@ int make_connection(uint32_t ip, uint16_t port)
session[fd]->created = TimeT::now();
session[fd]->connected = 1;
- currentuse++;
return fd;
}
@@ -326,13 +321,6 @@ void delete_session(int fd)
// just close() would try to keep sending buffers
shutdown(fd, SHUT_RDWR);
close(fd);
- currentuse--;
- if (currentuse < 0)
- {
- FPRINTF(stderr, "delete_session: current sessions negative!\n");
- currentuse = 0;
- }
- return;
}
void realloc_fifo(int fd, size_t rfifo_size, size_t wfifo_size)
@@ -446,7 +434,6 @@ void do_socket(void)
#pragma GCC diagnostic ignored "-Wold-style-cast"
FD_ZERO(&readfds);
#pragma GCC diagnostic pop
- currentuse = 3;
}
void RFIFOSKIP(int fd, size_t len)
@@ -460,23 +447,3 @@ void RFIFOSKIP(int fd, size_t len)
abort();
}
}
-
-void fclose_(FILE * fp)
-{
- if (fclose(fp))
- perror("fclose"), abort();
- currentuse--;
-}
-
-FILE *fopen_(const char *path, const char *mode)
-{
- FILE *f = fopen(path, mode);
- if (f)
- currentuse++;
- return f;
-}
-
-bool free_fds(void)
-{
- return currentuse < SOFT_LIMIT;
-}
diff --git a/src/common/socket.hpp b/src/common/socket.hpp
index 2366373..dd1c872 100644
--- a/src/common/socket.hpp
+++ b/src/common/socket.hpp
@@ -96,12 +96,6 @@ void do_socket(void);
// themselves as servers
void set_defaultparse(void(*defaultparse)(int));
-/// Wrappers to track number of free FDs
-void fclose_(FILE * fp);
-FILE *fopen_(const char *path, const char *mode);
-
-bool free_fds(void);
-
template<class T>
uint8_t *pod_addressof_m(T& structure)
{
@@ -149,10 +143,22 @@ void RFIFO_STRUCT(int fd, size_t pos, T& structure)
{
really_memcpy(pod_addressof_m(structure), static_cast<const uint8_t *>(RFIFOP(fd, pos)), sizeof(T));
}
+template<uint8_t len>
+inline
+VString<len-1> RFIFO_STRING(int fd, size_t pos)
+{
+ const char *const begin = static_cast<const char *>(RFIFOP(fd, pos));
+ const char *const end = begin + len-1;
+ const char *const mid = std::find(begin, end, '\0');
+ return XString(begin, mid, nullptr);
+}
inline
-void RFIFO_STRING(int fd, size_t pos, char *out, size_t len)
+FString RFIFO_STRING(int fd, size_t pos, size_t len)
{
- strzcpy(out, static_cast<const char *>(RFIFOP(fd, pos)), len);
+ const char *const begin = static_cast<const char *>(RFIFOP(fd, pos));
+ const char *const end = begin + len;
+ const char *const mid = std::find(begin, end, '\0');
+ return XString(begin, mid, nullptr);
}
inline
void RFIFO_BUF_CLONE(int fd, uint8_t *buf, size_t len)
@@ -189,14 +195,27 @@ void RBUF_STRUCT(const uint8_t *p, size_t pos, T& structure)
{
really_memcpy(pod_addressof_m(structure), p + pos, sizeof(T));
}
+template<uint8_t len>
+inline
+VString<len-1> RBUF_STRING(const uint8_t *p, size_t pos)
+{
+ const char *const begin = static_cast<const char *>(RBUFP(p, pos));
+ const char *const end = begin + len-1;
+ const char *const mid = std::find(begin, end, '\0');
+ return XString(begin, mid, nullptr);
+}
inline
-void RBUF_STRING(const uint8_t *p, size_t pos, char *out, size_t len)
+FString RBUF_STRING(const uint8_t *p, size_t pos, size_t len)
{
- strzcpy(out, static_cast<const char *>(RBUFP(p, pos)), len);
+ const char *const begin = static_cast<const char *>(RBUFP(p, pos));
+ const char *const end = begin + len;
+ const char *const mid = std::find(begin, end, '\0');
+ return XString(begin, mid, nullptr);
}
/// Unused - check how much data can be written
+// the existence of this seems scary
inline
size_t WFIFOSPACE(int fd)
{
@@ -229,9 +248,12 @@ void WFIFO_STRUCT(int fd, size_t pos, T& structure)
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)
+void WFIFO_STRING(int fd, size_t pos, XString s, size_t len)
{
- strzcpy(static_cast<char *>(WFIFOP(fd, pos)), s, len);
+ char *const begin = static_cast<char *>(WFIFOP(fd, pos));
+ char *const end = begin + len;
+ char *const mid = std::copy(s.begin(), s.end(), begin);
+ std::fill(mid, end, '\0');
}
inline
void WFIFO_ZERO(int fd, size_t pos, size_t len)
@@ -276,9 +298,12 @@ void WBUF_STRUCT(uint8_t *p, size_t pos, T& structure)
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)
+void WBUF_STRING(uint8_t *p, size_t pos, XString s, size_t len)
{
- strzcpy(static_cast<char *>(WBUFP(p, pos)), s, len);
+ char *const begin = static_cast<char *>(WBUFP(p, pos));
+ char *const end = begin + len;
+ char *const mid = std::copy(s.begin(), s.end(), begin);
+ std::fill(mid, end, '\0');
}
inline
void WBUF_ZERO(uint8_t *p, size_t pos, size_t len)
diff --git a/src/common/strings.hpp b/src/common/strings.hpp
index 8562ec4..ead3f52 100644
--- a/src/common/strings.hpp
+++ b/src/common/strings.hpp
@@ -22,10 +22,17 @@
#include "sanity.hpp"
#include <cassert>
+#include <cstdarg>
#include <cstring>
+#include <algorithm>
+#include <deque>
#include <iterator>
-#include <string>
+#include <memory>
+#include <type_traits>
+#include <vector>
+
+#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
@@ -64,13 +71,13 @@ namespace strings
_iterator(const char *p=nullptr) : _ptr(p) {}
// iterator
- reference operator *() { return *_ptr; }
+ 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->() { return _ptr; }
+ 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
@@ -83,8 +90,8 @@ namespace strings
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; }
+ 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); }
@@ -92,7 +99,7 @@ namespace strings
/// 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>
+ template<class T, class O, class Z, class X=XString>
class _crtp_string
{
public:
@@ -108,22 +115,67 @@ namespace strings
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(); }
+ 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(); }
- 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;
+ 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
};
@@ -134,53 +186,112 @@ namespace strings
class MString
{
public:
- typedef char *iterator;
- typedef _iterator<MString> const_iterator;
+ typedef std::deque<char>::iterator iterator;
+ typedef std::deque<char>::const_iterator const_iterator;
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
private:
- std::string _hack;
+ std::deque<char> _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(); }
+ 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<FString>
+ class FString : public _crtp_string<FString, FString, ZString, XString>
{
- /*const*/ std::string _hack;
+ std::shared_ptr<std::vector<char>> _hack2;
+
+ template<class It>
+ void _assign(It b, It e)
+ {
+ if (b == e)
+ {
+ // TODO use a special empty object
+ // return;
+ }
+ if (!std::is_base_of<std::forward_iterator_tag, typename std::iterator_traits<It>::iterator_category>::value)
+ {
+ // can't use std::distance
+ _hack2 = std::make_shared<std::vector<char>>();
+ 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<std::vector<char>>(diff + 1, '\0');
+ std::copy(b, e, _hack2->begin());
+ }
public:
-#ifndef __clang__
- __attribute__((warning("This should be removed in the next diff")))
-#endif
- FString(std::string s) : _hack(std::move(s)) {}
+ FString()
+ {
+ const char *sadness = "";
+ _assign(sadness, sadness);
+ }
+
+ explicit FString(const MString& s)
+ {
+ _assign(s.begin(), s.end());
+ }
- 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) {}
+ FString(const char (&s)[n])
+ {
+ _assign(s, s + strlen(s));
+ }
+
template<class It>
- FString(It b, It e) : _hack(b, e) {}
+ FString(It b, It e)
+ {
+ _assign(b, e);
+ }
+
- iterator begin() const { return &*_hack.begin(); }
- iterator end() const { return &*_hack.end(); }
+ 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(); }
@@ -188,13 +299,16 @@ namespace strings
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<TString>
+ class TString : public _crtp_string<TString, TString, ZString, XString>
{
friend class SString;
FString _s;
@@ -206,6 +320,8 @@ namespace strings
TString(char (&s)[n]) = delete;
template<size_t n>
TString(const char (&s)[n]) : _s(s), _o(0) {}
+ //template<class It>
+ //TString(It b, It e) : _s(b, e), _o(0) {}
iterator begin() const { return &_s.begin()[_o]; }
iterator end() const { return &*_s.end(); }
@@ -216,28 +332,32 @@ namespace strings
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()
+ 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<SString>
+ class SString : public _crtp_string<SString, SString, XString, XString>
{
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(TString t) : _s(t._s), _b(0), _e(_s.size()) {}
template<size_t n>
SString(char (&s)[n]) = delete;
template<size_t n>
SString(const char (&s)[n]) : _s(s), _b(0), _e(_s.size()) {}
-
+ //template<class It>
+ //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]; }
@@ -248,34 +368,34 @@ namespace strings
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()
+ operator FString() const
{ if (_b == 0 && _e == _s.size()) return _s; else return FString(begin(), end()); }
- operator TString()
+ 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<ZString>
+ class ZString : public _crtp_string<ZString, FString, ZString, XString>
{
iterator _b, _e;
// optional
const FString *_base;
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()), _base(nullptr) {}
-
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<size_t n>
ZString(char (&s)[n]) = delete;
@@ -291,27 +411,30 @@ namespace strings
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()
+ operator FString() const
{ if (_base) return SString(*_base, &*_b - &*_base->begin(), &*_e - &*_base->begin()); else return FString(_b, _e); }
- operator TString()
+ operator TString() const
{ if (_base) return SString(*_base, &*_b - &*_base->begin(), &*_e - &*_base->begin()); else return FString(_b, _e); }
- operator SString()
+ 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<XString>
+ class XString : public _crtp_string<XString, FString, XString, XString>
{
iterator _b, _e;
// optional
const FString *_base;
public:
// do I really want this?
- XString() : _b(nullptr), _e(nullptr) {}
+ 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()) {}
@@ -331,32 +454,34 @@ namespace strings
iterator begin() const { return _b; }
iterator end() const { return _e; }
- const FString *base() const { return _base; };
+ 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()
+ operator FString() const
{ if (_base) return SString(*_base, &*_b - &*_base->begin(), &*_e - &*_base->begin()); else return FString(_b, _e); }
- operator TString()
+ operator TString() const
{ if (_base) return SString(*_base, &*_b - &*_base->begin(), &*_e - &*_base->begin()); else return FString(_b, _e); }
- operator SString()
+ operator SString() const
{ if (_base) return SString(*_base, &*_b - &*_base->begin(), &*_e - &*_base->begin()); else return FString(_b, _e); }
- operator ZString() = delete;
+ operator ZString() const = delete;
};
template<uint8_t n>
- class VString : public _crtp_string<VString<n>>
+ class VString : public _crtp_string<VString<n>, VString<n>, ZString, XString>
{
char _data[n];
unsigned char _special;
- typedef typename _crtp_string<VString<n>>::iterator iterator;
public:
- static_assert(n & 1, "Size should probably be odd.");
+ typedef typename _crtp_string<VString<n>, VString<n>, ZString, XString>::iterator iterator;
VString(XString x) : _data(), _special()
{
if (x.size() > n)
@@ -397,71 +522,181 @@ namespace strings
{
*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 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<uint8_t m>
+ operator VString<m>() const
+ {
+ static_assert(m > n, "can only grow");
+ XString x = *this;
+ return VString<m>(XString(x));
+ }
+ };
// not really intended for public use
inline
int xstr_compare(XString l, XString r)
{
- return std::lexicographical_compare(
+ 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 L, class R>
- bool operator == (const L& l, const R& r)
+ class string_comparison_allowed
+ {
+ constexpr static bool l_is_vstring_exact = std::is_same<VString<sizeof(L) - 1>, L>::value;
+ constexpr static bool l_is_vstring_approx = std::is_base_of<VString<sizeof(L) - 1>, L>::value;
+ constexpr static bool r_is_vstring_exact = std::is_same<VString<sizeof(R) - 1>, R>::value;
+ constexpr static bool r_is_vstring_approx = std::is_base_of<VString<sizeof(R) - 1>, 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<L, R>::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>, 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<VString<1>, XString>::value, "vx");
+ static_assert(string_comparison_allowed<XString, XString>::value, "xx");
+ static_assert(string_comparison_allowed<XString, FString>::value, "xf");
+
+ template<class L, class R, typename=typename std::enable_if<string_comparison_allowed<L, R>::value>::type>
+ auto operator == (const L& l, const R& r) -> decltype((xstr_compare(l, r), true))
{
return xstr_compare(l, r) == 0;
}
- template<class L, class R>
- bool operator != (const L& l, const R& r)
+ template<class L, class R, typename=typename std::enable_if<string_comparison_allowed<L, R>::value>::type>
+ auto operator != (const L& l, const R& r) -> decltype((xstr_compare(l, r), true))
{
return xstr_compare(l, r) != 0;
}
- template<class L, class R>
- bool operator < (const L& l, const R& r)
+ template<class L, class R, typename=typename std::enable_if<string_comparison_allowed<L, R>::value>::type>
+ auto operator < (const L& l, const R& r) -> decltype((xstr_compare(l, r), true))
{
return xstr_compare(l, r) < 0;
}
- template<class L, class R>
- bool operator <= (const L& l, const R& r)
+ template<class L, class R, typename=typename std::enable_if<string_comparison_allowed<L, R>::value>::type>
+ auto operator <= (const L& l, const R& r) -> decltype((xstr_compare(l, r), true))
{
return xstr_compare(l, r) <= 0;
}
- template<class L, class R>
- bool operator > (const L& l, const R& r)
+ template<class L, class R, typename=typename std::enable_if<string_comparison_allowed<L, R>::value>::type>
+ auto operator > (const L& l, const R& r) -> decltype((xstr_compare(l, r), true))
{
return xstr_compare(l, r) > 0;
}
- template<class L, class R>
- bool operator >= (const L& l, const R& r)
+ template<class L, class R, typename=typename std::enable_if<string_comparison_allowed<L, R>::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;
@@ -472,30 +707,176 @@ namespace strings
typedef XString XS;
// _crtp_string
- template<class T>
- XS _crtp_string<T>::xslice_t(size_t o) const
- { return XS(&begin()[o], &*end(), base()); }
- template<class T>
- XS _crtp_string<T>::xslice_h(size_t o) const
- { return XS(&*begin(), &begin()[o], base()); }
- template<class T>
- XS _crtp_string<T>::xrslice_t(size_t no) const
- { return XS(&end()[-no], &*end(), base()); }
- template<class T>
- XS _crtp_string<T>::xrslice_h(size_t no) const
- { return XS(&*begin(), &end()[-no], base()); }
- template<class T>
- XS _crtp_string<T>::xlslice(size_t o, size_t l) const
- { return XS(&begin()[o], &begin()[o + l], base()); }
- template<class T>
- XS _crtp_string<T>::xpslice(size_t b, size_t e) const
- { return XS(&begin()[b], &begin()[e], base()); }
- 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
+ template<class T, class O, class Z, class X>
+ Z _crtp_string<T, O, Z, X>::xslice_t(size_t o) const
+ { return Z(&begin()[o], &*end(), base()); }
+ template<class T, class O, class Z, class X>
+ X _crtp_string<T, O, Z, X>::xslice_h(size_t o) const
+ { return X(&*begin(), &begin()[o], base()); }
+ template<class T, class O, class Z, class X>
+ Z _crtp_string<T, O, Z, X>::xrslice_t(size_t no) const
+ { return Z(&end()[-no], &*end(), base()); }
+ template<class T, class O, class Z, class X>
+ X _crtp_string<T, O, Z, X>::xrslice_h(size_t no) const
+ { return X(&*begin(), &end()[-no], base()); }
+ template<class T, class O, class Z, class X>
+ Z _crtp_string<T, O, Z, X>::xislice_t(iterator it) const
+ { return Z(&*it, &*end(), base()); }
+ template<class T, class O, class Z, class X>
+ X _crtp_string<T, O, Z, X>::xislice_h(iterator it) const
+ { return X(&*begin(), &*it, base()); }
+ template<class T, class O, class Z, class X>
+ X _crtp_string<T, O, Z, X>::xlslice(size_t o, size_t l) const
+ { return X(&begin()[o], &begin()[o + l], base()); }
+ template<class T, class O, class Z, class X>
+ X _crtp_string<T, O, Z, X>::xpslice(size_t b, size_t e) const
+ { return X(&begin()[b], &begin()[e], base()); }
+ template<class T, class O, class Z, class X>
+ X _crtp_string<T, O, Z, X>::xislice(iterator b, iterator e) const
+ { return X(&*b, &*e, base()); }
+ template<class T, class O, class Z, class X>
+ Z _crtp_string<T, O, Z, X>::lstrip() const
+ {
+ Z z = _ref();
+ while (z.startswith(' '))
+ z = z.xslice_t(1);
+ return z;
+ }
+ template<class T, class O, class Z, class X>
+ X _crtp_string<T, O, Z, X>::rstrip() const
+ {
+ X x = _ref();
+ while (x.endswith(' '))
+ x = x.xrslice_h(1);
+ return x;
+ }
+ template<class T, class O, class Z, class X>
+ X _crtp_string<T, O, Z, X>::strip() const
+ { return lstrip().rstrip(); }
+
+ template<class T, class O, class Z, class X>
+ bool _crtp_string<T, O, Z, X>::startswith(XS x) const
+ { return size() >= x.size() && xslice_h(x.size()) == x; }
+ template<class T, class O, class Z, class X>
+ bool _crtp_string<T, O, Z, X>::endswith(XS x) const
{ return size() > x.size() && xrslice_t(x.size()) == x; }
+ template<class T, class O, class Z, class X>
+ bool _crtp_string<T, O, Z, X>::startswith(char c) const
+ { return size() && front() == c; }
+ template<class T, class O, class Z, class X>
+ bool _crtp_string<T, O, Z, X>::endswith(char c) const
+ { return size() && back() == c; }
+ template<class T, class O, class Z, class X>
+ bool _crtp_string<T, O, Z, X>::contains(char c) const
+ { return std::find(begin(), end(), c) != end(); }
+ template<class T, class O, class Z, class X>
+ bool _crtp_string<T, O, Z, X>::contains_seq(XString s) const
+ { return std::search(begin(), end(), s.begin(), s.end()) != end(); }
+ template<class T, class O, class Z, class X>
+ bool _crtp_string<T, O, Z, X>::contains_any(XString s) const
+ { return std::find_if(begin(), end(), [s](char c) { return s.contains(c); }) != end(); }
+
+ template<class T, class O, class Z, class X>
+ bool _crtp_string<T, O, Z, X>::has_print() const
+ { return std::find_if(begin(), end(), detail::is_print) != end(); }
+ template<class T, class O, class Z, class X>
+ bool _crtp_string<T, O, Z, X>::is_print() const
+ { return std::find_if_not(begin(), end(), detail::is_print) == end(); }
+ template<class T, class O, class Z, class X>
+ O _crtp_string<T, O, Z, X>::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<class T, class O, class Z, class X>
+ bool _crtp_string<T, O, Z, X>::has_graph() const
+ { return std::find_if(begin(), end(), detail::is_graph) != end(); }
+ template<class T, class O, class Z, class X>
+ bool _crtp_string<T, O, Z, X>::is_graph() const
+ { return std::find_if_not(begin(), end(), detail::is_graph) == end(); }
+
+ template<class T, class O, class Z, class X>
+ bool _crtp_string<T, O, Z, X>::has_lower() const
+ { return std::find_if(begin(), end(), detail::is_lower) != end(); }
+ template<class T, class O, class Z, class X>
+ bool _crtp_string<T, O, Z, X>::is_lower() const
+ { return std::find_if_not(begin(), end(), detail::is_lower) == end(); }
+ template<class T, class O, class Z, class X>
+ O _crtp_string<T, O, Z, X>::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<class T, class O, class Z, class X>
+ bool _crtp_string<T, O, Z, X>::has_upper() const
+ { return std::find_if(begin(), end(), detail::is_upper) != end(); }
+ template<class T, class O, class Z, class X>
+ bool _crtp_string<T, O, Z, X>::is_upper() const
+ { return std::find_if_not(begin(), end(), detail::is_upper) == end(); }
+ template<class T, class O, class Z, class X>
+ O _crtp_string<T, O, Z, X>::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<class T, class O, class Z, class X>
+ bool _crtp_string<T, O, Z, X>::has_alpha() const
+ { return std::find_if(begin(), end(), detail::is_alpha) != end(); }
+ template<class T, class O, class Z, class X>
+ bool _crtp_string<T, O, Z, X>::is_alpha() const
+ { return std::find_if_not(begin(), end(), detail::is_alpha) == end(); }
+
+ template<class T, class O, class Z, class X>
+ bool _crtp_string<T, O, Z, X>::has_digit2() const
+ { return std::find_if(begin(), end(), detail::is_digit2) != end(); }
+ template<class T, class O, class Z, class X>
+ bool _crtp_string<T, O, Z, X>::is_digit2() const
+ { return std::find_if_not(begin(), end(), detail::is_digit2) == end(); }
+ template<class T, class O, class Z, class X>
+ bool _crtp_string<T, O, Z, X>::has_digit8() const
+ { return std::find_if(begin(), end(), detail::is_digit8) != end(); }
+ template<class T, class O, class Z, class X>
+ bool _crtp_string<T, O, Z, X>::is_digit8() const
+ { return std::find_if_not(begin(), end(), detail::is_digit8) == end(); }
+ template<class T, class O, class Z, class X>
+ bool _crtp_string<T, O, Z, X>::has_digit10() const
+ { return std::find_if(begin(), end(), detail::is_digit10) != end(); }
+ template<class T, class O, class Z, class X>
+ bool _crtp_string<T, O, Z, X>::is_digit10() const
+ { return std::find_if_not(begin(), end(), detail::is_digit10) == end(); }
+ template<class T, class O, class Z, class X>
+ bool _crtp_string<T, O, Z, X>::has_digit16() const
+ { return std::find_if(begin(), end(), detail::is_digit16) != end(); }
+ template<class T, class O, class Z, class X>
+ bool _crtp_string<T, O, Z, X>::is_digit16() const
+ { return std::find_if_not(begin(), end(), detail::is_digit16) == end(); }
+
+ template<class T, class O, class Z, class X>
+ bool _crtp_string<T, O, Z, X>::has_alnum() const
+ { return std::find_if(begin(), end(), detail::is_alnum) != end(); }
+ template<class T, class O, class Z, class X>
+ bool _crtp_string<T, O, Z, X>::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
@@ -511,11 +892,20 @@ namespace strings
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
@@ -531,11 +921,20 @@ namespace strings
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
@@ -551,11 +950,20 @@ namespace strings
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
@@ -571,22 +979,97 @@ namespace strings
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 *convert_for_printf(const FString& fs) { return fs.c_str(); }
+ const char *decay_for_printf(const TString& ts) { return ts.c_str(); }
inline
- const char *convert_for_printf(const TString& ts) { return ts.c_str(); }
+ const char *decay_for_printf(const ZString& zs) { return zs.c_str(); }
+ template<uint8_t n>
inline
- const char *convert_for_printf(const ZString& zs) { return zs.c_str(); }
+ const char *decay_for_printf(const VString<n>& vs) { return vs.c_str(); }
+
+ template<uint8_t len>
+ inline __attribute__((format(printf, 2, 0)))
+ int do_vprint(VString<len>& 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 &mid;
+ }
+ };
+
+ inline
+ StringConverter convert_for_scanf(FString& s)
+ {
+ return StringConverter(s);
+ }
} // namespace strings
// owning
diff --git a/src/common/strings2_test.cpp b/src/common/strings2_test.cpp
new file mode 100644
index 0000000..fa4dc6f
--- /dev/null
+++ b/src/common/strings2_test.cpp
@@ -0,0 +1,118 @@
+#include "strings.hpp"
+
+#include <gtest/gtest.h>
+
+TEST(StringTests, traits2)
+{
+ ZString print_non = "\t\e";
+ ZString print_mix = "n\t";
+ FString print_all = "n ";
+ EXPECT_FALSE(print_non.has_print());
+ EXPECT_TRUE(print_mix.has_print());
+ EXPECT_TRUE(print_all.has_print());
+ EXPECT_FALSE(print_non.is_print());
+ EXPECT_FALSE(print_mix.is_print());
+ EXPECT_TRUE(print_all.is_print());
+ EXPECT_EQ("__", print_non.to_print());
+ EXPECT_EQ("n_", print_mix.to_print());
+ EXPECT_EQ("n ", print_all.to_print());
+ EXPECT_EQ(print_all.begin(), print_all.to_print().begin());
+
+ ZString graph_non = " \e";
+ ZString graph_mix = "n ";
+ FString graph_all = "n.";
+ EXPECT_FALSE(graph_non.has_graph());
+ EXPECT_TRUE(graph_mix.has_graph());
+ EXPECT_TRUE(graph_all.has_graph());
+ EXPECT_FALSE(graph_non.is_graph());
+ EXPECT_FALSE(graph_mix.is_graph());
+ EXPECT_TRUE(graph_all.is_graph());
+
+ ZString lower_non = "0A";
+ ZString lower_mix = "Oa";
+ FString lower_all = "oa";
+ EXPECT_FALSE(lower_non.has_lower());
+ EXPECT_TRUE(lower_mix.has_lower());
+ EXPECT_TRUE(lower_all.has_lower());
+ EXPECT_FALSE(lower_non.is_lower());
+ EXPECT_FALSE(lower_mix.is_lower());
+ EXPECT_TRUE(lower_all.is_lower());
+ EXPECT_EQ("0a", lower_non.to_lower());
+ EXPECT_EQ("oa", lower_mix.to_lower());
+ EXPECT_EQ("oa", lower_all.to_lower());
+ EXPECT_EQ(lower_all.begin(), lower_all.to_lower().begin());
+
+ ZString upper_non = "0a";
+ ZString upper_mix = "oA";
+ FString upper_all = "OA";
+ EXPECT_FALSE(upper_non.has_upper());
+ EXPECT_TRUE(upper_mix.has_upper());
+ EXPECT_TRUE(upper_all.has_upper());
+ EXPECT_FALSE(upper_non.is_upper());
+ EXPECT_FALSE(upper_mix.is_upper());
+ EXPECT_TRUE(upper_all.is_upper());
+ EXPECT_EQ("0A", upper_non.to_upper());
+ EXPECT_EQ("OA", upper_mix.to_upper());
+ EXPECT_EQ("OA", upper_all.to_upper());
+ EXPECT_EQ(upper_all.begin(), upper_all.to_upper().begin());
+
+ ZString alpha_non = " 0";
+ ZString alpha_mix = "n ";
+ FString alpha_all = "nA";
+ EXPECT_FALSE(alpha_non.has_alpha());
+ EXPECT_TRUE(alpha_mix.has_alpha());
+ EXPECT_TRUE(alpha_all.has_alpha());
+ EXPECT_FALSE(alpha_non.is_alpha());
+ EXPECT_FALSE(alpha_mix.is_alpha());
+ EXPECT_TRUE(alpha_all.is_alpha());
+
+ ZString digit2_non = "a9";
+ ZString digit2_mix = "20";
+ FString digit2_all = "01";
+ EXPECT_FALSE(digit2_non.has_digit2());
+ EXPECT_TRUE(digit2_mix.has_digit2());
+ EXPECT_TRUE(digit2_all.has_digit2());
+ EXPECT_FALSE(digit2_non.is_digit2());
+ EXPECT_FALSE(digit2_mix.is_digit2());
+ EXPECT_TRUE(digit2_all.is_digit2());
+
+ ZString digit8_non = "a9";
+ ZString digit8_mix = "80";
+ FString digit8_all = "37";
+ EXPECT_FALSE(digit8_non.has_digit8());
+ EXPECT_TRUE(digit8_mix.has_digit8());
+ EXPECT_TRUE(digit8_all.has_digit8());
+ EXPECT_FALSE(digit8_non.is_digit8());
+ EXPECT_FALSE(digit8_mix.is_digit8());
+ EXPECT_TRUE(digit8_all.is_digit8());
+
+ ZString digit10_non = "az";
+ ZString digit10_mix = "a9";
+ FString digit10_all = "42";
+ EXPECT_FALSE(digit10_non.has_digit10());
+ EXPECT_TRUE(digit10_mix.has_digit10());
+ EXPECT_TRUE(digit10_all.has_digit10());
+ EXPECT_FALSE(digit10_non.is_digit10());
+ EXPECT_FALSE(digit10_mix.is_digit10());
+ EXPECT_TRUE(digit10_all.is_digit10());
+
+ ZString digit16_non = "gz";
+ ZString digit16_mix = "ao";
+ FString digit16_all = "be";
+ EXPECT_FALSE(digit16_non.has_digit16());
+ EXPECT_TRUE(digit16_mix.has_digit16());
+ EXPECT_TRUE(digit16_all.has_digit16());
+ EXPECT_FALSE(digit16_non.is_digit16());
+ EXPECT_FALSE(digit16_mix.is_digit16());
+ EXPECT_TRUE(digit16_all.is_digit16());
+
+ ZString alnum_non = " .";
+ ZString alnum_mix = "n ";
+ FString alnum_all = "n0";
+ EXPECT_FALSE(alnum_non.has_alnum());
+ EXPECT_TRUE(alnum_mix.has_alnum());
+ EXPECT_TRUE(alnum_all.has_alnum());
+ EXPECT_FALSE(alnum_non.is_alnum());
+ EXPECT_FALSE(alnum_mix.is_alnum());
+ EXPECT_TRUE(alnum_all.is_alnum());
+}
diff --git a/src/common/strings_test.cpp b/src/common/strings_test.cpp
index b6a6f67..fa04f1c 100644
--- a/src/common/strings_test.cpp
+++ b/src/common/strings_test.cpp
@@ -1,4 +1,6 @@
-#include "../../src/common/strings.hpp"
+#include "strings.hpp"
+
+#include <algorithm>
#include <gtest/gtest.h>
@@ -22,6 +24,118 @@ TYPED_TEST_P(StringTest, basic)
const FString *base = hi.base();
}
+TYPED_TEST_P(StringTest, order)
+{
+ TypeParam a;
+ TypeParam b("Hello");
+ TypeParam c("Hello,");
+ TypeParam d("World!");
+
+ // not using EXPECT_LT, etc. for better visibility
+
+ EXPECT_FALSE(a < a);
+ EXPECT_TRUE(a < b);
+ EXPECT_TRUE(a < c);
+ EXPECT_TRUE(a < d);
+ EXPECT_FALSE(b < a);
+ EXPECT_FALSE(b < b);
+ EXPECT_TRUE(b < c);
+ EXPECT_TRUE(b < d);
+ EXPECT_FALSE(c < a);
+ EXPECT_FALSE(c < b);
+ EXPECT_FALSE(c < c);
+ EXPECT_TRUE(c < d);
+ EXPECT_FALSE(d < a);
+ EXPECT_FALSE(d < b);
+ EXPECT_FALSE(d < c);
+ EXPECT_FALSE(d < d);
+
+ EXPECT_TRUE(a <= a);
+ EXPECT_TRUE(a <= b);
+ EXPECT_TRUE(a <= c);
+ EXPECT_TRUE(a <= d);
+ EXPECT_FALSE(b <= a);
+ EXPECT_TRUE(b <= b);
+ EXPECT_TRUE(b <= c);
+ EXPECT_TRUE(b <= d);
+ EXPECT_FALSE(c <= a);
+ EXPECT_FALSE(c <= b);
+ EXPECT_TRUE(c <= c);
+ EXPECT_TRUE(c <= d);
+ EXPECT_FALSE(d <= a);
+ EXPECT_FALSE(d <= b);
+ EXPECT_FALSE(d <= c);
+ EXPECT_TRUE(d <= d);
+
+ EXPECT_TRUE(a >= a);
+ EXPECT_FALSE(a >= b);
+ EXPECT_FALSE(a >= c);
+ EXPECT_FALSE(a >= d);
+ EXPECT_TRUE(b >= a);
+ EXPECT_TRUE(b >= b);
+ EXPECT_FALSE(b >= c);
+ EXPECT_FALSE(b >= d);
+ EXPECT_TRUE(c >= a);
+ EXPECT_TRUE(c >= b);
+ EXPECT_TRUE(c >= c);
+ EXPECT_FALSE(c >= d);
+ EXPECT_TRUE(d >= a);
+ EXPECT_TRUE(d >= b);
+ EXPECT_TRUE(d >= c);
+ EXPECT_TRUE(d >= d);
+
+ EXPECT_FALSE(a > a);
+ EXPECT_FALSE(a > b);
+ EXPECT_FALSE(a > c);
+ EXPECT_FALSE(a > d);
+ EXPECT_TRUE(b > a);
+ EXPECT_FALSE(b > b);
+ EXPECT_FALSE(b > c);
+ EXPECT_FALSE(b > d);
+ EXPECT_TRUE(c > a);
+ EXPECT_TRUE(c > b);
+ EXPECT_FALSE(c > c);
+ EXPECT_FALSE(c > d);
+ EXPECT_TRUE(d > a);
+ EXPECT_TRUE(d > b);
+ EXPECT_TRUE(d > c);
+ EXPECT_FALSE(d > d);
+
+ EXPECT_TRUE(a == a);
+ EXPECT_FALSE(a == b);
+ EXPECT_FALSE(a == c);
+ EXPECT_FALSE(a == d);
+ EXPECT_FALSE(b == a);
+ EXPECT_TRUE(b == b);
+ EXPECT_FALSE(b == c);
+ EXPECT_FALSE(b == d);
+ EXPECT_FALSE(c == a);
+ EXPECT_FALSE(c == b);
+ EXPECT_TRUE(c == c);
+ EXPECT_FALSE(c == d);
+ EXPECT_FALSE(d == a);
+ EXPECT_FALSE(d == b);
+ EXPECT_FALSE(d == c);
+ EXPECT_TRUE(d == d);
+
+ EXPECT_FALSE(a != a);
+ EXPECT_TRUE(a != b);
+ EXPECT_TRUE(a != c);
+ EXPECT_TRUE(a != d);
+ EXPECT_TRUE(b != a);
+ EXPECT_FALSE(b != b);
+ EXPECT_TRUE(b != c);
+ EXPECT_TRUE(b != d);
+ EXPECT_TRUE(c != a);
+ EXPECT_TRUE(c != b);
+ EXPECT_FALSE(c != c);
+ EXPECT_TRUE(c != d);
+ EXPECT_TRUE(d != a);
+ EXPECT_TRUE(d != b);
+ EXPECT_TRUE(d != c);
+ EXPECT_FALSE(d != d);
+}
+
TYPED_TEST_P(StringTest, iterators)
{
TypeParam hi("Hello");
@@ -39,8 +153,12 @@ TYPED_TEST_P(StringTest, xslice)
EXPECT_EQ("Hello,", hi.xslice_h(6));
EXPECT_EQ("World!", hi.xrslice_t(6));
EXPECT_EQ("Hello, ", hi.xrslice_h(6));
+ typename TypeParam::iterator it = std::find(hi.begin(), hi.end(), ' ');
+ EXPECT_EQ(" World!", hi.xislice_t(it));
+ EXPECT_EQ("Hello,", hi.xislice_h(it));
EXPECT_EQ("World", hi.xlslice(7, 5));
EXPECT_EQ("World", hi.xpslice(7, 12));
+ EXPECT_EQ("World", hi.xislice(hi.begin() + 7, hi.begin() + 12));
EXPECT_TRUE(hi.startswith("Hello"));
EXPECT_TRUE(hi.endswith("World!"));
}
@@ -52,8 +170,12 @@ TYPED_TEST_P(StringTest, oslice)
EXPECT_EQ("Hello,", hi.oslice_h(6));
EXPECT_EQ("World!", hi.orslice_t(6));
EXPECT_EQ("Hello, ", hi.orslice_h(6));
+ typename TypeParam::iterator it = std::find(hi.begin(), hi.end(), ' ');
+ EXPECT_EQ(" World!", hi.oislice_t(it));
+ EXPECT_EQ("Hello,", hi.oislice_h(it));
EXPECT_EQ("World", hi.olslice(7, 5));
EXPECT_EQ("World", hi.opslice(7, 12));
+ EXPECT_EQ("World", hi.oislice(hi.begin() + 7, hi.begin() + 12));
}
TYPED_TEST_P(StringTest, convert)
@@ -67,6 +189,8 @@ TYPED_TEST_P(StringTest, convert)
ZString z = "z";
Xstring x = "x";
VString<255> v = "v";
+ const char l[] = "l";
+ VString<5> hi = "hello";
TypeParam f2 = f;
TypeParam t2 = t;
@@ -74,32 +198,40 @@ TYPED_TEST_P(StringTest, convert)
TypeParam z2 = z;
TypeParam x2 = x;
TypeParam v2 = v;
+ TypeParam l2 = l;
+ TypeParam hi2 = hi;
- EXPECT_EQ(f2, f);
- EXPECT_EQ(t2, t);
- EXPECT_EQ(s2, s);
- EXPECT_EQ(z2, z);
- EXPECT_EQ(x2, x);
- EXPECT_EQ(v2, v);
+ EXPECT_EQ(f, f2);
+ EXPECT_EQ(t, t2);
+ EXPECT_EQ(s, s2);
+ EXPECT_EQ(z, z2);
+ EXPECT_EQ(x, x2);
+ EXPECT_EQ(v, v2);
+ EXPECT_EQ(l, l2);
+ EXPECT_EQ(hi, hi2);
- TypeParam f3, t3, s3, z3, x3, v3;
+ TypeParam f3, t3, s3, z3, x3, v3, l3, hi3;
f3 = f;
t3 = t;
s3 = s;
z3 = z;
x3 = x;
v3 = v;
+ l3 = l;
+ hi3 = hi;
- EXPECT_EQ(f3, f);
- EXPECT_EQ(t3, t);
- EXPECT_EQ(s3, s);
- EXPECT_EQ(z3, z);
- EXPECT_EQ(x3, x);
- EXPECT_EQ(v3, v);
+ EXPECT_EQ(f, f3);
+ EXPECT_EQ(t, t3);
+ EXPECT_EQ(s, s3);
+ EXPECT_EQ(z, z3);
+ EXPECT_EQ(x, x3);
+ EXPECT_EQ(v, v3);
+ EXPECT_EQ(l, l3);
+ EXPECT_EQ(hi, hi3);
}
REGISTER_TYPED_TEST_CASE_P(StringTest,
- basic, iterators, xslice, oslice, convert);
+ basic, order, iterators, xslice, oslice, convert);
typedef ::testing::Types<
FString, TString, SString, ZString, XString, VString<255>
diff --git a/src/common/timer.cpp b/src/common/timer.cpp
index ec1f6b2..c00f06d 100644
--- a/src/common/timer.cpp
+++ b/src/common/timer.cpp
@@ -184,10 +184,10 @@ interval_t do_timer(tick_t tick)
return std::max(nextmin, std::chrono::milliseconds(10));
}
-tick_t file_modified(const char *name)
+tick_t file_modified(ZString name)
{
struct stat buf;
- if (stat(name, &buf))
+ if (stat(name.c_str(), &buf))
return tick_t();
return tick_t(std::chrono::seconds(buf.st_mtime));
}
diff --git a/src/common/timer.hpp b/src/common/timer.hpp
index c581377..b9c9588 100644
--- a/src/common/timer.hpp
+++ b/src/common/timer.hpp
@@ -5,6 +5,8 @@
# include "sanity.hpp"
+# include "strings.hpp"
+
// updated automatically when using milli_clock::now()
// which is done only by core.cpp
extern tick_t gettick_cache;
@@ -20,6 +22,6 @@ tick_t gettick(void)
interval_t do_timer(tick_t tick);
/// Stat a file, and return its modification time, truncated to seconds.
-tick_t file_modified(const char *name);
+tick_t file_modified(ZString name);
#endif // TIMER_HPP
diff --git a/src/common/utils.cpp b/src/common/utils.cpp
index 4e00808..c9c22b9 100644
--- a/src/common/utils.cpp
+++ b/src/common/utils.cpp
@@ -5,120 +5,97 @@
#include <algorithm>
-#include "../poison.hpp"
-
-//-----------------------------------------------------
-// Function to suppress control characters in a string.
-//-----------------------------------------------------
-int remove_control_chars(char *str)
-{
- int i;
- int change = 0;
-
- for (i = 0; str[i]; i++)
- {
- if (0 <= str[i] && str[i] < 32)
- {
- str[i] = '_';
- change = 1;
- }
- }
+#include "cxxstdio.hpp"
+#include "extract.hpp"
- return change;
-}
+#include "../poison.hpp"
//---------------------------------------------------
// E-mail check: return 0 (not correct) or 1 (valid).
//---------------------------------------------------
-int e_mail_check(const char *email)
+bool e_mail_check(XString email)
{
- char ch;
- const char *last_arobas;
-
// athena limits
- if (strlen(email) < 3 || strlen(email) > 39)
+ if (email.size() < 3 || email.size() > 39)
return 0;
// part of RFC limits (official reference of e-mail description)
- if (strchr(email, '@') == NULL || email[strlen(email) - 1] == '@')
+ XString::iterator at = std::find(email.begin(), email.end(), '@');
+ if (at == email.end())
return 0;
-
- if (email[strlen(email) - 1] == '.')
+ XString username = email.xislice_h(at);
+ XString hostname = email.xislice_t(at + 1);
+ if (!username || !hostname)
return 0;
-
- last_arobas = strrchr(email, '@');
-
- if (strstr(last_arobas, "@.") != NULL ||
- strstr(last_arobas, "..") != NULL)
+ if (hostname.contains('@'))
return 0;
-
- for (ch = 1; ch < 32; ch++)
- {
- if (strchr(last_arobas, ch) != NULL)
- {
- return 0;
- }
- }
-
- if (strchr(last_arobas, ' ') != NULL ||
- strchr(last_arobas, ';') != NULL)
+ if (hostname.front() == '.' || hostname.back() == '.')
return 0;
-
- // all correct
- return 1;
+ if (hostname.contains_seq(".."))
+ return 0;
+ if (email.contains_any(" ;"))
+ return 0;
+ return email.is_print();
}
//-------------------------------------------------
// Return numerical value of a switch configuration
// on/off, english, français, deutsch, español
//-------------------------------------------------
-int config_switch (const char *str)
+int config_switch (ZString str)
{
- if (strcasecmp(str, "on") == 0 || strcasecmp(str, "yes") == 0
- || strcasecmp(str, "oui") == 0 || strcasecmp(str, "ja") == 0
- || strcasecmp(str, "si") == 0)
+ if (str == "on" || str == "yes"
+ || str == "oui" || str == "ja"
+ || str == "si")
return 1;
- if (strcasecmp(str, "off") == 0 || strcasecmp(str, "no") == 0
- || strcasecmp(str, "non") == 0 || strcasecmp(str, "nein") == 0)
+ if (str == "off" || str == "no"
+ || str == "non" || str == "nein")
return 0;
- return atoi(str);
+ int rv;
+ if (extract(str, &rv))
+ return rv;
+ FPRINTF(stderr, "Fatal: bad option value %s", str);
+ abort();
}
-const char *ip2str(struct in_addr ip, bool extra_dot)
+IP_String ip2str(struct in_addr ip)
+{
+ const uint8_t *p = reinterpret_cast<const uint8_t *>(&ip);
+
+ IP_String out;
+ SNPRINTF(out, 16, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
+ return out;
+}
+VString<16> ip2str_extradot(struct in_addr ip)
{
const uint8_t *p = reinterpret_cast<const uint8_t *>(&ip);
- static char buf[17];
- if (extra_dot)
- sprintf(buf, "%d.%d.%d.%d.", p[0], p[1], p[2], p[3]);
- else
- sprintf(buf, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
- return buf;
+ VString<16> out;
+ SNPRINTF(out, 17, "%d.%d.%d.%d.", p[0], p[1], p[2], p[3]);
+ return out;
}
-bool split_key_value(const std::string& line, std::string *w1, std::string *w2)
+bool split_key_value(const FString& line, SString *w1, TString *w2)
{
- std::string::const_iterator begin = line.begin(), end = line.end();
+ FString::iterator begin = line.begin(), end = line.end();
- if (line[0] == '/' && line[1] == '/')
+ if (line.startswith("//"))
return false;
- if (line.back() == '\r')
- --end;
- if (line.empty())
+ if (begin == end)
return false;
if (std::find_if(begin, end,
[](unsigned char c) { return c < ' '; }
) != line.end())
return false;
- std::string::const_iterator colon = std::find(begin, end, ':');
+ FString::iterator colon = std::find(begin, end, ':');
if (colon == end)
return false;
- w1->assign(begin, colon);
+ *w1 = line.oislice(begin, colon);
++colon;
while (std::isspace(*colon))
++colon;
- w2->assign(colon, end);
+ *w2 = line.oislice(colon, end);
return true;
}
@@ -128,18 +105,22 @@ static_assert(sizeof(timestamp_milliseconds_buffer) == 24, "millis buffer");
void stamp_time(timestamp_seconds_buffer& out, const TimeT *t)
{
struct tm when = t ? *t : TimeT::now();
- strftime(out, 20, "%Y-%m-%d %H:%M:%S", &when);
+ char buf[20];
+ strftime(buf, 20, "%Y-%m-%d %H:%M:%S", &when);
+ out = stringish<timestamp_seconds_buffer>(const_(buf));
}
void stamp_time(timestamp_milliseconds_buffer& out)
{
struct timeval tv;
gettimeofday(&tv, NULL);
struct tm when = TimeT(tv.tv_sec);
- strftime(out, 20, "%Y-%m-%d %H:%M:%S", &when);
- sprintf(out + 19, ".%03d", int(tv.tv_usec / 1000));
+ char buf[24];
+ strftime(buf, 20, "%Y-%m-%d %H:%M:%S", &when);
+ sprintf(buf + 19, ".%03d", static_cast<int>(tv.tv_usec / 1000));
+ out = stringish<timestamp_milliseconds_buffer>(const_(buf));
}
-void log_with_timestamp(FILE *out, const_string line)
+void log_with_timestamp(FILE *out, XString line)
{
if (!line)
{
@@ -148,7 +129,7 @@ void log_with_timestamp(FILE *out, const_string line)
}
timestamp_milliseconds_buffer tmpstr;
stamp_time(tmpstr);
- fwrite(tmpstr, 1, sizeof(tmpstr), out);
+ fputs(tmpstr.c_str(), out);
fputs(": ", out);
fwrite(line.data(), 1, line.size(), out);
if (line.back() != '\n')
diff --git a/src/common/utils.hpp b/src/common/utils.hpp
index ab32948..196bb3e 100644
--- a/src/common/utils.hpp
+++ b/src/common/utils.hpp
@@ -6,13 +6,15 @@
#include <cstdio>
#include <cstring>
-#include <string>
#include <type_traits>
#include "const_array.hpp"
#include "operators.hpp"
+#include "strings.hpp"
#include "utils2.hpp"
+struct IP_String : VString<15> {};
+
template<class T>
struct is_trivially_copyable
: std::integral_constant<bool,
@@ -22,23 +24,12 @@ struct is_trivially_copyable
&& __has_trivial_destructor(T)>
{};
-int remove_control_chars(char *str);
-int e_mail_check(const char *email);
-int config_switch (const char *str);
-const char *ip2str(struct in_addr ip, bool extra_dot = false);
+bool e_mail_check(XString email);
+int config_switch (ZString str);
+IP_String ip2str(struct in_addr ip);
+VString<15 + 1> ip2str_extradot(struct in_addr ip);
-bool split_key_value(const std::string& line, std::string *w1, std::string *w2);
-
-inline
-void strzcpy(char *dest, const char *src, size_t n)
-{
- if (n)
- {
- // hmph
- strncpy(dest, src, n - 1);
- dest[n - 1] = '\0';
- }
-}
+bool split_key_value(const FString& line, SString *w1, TString *w2);
inline
void really_memcpy(uint8_t *dest, const uint8_t *src, size_t n)
@@ -123,13 +114,14 @@ long long& convert_for_scanf(TimeT& t)
return t.value;
}
-typedef char timestamp_seconds_buffer[20];
-typedef char timestamp_milliseconds_buffer[24];
+struct timestamp_seconds_buffer : VString<19> {};
+struct timestamp_milliseconds_buffer : VString<23> {};
void stamp_time(timestamp_seconds_buffer&, const TimeT *t=nullptr);
void stamp_time(timestamp_milliseconds_buffer&);
-void log_with_timestamp(FILE *out, const_string line);
+void log_with_timestamp(FILE *out, XString line);
+// TODO VString?
#define TIMESTAMP_DUMMY "YYYY-MM-DD HH:MM:SS"
static_assert(sizeof(TIMESTAMP_DUMMY) == sizeof(timestamp_seconds_buffer),
"timestamp size");
@@ -138,6 +130,7 @@ static_assert(sizeof(TIMESTAMP_DUMMY) == sizeof(timestamp_seconds_buffer),
// sizeof: 01234567890123456789012345678
// str + sizeof: ^
// -1: ^
+// there's probably a better way to do this now
#define REPLACE_TIMESTAMP(str, t) \
stamp_time( \
reinterpret_cast<timestamp_seconds_buffer *>( \
@@ -146,10 +139,4 @@ 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 d7d6f8a..119cc13 100644
--- a/src/common/utils2.hpp
+++ b/src/common/utils2.hpp
@@ -248,4 +248,33 @@ typename std::enable_if<is_array_of_unknown_bound<T>::value, std::unique_ptr<T,
return std::unique_ptr<E[], D>(new E[sz]());
}
+template<class T>
+const T& const_(T& t)
+{
+ return t;
+}
+
+template<class T, class U>
+T no_cast(U&& u)
+{
+ typedef typename std::remove_reference<T>::type Ti;
+ typedef typename std::remove_reference<U>::type Ui;
+ typedef typename std::remove_cv<Ti>::type Tb;
+ typedef typename std::remove_cv<Ui>::type Ub;
+ static_assert(std::is_same<Tb, Ub>::value, "not no cast");
+ return std::forward<U>(u);
+}
+
+template<class T, class U>
+T base_cast(U&& u)
+{
+ static_assert(std::is_reference<T>::value, "must base cast with references");
+ typedef typename std::remove_reference<T>::type Ti;
+ typedef typename std::remove_reference<U>::type Ui;
+ typedef typename std::remove_cv<Ti>::type Tb;
+ typedef typename std::remove_cv<Ui>::type Ub;
+ static_assert(std::is_base_of<Tb, Ub>::value, "not base cast");
+ return std::forward<U>(u);
+}
+
#endif // UTILS2_HPP