diff options
Diffstat (limited to 'src/common')
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 ∣ - } - }; - - 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 ∣ + } + }; + + 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 |