diff options
author | Ben Longbons <b.r.longbons@gmail.com> | 2014-03-15 19:34:59 -0700 |
---|---|---|
committer | Ben Longbons <b.r.longbons@gmail.com> | 2014-03-16 18:58:48 -0700 |
commit | c812c92d1a1835f0bda783e709481188c8d92225 (patch) | |
tree | b401ede48a088ad1aaed88fe3b997cd26ff7ae08 /src/generic | |
parent | de9ee1b9754af9d954487121947352f32d7ebb7e (diff) | |
download | tmwa-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.cpp | 3 | ||||
-rw-r--r-- | src/generic/const_array.hpp | 132 | ||||
-rw-r--r-- | src/generic/db.cpp | 3 | ||||
-rw-r--r-- | src/generic/db.hpp | 180 | ||||
-rw-r--r-- | src/generic/enum.cpp | 3 | ||||
-rw-r--r-- | src/generic/enum.hpp | 170 | ||||
-rw-r--r-- | src/generic/intern-pool.cpp | 3 | ||||
-rw-r--r-- | src/generic/intern-pool.hpp | 41 | ||||
-rw-r--r-- | src/generic/intern-pool_test.cpp | 20 | ||||
-rw-r--r-- | src/generic/matrix.cpp | 3 | ||||
-rw-r--r-- | src/generic/matrix.hpp | 54 | ||||
-rw-r--r-- | src/generic/md5.cpp | 234 | ||||
-rw-r--r-- | src/generic/md5.hpp | 44 | ||||
-rw-r--r-- | src/generic/md5_test.cpp | 28 | ||||
-rw-r--r-- | src/generic/operators.cpp | 3 | ||||
-rw-r--r-- | src/generic/operators.hpp | 47 | ||||
-rw-r--r-- | src/generic/random.cpp | 8 | ||||
-rw-r--r-- | src/generic/random.hpp | 69 | ||||
-rw-r--r-- | src/generic/random.t.hpp | 23 | ||||
-rw-r--r-- | src/generic/random2.hpp | 75 |
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 |