summaryrefslogtreecommitdiff
path: root/src/generic
diff options
context:
space:
mode:
authorBen Longbons <b.r.longbons@gmail.com>2014-03-15 19:34:59 -0700
committerBen Longbons <b.r.longbons@gmail.com>2014-03-16 18:58:48 -0700
commitc812c92d1a1835f0bda783e709481188c8d92225 (patch)
treeb401ede48a088ad1aaed88fe3b997cd26ff7ae08 /src/generic
parentde9ee1b9754af9d954487121947352f32d7ebb7e (diff)
downloadtmwa-c812c92d1a1835f0bda783e709481188c8d92225.tar.gz
tmwa-c812c92d1a1835f0bda783e709481188c8d92225.tar.bz2
tmwa-c812c92d1a1835f0bda783e709481188c8d92225.tar.xz
tmwa-c812c92d1a1835f0bda783e709481188c8d92225.zip
Clean up header organization
Diffstat (limited to 'src/generic')
-rw-r--r--src/generic/const_array.cpp3
-rw-r--r--src/generic/const_array.hpp132
-rw-r--r--src/generic/db.cpp3
-rw-r--r--src/generic/db.hpp180
-rw-r--r--src/generic/enum.cpp3
-rw-r--r--src/generic/enum.hpp170
-rw-r--r--src/generic/intern-pool.cpp3
-rw-r--r--src/generic/intern-pool.hpp41
-rw-r--r--src/generic/intern-pool_test.cpp20
-rw-r--r--src/generic/matrix.cpp3
-rw-r--r--src/generic/matrix.hpp54
-rw-r--r--src/generic/md5.cpp234
-rw-r--r--src/generic/md5.hpp44
-rw-r--r--src/generic/md5_test.cpp28
-rw-r--r--src/generic/operators.cpp3
-rw-r--r--src/generic/operators.hpp47
-rw-r--r--src/generic/random.cpp8
-rw-r--r--src/generic/random.hpp69
-rw-r--r--src/generic/random.t.hpp23
-rw-r--r--src/generic/random2.hpp75
20 files changed, 1143 insertions, 0 deletions
diff --git a/src/generic/const_array.cpp b/src/generic/const_array.cpp
new file mode 100644
index 0000000..0c09333
--- /dev/null
+++ b/src/generic/const_array.cpp
@@ -0,0 +1,3 @@
+#include "const_array.hpp"
+
+#include "../poison.hpp"
diff --git a/src/generic/const_array.hpp b/src/generic/const_array.hpp
new file mode 100644
index 0000000..1c70f5d
--- /dev/null
+++ b/src/generic/const_array.hpp
@@ -0,0 +1,132 @@
+#ifndef TMWA_GENERIC_CONST_ARRAY_HPP
+#define TMWA_GENERIC_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 <vector>
+
+# ifdef WORKAROUND_GCC46_COMPILER
+// constexpr is buggy with templates in this version
+// Is this still needed now that const_string is removed?
+# define constexpr /* nothing */
+# endif
+
+// 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;
+
+ // Oops. see src/warnings.hpp
+ constexpr
+ const T *data() const { return d; }
+ constexpr
+ size_t size() const { return n; }
+ constexpr
+ bool empty() const { return not n; }
+ constexpr explicit
+ operator bool() const { return n; }
+
+ constexpr
+ std::pair<const_array, const_array> cut(size_t o) const
+ {
+ return {const_array(d, o), const_array(d + o, n - o)};
+ }
+
+ constexpr
+ const_array first(size_t o) const
+ {
+ return cut(o).first;
+ }
+
+ constexpr
+ const_array last(size_t l) const
+ {
+ return cut(size() - l).second;
+ }
+
+ constexpr
+ const_array after(size_t o) const
+ {
+ return cut(o).second;
+ }
+
+ constexpr
+ iterator begin() const { return d; }
+ constexpr
+ iterator end() const { return d + n; }
+ constexpr
+ reverse_iterator rbegin() const { return reverse_iterator(end()); }
+ constexpr
+ reverse_iterator rend() const { return reverse_iterator(begin()); }
+
+ constexpr
+ const T& front() const { return *begin(); }
+ constexpr
+ const T& back() const { return *rbegin(); }
+
+ // This probably shouldn't be used, but I'm adding it for porting.
+ T& operator[](size_t i)
+ {
+ return const_cast<T&>(d[i]);
+ }
+};
+
+# ifdef WORKAROUND_GCC46_COMPILER
+# undef constexpr
+# endif
+
+#endif // TMWA_GENERIC_CONST_ARRAY_HPP
diff --git a/src/generic/db.cpp b/src/generic/db.cpp
new file mode 100644
index 0000000..cc58ad8
--- /dev/null
+++ b/src/generic/db.cpp
@@ -0,0 +1,3 @@
+#include "db.hpp"
+
+#include "../poison.hpp"
diff --git a/src/generic/db.hpp b/src/generic/db.hpp
new file mode 100644
index 0000000..314c449
--- /dev/null
+++ b/src/generic/db.hpp
@@ -0,0 +1,180 @@
+#ifndef TMWA_GENERIC_DB_HPP
+#define TMWA_GENERIC_DB_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 "../sanity.hpp"
+
+# include <map>
+# include <memory>
+
+template<class K, class V>
+class Map
+{
+ typedef std::map<K, V> Impl;
+
+ 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;
+
+ iterator begin() { return impl.begin(); }
+ iterator end() { return impl.end(); }
+ const_iterator begin() const { return impl.begin(); }
+ const_iterator end() const { return impl.end(); }
+
+ 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(const 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;
+
+ }
+ V *init(const K& k)
+ {
+ return &impl[k];
+ }
+ void erase(const K& k)
+ {
+ impl.erase(k);
+ }
+ void clear()
+ {
+ impl.clear();
+ }
+ bool empty() const
+ {
+ return impl.empty();
+ }
+ size_t size() const
+ {
+ return impl.size();
+ }
+};
+
+template<class K, class V>
+class DMap
+{
+ typedef Map<K, V> Impl;
+
+ Impl impl;
+public:
+ typedef typename Impl::iterator iterator;
+ typedef typename Impl::const_iterator const_iterator;
+
+ iterator begin() { return impl.begin(); }
+ iterator end() { return impl.end(); }
+ const_iterator begin() const { return impl.begin(); }
+ const_iterator end() const { return impl.end(); }
+
+ // const V& ? with a static default V?
+ V get(const K& k)
+ {
+ V *vp = impl.search(k);
+ return vp ? *vp : V();
+ }
+ void put(const K& k, V v)
+ {
+ if (v == V())
+ impl.erase(k);
+ else
+ impl.insert(k, std::move(v));
+ }
+ void clear()
+ {
+ impl.clear();
+ }
+ bool empty() const
+ {
+ return impl.empty();
+ }
+ size_t size() const
+ {
+ return impl.size();
+ }
+};
+
+template<class K, class V>
+class UPMap
+{
+ typedef std::unique_ptr<V> U;
+ typedef Map<K, U> Impl;
+
+ Impl impl;
+public:
+ typedef typename Impl::iterator iterator;
+ typedef typename Impl::const_iterator const_iterator;
+
+ iterator begin() { return impl.begin(); }
+ iterator end() { return impl.end(); }
+ const_iterator begin() const { return impl.begin(); }
+ const_iterator end() const { return impl.end(); }
+
+ // const V& ? with a static default V?
+ V *get(const K& k)
+ {
+ U *up = impl.search(k);
+ return up ? up->get() : nullptr;
+ }
+ void put(const K& k, U v)
+ {
+ if (!v)
+ impl.erase(k);
+ else
+ impl.insert(k, std::move(v));
+ }
+ void clear()
+ {
+ impl.clear();
+ }
+ bool empty() const
+ {
+ return impl.empty();
+ }
+ size_t size() const
+ {
+ return impl.size();
+ }
+};
+
+#endif // TMWA_GENERIC_DB_HPP
diff --git a/src/generic/enum.cpp b/src/generic/enum.cpp
new file mode 100644
index 0000000..c5ab250
--- /dev/null
+++ b/src/generic/enum.cpp
@@ -0,0 +1,3 @@
+#include "enum.hpp"
+
+#include "../poison.hpp"
diff --git a/src/generic/enum.hpp b/src/generic/enum.hpp
new file mode 100644
index 0000000..6f29981
--- /dev/null
+++ b/src/generic/enum.hpp
@@ -0,0 +1,170 @@
+#ifndef TMWA_GENERIC_ENUM_HPP
+#define TMWA_GENERIC_ENUM_HPP
+
+# include "../sanity.hpp"
+
+# include <type_traits>
+
+# include "../compat/iter.hpp"
+
+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);
+ }
+
+ const T *begin() const
+ {
+ return _data;
+ }
+
+ const T *end() const
+ {
+ return _data + size_t(max);
+ }
+
+ friend bool operator == (const earray& l, const earray& r)
+ {
+ return std::equal(l.begin(), l.end(), r.begin());
+ }
+
+ friend bool operator != (const earray& l, const earray& r)
+ {
+ return !(l == r);
+ }
+};
+
+template<class T, class E>
+class eptr
+{
+ T *_data;
+public:
+ eptr(std::nullptr_t=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;
+ }
+};
+
+// 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 EnumMath
+{
+ typedef typename underlying_type<E>::type U;
+public:
+ static
+ E inced(E v)
+ {
+ return E(U(v) + 1);
+ }
+};
+
+template<class E>
+IteratorPair<ValueIterator<E, EnumMath<E>>> erange(E b, E e)
+{
+ return {b, e};
+}
+
+#endif // TMWA_GENERIC_ENUM_HPP
diff --git a/src/generic/intern-pool.cpp b/src/generic/intern-pool.cpp
new file mode 100644
index 0000000..33649f2
--- /dev/null
+++ b/src/generic/intern-pool.cpp
@@ -0,0 +1,3 @@
+#include "intern-pool.hpp"
+
+#include "../poison.hpp"
diff --git a/src/generic/intern-pool.hpp b/src/generic/intern-pool.hpp
new file mode 100644
index 0000000..69f20ef
--- /dev/null
+++ b/src/generic/intern-pool.hpp
@@ -0,0 +1,41 @@
+#ifndef TMWA_GENERIC_INTERN_POOL_HPP
+#define TMWA_GENERIC_INTERN_POOL_HPP
+
+# include <cassert>
+
+# include <map>
+# include <vector>
+
+# include "../strings/rstring.hpp"
+# include "../strings/zstring.hpp"
+# include "../strings/xstring.hpp"
+
+class InternPool
+{
+ std::map<RString, size_t> known;
+ std::vector<RString> names;
+public:
+ size_t intern(XString name_)
+ {
+ // TODO just look up the XString, the memory should not move by now
+ RString name = name_;
+ // hm, I could change this to do aliases
+ auto pair = known.insert({name, known.size()});
+ if (pair.second)
+ names.push_back(name);
+ assert (known.size() == names.size());
+ return pair.first->second;
+ }
+
+ ZString outtern(size_t sz) const
+ {
+ return names[sz];
+ }
+
+ size_t size() const
+ {
+ return known.size();
+ }
+};
+
+#endif // TMWA_GENERIC_INTERN_POOL_HPP
diff --git a/src/generic/intern-pool_test.cpp b/src/generic/intern-pool_test.cpp
new file mode 100644
index 0000000..bf17b67
--- /dev/null
+++ b/src/generic/intern-pool_test.cpp
@@ -0,0 +1,20 @@
+#include "intern-pool.hpp"
+
+#include <gtest/gtest.h>
+
+#include "../strings/base.hpp"
+
+TEST(InternPool, whydoesthisalwaysneedasecondname)
+{
+ InternPool p;
+ EXPECT_EQ(0, p.size());
+ EXPECT_EQ(0, p.intern("hello"));
+ EXPECT_EQ(0, p.intern("hello"));
+ EXPECT_EQ(1, p.size());
+ EXPECT_EQ(1, p.intern("world"));
+ EXPECT_EQ(0, p.intern("hello"));
+ EXPECT_EQ(1, p.intern("world"));
+ EXPECT_EQ(2, p.size());
+ EXPECT_EQ("hello", p.outtern(0));
+ EXPECT_EQ("world", p.outtern(1));
+}
diff --git a/src/generic/matrix.cpp b/src/generic/matrix.cpp
new file mode 100644
index 0000000..9ee049e
--- /dev/null
+++ b/src/generic/matrix.cpp
@@ -0,0 +1,3 @@
+#include "matrix.hpp"
+
+#include "../poison.hpp"
diff --git a/src/generic/matrix.hpp b/src/generic/matrix.hpp
new file mode 100644
index 0000000..3530ba7
--- /dev/null
+++ b/src/generic/matrix.hpp
@@ -0,0 +1,54 @@
+#ifndef TMWA_GENERIC_MATRIX_HPP
+#define TMWA_GENERIC_MATRIX_HPP
+
+# include "../sanity.hpp"
+
+# include "../compat/memory.hpp"
+
+template<class T>
+class Matrix
+{
+ std::unique_ptr<T[]> _data;
+ size_t _xs, _ys;
+public:
+ Matrix()
+ : _data()
+ , _xs()
+ , _ys()
+ {}
+ Matrix(size_t x, size_t y)
+ : _data(make_unique<T[]>(x * y))
+ , _xs(x)
+ , _ys(y)
+ {}
+ // no copy-ctor or copy-assign
+
+ void reset(size_t x, size_t y)
+ {
+ *this = Matrix(x, y);
+ }
+ void clear()
+ {
+ *this = Matrix();
+ }
+
+ T& ref(size_t x, size_t y)
+ {
+ return _data[x + y * _xs];
+ }
+ const T& ref(size_t x, size_t y) const
+ {
+ return _data[x + y * _xs];
+ }
+
+ size_t xs() const
+ {
+ return _xs;
+ }
+ size_t ys() const
+ {
+ return _ys;
+ }
+};
+
+#endif // TMWA_GENERIC_MATRIX_HPP
diff --git a/src/generic/md5.cpp b/src/generic/md5.cpp
new file mode 100644
index 0000000..a626dc5
--- /dev/null
+++ b/src/generic/md5.cpp
@@ -0,0 +1,234 @@
+#include "md5.hpp"
+
+#include <cstring>
+
+#include "../compat/rawmem.hpp"
+
+#include "../strings/xstring.hpp"
+#include "../strings/vstring.hpp"
+
+#include "random.hpp"
+
+#include "../poison.hpp"
+
+// auxilary data
+/*
+sin() constant table
+#Reformatted output of:
+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] =
+{
+ // used by round 1
+ 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, //0
+ 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, //4
+ 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, //8
+ 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, //12
+ // used by round 2
+ 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, //16
+ 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, //20
+ 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, //24
+ 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, //28
+ // used by round 3
+ 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, //32
+ 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, //36
+ 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, //40
+ 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, //44
+ // used by round 4
+ 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, //48
+ 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, //52
+ 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, //56
+ 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391, //60
+};
+
+// auxilary functions
+// note - the RFC defines these by non-CS conventions: or=v, and=(empty)
+static
+uint32_t rotate_left(uint32_t val, unsigned shift)
+{
+ return val << shift | val >> (32 - shift);
+}
+
+static
+uint32_t F(uint32_t X, uint32_t Y, uint32_t Z)
+{
+ return (X & Y) | (~X & Z);
+}
+static
+uint32_t G(uint32_t X, uint32_t Y, uint32_t Z)
+{
+ return (X & Z) | (Y & ~Z);
+}
+static
+uint32_t H(uint32_t X, uint32_t Y, uint32_t Z)
+{
+ return X ^ Y ^ Z;
+}
+static
+uint32_t I(uint32_t X, uint32_t Y, uint32_t Z)
+{
+ return Y ^ (X | ~Z);
+}
+
+static
+const struct
+{
+ uint8_t k : 4;
+ uint8_t : 0;
+ uint8_t s : 5;
+// uint8_t i : 6; just increments constantly, from 1 .. 64 over all rounds
+}
+MD5_round1[16] =
+{
+ { 0, 7}, { 1, 12}, { 2, 17}, { 3, 22},
+ { 4, 7}, { 5, 12}, { 6, 17}, { 7, 22},
+ { 8, 7}, { 9, 12}, {10, 17}, {11, 22},
+ {12, 7}, {13, 12}, {14, 17}, {15, 22},
+},
+MD5_round2[16] =
+{
+ { 1, 5}, { 6, 9}, {11, 14}, { 0, 20},
+ { 5, 5}, {10, 9}, {15, 14}, { 4, 20},
+ { 9, 5}, {14, 9}, { 3, 14}, { 8, 20},
+ {13, 5}, { 2, 9}, { 7, 14}, {12, 20},
+},
+MD5_round3[16] =
+{
+ { 5, 4}, { 8, 11}, {11, 16}, {14, 23},
+ { 1, 4}, { 4, 11}, { 7, 16}, {10, 23},
+ {13, 4}, { 0, 11}, { 3, 16}, { 6, 23},
+ { 9, 4}, {12, 11}, {15, 16}, { 2, 23},
+},
+MD5_round4[16] =
+{
+ { 0, 6}, { 7, 10}, {14, 15}, { 5, 21},
+ {12, 6}, { 3, 10}, {10, 15}, { 1, 21},
+ { 8, 6}, {15, 10}, { 6, 15}, {13, 21},
+ { 4, 6}, {11, 10}, { 2, 15}, { 9, 21},
+};
+
+
+void MD5_init(MD5_state* state)
+{
+ // in the RFC, these are specified as bytes, interpreted as little-endian
+ state->val[0] = 0x67452301;
+ state->val[1] = 0xEFCDAB89;
+ state->val[2] = 0x98BADCFE;
+ state->val[3] = 0x10325476;
+}
+
+#define X block.data
+
+void MD5_do_block(MD5_state* state, MD5_block block)
+{
+#define a state->val[(16 - i) % 4]
+#define b state->val[(17 - i) % 4]
+#define c state->val[(18 - i) % 4]
+#define d state->val[(19 - i) % 4]
+ // save the values
+ const MD5_state saved = *state;
+ // round 1
+ for (int i = 0; i < 16; i++)
+ {
+#define k MD5_round1[i].k
+#define s MD5_round1[i].s
+ a = b + rotate_left(a + F(b, c, d) + X[k] + T[i + 0x0], s);
+#undef k
+#undef s
+ }
+ // round 2
+ for (int i = 0; i < 16; i++)
+ {
+#define k MD5_round2[i].k
+#define s MD5_round2[i].s
+ a = b + rotate_left(a + G(b, c, d) + X[k] + T[i + 0x10], s);
+#undef k
+#undef s
+ }
+ // round 3
+ for (int i = 0; i < 16; i++)
+ {
+#define k MD5_round3[i].k
+#define s MD5_round3[i].s
+ a = b + rotate_left(a + H(b, c, d) + X[k] + T[i + 0x20], s);
+#undef k
+#undef s
+ }
+ // round 4
+ for (int i = 0; i < 16; i++)
+ {
+#define k MD5_round4[i].k
+#define s MD5_round4[i].s
+ a = b + rotate_left(a + I(b, c, d) + X[k] + T[i + 0x30], s);
+#undef k
+#undef s
+ }
+ // adjust state based on original
+ state->val[0] += saved.val[0];
+ state->val[1] += saved.val[1];
+ state->val[2] += saved.val[2];
+ state->val[3] += saved.val[3];
+#undef a
+#undef b
+#undef c
+#undef d
+}
+
+void MD5_to_bin(MD5_state state, md5_binary& out)
+{
+ for (int i = 0; i < 0x10; i++)
+ out[i] = state.val[i / 4] >> 8 * (i % 4);
+}
+
+static
+const char hex[] = "0123456789abcdef";
+
+void MD5_to_str(MD5_state state, md5_string& out_)
+{
+ md5_binary bin;
+ MD5_to_bin(state, bin);
+ char out[0x20];
+ for (int i = 0; i < 0x10; i++)
+ out[2 * i] = hex[bin[i] >> 4],
+ out[2 * i + 1] = hex[bin[i] & 0xf];
+ out_ = stringish<md5_string>(XString(out, out + 0x20, nullptr));
+}
+
+MD5_state MD5_from_string(XString msg)
+{
+ MD5_state state;
+ MD5_init(&state);
+ MD5_block block;
+ const uint64_t msg_full_len = msg.size();
+ while (msg.size() >= 64)
+ {
+ for (int i = 0; i < 0x10; i++)
+ X[i] = msg[4 * i + 0] | msg[4 * i + 1] << 8 | msg[4 * i + 2] << 16 | msg[4 * i + 3] << 24;
+ MD5_do_block(&state, block);
+ msg = msg.xslice_t(64);
+ }
+ // now pad 1-512 bits + the 64-bit length - may be two blocks
+ uint8_t buf[0x40] = {};
+ really_memcpy(buf, reinterpret_cast<const uint8_t *>(msg.data()), msg.size());
+ buf[msg.size()] = 0x80; // a single one bit
+ if (64 - msg.size() > 8)
+ {
+ for (int i = 0; i < 8; i++)
+ buf[0x38 + i] = (msg_full_len * 8) >> (i * 8);
+ }
+ for (int i = 0; i < 0x10; i++)
+ X[i] = buf[4 * i + 0] | buf[4 * i + 1] << 8 | buf[4 * i + 2] << 16 | buf[4 * i + 3] << 24;
+ MD5_do_block(&state, block);
+ if (64 - msg.size() <= 8)
+ {
+ really_memset0(buf, 0x38);
+ for (int i = 0; i < 8; i++)
+ buf[0x38 + i] = (msg_full_len * 8) >> (i * 8);
+ for (int i = 0; i < 0x10; i++)
+ X[i] = buf[4 * i + 0] | buf[4 * i + 1] << 8 | buf[4 * i + 2] << 16 | buf[4 * i + 3] << 24;
+ MD5_do_block(&state, block);
+ }
+ return state;
+}
diff --git a/src/generic/md5.hpp b/src/generic/md5.hpp
new file mode 100644
index 0000000..ba1212a
--- /dev/null
+++ b/src/generic/md5.hpp
@@ -0,0 +1,44 @@
+#ifndef TMWA_GENERIC_MD5CALC_HPP
+#define TMWA_GENERIC_MD5CALC_HPP
+
+# include "../sanity.hpp"
+
+# include <netinet/in.h>
+
+# include <cstdint>
+# include <cstddef>
+# include <cstdio>
+
+# include <array>
+
+# include "../strings/fwd.hpp"
+# include "../strings/vstring.hpp"
+
+/// The digest state - becomes the output
+struct MD5_state
+{
+ // classically named {A,B,C,D}
+ // but use an array so we can index
+ uint32_t val[4];
+};
+struct MD5_block
+{
+ uint32_t data[16];
+};
+
+struct md5_binary : std::array<uint8_t, 0x10> {};
+struct md5_string : VString<0x20> {};
+struct SaltString : VString<5> {};
+
+// Implementation
+void MD5_init(MD5_state *state);
+void MD5_do_block(MD5_state *state, MD5_block block);
+
+// Output formatting
+void MD5_to_bin(MD5_state state, md5_binary& out);
+void MD5_to_str(MD5_state state, md5_string& out);
+
+// Convenience
+MD5_state MD5_from_string(XString msg);
+
+#endif // TMWA_GENERIC_MD5CALC_HPP
diff --git a/src/generic/md5_test.cpp b/src/generic/md5_test.cpp
new file mode 100644
index 0000000..7086c8c
--- /dev/null
+++ b/src/generic/md5_test.cpp
@@ -0,0 +1,28 @@
+#include "md5.hpp"
+
+#include <gtest/gtest.h>
+
+#include "../strings/xstring.hpp"
+#include "../strings/vstring.hpp"
+
+// This should be made part of the main API,
+// but is not yet to keep the diff small.
+// Edit: hack to fix the new strict comparison.
+static
+VString<32> MD5(XString in)
+{
+ md5_string out;
+ MD5_to_str(MD5_from_string(in), out);
+ return out;
+}
+
+TEST(md5calc, rfc1321)
+{
+ EXPECT_EQ("d41d8cd98f00b204e9800998ecf8427e", MD5(""));
+ EXPECT_EQ("0cc175b9c0f1b6a831c399e269772661", MD5("a"));
+ EXPECT_EQ("900150983cd24fb0d6963f7d28e17f72", MD5("abc"));
+ EXPECT_EQ("f96b697d7cb7938d525a2f31aaf161d0", MD5("message digest"));
+ EXPECT_EQ("c3fcd3d76192e4007dfb496cca67e13b", MD5("abcdefghijklmnopqrstuvwxyz"));
+ EXPECT_EQ("d174ab98d277d9f5a5611c2c9f419d9f", MD5("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"));
+ EXPECT_EQ("57edf4a22be3c955ac49da2e2107b67a", MD5("12345678901234567890123456789012345678901234567890123456789012345678901234567890"));
+}
diff --git a/src/generic/operators.cpp b/src/generic/operators.cpp
new file mode 100644
index 0000000..877aec6
--- /dev/null
+++ b/src/generic/operators.cpp
@@ -0,0 +1,3 @@
+#include "operators.hpp"
+
+#include "../poison.hpp"
diff --git a/src/generic/operators.hpp b/src/generic/operators.hpp
new file mode 100644
index 0000000..3d3dccd
--- /dev/null
+++ b/src/generic/operators.hpp
@@ -0,0 +1,47 @@
+#ifndef TMWA_GENERIC_OPERATORS_HPP
+#define TMWA_GENERIC_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 // TMWA_GENERIC_OPERATORS_HPP
diff --git a/src/generic/random.cpp b/src/generic/random.cpp
new file mode 100644
index 0000000..273dcec
--- /dev/null
+++ b/src/generic/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/generic/random.hpp b/src/generic/random.hpp
new file mode 100644
index 0000000..5d7a7af
--- /dev/null
+++ b/src/generic/random.hpp
@@ -0,0 +1,69 @@
+#ifndef TMWA_GENERIC_RANDOM_HPP
+#define TMWA_GENERIC_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 // TMWA_GENERIC_RANDOM_HPP
diff --git a/src/generic/random.t.hpp b/src/generic/random.t.hpp
new file mode 100644
index 0000000..feea2b0
--- /dev/null
+++ b/src/generic/random.t.hpp
@@ -0,0 +1,23 @@
+#ifndef TMWA_GENERIC_RANDOM_T_HPP
+#define TMWA_GENERIC_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 // TMWA_GENERIC_RANDOM_T_HPP
diff --git a/src/generic/random2.hpp b/src/generic/random2.hpp
new file mode 100644
index 0000000..4bdc72e
--- /dev/null
+++ b/src/generic/random2.hpp
@@ -0,0 +1,75 @@
+#ifndef TMWA_GENERIC_RANDOM2_HPP
+#define TMWA_GENERIC_RANDOM2_HPP
+
+# include "random.hpp"
+
+# include <algorithm>
+
+# include "../compat/iter.hpp"
+
+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 // TMWA_GENERIC_RANDOM2_HPP