summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/const_array.hpp175
-rw-r--r--src/common/core.cpp75
-rw-r--r--src/common/core.hpp8
-rw-r--r--src/common/cxxstdio.cpp33
-rw-r--r--src/common/cxxstdio.hpp297
-rw-r--r--src/common/db.cpp545
-rw-r--r--src/common/db.hpp181
-rw-r--r--src/common/extract.cpp68
-rw-r--r--src/common/extract.hpp166
-rw-r--r--src/common/grfio.cpp216
-rw-r--r--src/common/grfio.hpp17
-rw-r--r--src/common/lock.cpp46
-rw-r--r--src/common/lock.hpp11
-rw-r--r--src/common/md5calc.cpp39
-rw-r--r--src/common/md5calc.hpp20
-rw-r--r--src/common/mmo.hpp336
-rw-r--r--src/common/mt_rand.cpp117
-rw-r--r--src/common/mt_rand.hpp24
-rw-r--r--src/common/nullpo.cpp69
-rw-r--r--src/common/nullpo.hpp57
-rw-r--r--src/common/operators.hpp47
-rw-r--r--src/common/random.cpp8
-rw-r--r--src/common/random.hpp69
-rw-r--r--src/common/random.t.hpp23
-rw-r--r--src/common/random2.hpp74
-rw-r--r--src/common/sanity.hpp61
-rw-r--r--src/common/socket.cpp264
-rw-r--r--src/common/socket.hpp192
-rw-r--r--src/common/timer.cpp334
-rw-r--r--src/common/timer.hpp64
-rw-r--r--src/common/timer.t.hpp66
-rw-r--r--src/common/utils.cpp107
-rw-r--r--src/common/utils.hpp128
-rw-r--r--src/common/utils2.hpp230
34 files changed, 2323 insertions, 1844 deletions
diff --git a/src/common/const_array.hpp b/src/common/const_array.hpp
new file mode 100644
index 0000000..93ae337
--- /dev/null
+++ b/src/common/const_array.hpp
@@ -0,0 +1,175 @@
+#ifndef CONST_ARRAY_HPP
+#define CONST_ARRAY_HPP
+// const_array.hpp - just a pointer-to-const and a length
+//
+// Copyright © 2011-2012 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 <cstring>
+
+#include <iterator>
+#include <ostream>
+#include <string>
+#include <vector>
+
+#ifdef WORKAROUND_GCC46_COMPILER
+// constexpr is buggy with templates in this version
+# define constexpr /* nothing */
+#endif
+
+// TODO see if I ever actually use this, and not the subclass
+template<class T>
+class const_array
+{
+ const T *d;
+ size_t n;
+public:
+ typedef const T *iterator;
+ typedef std::reverse_iterator<iterator> reverse_iterator;
+
+ constexpr
+ const_array(std::nullptr_t)
+ : d(nullptr), n(0)
+ {}
+
+ constexpr
+ const_array(const T *p, size_t z)
+ : d(p), n(z)
+ {}
+
+ constexpr
+ const_array(const T *b, const T *e)
+ : d(b), n(e - b)
+ {}
+
+ const_array(std::initializer_list<T> list)
+ : d(list.begin()), n(list.size())
+ {}
+
+ // Implicit conversion from std::vector
+ const_array(const std::vector<T>& v)
+ : d(v.data()), n(v.size())
+ {}
+
+ // but disallow conversion from a temporary
+ const_array(std::vector<T>&&) = delete;
+
+ // All methods are non-const to "encourage" you
+ // to always pass a const_array by value.
+ // After all, "const const_array" looks funny.
+ constexpr
+ const T *data() { return d; }
+ constexpr
+ size_t size() { return n; }
+ constexpr
+ bool empty() { return not n; }
+ constexpr explicit
+ operator bool() { return n; }
+
+ constexpr
+ std::pair<const_array, const_array> cut(size_t o)
+ {
+ return {const_array(d, o), const_array(d + o, n - o)};
+ }
+
+ constexpr
+ const_array first(size_t o)
+ {
+ return cut(o).first;
+ }
+
+ constexpr
+ const_array last(size_t l)
+ {
+ return cut(size() - l).second;
+ }
+
+ constexpr
+ const_array after(size_t o)
+ {
+ return cut(o).second;
+ }
+
+ constexpr
+ iterator begin() { return d; }
+ constexpr
+ iterator end() { return d + n; }
+ constexpr
+ reverse_iterator rbegin() { return reverse_iterator(end()); }
+ constexpr
+ reverse_iterator rend() { return reverse_iterator(begin()); }
+
+ constexpr
+ const T& front() { return *begin(); }
+ constexpr
+ const T& back() { return *rbegin(); }
+};
+
+// subclass just provides a simpler name and some conversions
+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 db26e31..994de93 100644
--- a/src/common/core.cpp
+++ b/src/common/core.cpp
@@ -1,15 +1,18 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <signal.h>
+#include "core.hpp"
+
#include <sys/wait.h>
-#include "core.hpp"
+#include <unistd.h>
+
+#include <csignal>
+#include <cstdlib>
+#include <ctime>
+
+#include "random.hpp"
#include "socket.hpp"
#include "timer.hpp"
-#include "version.hpp"
-#include "mt_rand.hpp"
-#include "nullpo.hpp"
+
+#include "../poison.hpp"
// Added by Gabuzomeu
//
@@ -17,13 +20,14 @@
// (sigaction() is POSIX; signal() is not.) Taken from Stevens' _Advanced
// Programming in the UNIX Environment_.
//
-typedef void (*sigfunc)(int);
-static sigfunc compat_signal (int signo, sigfunc func)
+typedef void(*sigfunc)(int);
+static
+sigfunc compat_signal(int signo, sigfunc func)
{
struct sigaction sact, oact;
sact.sa_handler = func;
- sigfillset (&sact.sa_mask);
+ sigfillset(&sact.sa_mask);
sigdelset(&sact.sa_mask, SIGSEGV);
sigdelset(&sact.sa_mask, SIGBUS);
sigdelset(&sact.sa_mask, SIGTRAP);
@@ -31,22 +35,24 @@ static sigfunc compat_signal (int signo, sigfunc func)
sigdelset(&sact.sa_mask, SIGFPE);
sact.sa_flags = 0;
- if (sigaction (signo, &sact, &oact) < 0)
+ if (sigaction(signo, &sact, &oact) < 0)
return SIG_ERR;
return oact.sa_handler;
}
-static void chld_proc (int UNUSED)
+static
+void chld_proc(int)
{
wait(NULL);
}
-static void sig_proc (int UNUSED)
+static __attribute__((noreturn))
+void sig_proc(int)
{
for (int i = 1; i < 31; ++i)
compat_signal(i, SIG_IGN);
- term_func ();
- _exit (0);
+ term_func();
+ _exit(0);
}
bool runflag = true;
@@ -60,34 +66,35 @@ bool runflag = true;
Unless you use SA_SIGINFO and *carefully* check the origin,
that means they must be SIG_DFL.
*/
-int main (int argc, char **argv)
+int main(int argc, char **argv)
{
- /// Note that getpid() and getppid() may be very close
- mt_seed (time (NULL) ^ (getpid () << 16) ^ (getppid () << 8));
-
- do_socket ();
+ do_socket();
- do_init (argc, argv);
+ do_init(argc, argv);
// set up exit handlers *after* the initialization has happened.
// This is because term_func is likely to depend on successful init.
- compat_signal (SIGPIPE, SIG_IGN);
- compat_signal (SIGTERM, sig_proc);
- compat_signal (SIGINT, sig_proc);
- compat_signal (SIGCHLD, chld_proc);
+ compat_signal(SIGPIPE, SIG_IGN);
+ compat_signal(SIGTERM, sig_proc);
+ compat_signal(SIGINT, sig_proc);
+ compat_signal(SIGCHLD, chld_proc);
// Signal to create coredumps by system when necessary (crash)
- compat_signal (SIGSEGV, SIG_DFL);
- compat_signal (SIGBUS, SIG_DFL);
- compat_signal (SIGTRAP, SIG_DFL);
- compat_signal (SIGILL, SIG_DFL);
- compat_signal (SIGFPE, SIG_DFL);
+ compat_signal(SIGSEGV, SIG_DFL);
+ compat_signal(SIGBUS, SIG_DFL);
+ compat_signal(SIGTRAP, SIG_DFL);
+ compat_signal(SIGILL, SIG_DFL);
+ compat_signal(SIGFPE, SIG_DFL);
- atexit (term_func);
+ atexit(term_func);
while (runflag)
{
- do_sendrecv (do_timer (gettick_nocache ()));
- do_parsepacket ();
+ // TODO - if timers take a long time to run, this
+ // may wait too long in sendrecv
+ tick_t now = milli_clock::now();
+ interval_t next = do_timer(now);
+ do_sendrecv(next);
+ do_parsepacket();
}
}
diff --git a/src/common/core.hpp b/src/common/core.hpp
index 8a52c55..0c11efb 100644
--- a/src/common/core.hpp
+++ b/src/common/core.hpp
@@ -1,6 +1,8 @@
#ifndef CORE_HPP
#define CORE_HPP
-#include <stdbool.h>
+
+#include "sanity.hpp"
+
/// core.c contains a server-independent main() function
/// and then runs a do_sendrecv loop
@@ -10,10 +12,10 @@ 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, char **);
/// Cleanup function called whenever a signal kills us
/// or when if we manage to exit() gracefully.
-extern void term_func (void);
+extern void term_func(void);
#endif // CORE_HPP
diff --git a/src/common/cxxstdio.cpp b/src/common/cxxstdio.cpp
new file mode 100644
index 0000000..8f4001f
--- /dev/null
+++ b/src/common/cxxstdio.cpp
@@ -0,0 +1,33 @@
+#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
new file mode 100644
index 0000000..96c3ca2
--- /dev/null
+++ b/src/common/cxxstdio.hpp
@@ -0,0 +1,297 @@
+#ifndef CXXSTDIO_HPP
+#define CXXSTDIO_HPP
+// cxxstdio.hpp - pass C++ types through scanf/printf
+//
+// Copyright © 2011-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 <cstdarg>
+#include <cstdio>
+
+#include <string>
+
+#include "const_array.hpp"
+#include "utils2.hpp"
+
+
+namespace cxxstdio
+{
+ 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)
+ {
+ return vfscanf(in, fmt, ap);
+ }
+
+#if 0
+ inline __attribute__((format(scanf, 2, 0)))
+ int do_vscan(const char *in, const char *fmt, va_list ap)
+ {
+ return vsscanf(in, fmt, ap);
+ }
+#else
+ inline
+ 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()
+ {
+ int rv;
+ va_list ap;
+ va_start(ap, fmt);
+ rv = do_vprint(std::forward<T>(t), fmt, ap);
+ va_end(ap);
+ return rv;
+ }
+
+ template<class T>
+ inline __attribute__((format(scanf, 2, 3)))
+ int do_scan(T&& t, const char *fmt, ...) throw()
+ {
+ int rv;
+ va_list ap;
+ va_start(ap, fmt);
+ rv = do_vscan(std::forward<T>(t), fmt, ap);
+ va_end(ap);
+ return rv;
+ }
+
+
+ template<class T>
+ typename remove_enum<T>::type convert_for_printf(T v)
+ {
+ typedef typename remove_enum<T>::type repr_type;
+ return repr_type(v);
+ }
+
+ template<class T, typename = typename std::enable_if<!std::is_enum<T>::value>::type>
+ T& convert_for_scanf(T& v)
+ {
+ return v;
+ }
+
+#if 0
+ template<class E>
+ constexpr
+ E get_enum_min_value(decltype(E::min_value))
+ {
+ return E::min_value;
+ }
+ template<class E>
+ constexpr
+ E get_enum_min_value(E def)
+ {
+ return def;
+ }
+
+ template<class E>
+ constexpr
+ E get_enum_max_value(decltype(E::max_value))
+ {
+ return E::max_value;
+ }
+ template<class E>
+ constexpr
+ E get_enum_max_value(E def)
+ {
+ return def;
+ }
+#else
+ template<class E>
+ constexpr
+ E get_enum_min_value(E)
+ {
+ return E::min_value;
+ }
+ template<class E>
+ constexpr
+ E get_max_value(E)
+ {
+ return E::max_value;
+ }
+#endif
+
+ template<class E>
+ class EnumConverter
+ {
+ E& out;
+ typedef typename underlying_type<E>::type U;
+#if 0
+ constexpr static
+ U min_value = U(get_enum_min_value<E>(E(std::numeric_limits<U>::min())));
+ constexpr static
+ U max_value = U(get_enum_max_value<E>(E(std::numeric_limits<U>::max())));
+#else
+ constexpr static
+ U min_value = U(get_enum_min_value(E()));
+ constexpr static
+ U max_value = U(get_enum_max_value(E()));
+#endif
+ U mid;
+ public:
+ EnumConverter(E& e)
+ : out(e), mid(0)
+ {}
+ ~EnumConverter()
+ {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wtype-limits"
+ if (min_value <= mid && mid <= max_value)
+#pragma GCC diagnostic pop
+ out = E(mid);
+ }
+ U *operator &()
+ {
+ return &mid;
+ }
+ };
+
+ template<class T, typename = typename std::enable_if<std::is_enum<T>::value>::type>
+ EnumConverter<T> convert_for_scanf(T& v)
+ {
+ return v;
+ }
+
+
+ inline
+ const char *convert_for_printf(const std::string& s)
+ {
+ return s.c_str();
+ }
+
+ class StringConverter
+ {
+ std::string& out;
+ char *mid;
+ public:
+ StringConverter(std::string& s)
+ : out(s), mid(nullptr)
+ {}
+ ~StringConverter();
+ char **operator &()
+ {
+ return &mid;
+ }
+ };
+
+ inline
+ StringConverter convert_for_scanf(std::string& s)
+ {
+ return StringConverter(s);
+ }
+
+ template<class Format>
+ class PrintFormatter
+ {
+ public:
+ template<class T, class... A>
+ static
+ int print(T&& t, A&&... a)
+ {
+ 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))...);
+ }
+ };
+
+ template<class Format>
+ class ScanFormatter
+ {
+ public:
+ template<class T, class... A>
+ static
+ int scan(T&& t, A&&... a)
+ {
+ constexpr static
+ const char *scan_format = Format::scan_format();
+ return do_scan(std::forward<T>(t), scan_format,
+ &convert_for_scanf(*a)...);
+ }
+ };
+
+#define FPRINTF(file, fmt, ...) \
+ ([&]() -> int \
+ { \
+ struct format_impl \
+ { \
+ constexpr static \
+ const char *print_format() { return fmt; } \
+ }; \
+ return cxxstdio::PrintFormatter<format_impl>::print(file, ## __VA_ARGS__); \
+ }())
+
+#define FSCANF(file, fmt, ...) \
+ ([&]() -> int \
+ { \
+ struct format_impl \
+ { \
+ constexpr static \
+ const char *scan_format() { return fmt; } \
+ }; \
+ return cxxstdio::ScanFormatter<format_impl>::scan(file, ## __VA_ARGS__); \
+ }())
+
+#define PRINTF(fmt, ...) FPRINTF(stdout, fmt, ## __VA_ARGS__)
+#define SPRINTF(str, fmt, ...) FPRINTF(str, fmt, ## __VA_ARGS__)
+#define SCANF(fmt, ...) FSCANF(stdin, fmt, ## __VA_ARGS__)
+#define SSCANF(str, fmt, ...) FSCANF(str, fmt, ## __VA_ARGS__)
+
+#define STRPRINTF(fmt, ...) \
+ ([&]() -> std::string \
+ { \
+ std::string _out_impl; \
+ SPRINTF(_out_impl, fmt, ## __VA_ARGS__);\
+ return _out_impl; \
+ }())
+
+} // namespace cxxstdio
+
+#endif // CXXSTDIO_HPP
diff --git a/src/common/db.cpp b/src/common/db.cpp
index 21a3597..cc58ad8 100644
--- a/src/common/db.cpp
+++ b/src/common/db.cpp
@@ -1,546 +1,3 @@
#include "db.hpp"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "utils.hpp"
-
-#define ROOT_SIZE 4096
-
-static int strdb_cmp (struct dbt *table, const char *a, const char* b)
-{
- if (table->maxlen)
- return strncmp (a, b, table->maxlen);
- return strcmp (a, b);
-}
-
-static hash_t strdb_hash (struct dbt *table, const char *a)
-{
- size_t i = table->maxlen;
- if (i == 0)
- i = (size_t)-1;
- hash_t h = 0;
- const unsigned char *p = (const unsigned char*)a;
- while (*p && i--)
- {
- h = (h * 33 + *p++) ^ (h >> 24);
- }
- return h;
-}
-
-struct dbt *strdb_init (size_t maxlen)
-{
- struct dbt *table;
- CREATE (table, struct dbt, 1);
- table->type = DB_STRING;
- table->maxlen = maxlen;
- return table;
-}
-
-static int numdb_cmp (numdb_key_t a, numdb_key_t b)
-{
- if (a == b)
- return 0;
- if (a < b)
- return -1;
- return 1;
-}
-
-static hash_t numdb_hash (numdb_key_t a)
-{
- return (hash_t) a;
-}
-
-struct dbt *numdb_init (void)
-{
- struct dbt *table;
- CREATE (table, struct dbt, 1);
- table->type = DB_NUMBER;
- return table;
-}
-
-static int table_cmp (struct dbt *table, db_key_t a, db_key_t b)
-{
- switch(table->type)
- {
- case DB_NUMBER: return numdb_cmp (a.i, b.i);
- case DB_STRING: return strdb_cmp (table, a.s, b.s);
- }
- abort();
-}
-
-static hash_t table_hash (struct dbt *table, db_key_t key)
-{
- switch(table->type)
- {
- case DB_NUMBER: return numdb_hash (key.i);
- case DB_STRING: return strdb_hash (table, key.s);
- }
- abort();
-}
-
-/// Search for a node with the given key
-db_val_t db_search (struct dbt *table, db_key_t key)
-{
- struct dbn *p = table->ht[table_hash (table, key) % HASH_SIZE];
-
- while (p)
- {
- int c = table_cmp (table, key, p->key);
- if (c == 0)
- return p->data;
- if (c < 0)
- p = p->left;
- else
- p = p->right;
- }
- return NULL;
-}
-
-// Tree maintainance methods
-static void db_rotate_left (struct dbn *p, struct dbn **root)
-{
- struct dbn *y = p->right;
- p->right = y->left;
- if (y->left)
- y->left->parent = p;
- y->parent = p->parent;
-
- if (p == *root)
- *root = y;
- else if (p == p->parent->left)
- p->parent->left = y;
- else
- p->parent->right = y;
- y->left = p;
- p->parent = y;
-}
-
-static void db_rotate_right (struct dbn *p, struct dbn **root)
-{
- struct dbn *y = p->left;
- p->left = y->right;
- if (y->right)
- y->right->parent = p;
- y->parent = p->parent;
-
- if (p == *root)
- *root = y;
- else if (p == p->parent->right)
- p->parent->right = y;
- else
- p->parent->left = y;
- y->right = p;
- p->parent = y;
-}
-
-static void db_rebalance (struct dbn *p, struct dbn **root)
-{
- p->color = RED;
- while (p != *root && p->parent->color == RED)
- {
- if (p->parent == p->parent->parent->left)
- {
- struct dbn *y = p->parent->parent->right;
- if (y && y->color == RED)
- {
- p->parent->color = BLACK;
- y->color = BLACK;
- p->parent->parent->color = RED;
- p = p->parent->parent;
- }
- else
- {
- if (p == p->parent->right)
- {
- p = p->parent;
- db_rotate_left (p, root);
- }
- p->parent->color = BLACK;
- p->parent->parent->color = RED;
- db_rotate_right (p->parent->parent, root);
- }
- }
- else
- {
- struct dbn *y = p->parent->parent->left;
- if (y && y->color == RED)
- {
- p->parent->color = BLACK;
- y->color = BLACK;
- p->parent->parent->color = RED;
- p = p->parent->parent;
- }
- else
- {
- if (p == p->parent->left)
- {
- p = p->parent;
- db_rotate_right (p, root);
- }
- p->parent->color = BLACK;
- p->parent->parent->color = RED;
- db_rotate_left (p->parent->parent, root);
- }
- }
- }
- (*root)->color = BLACK;
-}
-
-// param z = node to remove
-static void db_rebalance_erase (struct dbn *z, struct dbn **root)
-{
- struct dbn *y = z;
- struct dbn *x = NULL;
-
- if (!y->left)
- x = y->right;
- else if (!y->right)
- x = y->left;
- else
- {
- y = y->right;
- while (y->left)
- y = y->left;
- x = y->right;
- }
- struct dbn *x_parent = NULL;
- if (y != z)
- {
- z->left->parent = y;
- y->left = z->left;
- if (y != z->right)
- {
- x_parent = y->parent;
- if (x)
- x->parent = y->parent;
- y->parent->left = x;
- y->right = z->right;
- z->right->parent = y;
- }
- else
- x_parent = y;
- if (*root == z)
- *root = y;
- else if (z->parent->left == z)
- z->parent->left = y;
- else
- z->parent->right = y;
- y->parent = z->parent;
- {
- dbn_color tmp = y->color;
- y->color = z->color;
- z->color = tmp;
- }
- y = z;
- }
- else
- {
- x_parent = y->parent;
- if (x)
- x->parent = y->parent;
- if (*root == z)
- *root = x;
- else if (z->parent->left == z)
- z->parent->left = x;
- else
- z->parent->right = x;
- }
- if (y->color != RED)
- {
- while (x != *root && (!x || x->color == BLACK))
- if (x == x_parent->left)
- {
- struct dbn *w = x_parent->right;
- if (w->color == RED)
- {
- w->color = BLACK;
- x_parent->color = RED;
- db_rotate_left (x_parent, root);
- w = x_parent->right;
- }
- if ((!w->left || w->left->color == BLACK) &&
- (!w->right || w->right->color == BLACK))
- {
- w->color = RED;
- x = x_parent;
- x_parent = x->parent;
- }
- else
- {
- if (!w->right|| w->right->color == BLACK)
- {
- if (w->left)
- w->left->color = BLACK;
- w->color = RED;
- db_rotate_right (w, root);
- w = x_parent->right;
- }
- w->color = x_parent->color;
- x_parent->color = BLACK;
- if (w->right)
- w->right->color = BLACK;
- db_rotate_left (x_parent, root);
- break;
- }
- }
- else
- {
- // same as above, with right <-> left.
- struct dbn *w = x_parent->left;
- if (w->color == RED)
- {
- w->color = BLACK;
- x_parent->color = RED;
- db_rotate_right (x_parent, root);
- w = x_parent->left;
- }
- if ((!w->right || w->right->color == BLACK) &&
- (!w->left || w->left->color == BLACK))
- {
- w->color = RED;
- x = x_parent;
- x_parent = x_parent->parent;
- }
- else
- {
- if (!w->left || w->left->color == BLACK)
- {
- if (w->right)
- w->right->color = BLACK;
- w->color = RED;
- db_rotate_left (w, root);
- w = x_parent->left;
- }
- w->color = x_parent->color;
- x_parent->color = BLACK;
- if (w->left)
- w->left->color = BLACK;
- db_rotate_right (x_parent, root);
- break;
- }
- }
- if (x)
- x->color = BLACK;
- }
-}
-
-struct dbn *db_insert (struct dbt *table, db_key_t key, db_val_t data)
-{
- hash_t hash = table_hash (table, key) % HASH_SIZE;
- int c = 0;
- struct dbn *prev = NULL;
- struct dbn *p = table->ht[hash];
- while (p)
- {
- c = table_cmp (table, key, p->key);
- if (c == 0)
- {
- // key found in table, replace
- // Tell the user of the table to free the key and value
- if (table->release)
- table->release (p->key, p->data);
- p->data = data;
- p->key = key;
- return p;
- }
- // prev is always p->parent?
- prev = p;
- if (c < 0)
- p = p->left;
- else
- p = p->right;
- }
- CREATE (p, struct dbn, 1);
- p->key = key;
- p->data = data;
- p->color = RED;
- if (c == 0)
- { // hash entry is empty
- table->ht[hash] = p;
- p->color = BLACK;
- return p;
- }
- p->parent = prev;
- if (c < 0)
- prev->left = p;
- else
- prev->right = p;
- if (prev->color == RED)
- {
- // must rebalance
- db_rebalance (p, &table->ht[hash]);
- }
- return p;
-}
-
-db_val_t db_erase (struct dbt *table, db_key_t key)
-{
- hash_t hash = table_hash (table, key) % HASH_SIZE;
- struct dbn *p = table->ht[hash];
- while (p)
- {
- int c = table_cmp (table, key, p->key);
- if (c == 0)
- break;
- if (c < 0)
- p = p->left;
- else
- p = p->right;
- }
- if (!p)
- return NULL;
- db_val_t data = p->data;
- db_rebalance_erase (p, &table->ht[hash]);
- free (p);
- return data;
-}
-#ifdef SMART_WALK_TREE
-static inline void db_walk_tree (bool dealloc, struct dbn* p, db_func_t func, va_list ap)
-{
- if (!p)
- return;
- if (!dealloc && !func)
- {
- fprintf(stderr, "DEBUG: Must walk tree to either free or invoke a function.\n");
- abort();
- }
- if (p->parent)
- {
- fprintf(stderr, "DEBUG: Root nodes must not have parents\n");
- abort();
- }
- while (true)
- {
- // apply_func loop
- if (func)
- func (p->key, p->data, ap);
- if (p->left)
- {
- // continue descending
- p = p->left;
- continue; //goto apply_func;
- }
- if (p->right)
- {
- // descending the other side
- p = p->right;
- continue; //goto apply_func;
- }
- while (true)
- {
- // backtrack loop
- if (!p->parent)
- {
- if (dealloc)
- free (p);
- // if we have already done both children, there is no more to do
- return;
- }
- if (p->parent->left == p && p->parent->right)
- {
- // finished the left tree, now walk the right tree
- p = p->parent->right;
- if (dealloc)
- free (p->parent->left);
- break; //goto apply_func;
- }
- // p->parent->right == p
- // or p->parent->left == p but p->parent->right == NULL
- // keep backtracking
- p = p->parent;
- if (dealloc)
- free (p->right?:p->left);
- } //backtrack loop
- } // apply_func loop
-}
-#endif // SMART_WALK_TREE
-
-void db_foreach (struct dbt *table, db_func_t func, ...)
-{
- va_list ap;
- va_start (ap, func);
-
- for (int i = 0; i < HASH_SIZE; i++)
- {
-#ifdef SMART_WALK_TREE
- db_walk_tree (false, table->ht[i], func, ap);
-#else
- struct dbn *p = table->ht[i];
- if (!p)
- continue;
- struct dbn *stack[64];
- int sp = 0;
- while (1)
- {
- func (p->key, p->data, ap);
- struct dbn *pn = p->left;
- if (pn)
- {
- if (p->right)
- stack[sp++] = p->right;
- p = pn;
- }
- else // pn == NULL, time to do the right branch
- {
- if (p->right)
- p = p->right;
- else
- {
- if (sp == 0)
- break;
- p = stack[--sp];
- }
- } // if pn else if !pn
- } // while true
-#endif // else ! SMART_WALK_TREE
- } // for i
- va_end (ap);
-}
-
-// This function is suspiciously similar to the previous
-void db_final (struct dbt *table, db_func_t func, ...)
-{
- va_list ap;
- va_start (ap, func);
-
- for (int i = 0; i < HASH_SIZE; i++)
- {
-#ifdef SMART_WALK_TREE
- db_walk_tree (true, table->ht[i], func, ap);
-#else
- struct dbn *p = table->ht[i];
- if (!p)
- continue;
- struct dbn *stack[64];
- int sp = 0;
- while (1)
- {
- if (func)
- func (p->key, p->data, ap);
- struct dbn *pn = p->left;
- if (pn)
- {
- if (p->right)
- stack[sp++] = p->right;
- }
- else // pn == NULL, check the right
- {
- if (p->right)
- pn = p->right;
- else
- {
- if (sp == 0)
- break;
- pn = stack[--sp];
- }
- } // if pn else if !pn
- free (p);
- p = pn;
- } // while true
-#endif // else ! SMART_WALK_TREE
- } // for i
- free (table);
- va_end (ap);
-}
+#include "../poison.hpp"
diff --git a/src/common/db.hpp b/src/common/db.hpp
index 62125d8..bd47928 100644
--- a/src/common/db.hpp
+++ b/src/common/db.hpp
@@ -1,90 +1,123 @@
-// WARNING: there is a system header by this name
#ifndef DB_HPP
#define DB_HPP
-# include "sanity.hpp"
+// db.hpp - convenience wrappers over std::map<K, V>
+//
+// 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 <stdarg.h>
+# include "sanity.hpp"
-/// Number of tree roots
-// Somewhat arbitrary - larger wastes more space but is faster for large trees
-// num % HASH_SIZE minimize collisions even for similar num
-# define HASH_SIZE (256+27)
+# include <map>
-typedef enum dbn_color
+template<class K, class V>
+class Map
{
- RED,
- BLACK
-} dbn_color;
+ typedef std::map<K, V> Impl;
-typedef intptr_t numdb_key_t;
-typedef union db_key_t
-{
- char *ms __attribute__((deprecated));
- const char* s;
- numdb_key_t i;
+ Impl impl;
+public:
+ Map() = default;
+ Map(std::initializer_list<std::pair<const K, V>> il)
+ : impl(il)
+ {}
+ typedef typename Impl::iterator iterator;
+ typedef typename Impl::const_iterator const_iterator;
- db_key_t(numdb_key_t n) : i(n) {}
- db_key_t(const char * z) : s(z) {}
-} db_key_t;
-typedef void* db_val_t;
-typedef uint32_t hash_t;
-typedef void (*db_func_t)(db_key_t, db_val_t, va_list);
+ iterator begin() { return impl.begin(); }
+ iterator end() { return impl.end(); }
+ const_iterator begin() const { return impl.begin(); }
+ const_iterator end() const { return impl.end(); }
-/// DataBase Node
-struct dbn
-{
- struct dbn *parent, *left, *right;
- dbn_color color;
- db_key_t key;
- db_val_t data;
-};
+ V *search(const K& k)
+ {
+ iterator it = impl.find(k);
+ if (it == impl.end())
+ return nullptr;
+ return &it->second;
+ }
+ const V *search(const K& k) const
+ {
+ const_iterator it = impl.find(k);
+ if (it == impl.end())
+ return nullptr;
+ return &it->second;
+ }
+ void insert(K k, V v)
+ {
+ // As far as I can tell, this is the simplest way to
+ // implement move-only insert-with-replacement.
+ iterator it = impl.lower_bound(k);
+ // invariant: if it is valid, it->first >= k
+ if (it != impl.end() && it->first == k)
+ it->second = std::move(v);
+ else
+ it = impl.insert(std::pair<K, V>(std::move(k), std::move(v))).first;
+ return (void)&it->second;
-typedef enum dbt_type
-{
- DB_NUMBER,
- DB_STRING,
-} dbt_type;
+ }
+ V *init(K k)
+ {
+ return &impl[k];
+ }
+ void erase(const K& k)
+ {
+ impl.erase(k);
+ }
+ void clear()
+ {
+ impl.clear();
+ }
+ bool empty()
+ {
+ return impl.empty();
+ }
+};
-/// DataBase Table
-struct dbt
+template<class K, class V>
+class DMap
{
- dbt_type type;
- /// Note, before replacement, key/values to be replaced
- // TODO refactor to decrease/eliminate the uses of this?
- void (*release) (db_key_t, db_val_t) __attribute__((deprecated));
- /// Maximum length of a string key - TODO refactor to ensure all strings are NUL-terminated
- size_t maxlen __attribute__((deprecated));
- /// The root trees
- struct dbn *ht[HASH_SIZE];
-};
+ typedef Map<K, V> Impl;
-# define strdb_search(t,k) db_search((t),(db_key_t)(k))
-# define strdb_insert(t,k,d) db_insert((t),(db_key_t)(k),(db_val_t)(d))
-# define strdb_erase(t,k) db_erase ((t),(db_key_t)(k))
-# define strdb_foreach db_foreach
-# define strdb_final db_final
-# define numdb_search(t,k) db_search((t),(db_key_t)(k))
-# define numdb_insert(t,k,d) db_insert((t),(db_key_t)(k),(db_val_t)(d))
-# define numdb_erase(t,k) db_erase ((t),(db_key_t)(k))
-# define numdb_foreach db_foreach
-# define numdb_final db_final
+ Impl impl;
+public:
+ typedef typename Impl::iterator iterator;
+ typedef typename Impl::const_iterator const_iterator;
-/// Create a map from char* to void*, with strings possibly not null-terminated
-struct dbt *strdb_init (size_t maxlen);
-/// Create a map from int to void*
-struct dbt *numdb_init (void);
-/// Return the value corresponding to the key, or NULL if not found
-db_val_t db_search (struct dbt *table, db_key_t key);
-/// Add or replace table[key] = data
-// if it was already there, call release
-struct dbn *db_insert (struct dbt *table, db_key_t key, db_val_t data);
-/// Remove a key from the table, returning the data
-db_val_t db_erase (struct dbt *table, db_key_t key);
+ iterator begin() { return impl.begin(); }
+ iterator end() { return impl.end(); }
+ const_iterator begin() const { return impl.begin(); }
+ const_iterator end() const { return impl.end(); }
-/// Execute a function for every element, in unspecified order
-void db_foreach (struct dbt *, db_func_t, ...);
-// opposite of init? Calls release for every element and frees memory
-// This probably isn't really needed: we don't have to free memory while exiting
-void db_final (struct dbt *, db_func_t, ...) __attribute__((deprecated));
+ V get(K k)
+ {
+ V *vp = impl.search(k);
+ return vp ? *vp : V();
+ }
+ void put(K k, V v)
+ {
+ if (v == V())
+ impl.erase(k);
+ else
+ impl.insert(k, v);
+ }
+ void clear()
+ {
+ impl.clear();
+ }
+};
-#endif
+#endif // DB_HPP
diff --git a/src/common/extract.cpp b/src/common/extract.cpp
new file mode 100644
index 0000000..5e89e19
--- /dev/null
+++ b/src/common/extract.cpp
@@ -0,0 +1,68 @@
+#include "extract.hpp"
+// extract.cpp - a simple, hierarchical, tokenizer
+//
+// 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/>.
+
+bool extract(const_string str, const_string *rv)
+{
+ *rv = str;
+ return true;
+}
+
+bool extract(const_string str, std::string *rv)
+{
+ *rv = std::string(str.begin(), str.end());
+ return true;
+}
+
+bool extract(const_string str, struct global_reg *var)
+{
+ return extract(str,
+ record<','>(&var->str, &var->value));
+}
+
+bool extract(const_string str, struct item *it)
+{
+ return 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))
+ || 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]));
+}
diff --git a/src/common/extract.hpp b/src/common/extract.hpp
new file mode 100644
index 0000000..ae1a74b
--- /dev/null
+++ b/src/common/extract.hpp
@@ -0,0 +1,166 @@
+#ifndef EXTRACT_HPP
+#define EXTRACT_HPP
+// extract.hpp - a simple, hierarchical, tokenizer
+//
+// Copyright © 2012-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 <algorithm>
+
+#include "const_array.hpp"
+#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)
+{
+ if (!str || str.size() > 20)
+ return false;
+ if (!((str.front() == '-' && std::is_signed<T>::value)
+ || ('0' <= str.front() && str.front() <= '9')))
+ return false;
+ char buf[20 + 1];
+ std::copy(str.begin(), str.end(), buf);
+ buf[str.size()] = '\0';
+
+ char *end;
+ errno = 0;
+ if (std::is_signed<T>::value)
+ {
+ long long v = strtoll(buf, &end, 10);
+ if (errno || *end)
+ return false;
+ *iv = v;
+ return *iv == v;
+ }
+ else
+ {
+ unsigned long long v = strtoull(buf, &end, 10);
+ if (errno || *end)
+ return false;
+ *iv = v;
+ return *iv == v;
+ }
+}
+
+inline
+bool extract(const_string 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)
+{
+ typedef typename underlying_type<T>::type U;
+ U v;
+ // defer to integer version
+ if (!extract(str, &v))
+ return false;
+ // TODO check bounds using enum min/max as in SSCANF
+ *iv = static_cast<T>(v);
+ return true;
+}
+
+bool extract(const_string str, const_string *rv);
+
+bool extract(const_string str, std::string *rv);
+
+template<size_t N>
+__attribute__((deprecated))
+bool extract(const_string str, char (*out)[N])
+{
+ if (str.size() >= N)
+ return false;
+ std::copy(str.begin(), str.end(), *out);
+ std::fill(*out + str.size() , *out + N, '\0');
+ return true;
+}
+
+// basically just a std::tuple
+// but it provides its data members publically
+template<char split, class... T>
+class Record;
+template<char split>
+class Record<split>
+{
+};
+template<char split, class F, class... R>
+class Record<split, F, R...>
+{
+public:
+ F frist;
+ Record<split, R...> rest;
+public:
+ Record(F f, R... r)
+ : frist(f), rest(r...)
+ {}
+};
+template<char split, class... T>
+Record<split, T...> record(T... t)
+{
+ return Record<split, T...>(t...);
+}
+
+template<char split>
+bool extract(const_string str, Record<split>)
+{
+ return !str;
+}
+template<char split, class F, class... R>
+bool extract(const_string str, Record<split, F, R...> rec)
+{
+ const char *s = std::find(str.begin(), str.end(), split);
+ 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);
+}
+
+template<char split, class T>
+struct VRecord
+{
+ std::vector<T> *arr;
+};
+
+template<char split, class T>
+VRecord<split, T> vrec(std::vector<T> *arr)
+{
+ return {arr};
+}
+
+template<char split, class T>
+bool extract(const_string str, VRecord<split, T> rec)
+{
+ if (str.empty())
+ return true;
+ const char *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);
+}
+
+bool extract(const_string str, struct global_reg *var);
+
+bool extract(const_string str, struct item *it);
+
+#endif // EXTRACT_HPP
diff --git a/src/common/grfio.cpp b/src/common/grfio.cpp
deleted file mode 100644
index dd1e707..0000000
--- a/src/common/grfio.cpp
+++ /dev/null
@@ -1,216 +0,0 @@
-// Reads .gat files by name-mapping .wlk files
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-
-#include "utils.hpp"
-#include "grfio.hpp"
-#include "mmo.hpp"
-#include "socket.hpp"
-
-//----------------------------
-// file entry table struct
-//----------------------------
-typedef struct
-{
- size_t declen;
- int16_t next; // next index into the filelist[] array, or -1
- char fn[128 - 4 - 2]; // file name
-} FILELIST;
-
-#define FILELIST_LIMIT 32768 // limit to number of filelists - if you increase this, change all shorts to int
-#define FILELIST_ADDS 1024 // amount to increment when reallocing
-
-static FILELIST *filelist = NULL;
-/// Number of entries used
-static uint16_t filelist_entrys = 0;
-/// Number of FILELIST entries actually allocated
-static uint16_t filelist_maxentry = 0;
-
-/// First index of the given hash, into the filelist[] array
-#define l -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
-static int16_t filelist_hash[256] = {l,l,l,l,l,l,l,l,l,l,l,l,l,l,l,l};
-#undef l
-
-/// Hash a filename
-static uint8_t filehash (const char *fname)
-{
- // Larger than the return type - upper bits are used in the process
- uint32_t hash = 0;
- while (*fname)
- {
- hash = (hash << 1) + (hash >> 7) * 9 + (unsigned char)*fname;
- fname++;
- }
- return hash;
-}
-
-/// Find the filelist entry for the given filename, or NULL if it is not
-static
-FILELIST *filelist_find (const char *fname)
-{
- int16_t index = filelist_hash[filehash (fname)];
- while (index >= 0)
- {
- if (strcmp (filelist[index].fn, fname) == 0)
- return &filelist[index];
- index = filelist[index].next;
- }
- return NULL;
-}
-
-/// Copy a temporary entry into the hash map
-static FILELIST *filelist_add (FILELIST * entry)
-{
- if (filelist_entrys >= FILELIST_LIMIT)
- {
- fprintf (stderr, "filelist limit : filelist_add\n");
- exit (1);
- }
-
- if (filelist_entrys >= filelist_maxentry)
- {
- RECREATE(filelist, FILELIST, filelist_maxentry + FILELIST_ADDS);
- memset (filelist + filelist_maxentry, '\0',
- FILELIST_ADDS * sizeof (FILELIST));
- filelist_maxentry += FILELIST_ADDS;
- }
-
- uint16_t new_index = filelist_entrys++;
- uint8_t hash = filehash (entry->fn);
- entry->next = filelist_hash[hash];
- filelist_hash[hash] = new_index;
-
- filelist[new_index] = *entry;
-
- return &filelist[new_index];
-}
-
-static FILELIST *filelist_modify (FILELIST * entry)
-{
- FILELIST *fentry = filelist_find (entry->fn);
- if (fentry)
- {
- entry->next = fentry->next;
- *fentry = *entry;
- return fentry;
- }
- return filelist_add (entry);
-}
-
-/// Change fname data/*.gat to lfname data/*.wlk
-// TODO even if the file exists, don't keep reopening it every time one loads
-static
-void grfio_resnametable (const char *fname, char *lfname)
-{
- char restable[] = "data/resnametable.txt";
-
- FILE *fp = fopen_ (restable, "rb");
- if (fp == NULL)
- {
- fprintf(stderr, "No resnametable, can't look for %s\n", fname);
- strcpy(lfname, fname);
- char* ext = lfname + strlen(lfname) - 4;
- if (!strcmp(ext, ".gat"))
- strcpy(ext, ".wlk");
- return;
- }
-
- char line[512];
- while (fgets (line, sizeof (line), fp))
- {
- char w1[256], w2[256];
- if (
- // line is of the form foo.gat#foo.wlk#
- (sscanf (line, "%[^#]#%[^#]#", w1, w2) == 2)
- // strip data/ from foo.gat before comparing
- && (!strcmp (w1, fname + 5)))
- {
- strcpy (lfname, "data/");
- strcpy (lfname + 5, w2);
- fclose_ (fp);
- return;
- }
- }
- fprintf(stderr, "Unable to find resource: %s\n", fname);
- fclose_ (fp);
-
- strcpy(lfname, fname);
- char* ext = lfname + strlen(lfname) - 4;
- if (!strcmp(ext, ".gat"))
- strcpy(ext, ".wlk");
- return;
-}
-
-/// Size of resource
-size_t grfio_size (const char *fname)
-{
- FILELIST *entry = filelist_find (fname);
- if (entry)
- return entry->declen;
-
- char lfname[256];
- FILELIST lentry;
- struct stat st;
-
- grfio_resnametable (fname, lfname);
-
- for (char *p = lfname; *p; p++)
- if (*p == '\\')
- *p = '/';
-
- if (stat (lfname, &st) == 0)
- {
- strncpy (lentry.fn, fname, sizeof (lentry.fn) - 1);
- lentry.declen = st.st_size;
- entry = filelist_modify (&lentry);
- }
- else
- {
- printf ("%s not found\n", fname);
- return 0;
- }
- return entry->declen;
-}
-
-void *grfio_reads (const char *fname, size_t *size)
-{
- char lfname[256];
- grfio_resnametable (fname, lfname);
-
- for (char *p = &lfname[0]; *p != 0; p++)
- if (*p == '\\')
- *p = '/'; // * At the time of Unix
-
- FILE *in = fopen_ (lfname, "rb");
- if (!in)
- {
- fprintf (stderr, "%s not found\n", fname);
- return NULL;
- }
- FILELIST lentry;
- FILELIST *entry = filelist_find (fname);
- if (entry)
- {
- lentry.declen = entry->declen;
- }
- else
- {
- fseek (in, 0, SEEK_END);
- lentry.declen = ftell (in);
- fseek (in, 0, SEEK_SET);
- strncpy (lentry.fn, fname, sizeof (lentry.fn) - 1);
- entry = filelist_modify (&lentry);
- }
- uint8_t *buf2;
- CREATE (buf2, uint8_t, lentry.declen + 1024);
- if (fread (buf2, 1, lentry.declen, in) != lentry.declen)
- exit(1);
- fclose_ (in);
- in = NULL;
-
- if (size)
- *size = entry->declen;
- return buf2;
-}
diff --git a/src/common/grfio.hpp b/src/common/grfio.hpp
deleted file mode 100644
index 3485904..0000000
--- a/src/common/grfio.hpp
+++ /dev/null
@@ -1,17 +0,0 @@
-/// Accessor to the .gat map virtual files
-// Note .gat files are mapped to .wlk files by data/resnametable.txt
-// Note that there currently is a 1-1 correlation between them,
-// but it is possible for a single .wlk to have multiple .gats reference it
-#ifndef GRFIO_HPP
-#define GRFIO_HPP
-
-/// Load file into memory
-# define grfio_read(resourcename) grfio_reads (resourcename, NULL)
-/// Load file into memory and possibly record length
-// For some reason, this allocates an extra 1024 bytes at the end
-void *grfio_reads (const char *resourcename, size_t *size);
-/// Get size of file
-// This is only called once, and that is to check the existence of a file.
-size_t grfio_size (const char *resourcename) __attribute__((deprecated));
-
-#endif // GRFIO_HPP
diff --git a/src/common/lock.cpp b/src/common/lock.cpp
index 2ba9a0a..82856e1 100644
--- a/src/common/lock.cpp
+++ b/src/common/lock.cpp
@@ -1,36 +1,56 @@
-#include <unistd.h>
-#include <stdio.h>
#include "lock.hpp"
+
+#include <unistd.h>
+
+#include <cstdio>
+
+#include "cxxstdio.hpp"
#include "socket.hpp"
+#include "../poison.hpp"
+
+/// number of backups to keep
+static
+const int backup_count = 10;
+
/// Protected file writing
/// (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(const char *filename, int *info)
{
- char newfile[512];
FILE *fp;
- int no = getpid ();
+ int no = getpid();
// Get a filename that doesn't already exist
+ std::string newfile;
do
{
- sprintf (newfile, "%s_%d.tmp", filename, no++);
+ newfile = STRPRINTF("%s_%d.tmp", filename, no++);
+ fp = fopen_(newfile.c_str(), "wx");
}
- while ((fp = fopen_ (newfile, "r")) && (fclose_ (fp), 1));
+ while (!fp);
*info = --no;
- return fopen_ (newfile, "w");
+ return fp;
}
// Delete the old file and rename the new file
-void lock_fclose (FILE * fp, const char *filename, int *info)
+void lock_fclose(FILE *fp, const char *filename, int *info)
{
- char newfile[512];
if (fp)
{
- fclose_ (fp);
- sprintf (newfile, "%s_%d.tmp", filename, *info);
- rename (newfile, filename);
+ fclose_(fp);
+ int n = backup_count;
+ std::string old_filename = STRPRINTF("%s.%d", filename, n);
+ while (--n)
+ {
+ std::string 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());
+
+ std::string tmpfile = STRPRINTF("%s_%d.tmp", filename, *info);
+ rename(tmpfile.c_str(), filename);
}
}
diff --git a/src/common/lock.hpp b/src/common/lock.hpp
index 19c1302..f7ce2d8 100644
--- a/src/common/lock.hpp
+++ b/src/common/lock.hpp
@@ -1,8 +1,15 @@
#ifndef LOCK_HPP
#define LOCK_HPP
+
+#include "sanity.hpp"
+
+#include <cstdio>
+
+// 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(const char *filename, int *info);
+void lock_fclose(FILE * fp, const char *filename, int *info);
#endif // LOCK_HPP
diff --git a/src/common/md5calc.cpp b/src/common/md5calc.cpp
index b0f8e5f..1625912 100644
--- a/src/common/md5calc.cpp
+++ b/src/common/md5calc.cpp
@@ -1,6 +1,10 @@
#include "md5calc.hpp"
-#include <string.h>
-#include "mt_rand.hpp"
+
+#include <cstring>
+
+#include "random.hpp"
+
+#include "../poison.hpp"
// auxilary data
/*
@@ -9,7 +13,8 @@ sin() constant table
echo 'scale=40; obase=16; for (i=1;i<=64;i++) print 2^32 * sin(i), "\n"' |
bc | sed 's/^-//;s/^/0x/;s/\..*$/,/'
*/
-static const uint32_t T[64] =
+static
+const uint32_t T[64] =
{
// used by round 1
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, //0
@@ -35,29 +40,35 @@ static const uint32_t T[64] =
// auxilary functions
// note - the RFC defines these by non-CS conventions: or=v, and=(empty)
-static inline uint32_t rotate_left(uint32_t val, unsigned shift)
+static
+uint32_t rotate_left(uint32_t val, unsigned shift)
{
return val << shift | val >> (32-shift);
}
-static inline uint32_t F(uint32_t X, uint32_t Y, uint32_t Z)
+static
+uint32_t F(uint32_t X, uint32_t Y, uint32_t Z)
{
return (X & Y) | (~X & Z);
}
-static inline uint32_t G(uint32_t X, uint32_t Y, uint32_t Z)
+static
+uint32_t G(uint32_t X, uint32_t Y, uint32_t Z)
{
return (X & Z) | (Y & ~Z);
}
-static inline uint32_t H(uint32_t X, uint32_t Y, uint32_t Z)
+static
+uint32_t H(uint32_t X, uint32_t Y, uint32_t Z)
{
return X ^ Y ^ Z;
}
-static inline uint32_t I(uint32_t X, uint32_t Y, uint32_t Z)
+static
+uint32_t I(uint32_t X, uint32_t Y, uint32_t Z)
{
return Y ^ (X | ~Z);
}
-static const struct
+static
+const struct
{
uint8_t k : 4;
uint8_t : 0;
@@ -165,7 +176,8 @@ void MD5_to_bin(MD5_state state, uint8_t out[0x10])
out[i] = state.val[i/4] >> 8*(i%4);
}
-static const char hex[] = "0123456789abcdef";
+static
+const char hex[] = "0123456789abcdef";
void MD5_to_str(MD5_state state, char out[0x21])
{
@@ -193,7 +205,7 @@ MD5_state MD5_from_string(const char* msg, const size_t msglen)
}
// now pad 1-512 bits + the 64-bit length - may be two blocks
uint8_t buf[0x40] = {};
- memcpy (buf, msg, rem);
+ memcpy(buf, msg, rem);
buf[rem] = 0x80; // a single one bit
if (64 - rem > 8)
{
@@ -293,8 +305,9 @@ const char *MD5_saltcrypt(const char *key, const char *salt)
const char *make_salt(void) {
static char salt[6];
- for (int i=0; i<5; i++)
- salt[i] = MPRAND(48, 78);
+ for (int i = 0; i < 5; i++)
+ // 126 would probably actually be okay
+ salt[i] = random_::in(48, 125);
return salt;
}
diff --git a/src/common/md5calc.hpp b/src/common/md5calc.hpp
index 2dfaecb..de19e0f 100644
--- a/src/common/md5calc.hpp
+++ b/src/common/md5calc.hpp
@@ -5,9 +5,9 @@
#include <netinet/in.h>
-#include <stdint.h> // uint32_t, uint8_t
-#include <stddef.h> // size_t
-#include <stdio.h> // FILE*
+#include <cstdint>
+#include <cstddef>
+#include <cstdio>
/// The digest state - becomes the output
typedef struct
@@ -35,18 +35,6 @@ MD5_state MD5_from_cstring(const char* msg);
MD5_state MD5_from_FILE(FILE* in);
-/// Output in ASCII - with lowercase hex digits, null-terminated
-// these may overlap safely
-static void MD5_String (const char *string, char output[33]) __attribute__((deprecated));
-static inline void MD5_String (const char *string, char output[33]) {
- MD5_to_str(MD5_from_cstring(string), output);
-}
-/// Output in binary
-static void MD5_String2binary (const char *string, uint8_t output[16]) __attribute__((deprecated));
-static inline void MD5_String2binary (const char *string, uint8_t output[16]) {
- MD5_to_bin(MD5_from_cstring(string), output);
-}
-
// statically-allocated output
// whoever wrote this fails basic understanding of
const char *MD5_saltcrypt(const char *key, const char *salt);
@@ -61,4 +49,4 @@ bool pass_ok(const char *password, const char *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);
-#endif
+#endif // MD5CALC_HPP
diff --git a/src/common/mmo.hpp b/src/common/mmo.hpp
index 151fa03..32d4285 100644
--- a/src/common/mmo.hpp
+++ b/src/common/mmo.hpp
@@ -2,39 +2,31 @@
#ifndef MMO_HPP
#define MMO_HPP
-# include <time.h>
-# include "utils.hpp" // LCCWIN32
-
-# define FIFOSIZE_SERVERLINK 256*1024
-
-// set to 0 to not check IP of player between each server.
-// set to another value if you want to check (1)
-# define CMP_AUTHFIFO_IP 1
-
-# define CMP_AUTHFIFO_LOGIN2 1
-
-# define MAX_MAP_PER_SERVER 512
-# define MAX_INVENTORY 100
-# define MAX_AMOUNT 30000
-# define MAX_ZENY 1000000000 // 1G zeny
-# define MAX_CART 100
-# define MAX_SKILL 450
-# define GLOBAL_REG_NUM 96
-# define ACCOUNT_REG_NUM 16
-# define ACCOUNT_REG2_NUM 16
-# define DEFAULT_WALK_SPEED 150
-# define MIN_WALK_SPEED 0
-# define MAX_WALK_SPEED 1000
-# define MAX_STORAGE 300
-# define MAX_GUILD_STORAGE 1000
-# define MAX_PARTY 12
-# define MAX_GUILD 120 // increased max guild members to accomodate for +2 increase for extension levels [Valaris] (removed) [PoW]
-# define MAX_GUILDPOSITION 20 // increased max guild positions to accomodate for all members [Valaris] (removed) [PoW]
-# define MAX_GUILDEXPLUSION 32
-# define MAX_GUILDALLIANCE 16
-# define MAX_GUILDSKILL 8
-# define MAX_GUILDCASTLE 24 // increased to include novice castles [Valaris]
-# define MAX_GUILDLEVEL 50
+# include "sanity.hpp"
+# include "timer.t.hpp"
+# include "utils.hpp"
+
+constexpr int FIFOSIZE_SERVERLINK = 256 * 1024;
+
+constexpr int MAX_MAP_PER_SERVER = 512;
+constexpr int MAX_INVENTORY = 100;
+constexpr int MAX_AMOUNT = 30000;
+constexpr int MAX_ZENY = 1000000000; // 1G zeny
+constexpr int MAX_CART = 100;
+
+enum class SkillID : uint16_t;
+constexpr SkillID MAX_SKILL = SkillID(474); // not 450
+constexpr SkillID get_enum_min_value(SkillID) { return SkillID(); }
+constexpr SkillID get_enum_max_value(SkillID) { return MAX_SKILL; }
+
+constexpr int GLOBAL_REG_NUM = 96;
+constexpr int ACCOUNT_REG_NUM = 16;
+constexpr int ACCOUNT_REG2_NUM = 16;
+constexpr interval_t DEFAULT_WALK_SPEED = std::chrono::milliseconds(150);
+constexpr interval_t MIN_WALK_SPEED = interval_t::zero();
+constexpr interval_t MAX_WALK_SPEED = std::chrono::seconds(1);
+constexpr int MAX_STORAGE = 300;
+constexpr int MAX_PARTY = 12;
# define MIN_HAIR_STYLE battle_config.min_hair_style
# define MAX_HAIR_STYLE battle_config.max_hair_style
@@ -43,29 +35,43 @@
# define MIN_CLOTH_COLOR battle_config.min_cloth_color
# define MAX_CLOTH_COLOR battle_config.max_cloth_color
-// for produce
-# define MIN_ATTRIBUTE 0
-# define MAX_ATTRIBUTE 4
-# define ATTRIBUTE_NORMAL 0
-# define MIN_STAR 0
-# define MAX_STAR 3
+# define CHAR_CONF_NAME "conf/char_athena.conf"
-# define MIN_PORTAL_MEMO 0
-# define MAX_PORTAL_MEMO 2
+namespace e
+{
+enum class EPOS : uint16_t
+{
+ ZERO = 0x0000,
-# define MAX_STATUS_TYPE 5
+ LEGS = 0x0001,
+ WEAPON = 0x0002,
+ GLOVES = 0x0004,
+ CAPE = 0x0008,
+ MISC1 = 0x0010,
+ SHIELD = 0x0020,
+ SHOES = 0x0040,
+ MISC2 = 0x0080,
+ HAT = 0x0100,
+ TORSO = 0x0200,
-# define CHAR_CONF_NAME "conf/char_athena.conf"
+ ARROW = 0x8000,
+};
+ENUM_BITWISE_OPERATORS(EPOS)
+
+constexpr EPOS get_enum_min_value(EPOS) { return EPOS(0x0000); }
+constexpr EPOS get_enum_max_value(EPOS) { return EPOS(0xffff); }
+}
+using e::EPOS;
struct item
{
- int id;
+ int id;
short nameid;
short amount;
- unsigned short equip;
- char identify;
- char refine;
- char attribute;
+ EPOS equip;
+ uint8_t identify;
+ uint8_t refine;
+ uint8_t attribute;
short card[4];
short broken;
};
@@ -76,38 +82,100 @@ struct point
short x, y;
};
-struct skill
+namespace e
+{
+enum class SkillFlags : uint16_t;
+}
+using e::SkillFlags;
+
+struct skill_value
{
- unsigned short id, lv, flags;
+ unsigned short lv;
+ SkillFlags flags;
};
struct global_reg
{
char str[32];
- int value;
+ int value;
+};
+
+// Option and Opt1..3 in map.hpp
+namespace e
+{
+enum class Option : uint16_t;
+constexpr Option get_enum_min_value(Option) { return Option(0x0000); }
+constexpr Option get_enum_max_value(Option) { return Option(0xffff); }
+}
+using e::Option;
+
+enum class ATTR
+{
+ STR = 0,
+ AGI = 1,
+ VIT = 2,
+ INT = 3,
+ DEX = 4,
+ LUK = 5,
+
+ COUNT = 6,
+};
+
+constexpr ATTR ATTRs[6] =
+{
+ ATTR::STR,
+ ATTR::AGI,
+ ATTR::VIT,
+ ATTR::INT,
+ ATTR::DEX,
+ ATTR::LUK,
+};
+
+enum class ItemLook : uint16_t
+{
+ NONE = 0,
+ BLADE = 1, // or some other common weapons
+ _2,
+ SETZER_AND_SCYTHE = 3,
+ _6,
+ STAFF = 10,
+ BOW = 11,
+ _13 = 13,
+ _14 = 14,
+ _16 = 16,
+ SINGLE_HANDED_COUNT = 17,
+
+ DUAL_BLADE = 0x11,
+ DUAL_2 = 0x12,
+ DUAL_6 = 0x13,
+ DUAL_12 = 0x14,
+ DUAL_16 = 0x15,
+ DUAL_26 = 0x16,
};
struct mmo_charstatus
{
- int char_id;
- int account_id;
- int partner_id;
+ int char_id;
+ int account_id;
+ int partner_id;
- int base_exp, job_exp, zeny;
+ int base_exp, job_exp, zeny;
- short pc_class;
+ short species;
short status_point, skill_point;
- int hp, max_hp, sp, max_sp;
- short option, karma, manner;
+ int hp, max_hp, sp, max_sp;
+ Option option;
+ short karma, manner;
short hair, hair_color, clothes_color;
- int party_id, guild_id;
+ int party_id;
- short weapon, shield;
+ ItemLook weapon;
+ short shield;
short head_top, head_mid, head_bottom;
char name[24];
unsigned char base_level, job_level;
- short str, agi, vit, int_, dex, luk;
+ earray<short, ATTR, ATTR::COUNT> attrs;
unsigned char char_num, sex;
unsigned long mapip;
@@ -115,169 +183,53 @@ struct mmo_charstatus
struct point last_point, save_point, memo_point[10];
struct item inventory[MAX_INVENTORY], cart[MAX_CART];
- struct skill skill[MAX_SKILL];
- int global_reg_num;
+ earray<skill_value, SkillID, MAX_SKILL> skill;
+ int global_reg_num;
struct global_reg global_reg[GLOBAL_REG_NUM];
- int account_reg_num;
+ int account_reg_num;
struct global_reg account_reg[ACCOUNT_REG_NUM];
- int account_reg2_num;
+ int account_reg2_num;
struct global_reg account_reg2[ACCOUNT_REG2_NUM];
};
struct storage
{
- int dirty;
- int account_id;
+ int dirty;
+ int account_id;
short storage_status;
short storage_amount;
struct item storage_[MAX_STORAGE];
};
-struct guild_storage
-{
- int dirty;
- int guild_id;
- short storage_status;
- short storage_amount;
- struct item storage_[MAX_GUILD_STORAGE];
-};
-
struct map_session_data;
struct gm_account
{
- int account_id;
- int level;
+ int account_id;
+ int level;
};
struct party_member
{
- int account_id;
+ int account_id;
char name[24], map[24];
- int leader, online, lv;
+ int leader, online, lv;
struct map_session_data *sd;
};
struct party
{
- int party_id;
+ int party_id;
char name[24];
- int exp;
- int item;
+ int exp;
+ int item;
struct party_member member[MAX_PARTY];
};
-struct guild_member
-{
- int account_id, char_id;
- short hair, hair_color, gender, pc_class, lv;
- int exp, exp_payper;
- short online, position;
- int rsv1, rsv2;
- char name[24];
- struct map_session_data *sd;
-};
-
-struct guild_position
-{
- char name[24];
- int mode;
- int exp_mode;
-};
-
-struct GuildAlliance
-{
- int opposition;
- int guild_id;
- char name[24];
-};
-
-struct GuildExpulsion
-{
- char name[24];
- char mes[40];
- char acc[40];
- int account_id;
- int rsv1, rsv2, rsv3;
-};
-
-struct guild_skill
-{
- int id, lv;
-};
-
-struct guild
-{
- int guild_id;
- short guild_lv, connect_member, max_member, average_lv;
- int exp, next_exp, skill_point, castle_id;
- char name[24], master[24];
- struct guild_member member[MAX_GUILD];
- struct guild_position position[MAX_GUILDPOSITION];
- char mes1[60], mes2[120];
- int emblem_len, emblem_id;
- char emblem_data[2048];
- GuildAlliance alliance[MAX_GUILDALLIANCE];
- GuildExpulsion explusion[MAX_GUILDEXPLUSION];
- struct guild_skill skill[MAX_GUILDSKILL];
-};
-
-struct guild_castle
-{
- int castle_id;
- char map_name[24];
- char castle_name[24];
- char castle_event[24];
- int guild_id;
- int economy;
- int defense;
- int triggerE;
- int triggerD;
- int nextTime;
- int payTime;
- int createTime;
- int visibleC;
- int visibleG0;
- int visibleG1;
- int visibleG2;
- int visibleG3;
- int visibleG4;
- int visibleG5;
- int visibleG6;
- int visibleG7;
- int Ghp0; // added Guardian HP [Valaris]
- int Ghp1;
- int Ghp2;
- int Ghp3;
- int Ghp4;
- int Ghp5;
- int Ghp6;
- int Ghp7;
- int GID0;
- int GID1;
- int GID2;
- int GID3;
- int GID4;
- int GID5;
- int GID6;
- int GID7; // end addition [Valaris]
-};
struct square
{
- int val1[5];
- int val2[5];
-};
-
-enum
-{
- GBI_EXP = 1, // ギルドのEXP
- GBI_GUILDLV = 2, // ギルドのLv
- GBI_SKILLPOINT = 3, // ギルドのスキルポイント
- GBI_SKILLLV = 4, // ギルドスキルLv
-
- GMI_POSITION = 0, // メンバーの役職変更
- GMI_EXP = 1, // メンバーのEXP
-
+ int val1[5];
+ int val2[5];
};
#endif // MMO_HPP
diff --git a/src/common/mt_rand.cpp b/src/common/mt_rand.cpp
deleted file mode 100644
index 676eca1..0000000
--- a/src/common/mt_rand.cpp
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
-// This is the ``Mersenne Twister'' random number generator MT19937, which
-// generates pseudorandom integers uniformly distributed in 0..(2^32 - 1)
-// starting from any odd seed in 0..(2^32 - 1). This version is a recode
-// by Shawn Cokus (Cokus@math.washington.edu) on March 8, 1998 of a version by
-// Takuji Nishimura (who had suggestions from Topher Cooper and Marc Rieffel in
-// July-August 1997).
-//
-// Effectiveness of the recoding (on Goedel2.math.washington.edu, a DEC Alpha
-// running OSF/1) using GCC -O3 as a compiler: before recoding: 51.6 sec. to
-// generate 300 million random numbers; after recoding: 24.0 sec. for the same
-// (i.e., 46.5% of original time), so speed is now about 12.5 million random
-// number generations per second on this machine.
-//
-// According to the URL <http://www.math.keio.ac.jp/~matumoto/emt.html>
-// (and paraphrasing a bit in places), the Mersenne Twister is ``designed
-// with consideration of the flaws of various existing generators,'' has
-// a period of 2^19937 - 1, gives a sequence that is 623-dimensionally
-// equidistributed, and ``has passed many stringent tests, including the
-// die-hard test of G. Marsaglia and the load test of P. Hellekalek and
-// S. Wegenkittl.'' It is efficient in memory usage (typically using 2506
-// to 5012 bytes of static data, depending on data type sizes, and the code
-// is quite short as well). It generates random numbers in batches of 624
-// at a time, so the caching and pipelining of modern systems is exploited.
-// It is also divide- and mod-free.
-//
-// This library is free software; you can redistribute it and/or modify it
-// under the terms of the GNU Library General Public License as published by
-// the Free Software Foundation (either version 2 of the License or, at your
-// option, any later version). This library 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 Library General Public License for more details. You should have
-// received a copy of the GNU Library General Public License along with this
-// library; if not, write to the Free Software Foundation, Inc., 59 Temple
-// Place, Suite 330, Boston, MA 02111-1307, USA.
-//
-// The code as Shawn received it included the following notice:
-//
-// Copyright (C) 1997 Makoto Matsumoto and Takuji Nishimura. When
-// you use this, send an e-mail to <matumoto@math.keio.ac.jp> with
-// an appropriate reference to your work.
-//
-// It would be nice to CC: <Cokus@math.washington.edu> when you write.
-//
-*/
-
-#include <time.h>
-#include "mt_rand.hpp"
-
-#define N 624 // length of state vector
-#define M 397 // a period parameter
-#define K 0x9908B0DFU // a magic constant
-
-#define hiBit(u) ((u) & 0x80000000U) // mask all but highest bit of u
-#define loBit(u) ((u) & 0x00000001U) // mask all but lowest bit of u
-#define loBits(u) ((u) & 0x7FFFFFFFU) // mask the highest bit of u
-#define mixBits(u, v) (hiBit(u)|loBits(v)) // move hi bit of u to hi bit of v
-
-static uint32_t state[N+1]; // state vector the +1 is needed due to the coding
-static uint32_t *next; // next random value is computed from here
-static int left = -1; // can *next++ this many times before reloading
-
-void mt_seed (uint32_t seed)
-{
- uint32_t x = seed | 1U;
- uint32_t *s = state;
- left = 0;
-
- for (int j = N; *s++ = x, --j; x *= 69069U);
-}
-
-static
-void mt_reload (void)
-{
- // if mt_seed has never been called
- if (left < -1)
- mt_seed (time (NULL));
-
- // conceptually, these are indices into the state that wrap
- uint32_t *p0 = state;
- uint32_t *p2 = state + 2;
- uint32_t *pM = state + M;
-
- uint32_t s0 = state[0];
- uint32_t s1 = state[1];
-
- // regenerate the lower N-M elements of the state
- for (int j = N-M+1; --j != 0; s0 = s1, s1 = *p2++)
- *p0++ = *pM++ ^ (mixBits (s0, s1) >> 1) ^ (loBit (s1) ? K : 0U);
-
- pM = state;
- // regenerate the next M-1 elements of the state
- // note that s1 is set to state[N] at the end, but discarded
- for (int j = M; --j != 0; s0 = s1, s1 = *p2++)
- *p0++ = *pM++ ^ (mixBits (s0, s1) >> 1) ^ (loBit (s1) ? K : 0U);
-
- // regenerate the last 1 element of the state
- s1 = state[0];
- *p0 = *pM ^ (mixBits (s0, s1) >> 1) ^ (loBit (s1) ? K : 0U);
-
- // ready for the normal mt_random algorithm
- left = N;
- next = state;
-}
-
-uint32_t mt_random (void)
-{
- if (--left < 0)
- mt_reload ();
-
- uint32_t y = *next++;
- y ^= (y >> 11);
- y ^= (y << 7) & 0x9D2C5680U;
- y ^= (y << 15) & 0xEFC60000U;
- return y ^ (y >> 18);
-}
diff --git a/src/common/mt_rand.hpp b/src/common/mt_rand.hpp
deleted file mode 100644
index c7bae4e..0000000
--- a/src/common/mt_rand.hpp
+++ /dev/null
@@ -1,24 +0,0 @@
-#ifndef MT_RAND_HPP
-#define MT_RAND_HPP
-
-# include "sanity.hpp"
-
-/// Initialize the generator (called automatically with time() if you don't)
-void mt_seed (uint32_t seed);
-/// Get a random number
-uint32_t mt_random (void);
-
-/**
- * ModuloRand and ModuloPlusRand
- * These macros are used to replace the vast number of calls to rand()%mod
- * TODO eliminate the rest of the calls to rand()
- * MRAND(10) returns 0..9
- * MPRAND(5,10) returns 5..14
- */
-// The cast is essential because the result is sometimes
-// compared with a possibly negative number.
-// Because it's using modulus, high numbers shouldn't happen anyway.
-# define MRAND(mod) ((int)(mt_random() % (mod)))
-# define MPRAND(add, mod) ((add) + MRAND(mod))
-
-#endif // MT_RAND_HPP
diff --git a/src/common/nullpo.cpp b/src/common/nullpo.cpp
index 67c839f..423ed8c 100644
--- a/src/common/nullpo.cpp
+++ b/src/common/nullpo.cpp
@@ -1,71 +1,28 @@
-#include <stdio.h>
-#include <stdarg.h>
-#include <string.h>
#include "nullpo.hpp"
-static void nullpo_info_core (const char *file, int line, const char *func);
-__attribute__((format(printf, 4, 0)))
-static void nullpo_info_core (const char *file, int line, const char *func,
- const char *fmt, va_list ap);
+#include <cstdio>
-/// Null check and print format
-bool nullpo_chk_f (const char *file, int line, const char *func,
- const void *target, const char *fmt, ...)
-{
- va_list ap;
-
- if (target)
- return 0;
-
- va_start (ap, fmt);
- nullpo_info_core (file, line, func, fmt, ap);
- va_end (ap);
- return 1;
-}
-bool nullpo_chk (const char *file, int line, const char *func,
- const void *target)
-{
- if (target)
- return 0;
-
- nullpo_info_core (file, line, func);
- return 1;
-}
-
-/// External functions
-void nullpo_info_f (const char *file, int line, const char *func,
- const char *fmt, ...)
-{
- va_list ap;
-
- va_start (ap, fmt);
- nullpo_info_core (file, line, func, fmt, ap);
- va_end (ap);
-}
-void nullpo_info (const char *file, int line, const char *func)
-{
- nullpo_info_core (file, line, func);
-}
+#include "../poison.hpp"
/// Actual output function
-static void nullpo_info_core (const char *file, int line, const char *func)
+static
+void nullpo_info(const char *file, int line, const char *func)
{
if (!file)
file = "??";
if (!func || !*func)
func = "unknown";
- fprintf (stderr, "%s:%d: in func `%s': NULL pointer\n", file, line, func);
+ fprintf(stderr, "%s:%d: in func `%s': NULL pointer\n",
+ file, line, func);
}
-static void nullpo_info_core (const char *file, int line, const char *func,
- const char *fmt, va_list ap)
+bool nullpo_chk(const char *file, int line, const char *func,
+ const void *target)
{
- nullpo_info_core(file, line, func);
- if (fmt && *fmt)
- {
- vfprintf (stderr, fmt, ap);
- if (fmt[strlen (fmt) - 1] != '\n')
- fputc('\n', stderr);
- }
+ if (target)
+ return 0;
+
+ nullpo_info(file, line, func);
+ return 1;
}
diff --git a/src/common/nullpo.hpp b/src/common/nullpo.hpp
index 7aff691..305448f 100644
--- a/src/common/nullpo.hpp
+++ b/src/common/nullpo.hpp
@@ -1,61 +1,30 @@
/// return wrappers for unexpected NULL pointers
#ifndef NULLPO_HPP
#define NULLPO_HPP
-/// Comment this out to live dangerously
-# define NULLPO_CHECK
+/// Uncomment this to live dangerously
+/// (really exist to detect mostly-unused variables)
+//# define BUG_FREE
/// All functions print to standard error (was: standard output)
/// nullpo_ret(cond) - return 0 if given pointer is NULL
/// nullpo_retv(cond) - just return (function returns void)
/// nullpo_retr(rv, cond) - return given value instead
-/// the _f variants take a printf-format string and arguments
-# ifdef NULLPO_CHECK
-# define NLP_MARK __FILE__, __LINE__, __func__
-# define nullpo_ret(t) \
- if (nullpo_chk(NLP_MARK, t)) \
- return 0;
-# define nullpo_retv(t) \
- if (nullpo_chk(NLP_MARK, t)) \
- return;
+# ifndef BUG_FREE
# define nullpo_retr(ret, t) \
- if (nullpo_chk(NLP_MARK, t)) \
+ if (nullpo_chk(__FILE__, __LINE__, __PRETTY_FUNCTION__, t)) \
return ret;
-# define nullpo_ret_f(t, fmt, ...) \
- if (nullpo_chk_f(NLP_MARK, t, fmt, ##__VA_ARGS__)) \
- return 0;
-# define nullpo_retv_f(t, fmt, ...) \
- if (nullpo_chk_f(NLP_MARK, t, fmt, ##__VA_ARGS__)) \
- return;
-# define nullpo_retr_f(ret, t, fmt, ...) \
- if (nullpo_chk_f(NLP_MARK, t, fmt, ##__VA_ARGS__)) \
- return ret;
-# else // NULLPO_CHECK
-# define nullpo_ret(t) t;
-# define nullpo_retv(t) t;
-# define nullpo_retr(ret, t) t;
-# define nullpo_ret_f(t, fmt, ...) t;
-# define nullpo_retv_f(t, fmt, ...) t;
-# define nullpo_retr_f(ret, t, fmt, ...) t;
-# endif // NULLPO_CHECK
+# else // BUG_FREE
+# define nullpo_retr(ret, t) /*t*/
+# endif // BUG_FREE
-# include "sanity.hpp"
+# define nullpo_ret(t) nullpo_retr(0, t)
+# define nullpo_retv(t) nullpo_retr(, t)
-/// Used by macros in this header
-bool nullpo_chk (const char *file, int line, const char *func,
- const void *target);
+# include "sanity.hpp"
/// Used by macros in this header
-bool nullpo_chk_f (const char *file, int line, const char *func,
- const void *target, const char *fmt, ...)
- __attribute__ ((format (printf, 5, 6)));
-
-/// Used only by map/battle.c
-void nullpo_info (const char *file, int line, const char *func);
-
-/// Not used
-void nullpo_info_f (const char *file, int line, const char *func,
- const char *fmt, ...)
- __attribute__ ((format (printf, 4, 5)));
+bool nullpo_chk(const char *file, int line, const char *func,
+ const void *target);
#endif // NULLPO_HPP
diff --git a/src/common/operators.hpp b/src/common/operators.hpp
new file mode 100644
index 0000000..3d44b81
--- /dev/null
+++ b/src/common/operators.hpp
@@ -0,0 +1,47 @@
+#ifndef OPERATORS_HPP
+#define OPERATORS_HPP
+
+namespace _operators
+{
+ class Comparable {};
+
+ template<class T>
+ bool operator == (T l, T r)
+ {
+ return l.value == r.value;
+ }
+
+ template<class T>
+ bool operator != (T l, T r)
+ {
+ return l.value != r.value;
+ }
+
+ template<class T>
+ bool operator < (T l, T r)
+ {
+ return l.value < r.value;
+ }
+
+ template<class T>
+ bool operator <= (T l, T r)
+ {
+ return l.value <= r.value;
+ }
+
+ template<class T>
+ bool operator > (T l, T r)
+ {
+ return l.value > r.value;
+ }
+
+ template<class T>
+ bool operator >= (T l, T r)
+ {
+ return l.value >= r.value;
+ }
+}
+
+using _operators::Comparable;
+
+#endif // OPERATORS_HPP
diff --git a/src/common/random.cpp b/src/common/random.cpp
new file mode 100644
index 0000000..273dcec
--- /dev/null
+++ b/src/common/random.cpp
@@ -0,0 +1,8 @@
+#include "random2.hpp"
+
+#include "../poison.hpp"
+
+namespace random_
+{
+ std::mt19937 generate{std::random_device()()};
+} // namespace random_
diff --git a/src/common/random.hpp b/src/common/random.hpp
new file mode 100644
index 0000000..44057ed
--- /dev/null
+++ b/src/common/random.hpp
@@ -0,0 +1,69 @@
+#ifndef RANDOM_HPP
+#define RANDOM_HPP
+
+# include "random.t.hpp"
+
+# include "sanity.hpp"
+
+# include <random>
+
+// This is not namespace random since that collides with a C function,
+// but this can be revisited when everything goes into namespace tmwa.
+namespace random_
+{
+ /// Get a random number from 0 .. 2**32 - 1
+ extern std::mt19937 generate;
+
+ /// Get a random number from 0 .. bound - 1
+ inline
+ int to(int bound)
+ {
+ std::uniform_int_distribution<int> dist(0, bound - 1);
+ return dist(generate);
+ }
+
+ /// Get a random number from low .. high (inclusive!)
+ inline
+ int in(int low, int high)
+ {
+ std::uniform_int_distribution<int> dist(low, high);
+ return dist(generate);
+ }
+
+ inline
+ bool coin()
+ {
+ // sigh, can't specify <bool> directly ...
+ std::uniform_int_distribution<int> dist(false, true);
+ return dist(generate);
+ }
+
+ inline
+ bool chance(Fraction f)
+ {
+ if (f.num <= 0)
+ return false;
+ if (f.num >= f.den)
+ return true;
+ return random_::to(f.den) < f.num;
+ }
+
+ // C is usually one of:
+ // std::vector<T>
+ // std::initializer_list<T>
+ // std::array<T, n>
+ template<class C>
+ auto choice(C&& c) -> decltype(*c.begin())
+ {
+ return *(c.begin() + random_::to(c.size()));
+ }
+
+ // allow bare braces
+ template<class T>
+ T choice(std::initializer_list<T>&& il)
+ {
+ return random_::choice(il);
+ }
+} // namespace random_
+
+#endif // RANDOM_HPP
diff --git a/src/common/random.t.hpp b/src/common/random.t.hpp
new file mode 100644
index 0000000..98a6c59
--- /dev/null
+++ b/src/common/random.t.hpp
@@ -0,0 +1,23 @@
+#ifndef RANDOM_T_HPP
+#define RANDOM_T_HPP
+
+namespace random_
+{
+ struct Fraction
+ {
+ int num, den;
+ };
+
+ template<class T, T den>
+ struct Fixed
+ {
+ T num;
+
+ operator Fraction()
+ {
+ return {num, den};
+ }
+ };
+} // namespace random_
+
+#endif // RANDOM_T_HPP
diff --git a/src/common/random2.hpp b/src/common/random2.hpp
new file mode 100644
index 0000000..86deddf
--- /dev/null
+++ b/src/common/random2.hpp
@@ -0,0 +1,74 @@
+#ifndef RANDOM2_HPP
+#define RANDOM2_HPP
+
+# include "random.hpp"
+# include "utils2.hpp"
+
+# include <algorithm>
+
+namespace random_
+{
+ namespace detail
+ {
+ struct RandomIterator
+ {
+ int bound;
+ int current;
+ bool frist;
+
+ void operator ++()
+ {
+ frist = false;
+ // TODO: reimplement in terms of LFSRs, so that certain
+ // blockage patterns don't favor adjacent cells.
+ current = current + 1;
+ if (current == bound)
+ current = 0;
+ }
+ int operator *()
+ {
+ return current;
+ }
+ };
+
+ inline
+ bool operator == (RandomIterator l, RandomIterator r)
+ {
+ return l.current == r.current && l.frist == r.frist;
+ }
+
+ inline
+ bool operator != (RandomIterator l, RandomIterator r)
+ {
+ return !(l == r);
+ }
+ }
+
+ /// Yield every cell from 0 .. bound - 1 in some order.
+ /// The starting position is random, but not the order itself.
+ ///
+ /// Expected usage:
+ /// for (int i : random_::iterator(vec.size()))
+ /// if (vec[i].okay())
+ /// return frob(vec[i]);
+ inline
+ IteratorPair<detail::RandomIterator> iterator(int bound)
+ {
+ int current = random_::to(bound);
+ return
+ {
+ detail::RandomIterator{bound, current, true},
+ detail::RandomIterator{bound, current, false}
+ };
+ }
+
+ /// similar to std::random_shuffle(c.begin(), c.end()), but guaranteed
+ /// to use a good source of randomness.
+ template<class C>
+ void shuffle(C&& c)
+ {
+ std::random_shuffle(c.begin(), c.end(), random_::to);
+ }
+} // namespace random_
+
+#endif // RANDOM2_HPP
diff --git a/src/common/sanity.hpp b/src/common/sanity.hpp
index 7ffd077..e54739f 100644
--- a/src/common/sanity.hpp
+++ b/src/common/sanity.hpp
@@ -1,31 +1,54 @@
/// return wrappers for unexpected NULL pointers
#ifndef SANITY_HPP
#define SANITY_HPP
+
# ifndef __cplusplus
# error "Please compile in C++ mode"
-# endif
-# if __GNUC__ < 3
-// I don't specifically know what version this requires,
-// but GCC 3 was the beginning of modern GCC
-# error "Please upgrade your compiler to at least GCC 3"
-# endif
-# ifndef __i386__
-// Known platform dependencies:
-// endianness for the [RW]FIFO.* macros
-// possibly, some signal-handling
-# error "Unsupported platform"
-# endif
-# ifdef __x86_64__
-// I'm working on it - I know there are some pointer-size assumptions.
-# error "Sorry, this code is believed not to be 64-bit safe"
-# error "please compile with -m32"
-# endif
+# endif // __cplusplus
+
+# if __GNUC__ < 4
+# error "Your compiler is absolutely ancient. You have no chance ..."
+# endif // __GNUC__ < 4
-/// A name for unused function arguments - can be repeated
-# define UNUSED /* empty works for C++ */
/// Convert type assumptions to use the standard types here
# include <cstdint>
/// size_t, NULL
# include <cstddef>
+# if __GNUC__ == 4
+// clang identifies as GCC 4.2, but is mostly okay.
+// Until a bug-free release of it happens, though, I won't recommend it.
+// (patched) clang 3.1 would be the requirement
+# 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
+# if __GLIBCXX__ == 20110325 \
+ || __GLIBCXX__ == 20110627 \
+ || __GLIBCXX__ == 20111026 \
+ || __GLIBCXX__ == 20120301 \
+ || __GLIBCXX__ == 20121127 // prerelease in Debian wheezy
+# 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__)
+// Known platform dependencies:
+// endianness for the [RW]FIFO.* macros
+// possibly, some signal-handling
+# error "Unsupported platform use x86 / amd64 only"
+# endif // not __i386__
+
#endif // SANITY_HPP
diff --git a/src/common/socket.cpp b/src/common/socket.cpp
index fab3804..2acbeb8 100644
--- a/src/common/socket.cpp
+++ b/src/common/socket.cpp
@@ -1,51 +1,74 @@
-// $Id: socket.c,v 1.1.1.1 2004/09/10 17:44:49 MagicalTux Exp $
-// original : core.c 2003/02/26 18:03:12 Rev 1.7
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <errno.h>
+#include "socket.hpp"
-#include <sys/socket.h>
-#include <netinet/in.h>
+#include <arpa/inet.h>
#include <netinet/tcp.h>
-#include <sys/time.h>
-#include <unistd.h>
+#include <sys/socket.h>
+//#include <sys/types.h>
#include <fcntl.h>
-#include <string.h>
+#include <unistd.h>
-#include "mmo.hpp" // [Valaris] thanks to fov
-#include "socket.hpp"
+#include <cstdlib>
+#include <cstring>
+#include <ctime>
+
+#include "cxxstdio.hpp"
+//#include "mmo.hpp"
#include "utils.hpp"
+#include "../poison.hpp"
+
+static
fd_set readfds;
-int fd_max;
-int currentuse;
+int fd_max;
+static
+int currentuse;
+static
const uint32_t RFIFO_SIZE = 65536;
+static
const uint32_t WFIFO_SIZE = 65536;
struct socket_data *session[FD_SETSIZE];
+/// clean up by discarding handled bytes
+inline
+void RFIFOFLUSH(int fd)
+{
+ memmove(session[fd]->rdata, RFIFOP(fd, 0), RFIFOREST(fd));
+ session[fd]->rdata_size = RFIFOREST(fd);
+ session[fd]->rdata_pos = 0;
+}
+
+/// how much room there is to read more data
+inline
+size_t RFIFOSPACE(int fd)
+{
+ return session[fd]->max_rdata - session[fd]->rdata_size;
+}
+
+
/// Discard all input
-static void null_parse (int fd);
+static
+void null_parse(int fd);
/// Default parser for new connections
-static void (*default_func_parse) (int) = null_parse;
+static
+void(*default_func_parse)(int) = null_parse;
-void set_defaultparse (void (*defaultparse) (int))
+void set_defaultparse(void(*defaultparse)(int))
{
default_func_parse = defaultparse;
}
/// Read from socket to the queue
-static void recv_to_fifo (int fd)
+static
+void recv_to_fifo(int fd)
{
if (session[fd]->eof)
return;
- ssize_t len = read (fd, session[fd]->rdata + session[fd]->rdata_size,
- RFIFOSPACE (fd));
+ ssize_t len = read(fd, session[fd]->rdata + session[fd]->rdata_size,
+ RFIFOSPACE(fd));
if (len > 0)
{
@@ -58,19 +81,20 @@ static void recv_to_fifo (int fd)
}
}
-static void send_from_fifo (int fd)
+static
+void send_from_fifo(int fd)
{
if (session[fd]->eof)
return;
- ssize_t len = write (fd, session[fd]->wdata, session[fd]->wdata_size);
+ ssize_t len = write(fd, session[fd]->wdata, session[fd]->wdata_size);
if (len > 0)
{
session[fd]->wdata_size -= len;
if (session[fd]->wdata_size)
{
- memmove (session[fd]->wdata, session[fd]->wdata + len,
+ memmove(session[fd]->wdata, session[fd]->wdata + len,
session[fd]->wdata_size);
}
session[fd]->connected = 1;
@@ -81,32 +105,34 @@ static void send_from_fifo (int fd)
}
}
-static void null_parse (int fd)
+static
+void null_parse(int fd)
{
- printf ("null_parse : %d\n", fd);
- RFIFOSKIP (fd, RFIFOREST (fd));
+ PRINTF("null_parse : %d\n", fd);
+ RFIFOSKIP(fd, RFIFOREST(fd));
}
-static void connect_client (int listen_fd)
+static
+void connect_client(int listen_fd)
{
struct sockaddr_in client_address;
- socklen_t len = sizeof (client_address);
+ socklen_t len = sizeof(client_address);
- int fd = accept (listen_fd, (struct sockaddr *) &client_address, &len);
+ int fd = accept(listen_fd, (struct sockaddr *) &client_address, &len);
if (fd == -1)
{
- perror ("accept");
+ perror("accept");
return;
}
if (fd_max <= fd)
{
fd_max = fd + 1;
}
- if (!free_fds ())
+ if (!free_fds())
{
- fprintf (stderr, "softlimit reached, disconnecting : %d\n", fd);
- delete_session (fd);
+ FPRINTF(stderr, "softlimit reached, disconnecting : %d\n", fd);
+ delete_session(fd);
return;
}
@@ -114,11 +140,11 @@ static void connect_client (int listen_fd)
/// Allow to bind() again after the server restarts.
// Since the socket is still in the TIME_WAIT, there's a possibility
// that formerly lost packets might be delivered and confuse the server.
- setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes);
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes);
/// Send packets as soon as possible
/// even if the kernel thinks there is too little for it to be worth it!
/// Testing shows this is indeed a good idea.
- setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof yes);
+ setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof yes);
// Linux-ism: Set socket options to optimize for thin streams
// See http://lwn.net/Articles/308919/ and
@@ -130,13 +156,13 @@ static void connect_client (int listen_fd)
setsockopt(fd, IPPROTO_TCP, TCP_THIN_DUPACK, &yes, sizeof yes);
#endif
- FD_SET (fd, &readfds);
+ FD_SET(fd, &readfds);
- fcntl (fd, F_SETFL, O_NONBLOCK);
+ fcntl(fd, F_SETFL, O_NONBLOCK);
- CREATE (session[fd], struct socket_data, 1);
- CREATE (session[fd]->rdata, uint8_t, RFIFO_SIZE);
- CREATE (session[fd]->wdata, uint8_t, WFIFO_SIZE);
+ CREATE(session[fd], struct socket_data, 1);
+ CREATE(session[fd]->rdata, uint8_t, RFIFO_SIZE);
+ CREATE(session[fd]->wdata, uint8_t, WFIFO_SIZE);
session[fd]->max_rdata = RFIFO_SIZE;
session[fd]->max_wdata = WFIFO_SIZE;
@@ -144,72 +170,72 @@ static void connect_client (int listen_fd)
session[fd]->func_send = send_from_fifo;
session[fd]->func_parse = default_func_parse;
session[fd]->client_addr = client_address;
- session[fd]->created = time (NULL);
+ session[fd]->created = TimeT::now();
session[fd]->connected = 0;
currentuse++;
}
-int make_listen_port (uint16_t port)
+int make_listen_port(uint16_t port)
{
struct sockaddr_in server_address;
- int fd = socket (AF_INET, SOCK_STREAM, 0);
+ int fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd == -1)
{
- perror ("socket");
+ perror("socket");
return -1;
}
if (fd_max <= fd)
fd_max = fd + 1;
- fcntl (fd, F_SETFL, O_NONBLOCK);
+ fcntl(fd, F_SETFL, O_NONBLOCK);
const int yes = 1;
/// Allow to bind() again after the server restarts.
// Since the socket is still in the TIME_WAIT, there's a possibility
// that formerly lost packets might be delivered and confuse the server.
- setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes);
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes);
/// Send packets as soon as possible
/// even if the kernel thinks there is too little for it to be worth it!
// I'm not convinced this is a good idea; although in minimizes the
// latency for an individual write, it increases traffic in general.
- setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof yes);
+ setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof yes);
server_address.sin_family = AF_INET;
- server_address.sin_addr.s_addr = htonl (INADDR_ANY);
- server_address.sin_port = htons (port);
+ server_address.sin_addr.s_addr = htonl(INADDR_ANY);
+ server_address.sin_port = htons(port);
- if (bind (fd, (struct sockaddr *) &server_address,
- sizeof (server_address)) == -1)
+ if (bind(fd, (struct sockaddr *) &server_address,
+ sizeof(server_address)) == -1)
{
- perror ("bind");
- exit (1);
+ perror("bind");
+ exit(1);
}
- if (listen (fd, 5) == -1)
+ if (listen(fd, 5) == -1)
{ /* error */
- perror ("listen");
- exit (1);
+ perror("listen");
+ exit(1);
}
- FD_SET (fd, &readfds);
+ FD_SET(fd, &readfds);
- CREATE (session[fd], struct socket_data, 1);
+ CREATE(session[fd], struct socket_data, 1);
session[fd]->func_recv = connect_client;
- session[fd]->created = time (NULL);
+ session[fd]->created = TimeT::now();
session[fd]->connected = 1;
currentuse++;
return fd;
}
-int make_connection (uint32_t ip, uint16_t port)
+int make_connection(uint32_t ip, uint16_t port)
{
struct sockaddr_in server_address;
- int fd = socket (AF_INET, SOCK_STREAM, 0);
+ int fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd == -1)
{
- perror ("socket");
+ perror("socket");
return -1;
}
if (fd_max <= fd)
@@ -219,43 +245,43 @@ int make_connection (uint32_t ip, uint16_t port)
/// Allow to bind() again after the server restarts.
// Since the socket is still in the TIME_WAIT, there's a possibility
// that formerly lost packets might be delivered and confuse the server.
- setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes);
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof yes);
/// Send packets as soon as possible
/// even if the kernel thinks there is too little for it to be worth it!
// I'm not convinced this is a good idea; although in minimizes the
// latency for an individual write, it increases traffic in general.
- setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof yes);
+ setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof yes);
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = ip;
- server_address.sin_port = htons (port);
+ server_address.sin_port = htons(port);
- fcntl (fd, F_SETFL, O_NONBLOCK);
+ fcntl(fd, F_SETFL, O_NONBLOCK);
/// Errors not caught - we must not block
/// Let the main select() loop detect when we know the state
- connect (fd, (struct sockaddr *) &server_address,
- sizeof (struct sockaddr_in));
+ connect(fd, (struct sockaddr *) &server_address,
+ sizeof(struct sockaddr_in));
- FD_SET (fd, &readfds);
+ FD_SET(fd, &readfds);
- CREATE (session[fd], struct socket_data, 1);
- CREATE (session[fd]->rdata, uint8_t, RFIFO_SIZE);
- CREATE (session[fd]->wdata, uint8_t, WFIFO_SIZE);
+ CREATE(session[fd], struct socket_data, 1);
+ CREATE(session[fd]->rdata, uint8_t, RFIFO_SIZE);
+ CREATE(session[fd]->wdata, uint8_t, WFIFO_SIZE);
session[fd]->max_rdata = RFIFO_SIZE;
session[fd]->max_wdata = WFIFO_SIZE;
session[fd]->func_recv = recv_to_fifo;
session[fd]->func_send = send_from_fifo;
session[fd]->func_parse = default_func_parse;
- session[fd]->created = time (NULL);
+ session[fd]->created = TimeT::now();
session[fd]->connected = 1;
currentuse++;
return fd;
}
-void delete_session (int fd)
+void delete_session(int fd)
{
if (fd < 0 || fd >= FD_SETSIZE)
return;
@@ -264,151 +290,155 @@ void delete_session (int fd)
// but this is cheap and good enough for the typical case
if (fd == fd_max - 1)
fd_max--;
- FD_CLR (fd, &readfds);
+ FD_CLR(fd, &readfds);
if (session[fd])
{
- free (session[fd]->rdata);
- free (session[fd]->wdata);
- free (session[fd]->session_data);
- free (session[fd]);
+ free(session[fd]->rdata);
+ free(session[fd]->wdata);
+ free(session[fd]->session_data);
+ free(session[fd]);
}
session[fd] = NULL;
// just close() would try to keep sending buffers
- shutdown (fd, SHUT_RDWR);
- close (fd);
+ shutdown(fd, SHUT_RDWR);
+ close(fd);
currentuse--;
if (currentuse < 0)
{
- fprintf (stderr, "delete_session: current sessions negative!\n");
+ FPRINTF(stderr, "delete_session: current sessions negative!\n");
currentuse = 0;
}
return;
}
-void realloc_fifo (int fd, size_t rfifo_size, size_t wfifo_size)
+void realloc_fifo(int fd, size_t rfifo_size, size_t wfifo_size)
{
struct socket_data *s = session[fd];
if (s->max_rdata != rfifo_size && s->rdata_size < rfifo_size)
{
- RECREATE (s->rdata, uint8_t, rfifo_size);
+ RECREATE(s->rdata, uint8_t, rfifo_size);
s->max_rdata = rfifo_size;
}
if (s->max_wdata != wfifo_size && s->wdata_size < wfifo_size)
{
- RECREATE (s->wdata, uint8_t, wfifo_size);
+ RECREATE(s->wdata, uint8_t, wfifo_size);
s->max_wdata = wfifo_size;
}
}
-void WFIFOSET (int fd, size_t len)
+void WFIFOSET(int fd, size_t len)
{
struct socket_data *s = session[fd];
if (s->wdata_size + len + 16384 > s->max_wdata)
{
- realloc_fifo (fd, s->max_rdata, s->max_wdata << 1);
- printf ("socket: %d wdata expanded to %d bytes.\n", fd, s->max_wdata);
+ realloc_fifo(fd, s->max_rdata, s->max_wdata << 1);
+ PRINTF("socket: %d wdata expanded to %zu bytes.\n", fd, s->max_wdata);
}
if (s->wdata_size + len + 2048 < s->max_wdata)
s->wdata_size += len;
else
- fprintf (stderr, "socket: %d wdata lost !!\n", fd), abort ();
+ FPRINTF(stderr, "socket: %d wdata lost !!\n", fd), abort();
}
-void do_sendrecv (uint32_t next)
+void do_sendrecv(interval_t next_ms)
{
fd_set rfd = readfds, wfd;
- FD_ZERO (&wfd);
+ FD_ZERO(&wfd);
for (int i = 0; i < fd_max; i++)
{
if (session[i] && session[i]->wdata_size)
- FD_SET (i, &wfd);
+ FD_SET(i, &wfd);
}
struct timeval timeout;
- timeout.tv_sec = next / 1000;
- timeout.tv_usec = next % 1000 * 1000;
- if (select (fd_max, &rfd, &wfd, NULL, &timeout) <= 0)
+ {
+ std::chrono::seconds next_s = std::chrono::duration_cast<std::chrono::seconds>(next_ms);
+ std::chrono::microseconds next_us = next_ms - next_s;
+ timeout.tv_sec = next_s.count();
+ timeout.tv_usec = next_us.count();
+ }
+ if (select(fd_max, &rfd, &wfd, NULL, &timeout) <= 0)
return;
for (int i = 0; i < fd_max; i++)
{
if (!session[i])
continue;
- if (FD_ISSET (i, &wfd))
+ if (FD_ISSET(i, &wfd))
{
if (session[i]->func_send)
//send_from_fifo(i);
- session[i]->func_send (i);
+ session[i]->func_send(i);
}
- if (FD_ISSET (i, &rfd))
+ if (FD_ISSET(i, &rfd))
{
if (session[i]->func_recv)
//recv_to_fifo(i);
//or connect_client(i);
- session[i]->func_recv (i);
+ session[i]->func_recv(i);
}
}
}
-void do_parsepacket (void)
+void do_parsepacket(void)
{
for (int i = 0; i < fd_max; i++)
{
if (!session[i])
continue;
if (!session[i]->connected
- && time (NULL) - session[i]->created > CONNECT_TIMEOUT)
+ && static_cast<time_t>(TimeT::now()) - static_cast<time_t>(session[i]->created) > CONNECT_TIMEOUT)
{
- printf ("Session #%d timed out\n", i);
+ PRINTF("Session #%d timed out\n", i);
session[i]->eof = 1;
}
if (!session[i]->rdata_size && !session[i]->eof)
continue;
if (session[i]->func_parse)
{
- session[i]->func_parse (i);
+ session[i]->func_parse(i);
/// some func_parse may call delete_session
if (!session[i])
continue;
}
/// Reclaim buffer space for what was read
- RFIFOFLUSH (i);
+ RFIFOFLUSH(i);
}
}
-void do_socket (void)
+void do_socket(void)
{
- FD_ZERO (&readfds);
+ FD_ZERO(&readfds);
currentuse = 3;
}
-void RFIFOSKIP (int fd, size_t len)
+void RFIFOSKIP(int fd, size_t len)
{
struct socket_data *s = session[fd];
s->rdata_pos += len;
if (s->rdata_size < s->rdata_pos)
{
- fprintf (stderr, "too many skip\n");
- abort ();
+ FPRINTF(stderr, "too many skip\n");
+ abort();
}
}
-void fclose_ (FILE * fp)
+void fclose_(FILE * fp)
{
- if (fclose (fp))
- perror ("fclose"), abort ();
+ if (fclose(fp))
+ perror("fclose"), abort();
currentuse--;
}
-FILE *fopen_ (const char *path, const char *mode)
+FILE *fopen_(const char *path, const char *mode)
{
- FILE *f = fopen (path, mode);
+ FILE *f = fopen(path, mode);
if (f)
currentuse++;
return f;
}
-bool free_fds (void)
+bool free_fds(void)
{
return currentuse < SOFT_LIMIT;
}
diff --git a/src/common/socket.hpp b/src/common/socket.hpp
index 00f2df0..d7c6b9c 100644
--- a/src/common/socket.hpp
+++ b/src/common/socket.hpp
@@ -3,62 +3,19 @@
# include "sanity.hpp"
-# include <stdio.h>
-
-# include <sys/types.h>
-# include <sys/socket.h>
# include <netinet/in.h>
-# include <time.h>
+# include <cstdio>
-/// Check how much can be read
-# define RFIFOREST(fd) (session[fd]->rdata_size-session[fd]->rdata_pos)
-/// Read from the queue
-# define RFIFOP(fd,pos) (session[fd]->rdata+session[fd]->rdata_pos+(pos))
-# define RFIFOB(fd,pos) (*(uint8_t*)(RFIFOP(fd, pos)))
-# define RFIFOW(fd,pos) (*(uint16_t*)(RFIFOP(fd, pos)))
-# define RFIFOL(fd,pos) (*(uint32_t*)(RFIFOP(fd, pos)))
-/// Done reading
-void RFIFOSKIP (int fd, size_t len);
-/// Internal - clean up by discarding handled bytes
-// Atm this is also called in char/char.c, but that is unnecessary
-# define RFIFOFLUSH(fd) (memmove(session[fd]->rdata,RFIFOP(fd,0),RFIFOREST(fd)),\
-session[fd]->rdata_size=RFIFOREST(fd),\
-session[fd]->rdata_pos=0)
-
-/// Used internally - how much room there is to read more data
-# define RFIFOSPACE(fd) (session[fd]->max_rdata-session[fd]->rdata_size)
-
-/// Read from an arbitrary buffer
-# define RBUFP(p,pos) (((uint8_t*)(p))+(pos))
-# define RBUFB(p,pos) (*(uint8_t*)RBUFP((p),(pos)))
-# define RBUFW(p,pos) (*(uint16_t*)RBUFP((p),(pos)))
-# define RBUFL(p,pos) (*(uint32_t*)RBUFP((p),(pos)))
-
-
-
-/// Unused - check how much data can be written
-# define WFIFOSPACE(fd) (session[fd]->max_wdata-session[fd]->wdata_size)
-/// Write to the queue
-# define WFIFOP(fd,pos) (session[fd]->wdata+session[fd]->wdata_size+(pos))
-# define WFIFOB(fd,pos) (*(uint8_t*)(WFIFOP(fd,pos)))
-# define WFIFOW(fd,pos) (*(uint16_t*)(WFIFOP(fd,pos)))
-# define WFIFOL(fd,pos) (*(uint32_t*)(WFIFOP(fd,pos)))
-/// Finish writing
-void WFIFOSET (int fd, size_t len);
-
-/// Write to an arbitrary buffer
-#define WBUFP(p,pos) (((uint8_t*)(p))+(pos))
-#define WBUFB(p,pos) (*(uint8_t*)WBUFP((p),(pos)))
-#define WBUFW(p,pos) (*(uint16_t*)WBUFP((p),(pos)))
-#define WBUFL(p,pos) (*(uint32_t*)WBUFP((p),(pos)))
+# include "utils.hpp"
+# include "timer.t.hpp"
// Struct declaration
struct socket_data
{
/// Checks whether a newly-connected socket actually does anything
- time_t created;
+ TimeT created;
bool connected;
/// Flag needed since structure must be freed in a server-dependent manner
@@ -80,22 +37,22 @@ struct socket_data
/// Only called when select() indicates the socket is ready
/// If, after that, nothing is read, it sets eof
// These could probably be hard-coded with a little work
- void (*func_recv) (int);
- void (*func_send) (int);
+ void(*func_recv)(int);
+ void(*func_send)(int);
/// This is the important one
/// Set to different functions depending on whether the connection
/// is a player or a server/ladmin
/// Can be set explicitly or via set_defaultparse
- void (*func_parse) (int);
+ void(*func_parse)(int);
/// Server-specific data type
void *session_data;
};
// save file descriptors for important stuff
-# define SOFT_LIMIT (FD_SETSIZE - 50)
+constexpr int SOFT_LIMIT = FD_SETSIZE - 50;
// socket timeout to establish a full connection in seconds
-# define CONNECT_TIMEOUT 15
+constexpr int CONNECT_TIMEOUT = 15;
/// Everyone who has connected
// note: call delete_session(i) to null out an element
@@ -106,30 +63,139 @@ extern int fd_max;
/// open a socket, bind, and listen. Return an fd, or -1 if socket() fails,
/// but exit if bind() or listen() fails
-int make_listen_port (uint16_t port);
+int make_listen_port(uint16_t port);
/// Connect to an address, return a connected socket or -1
// FIXME - this is IPv4 only!
-int make_connection (uint32_t ip, uint16_t port);
+int make_connection(uint32_t ip, uint16_t port);
/// free() the structure and close() the fd
-void delete_session (int);
+void delete_session(int);
/// Make a the internal queues bigger
-void realloc_fifo (int fd, size_t rfifo_size, size_t wfifo_size);
+void realloc_fifo(int fd, size_t rfifo_size, size_t wfifo_size);
/// Update all sockets that can be read/written from the queues
-void do_sendrecv (uint32_t next);
+void do_sendrecv(interval_t next);
/// Call the parser function for every socket that has read data
-void do_parsepacket (void);
+void do_parsepacket(void);
/// An init function
-void do_socket (void);
+void do_socket(void);
/// Change the default parser for newly connected clients
// typically called once per server, but individual clients may identify
// themselves as servers
-void set_defaultparse (void (*defaultparse) (int));
+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);
+void fclose_(FILE * fp);
+FILE *fopen_(const char *path, const char *mode);
+
+bool free_fds(void);
+
+
+
+/// Check how much can be read
+inline
+size_t RFIFOREST(int fd)
+{
+ return session[fd]->rdata_size - session[fd]->rdata_pos;
+}
+/// Read from the queue
+inline
+const void *RFIFOP(int fd, size_t pos)
+{
+ return session[fd]->rdata + session[fd]->rdata_pos + pos;
+}
+inline
+uint8_t RFIFOB(int fd, size_t pos)
+{
+ return *static_cast<const uint8_t *>(RFIFOP(fd, pos));
+}
+inline
+uint16_t RFIFOW(int fd, size_t pos)
+{
+ return *static_cast<const uint16_t *>(RFIFOP(fd, pos));
+}
+inline
+uint32_t RFIFOL(int fd, size_t pos)
+{
+ return *static_cast<const uint32_t *>(RFIFOP(fd, pos));
+}
+
+/// Done reading
+void RFIFOSKIP(int fd, size_t len);
+
+/// Read from an arbitrary buffer
+inline
+const void *RBUFP(const uint8_t *p, size_t pos)
+{
+ return p + pos;
+}
+inline
+uint8_t RBUFB(const uint8_t *p, size_t pos)
+{
+ return *static_cast<const uint8_t *>(RBUFP(p, pos));
+}
+inline
+uint16_t RBUFW(const uint8_t *p, size_t pos)
+{
+ return *static_cast<const uint16_t *>(RBUFP(p, pos));
+}
+inline
+uint32_t RBUFL(const uint8_t *p, size_t pos)
+{
+ return *static_cast<const uint32_t *>(RBUFP(p, pos));
+}
+
+
+/// Unused - check how much data can be written
+inline
+size_t WFIFOSPACE(int fd)
+{
+ return session[fd]->max_wdata - session[fd]->wdata_size;
+}
+/// Write to the queue
+inline
+void *WFIFOP(int fd, size_t pos)
+{
+ return session[fd]->wdata + session[fd]->wdata_size + pos;
+}
+inline
+uint8_t& WFIFOB(int fd, size_t pos)
+{
+ return *static_cast<uint8_t *>(WFIFOP(fd, pos));
+}
+inline
+uint16_t& WFIFOW(int fd, size_t pos)
+{
+ return *static_cast<uint16_t *>(WFIFOP(fd, pos));
+}
+inline
+uint32_t& WFIFOL(int fd, size_t pos)
+{
+ return *static_cast<uint32_t *>(WFIFOP(fd, pos));
+}
+/// Finish writing
+void WFIFOSET(int fd, size_t len);
+
+/// Write to an arbitrary buffer
+inline
+void *WBUFP(uint8_t *p, size_t pos)
+{
+ return p + pos;
+}
+inline
+uint8_t& WBUFB(uint8_t *p, size_t pos)
+{
+ return *static_cast<uint8_t *>(WBUFP(p, pos));
+}
+inline
+uint16_t& WBUFW(uint8_t *p, size_t pos)
+{
+ return *static_cast<uint16_t *>(WBUFP(p, pos));
+}
+inline
+uint32_t& WBUFL(uint8_t *p, size_t pos)
+{
+ return *static_cast<uint32_t *>(WBUFP(p, pos));
+}
#endif // SOCKET_HPP
diff --git a/src/common/timer.cpp b/src/common/timer.cpp
index 66aaa9b..7b115d9 100644
--- a/src/common/timer.cpp
+++ b/src/common/timer.cpp
@@ -1,257 +1,193 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
+#include "timer.hpp"
-#include <sys/socket.h>
+#include <sys/stat.h>
#include <sys/time.h>
-#include "timer.hpp"
+#include <cassert>
+#include <cstring>
+
+#include <queue>
+
+#include "cxxstdio.hpp"
#include "utils.hpp"
-static struct TimerData *timer_data;
-static uint32_t timer_data_max, timer_data_num;
-static timer_id *free_timer_list;
-static uint32_t free_timer_list_max, free_timer_list_pos;
+#include "../poison.hpp"
+
+struct TimerData
+{
+ /// This will be reset on call, to avoid problems.
+ Timer *owner;
+
+ /// When it will be triggered
+ tick_t tick;
+ /// What will be done
+ timer_func func;
+ /// Repeat rate - 0 for oneshot
+ interval_t interval;
+
+ TimerData(Timer *o, tick_t t, timer_func f, interval_t i)
+ : owner(o)
+ , tick(t)
+ , func(std::move(f))
+ , interval(i)
+ {}
+};
+
+struct TimerCompare
+{
+ /// implement "less than"
+ bool operator() (TimerData *l, TimerData *r)
+ {
+ // C++ provides a max-heap, but we want
+ // the smallest tick to be the head (a min-heap).
+ return l->tick > r->tick;
+ }
+};
-/// Okay, I think I understand this structure now:
-/// the timer heap is a magic queue that allows inserting timers and then popping them in order
-/// designed to copy only log2(N) entries instead of N
-// timer_heap[0] is the size (greatest index into the heap)
-// timer_heap[1] is the first actual element
-// timer_heap_max increases 256 at a time and never decreases
-static uint32_t timer_heap_max = 0;
-/// FIXME: refactor the code to put the size in a separate variable
-//nontrivial because indices get multiplied
-static timer_id *timer_heap = NULL;
+static
+std::priority_queue<TimerData *, std::vector<TimerData *>, TimerCompare> timer_heap;
-static uint32_t gettick_cache;
-static uint8_t gettick_count = 0;
+tick_t gettick_cache;
-uint32_t gettick_nocache (void)
+tick_t milli_clock::now(void) noexcept
{
struct timeval tval;
// BUG: This will cause strange behavior if the system clock is changed!
// it should be reimplemented in terms of clock_gettime(CLOCK_MONOTONIC, )
- gettimeofday (&tval, NULL);
- gettick_count = 255;
- return gettick_cache = tval.tv_sec * 1000 + tval.tv_usec / 1000;
+ gettimeofday(&tval, NULL);
+ return gettick_cache = tick_t(std::chrono::seconds(tval.tv_sec)
+ + std::chrono::duration_cast<std::chrono::milliseconds>(
+ std::chrono::microseconds(tval.tv_usec)));
}
-uint32_t gettick (void)
+static
+void do_nothing(TimerData *, tick_t)
{
- if (gettick_count--)
- return gettick_cache;
- return gettick_nocache ();
}
-static void push_timer_heap (timer_id index)
+void Timer::cancel()
{
- if (timer_heap == NULL || timer_heap[0] + 1 >= timer_heap_max)
- {
- timer_heap_max += 256;
- RECREATE (timer_heap, timer_id, timer_heap_max);
- memset (timer_heap + (timer_heap_max - 256), 0, sizeof (timer_id) * 256);
- }
-// timer_heap[0] is the greatest index into the heap, which increases
- timer_heap[0]++;
-
- timer_id h = timer_heap[0]-1, i = (h - 1) / 2;
- while (h)
- {
- // avoid wraparound problems, it really means this:
- // timer_data[index].tick >= timer_data[timer_heap[i+1]].tick
- if ( DIFF_TICK(timer_data[index].tick, timer_data[timer_heap[i+1]].tick) >= 0)
- break;
- timer_heap[h + 1] = timer_heap[i + 1];
- h = i;
- i = (h - 1) / 2;
- }
- timer_heap[h + 1] = index;
+ if (!td)
+ return;
+
+ assert (this == td->owner);
+ td->owner = nullptr;
+ td->func = do_nothing;
+ td->interval = interval_t::zero();
+ td = nullptr;
}
-static timer_id top_timer_heap (void)
+void Timer::detach()
{
- if (!timer_heap || !timer_heap[0])
- return -1;
- return timer_heap[1];
+ assert (this == td->owner);
+ td->owner = nullptr;
+ td = nullptr;
}
-static timer_id pop_timer_heap (void)
+static
+void push_timer_heap(TimerData *td)
{
- if (!timer_heap || !timer_heap[0])
- return -1;
- timer_id ret = timer_heap[1];
- timer_id last = timer_heap[timer_heap[0]];
- timer_heap[0]--;
-
- uint32_t h, k;
- for (h = 0, k = 2; k < timer_heap[0]; k = k * 2 + 2)
- {
- if (DIFF_TICK(timer_data[timer_heap[k + 1]].tick, timer_data[timer_heap[k]].tick) > 0)
- k--;
- timer_heap[h + 1] = timer_heap[k + 1], h = k;
- }
- if (k == timer_heap[0])
- timer_heap[h + 1] = timer_heap[k], h = k - 1;
-
- uint32_t i = (h - 1) / 2;
- while (h)
- {
- if (DIFF_TICK (timer_data[timer_heap[i + 1]].tick, timer_data[last].tick) <= 0)
- break;
- timer_heap[h + 1] = timer_heap[i + 1];
- h = i;
- i = (h - 1) / 2;
- }
- timer_heap[h + 1] = last;
+ timer_heap.push(td);
+}
- return ret;
+static
+TimerData *top_timer_heap(void)
+{
+ if (timer_heap.empty())
+ return nullptr;
+ return timer_heap.top();
}
-timer_id add_timer (tick_t tick, timer_func func, custom_id_t id, custom_data_t data)
+static
+void pop_timer_heap(void)
{
- timer_id i;
+ timer_heap.pop();
+}
- if (free_timer_list_pos)
- {
- // Retrieve a freed timer id instead of a new one
- // I think it should be possible to avoid the loop somehow
- do
- {
- i = free_timer_list[--free_timer_list_pos];
- }
- while (i >= timer_data_num && free_timer_list_pos > 0);
- }
- else
- i = timer_data_num;
+Timer::Timer(tick_t tick, timer_func func, interval_t interval)
+: td(new TimerData(this, tick, std::move(func), interval))
+{
+ assert (interval >= interval_t::zero());
- // I have no idea what this is doing
- if (i >= timer_data_num)
- for (i = timer_data_num; i < timer_data_max && timer_data[i].type; i++)
- ;
- if (i >= timer_data_num && i >= timer_data_max)
- {
- if (timer_data_max == 0)
- {
- timer_data_max = 256;
- CREATE (timer_data, struct TimerData, timer_data_max);
- }
- else
- {
- timer_data_max += 256;
- RECREATE (timer_data, struct TimerData, timer_data_max);
- memset (timer_data + (timer_data_max - 256), 0,
- sizeof (struct TimerData) * 256);
- }
- }
- timer_data[i].tick = tick;
- timer_data[i].func = func;
- timer_data[i].id = id;
- timer_data[i].data = data;
- timer_data[i].type = TIMER_ONCE_AUTODEL;
- timer_data[i].interval = 1000;
- push_timer_heap (i);
- if (i >= timer_data_num)
- timer_data_num = i + 1;
- return i;
+ push_timer_heap(td);
}
-timer_id add_timer_interval (tick_t tick, timer_func func, custom_id_t id,
- custom_data_t data, interval_t interval)
+Timer::Timer(Timer&& t)
+: td(t.td)
{
- timer_id tid = add_timer (tick, func, id, data);
- timer_data[tid].type = TIMER_INTERVAL;
- timer_data[tid].interval = interval;
- return tid;
+ t.td = nullptr;
+ if (td)
+ {
+ assert (td->owner == &t);
+ td->owner = this;
+ }
}
-void delete_timer (timer_id id, timer_func func)
+Timer& Timer::operator = (Timer&& t)
{
- if (id == 0 || id >= timer_data_num)
+ std::swap(td, t.td);
+ if (td)
{
- fprintf (stderr, "delete_timer error : no such timer %d\n", id);
- abort ();
+ assert (td->owner == &t);
+ td->owner = this;
}
- if (timer_data[id].func != func)
+ if (t.td)
{
- fprintf (stderr, "Timer mismatch\n");
- abort ();
+ assert (t.td->owner == this);
+ t.td->owner = &t;
}
- // "to let them disappear" - is this just in case?
- timer_data[id].func = NULL;
- timer_data[id].type = TIMER_ONCE_AUTODEL;
- timer_data[id].tick -= 60 * 60 * 1000;
-}
-
-tick_t addtick_timer (timer_id tid, interval_t tick)
-{
- return timer_data[tid].tick += tick;
+ return *this;
}
-struct TimerData *get_timer (timer_id tid)
+interval_t do_timer(tick_t tick)
{
- return &timer_data[tid];
-}
-
-interval_t do_timer (tick_t tick)
-{
- timer_id i;
/// Number of milliseconds until it calls this again
// this says to wait 1 sec if all timers get popped
- interval_t nextmin = 1000;
+ interval_t nextmin = std::chrono::seconds(1);
- while ((i = top_timer_heap ()) != (timer_id)-1)
+ while (TimerData *td = top_timer_heap())
{
// while the heap is not empty and
- if (DIFF_TICK (timer_data[i].tick, tick) > 0)
+ if (td->tick > tick)
{
/// Return the time until the next timer needs to goes off
- nextmin = DIFF_TICK (timer_data[i].tick, tick);
+ nextmin = td->tick - tick;
break;
}
- pop_timer_heap ();
- if (timer_data[i].func)
- {
- if (DIFF_TICK (timer_data[i].tick, tick) < -1000)
- {
- // If we are too far past the requested tick, call with the current tick instead to fix reregistering problems
- timer_data[i].func (i, tick, timer_data[i].id, timer_data[i].data);
- }
- else
- {
- timer_data[i].func (i, timer_data[i].tick, timer_data[i].id, timer_data[i].data);
- }
- }
- switch (timer_data[i].type)
+ pop_timer_heap();
+
+ // Prevent destroying the object we're in.
+ // Note: this would be surprising in an interval timer,
+ // but all interval timers do an immediate explicit detach().
+ if (td->owner)
+ td->owner->detach();
+ // If we are too far past the requested tick, call with
+ // the current tick instead to fix reregistration problems
+ if (td->tick + std::chrono::seconds(1) < tick)
+ td->func(td, tick);
+ else
+ td->func(td, td->tick);
+
+ if (td->interval == interval_t::zero())
{
- case TIMER_ONCE_AUTODEL:
- timer_data[i].type = TIMER_NONE;
- if (free_timer_list_pos >= free_timer_list_max)
- {
- free_timer_list_max += 256;
- RECREATE (free_timer_list, uint32_t, free_timer_list_max);
- memset (free_timer_list + (free_timer_list_max - 256),
- 0, 256 * sizeof (uint32_t));
- }
- free_timer_list[free_timer_list_pos++] = i;
- break;
- case TIMER_INTERVAL:
- if (DIFF_TICK (timer_data[i].tick, tick) < -1000)
- {
- timer_data[i].tick = tick + timer_data[i].interval;
- }
- else
- {
- timer_data[i].tick += timer_data[i].interval;
- }
- push_timer_heap (i);
- break;
+ delete td;
+ continue;
}
+ if (td->tick + std::chrono::seconds(1) < tick)
+ td->tick = tick + td->interval;
+ else
+ td->tick += td->interval;
+ push_timer_heap(td);
}
- if (nextmin < 10)
- nextmin = 10;
- return nextmin;
+ return std::max(nextmin, std::chrono::milliseconds(10));
+}
+
+tick_t file_modified(const char *name)
+{
+ struct stat buf;
+ if (stat(name, &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 fdda344..c581377 100644
--- a/src/common/timer.hpp
+++ b/src/common/timer.hpp
@@ -1,61 +1,25 @@
#ifndef TIMER_HPP
#define TIMER_HPP
-# include "sanity.hpp"
+# include "timer.t.hpp"
-enum TIMER_TYPE
-{
- TIMER_NONE,
- TIMER_ONCE_AUTODEL,
- TIMER_INTERVAL,
-};
-/// This is needed to produce a signed result when 2 ticks are subtracted
-# define DIFF_TICK(a,b) ((int32_t)((a)-(b)))
+# include "sanity.hpp"
-// TODO replace with signed 64-bit to make code more clear and protect from the future
-typedef uint32_t tick_t;
-typedef uint32_t interval_t;
-typedef uint32_t timer_id;
-// BUG: pointers are stored in here
-typedef int32_t custom_id_t;
-typedef int32_t custom_data_t;
-typedef void (*timer_func) (timer_id, tick_t, custom_id_t, custom_data_t);
+// updated automatically when using milli_clock::now()
+// which is done only by core.cpp
+extern tick_t gettick_cache;
-struct TimerData
+inline
+tick_t gettick(void)
{
- /// When it will be triggered
- tick_t tick;
- /// What will be done
- timer_func func;
- /// Arbitrary data. WARNING, callers are stupid and put pointers in here
- // Should we change to void* or intptr_t ?
- custom_id_t id;
- custom_data_t data;
- /// Type of timer - 0 initially
- enum TIMER_TYPE type;
- /// Repeat rate
- interval_t interval;
-};
-
-/// Server time, in milliseconds, since the epoch,
-/// but use of 32-bit integers means it wraps every 49 days.
-// The only external caller of this function is the core.c main loop, but that makes sense
-// in fact, it might make more sense if gettick() ALWAYS returned that cached value
-tick_t gettick_nocache (void);
-/// This function is called enough that it's worth caching the result for
-/// the next 255 times
-tick_t gettick (void);
-
-timer_id add_timer (tick_t, timer_func, custom_id_t, custom_data_t);
-timer_id add_timer_interval (tick_t, timer_func, custom_id_t, custom_data_t, interval_t);
-void delete_timer (timer_id, timer_func);
-
-tick_t addtick_timer (timer_id, interval_t);
-struct TimerData *get_timer (timer_id tid);
-
-/// Do all timers scheduled before tick, and return the number of milliseconds until the next timer happens
-interval_t do_timer (tick_t tick);
+ return gettick_cache;
+}
+/// Do all timers scheduled before tick, and return the number of
+/// milliseconds until the next timer happens
+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);
#endif // TIMER_HPP
diff --git a/src/common/timer.t.hpp b/src/common/timer.t.hpp
new file mode 100644
index 0000000..ee9b5d2
--- /dev/null
+++ b/src/common/timer.t.hpp
@@ -0,0 +1,66 @@
+#ifndef TIMER_T_HPP
+#define TIMER_T_HPP
+
+# include <chrono>
+# include <functional>
+
+struct TimerData;
+
+/// An implementation of the C++ "clock" concept, exposing
+/// durations in milliseconds.
+class milli_clock
+{
+public:
+ typedef std::chrono::milliseconds duration;
+ typedef duration::rep rep;
+ typedef duration::period period;
+ typedef std::chrono::time_point<milli_clock, duration> time_point;
+ static const bool is_steady = true; // assumed - not necessarily true
+
+ static time_point now() noexcept;
+};
+
+/// A point in time.
+typedef milli_clock::time_point tick_t;
+/// The difference between two points in time.
+typedef milli_clock::duration interval_t;
+/// (to get additional arguments, use std::bind or a lambda).
+typedef std::function<void (TimerData *, tick_t)> timer_func;
+
+class Timer
+{
+ friend struct TimerData;
+ TimerData *td;
+
+ Timer(const Timer&) = delete;
+ Timer& operator = (const Timer&) = delete;
+public:
+ /// Don't own anything yet.
+ Timer() : td(nullptr) {}
+ /// Schedule a timer for the given tick.
+ /// If you do not wish to keep track of it, call disconnect().
+ /// Otherwise, you may cancel() or replace (operator =) it later.
+ ///
+ /// If the interval argument is given, the timer will reschedule
+ /// itself again forever. Otherwise, it will disconnect() itself
+ /// just BEFORE it is called.
+ Timer(tick_t tick, timer_func func, interval_t interval=interval_t::zero());
+
+ Timer(Timer&& t);
+ Timer& operator = (Timer&& t);
+ ~Timer() { cancel(); }
+
+ /// Cancel the delivery of this timer's function, and make it falsy.
+ /// Implementation note: this doesn't actually remove it, just sets
+ /// the functor to do_nothing, and waits for the tick before removing.
+ void cancel();
+ /// Make it falsy without cancelling the timer,
+ void detach();
+
+ /// Check if there is a timer connected.
+ explicit operator bool() { return td; }
+ /// Check if there is no connected timer.
+ bool operator !() { return !td; }
+};
+
+#endif // TIMER_T_HPP
diff --git a/src/common/utils.cpp b/src/common/utils.cpp
index fbdd87e..49dce34 100644
--- a/src/common/utils.cpp
+++ b/src/common/utils.cpp
@@ -1,18 +1,19 @@
#include "utils.hpp"
-#include <cstring>
-#include <cstdio>
-#include <cstdlib>
-
#include <netinet/in.h>
+#include <sys/time.h>
+
+#include <algorithm>
+
+#include "../poison.hpp"
//-----------------------------------------------------
// Function to suppress control characters in a string.
//-----------------------------------------------------
-int remove_control_chars (char *str)
+int remove_control_chars(char *str)
{
- int i;
- int change = 0;
+ int i;
+ int change = 0;
for (i = 0; str[i]; i++)
{
@@ -29,39 +30,38 @@ int remove_control_chars (char *str)
//---------------------------------------------------
// E-mail check: return 0 (not correct) or 1 (valid).
//---------------------------------------------------
-int e_mail_check (const char *email)
+int e_mail_check(const char *email)
{
char ch;
const char *last_arobas;
// athena limits
- if (strlen (email) < 3 || strlen (email) > 39)
+ if (strlen(email) < 3 || strlen(email) > 39)
return 0;
// part of RFC limits (official reference of e-mail description)
- if (strchr (email, '@') == NULL || email[strlen (email) - 1] == '@')
+ if (strchr(email, '@') == NULL || email[strlen(email) - 1] == '@')
return 0;
- if (email[strlen (email) - 1] == '.')
+ if (email[strlen(email) - 1] == '.')
return 0;
- last_arobas = strrchr (email, '@');
+ last_arobas = strrchr(email, '@');
- if (strstr (last_arobas, "@.") != NULL ||
- strstr (last_arobas, "..") != NULL)
+ if (strstr(last_arobas, "@.") != NULL ||
+ strstr(last_arobas, "..") != NULL)
return 0;
for (ch = 1; ch < 32; ch++)
{
- if (strchr (last_arobas, ch) != NULL)
+ if (strchr(last_arobas, ch) != NULL)
{
return 0;
- break;
}
}
- if (strchr (last_arobas, ' ') != NULL ||
- strchr (last_arobas, ';') != NULL)
+ if (strchr(last_arobas, ' ') != NULL ||
+ strchr(last_arobas, ';') != NULL)
return 0;
// all correct
@@ -74,15 +74,15 @@ int e_mail_check (const char *email)
//-------------------------------------------------
int config_switch (const char *str)
{
- if (strcasecmp (str, "on") == 0 || strcasecmp (str, "yes") == 0
- || strcasecmp (str, "oui") == 0 || strcasecmp (str, "ja") == 0
- || strcasecmp (str, "si") == 0)
+ if (strcasecmp(str, "on") == 0 || strcasecmp(str, "yes") == 0
+ || strcasecmp(str, "oui") == 0 || strcasecmp(str, "ja") == 0
+ || strcasecmp(str, "si") == 0)
return 1;
- if (strcasecmp (str, "off") == 0 || strcasecmp (str, "no") == 0
- || strcasecmp (str, "non") == 0 || strcasecmp (str, "nein") == 0)
+ if (strcasecmp(str, "off") == 0 || strcasecmp(str, "no") == 0
+ || strcasecmp(str, "non") == 0 || strcasecmp(str, "nein") == 0)
return 0;
- return atoi (str);
+ return atoi(str);
}
const char *ip2str(struct in_addr ip, bool extra_dot)
@@ -95,3 +95,62 @@ const char *ip2str(struct in_addr ip, bool extra_dot)
sprintf(buf, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
return buf;
}
+
+bool split_key_value(const std::string& line, std::string *w1, std::string *w2)
+{
+ std::string::const_iterator begin = line.begin(), end = line.end();
+
+ if (line[0] == '/' && line[1] == '/')
+ return false;
+ if (line.back() == '\r')
+ --end;
+ if (line.empty())
+ 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, ':');
+ if (colon == end)
+ return false;
+ w1->assign(begin, colon);
+ ++colon;
+ while (std::isspace(*colon))
+ ++colon;
+ w2->assign(colon, end);
+ return true;
+}
+
+static_assert(sizeof(timestamp_seconds_buffer) == 20, "seconds buffer");
+static_assert(sizeof(timestamp_milliseconds_buffer) == 24, "millis buffer");
+
+void stamp_time(timestamp_seconds_buffer& out, TimeT *t)
+{
+ struct tm when = t ? *t : TimeT::now();
+ strftime(out, 20, "%Y-%m-%d %H:%M:%S", &when);
+}
+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));
+}
+
+void log_with_timestamp(FILE *out, const_string line)
+{
+ if (!line)
+ {
+ fputc('\n', out);
+ return;
+ }
+ timestamp_milliseconds_buffer tmpstr;
+ stamp_time(tmpstr);
+ fwrite(tmpstr, 1, sizeof(tmpstr), out);
+ fputs(": ", out);
+ fwrite(line.data(), 1, line.size(), out);
+ if (line.back() != '\n')
+ fputc('\n', out);
+}
diff --git a/src/common/utils.hpp b/src/common/utils.hpp
index 1097bf7..33117ac 100644
--- a/src/common/utils.hpp
+++ b/src/common/utils.hpp
@@ -3,6 +3,15 @@
#include "sanity.hpp"
+#include <cstdio>
+#include <cstring>
+
+#include <string>
+
+#include "const_array.hpp"
+#include "operators.hpp"
+#include "utils2.hpp"
+
/*
Notes about memory allocation in tmwAthena:
There used to be 3 sources of allocation: these macros,
@@ -10,17 +19,122 @@ a{C,M,Re}alloc in common/malloc.{h,c}, and direct calls.
I deleted malloc.{h,c} because it was redundant;
future calls should either use this or depend on the coming segfault.
*/
+template<class T>
+void create_impl(T *& result, size_t number)
+{
+ result = (T *)calloc(number, sizeof(T));
+ if (!result && number)
+ {
+ perror("SYSERR: malloc failure");
+ abort();
+ }
+}
+template<class T>
+void recreate_impl(T *& result, size_t number)
+{
+ result = (T *)realloc(result, sizeof(T) * number);
+ if (!result && number)
+ {
+ perror("SYSERR: realloc failure");
+ abort();
+ }
+}
+
# define CREATE(result, type, number) \
- if (!((result) = (type *) calloc ((number), sizeof(type)))) \
- { perror("SYSERR: malloc failure"); abort(); } else (void)0
+ create_impl<type>(result, number)
-# define RECREATE(result,type,number) \
- if (!((result) = (type *) realloc ((result), sizeof(type) * (number))))\
- { perror("SYSERR: realloc failure"); abort(); } else (void)0
+# define RECREATE(result, type, number) \
+ recreate_impl<type>(result, number)
-int remove_control_chars (char *str);
-int e_mail_check (const char *email);
+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 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)
+ {
+ strncpy(dest, src, n);
+ dest[n-1] = '\0';
+ }
+}
+
+// Exists in place of time_t, to give it a predictable printf-format.
+// (on x86 and amd64, time_t == long, but not on x32)
+static_assert(sizeof(long long) >= sizeof(time_t), "long long >= time_t");
+struct TimeT : Comparable
+{
+ long long value;
+
+ // conversion
+ TimeT(time_t t=0) : value(t) {}
+ TimeT(struct tm t) : value(timegm(&t)) {}
+ operator time_t() { return value; }
+ operator struct tm() { time_t v = value; return *gmtime(&v); }
+
+ explicit operator bool() { return value; }
+ bool operator !() { return !value; }
+
+ // prevent surprises
+ template<class T>
+ TimeT(T) = delete;
+ template<class T>
+ operator T() = delete;
+
+ static
+ TimeT now()
+ {
+ // poisoned, but this is still in header-land
+ return time(NULL);
+ }
+
+ bool error()
+ {
+ return value == -1;
+ }
+ bool okay()
+ {
+ return !error();
+ }
+};
+
+inline
+long long convert_for_printf(TimeT t)
+{
+ return t.value;
+}
+
+inline
+long long& convert_for_scanf(TimeT& t)
+{
+ return t.value;
+}
+
+typedef char timestamp_seconds_buffer[20];
+typedef char timestamp_milliseconds_buffer[24];
+void stamp_time(timestamp_seconds_buffer&, TimeT *t=nullptr);
+void stamp_time(timestamp_milliseconds_buffer&);
+
+void log_with_timestamp(FILE *out, const_string line);
+
+#define TIMESTAMP_DUMMY "YYYY-MM-DD HH:MM:SS"
+static_assert(sizeof(TIMESTAMP_DUMMY) == sizeof(timestamp_seconds_buffer),
+ "timestamp size");
+#define WITH_TIMESTAMP(str) str TIMESTAMP_DUMMY
+// str: prefix: YYYY-MM-DD HH:MM:SS
+// sizeof: 01234567890123456789012345678
+// str + sizeof: ^
+// -1: ^
+#define REPLACE_TIMESTAMP(str, t) \
+ stamp_time( \
+ reinterpret_cast<timestamp_seconds_buffer *>( \
+ str + sizeof(str) \
+ )[-1], \
+ &t \
+ )
+
#endif //UTILS_HPP
diff --git a/src/common/utils2.hpp b/src/common/utils2.hpp
new file mode 100644
index 0000000..5f02beb
--- /dev/null
+++ b/src/common/utils2.hpp
@@ -0,0 +1,230 @@
+#ifndef UTILS2_HPP
+#define UTILS2_HPP
+
+#include "sanity.hpp"
+
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <type_traits>
+
+#ifdef __clang__
+# define FALLTHROUGH [[clang::fallthrough]]
+#else
+# define FALLTHROUGH /* fallthrough */
+#endif
+
+template<class T, class E, E max>
+struct earray
+{
+ // no ctor/dtor and one public member variable for easy initialization
+ T _data[size_t(max)];
+
+ T& operator[](E v)
+ {
+ return _data[size_t(v)];
+ }
+
+ const T& operator[](E v) const
+ {
+ return _data[size_t(v)];
+ }
+
+ T *begin()
+ {
+ return _data;
+ }
+
+ T *end()
+ {
+ return _data + size_t(max);
+ }
+};
+
+template<class T, class E>
+class eptr
+{
+ T *_data;
+public:
+ eptr(decltype(nullptr)=nullptr)
+ : _data(nullptr)
+ {}
+
+ template<E max>
+ eptr(earray<T, E, max>& arr)
+ : _data(arr._data)
+ {}
+
+ T& operator [](E v)
+ {
+ return _data[size_t(v)];
+ }
+
+ explicit operator bool()
+ {
+ return _data;
+ }
+
+ bool operator not()
+ {
+ return not _data;
+ }
+};
+
+template<class It>
+class IteratorPair
+{
+ It _b, _e;
+public:
+ IteratorPair(It b, It e)
+ : _b(b), _e(e)
+ {}
+
+ It begin() { return _b; }
+ It end() { return _e; }
+};
+
+template<class It>
+IteratorPair<It> iterator_pair(It b, It e)
+{
+ return {b, e};
+}
+
+// std::underlying_type isn't supported until gcc 4.7
+// this is a poor man's emulation
+template<class E>
+struct underlying_type
+{
+ static_assert(std::is_enum<E>::value, "Only enums have underlying type!");
+ typedef typename std::conditional<
+ std::is_signed<E>::value,
+ typename std::make_signed<E>::type,
+ typename std::make_unsigned<E>::type
+ >::type type;
+};
+
+template<class E, bool=std::is_enum<E>::value>
+struct remove_enum
+{
+ typedef E type;
+};
+template<class E>
+struct remove_enum<E, true>
+{
+ typedef typename underlying_type<E>::type type;
+};
+
+
+#define ENUM_BITWISE_OPERATORS(E) \
+inline \
+E operator & (E l, E r) \
+{ \
+ typedef underlying_type<E>::type U; \
+ return E(U(l) & U(r)); \
+} \
+inline \
+E operator | (E l, E r) \
+{ \
+ typedef underlying_type<E>::type U; \
+ return E(U(l) | U(r)); \
+} \
+inline \
+E operator ^ (E l, E r) \
+{ \
+ typedef underlying_type<E>::type U; \
+ return E(U(l) ^ U(r)); \
+} \
+inline \
+E& operator &= (E& l, E r) \
+{ \
+ return l = l & r; \
+} \
+inline \
+E& operator |= (E& l, E r) \
+{ \
+ return l = l | r; \
+} \
+inline \
+E& operator ^= (E& l, E r) \
+{ \
+ return l = l ^ r; \
+} \
+inline \
+E operator ~ (E r) \
+{ \
+ return E(-1) ^ r; \
+}
+
+template<class E>
+class EnumValueIterator
+{
+ typedef typename underlying_type<E>::type U;
+ E value;
+public:
+ EnumValueIterator(E v)
+ : value(v)
+ {}
+
+ E operator *()
+ {
+ return value;
+ }
+ EnumValueIterator& operator++ ()
+ {
+ value = E(U(value) + 1);
+ return *this;
+ }
+ EnumValueIterator& operator-- ()
+ {
+ value = E(U(value) - 1);
+ return *this;
+ }
+ friend bool operator == (EnumValueIterator l, EnumValueIterator r)
+ {
+ return l.value == r.value;
+ }
+ friend bool operator != (EnumValueIterator l, EnumValueIterator r)
+ {
+ return !(l == r);
+ }
+};
+
+template<class E>
+IteratorPair<EnumValueIterator<E>> erange(E b, E e)
+{
+ return {b, e};
+}
+
+namespace ph = std::placeholders;
+
+template<class A, class B>
+typename std::common_type<A, B>::type min(A a, B b)
+{
+ return a < b ? a : b;
+}
+
+template<class A, class B>
+typename std::common_type<A, B>::type max(A a, B b)
+{
+ return b < a ? a : b;
+}
+
+template<class T>
+struct is_array_of_unknown_bound
+: std::is_same<T, typename std::remove_extent<T>::type[]>
+{};
+
+template<class T, class... A>
+typename std::enable_if<!is_array_of_unknown_bound<T>::value, std::unique_ptr<T>>::type make_unique(A&&... a)
+{
+ return std::unique_ptr<T>(new T(a...));
+}
+
+template<class T>
+typename std::enable_if<is_array_of_unknown_bound<T>::value, std::unique_ptr<T>>::type make_unique(size_t sz)
+{
+ typedef typename std::remove_extent<T>::type E;
+ return std::unique_ptr<E[]>(new E[sz]);
+}
+
+#endif // UTILS2_HPP