hash: use a consistency, less flexible, interface

This commit is contained in:
Danny Robson 2018-01-13 13:48:58 +11:00
parent e2f0d23a6d
commit 681768093e
80 changed files with 1698 additions and 2818 deletions

View File

@ -241,8 +241,6 @@ list (
geom/tri.hpp
hash.hpp
hash/fwd.hpp
hash/simple.hpp
hash/simple.cpp
hash/adler.cpp
hash/adler.hpp
hash/bsdsum.cpp
@ -259,8 +257,6 @@ list (
hash/hmac.hpp
hash/hotp.cpp
hash/hotp.hpp
hash/keccak.cpp
hash/keccak.hpp
hash/md2.cpp
hash/md2.hpp
hash/md4.cpp
@ -276,10 +272,6 @@ list (
hash/murmur/murmur2.hpp
hash/murmur/murmur3.cpp
hash/murmur/murmur3.hpp
hash/pbkdf1.cpp
hash/pbkdf1.hpp
hash/pbkdf2.cpp
hash/pbkdf2.hpp
hash/ripemd.cpp
hash/ripemd.hpp
hash/sha1.cpp
@ -287,7 +279,6 @@ list (
hash/sha2.cpp
hash/sha2.hpp
hash/wang.hpp
hash/wang.ipp
hash/xxhash.cpp
hash/xxhash.hpp
introspection.cpp
@ -509,9 +500,9 @@ if (TESTS)
hash/checksum
hash/crc
hash/fasthash
hash/fnv1a
hash/hmac
hash/hotp
hash/keccak
hash/md2
hash/md4
hash/md5

View File

@ -23,8 +23,18 @@
#include <cstdlib>
namespace util::hash {
constexpr std::uint32_t mix (std::uint32_t a, std::uint32_t b) { return murmur2::mix (a, b); }
constexpr std::uint64_t mix (std::uint64_t a, std::uint64_t b) { return murmur2::mix (a, b); }
constexpr std::uint32_t
mix (std::uint32_t a, std::uint32_t b)
{
return murmur2<std::uint32_t>::mix (a, b);
}
constexpr std::uint64_t
mix (std::uint64_t a, std::uint64_t b)
{
return murmur2<std::uint64_t>::mix (a, b);
}
}
#endif

View File

@ -16,9 +16,6 @@
#include "adler.hpp"
#include "fletcher.hpp"
#include "../debug.hpp"
static constexpr unsigned MODULUS = 65521;
using util::hash::adler32;
@ -29,21 +26,3 @@ adler32::adler32 ():
fletcher (MODULUS, 1, 0)
{ ; }
/////////////////////////////////////////////////////////////////////////////////
//uint32_t
//util::hash::adler32 (const void* restrict _data, size_t _size) noexcept
//{
// return adler32 (
// static_cast<const uint8_t*> (_data),
// static_cast<const uint8_t*> (_data) + _size
// );
//}
//
//
////-----------------------------------------------------------------------------
//uint32_t
//util::hash::adler32 (const uint8_t *restrict first, const uint8_t *restrict last) noexcept
//{
// return fletcher<32, MODULUS, 1, 0> (first, last - first);
//}

View File

@ -11,67 +11,24 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright 2010 Danny Robson <danny@nerdcruft.net>
* Copyright 2010-2018 Danny Robson <danny@nerdcruft.net>
*/
#include "bsdsum.hpp"
#include "../debug.hpp"
#include "../bitwise.hpp"
using util::hash::bsdsum;
///////////////////////////////////////////////////////////////////////////////
bsdsum::bsdsum ()
{
reset ();
}
//-----------------------------------------------------------------------------
void
bsdsum::reset (void)
{
m_accum = 0;
}
///////////////////////////////////////////////////////////////////////////////
void
bsdsum::update (const void *restrict data, size_t size) noexcept
{
auto first = static_cast<const uint8_t *restrict> (data);
update (first, first + size);
}
///////////////////////////////////////////////////////////////////////////////
void
bsdsum::update (const uint8_t *const restrict first,
const uint8_t *const restrict last) noexcept
{
CHECK (first);
CHECK (last);
CHECK_LE (first, last);
for (auto cursor = first; cursor != last; ++cursor) {
m_accum = (m_accum >> 1u) | ((m_accum & 0x01u) << 15u);
m_accum += *cursor;
}
}
//-----------------------------------------------------------------------------
void
bsdsum::finish (void)
{ ; }
//-----------------------------------------------------------------------------
typename bsdsum::digest_t
bsdsum::digest (void) const
bsdsum::operator() (util::view<const uint8_t*> data) const noexcept
{
return m_accum;
digest_t accum = 0;
for (const auto i: data)
accum = util::rotater (accum, 1) + i;
return accum;
}

View File

@ -11,11 +11,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright 2011 Danny Robson <danny@nerdcruft.net>
* Copyright 2011-2018 Danny Robson <danny@nerdcruft.net>
*/
#ifndef __UTIL_HASH_BSDSUM_HPP
#define __UTIL_HASH_BSDSUM_HPP
#ifndef CRUFT_UTIL_HASH_BSDSUM_HPP
#define CRUFT_UTIL_HASH_BSDSUM_HPP
#include "../view.hpp"
#include <cstdint>
#include <cstdlib>
@ -27,18 +29,7 @@ namespace util::hash {
public:
using digest_t = uint16_t;
bsdsum (void);
void reset (void);
void update (const void *restrict data, size_t bytes) noexcept;
void update (const uint8_t *restrict first, const uint8_t *restrict last) noexcept;
void finish (void);
digest_t digest (void) const;
private:
digest_t m_accum;
digest_t operator() (util::view<const uint8_t*>) const noexcept;
};
}

View File

@ -48,112 +48,23 @@ template <
bool ReflectIn,
bool ReflectOut
>
typename crc<DigestT,Generator,Initial,Final,ReflectIn,ReflectOut>::digest_t
crc<
DigestT,Generator,Initial,Final,ReflectIn,ReflectOut
>::crc () noexcept
>::operator() (const util::view<const uint8_t*> data) const noexcept
{
reset ();
}
auto accum = Initial;
//-----------------------------------------------------------------------------
template <
typename DigestT,
DigestT Generator,
DigestT Initial,
DigestT Final,
bool ReflectIn,
bool ReflectOut
>
void
crc<
DigestT,Generator,Initial,Final,ReflectIn,ReflectOut
>::reset (void) noexcept
{
m_digest = Initial;
}
///////////////////////////////////////////////////////////////////////////////
template <
typename DigestT,
DigestT Generator,
DigestT Initial,
DigestT Final,
bool ReflectIn,
bool ReflectOut
>
void
crc<
DigestT,Generator,Initial,Final,ReflectIn,ReflectOut
>::update (const uint8_t *restrict first,
const uint8_t *restrict last) noexcept
{
CHECK_LE (first, last);
for (auto cursor = first; cursor != last; ++cursor) {
for (auto i: data) {
if (ReflectIn)
m_digest = s_table[*cursor ^ (m_digest & 0xFFu)] ^ (m_digest >> 8u);
accum = s_table[i ^ (accum & 0xFFu)] ^ (accum >> 8u);
else {
constexpr auto shift = sizeof (DigestT) * 8u - 8u;
m_digest = (m_digest << 8u) ^ s_table[(m_digest >> shift) ^ *cursor];
accum = (accum << 8u) ^ s_table[(accum >> shift) ^ i];
}
}
}
//-----------------------------------------------------------------------------
template <
typename DigestT,
DigestT Generator,
DigestT Initial,
DigestT Final,
bool ReflectIn,
bool ReflectOut
>
void
crc<
DigestT,Generator,Initial,Final,ReflectIn,ReflectOut
>::update (const void *restrict _data, size_t len) noexcept
{
auto data = reinterpret_cast<const uint8_t *restrict> (_data);
return update(data, data + len);
}
//-----------------------------------------------------------------------------
template <
typename DigestT,
DigestT Generator,
DigestT Initial,
DigestT Final,
bool ReflectIn,
bool ReflectOut
>
void
crc<
DigestT,Generator,Initial,Final,ReflectIn,ReflectOut
>::finish (void)
{
;
}
//-----------------------------------------------------------------------------
template <
typename DigestT,
DigestT Generator,
DigestT Initial,
DigestT Final,
bool ReflectIn,
bool ReflectOut
>
DigestT
crc<
DigestT,Generator,Initial,Final,ReflectIn,ReflectOut
>::digest (void) const
{
return (ReflectIn != ReflectOut ? util::reverse (m_digest) : m_digest) ^ Final;
return (ReflectIn != ReflectOut ? util::reverse (accum) : accum) ^ Final;
}

View File

@ -11,11 +11,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright 2011 Danny Robson <danny@nerdcruft.net>
* Copyright 2011-2018 Danny Robson <danny@nerdcruft.net>
*/
#ifndef __UTIL_HASH_CRC_HPP
#define __UTIL_HASH_CRC_HPP
#ifndef CRUFT_UTIL_HASH_CRC_HPP
#define CRUFT_UTIL_HASH_CRC_HPP
#include "../view.hpp"
#include <array>
#include <cstdint>
@ -24,7 +26,7 @@
///////////////////////////////////////////////////////////////////////////////
namespace util { namespace hash {
namespace util::hash {
// Implements the crc checksum (from ethernet, png, etc).
//
// Adapted from the PNG specification (ISO/IEC 15948:2003), appendix D and
@ -37,8 +39,8 @@ namespace util { namespace hash {
// ReflectOut: whether to reverse the bits of the digest at finish time
//
// Note that reflection isn't necessarily explicitly performed at update
// time. Instead we construct the lookup table appropriately to directly
// use the data values directly.
// time. Instead we construct the lookup table appropriately to use the
// data values directly.
template <
typename DigestT,
DigestT Generator,
@ -53,33 +55,23 @@ namespace util { namespace hash {
static constexpr auto generator = Generator;
crc () noexcept;
void reset (void) noexcept;
void update (const void *restrict data, size_t bytes) noexcept;
void update (const uint8_t *restrict first, const uint8_t *restrict last) noexcept;
void finish (void);
digest_t digest (void) const;
digest_t operator() (util::view<const uint8_t*>) const noexcept;
static constexpr
std::array<DigestT,256>
table (void);
private:
digest_t m_digest;
static const std::array<DigestT,256> s_table;
};
using crc32 = crc<uint32_t, 0x04c11db7, 0xffffffff, 0xffffffff, true, true>;
using crc32b = crc<uint32_t, 0x04c11db7, 0xffffffff, 0xffffffff, false, false>;
using crc32c = crc<uint32_t, 0x1edc6f41, 0xffffffff, 0xffffffff, true, true>;
using crc32d = crc<uint32_t, 0xa833982b, 0xffffffff, 0xffffffff, true, true>;
using crc64 = crc<uint64_t, 0x42f0e1eba9ea3693, 0, 0, false, false>;
} }
}
#endif

View File

@ -16,35 +16,26 @@
#include "fasthash.hpp"
///////////////////////////////////////////////////////////////////////////////
uint64_t
util::hash::fasthash::mix (uint64_t v)
{
v ^= v >> 23;
v *= 0x2127599bf4325c37;
v ^= v >> 47;
return v;
}
using util::hash::fasthash;
///////////////////////////////////////////////////////////////////////////////
template <>
uint64_t
util::hash::fasthash::hash64 (const void *restrict data, size_t len, uint64_t seed)
fasthash<uint64_t>::operator() (uint64_t seed, const util::view<const uint8_t*> data) const
{
static const uint64_t m = 0x880355f21e6d1965;
uint64_t result = seed ^ (len * m);
uint64_t result = seed ^ (data.size () * m);
auto cursor = reinterpret_cast<const uint64_t*> (data);
auto last = cursor + len / sizeof (*cursor);
auto cursor = reinterpret_cast<const uint64_t*> (data.begin ());
auto last = cursor + data.size () / sizeof (*cursor);
for (; cursor < last; ++cursor) {
result ^= mix (*cursor);
result *= m;
}
size_t remain = len % sizeof (*cursor);
size_t remain = data.size () % sizeof (*cursor);
if (remain) {
auto tail = reinterpret_cast<const uint8_t*> (cursor);
@ -61,10 +52,11 @@ util::hash::fasthash::hash64 (const void *restrict data, size_t len, uint64_t se
//-----------------------------------------------------------------------------
template <>
uint32_t
util::hash::fasthash::hash32 (const void *restrict data, size_t len, uint32_t seed)
fasthash<uint32_t>::operator() (uint64_t seed, const util::view<const uint8_t*> data) const
{
uint64_t h = hash64 (data, len, seed);
auto h = fasthash<uint64_t> {} (seed, data);
return (h & 0xffffffff) - (h >> 32);
}

View File

@ -17,17 +17,31 @@
#ifndef __UTIL_HASH_FASTHASH_HPP
#define __UTIL_HASH_FASTHASH_HPP
#include "../view.hpp"
#include <cstddef>
#include <cstdint>
// Zilong Tan's FastHash, via George Marsaglia's "Xorshift RNGs"
namespace util::hash::fasthash {
uint64_t mix (uint64_t);
uint32_t hash32 (const void *restrict data, size_t len, uint32_t seed);
uint64_t hash64 (const void *restrict data, size_t len, uint64_t seed);
// Zilong Tan's FastHash, via George Marsaglia's "Xorshift RNGs"
namespace util::hash {
template <typename ValueT>
struct fasthash {
using digest_t = ValueT;
static constexpr
uint64_t mix (uint64_t v)
{
v ^= v >> 23;
v *= 0x2127599bf4325c37;
v ^= v >> 47;
return v;
}
digest_t operator() (uint64_t seed, util::view<const uint8_t*>) const;
};
}
#endif

View File

@ -27,61 +27,24 @@ using util::hash::fletcher;
template <typename T>
fletcher<T>::fletcher (part_t _modulus, part_t _a, part_t _b):
m_modulus { _modulus },
m_initial { _a, _b },
m_state { m_initial }
m_initial { _a, _b }
{ ; }
//-----------------------------------------------------------------------------
template <typename T>
void
fletcher<T>::reset (void)
{
m_state = m_initial;
}
///////////////////////////////////////////////////////////////////////////////
template <typename T>
void
fletcher<T>::update (const void *restrict data, size_t size) noexcept
template <typename DigestT>
typename fletcher<DigestT>::digest_t
fletcher<DigestT>::operator() (util::view<const std::uint8_t*> data) const noexcept
{
CHECK (data);
state_t accum = m_initial;
auto first = static_cast<const uint8_t *restrict> (data);
update (first, first + size);
}
//-----------------------------------------------------------------------------
template <typename T>
void
fletcher<T>::update (const uint8_t *restrict first, const uint8_t *restrict last) noexcept
{
CHECK (first);
CHECK (last);
CHECK_LE (first, last);
for (auto cursor = first; cursor < last; ++cursor) {
m_state.a = (m_state.a + *cursor ) % m_modulus;
m_state.b = (m_state.a + m_state.b) % m_modulus;
for (const auto i: data) {
accum.a = (accum.a + i) % m_modulus;
accum.b = (accum.a + accum.b) % m_modulus;
}
}
//-----------------------------------------------------------------------------
template <typename T>
void
fletcher<T>::finish (void)
{ ; }
///////////////////////////////////////////////////////////////////////////////
template <typename T>
typename fletcher<T>::digest_t
fletcher<T>::digest (void) const
{
return (m_state.b << (sizeof (part_t) * 8)) + m_state.a;
return accum.b << (sizeof(part_t) * 8u) | accum.a;
}

View File

@ -18,6 +18,7 @@
#define __UTIL_HASH_FLETCHER_HPP
#include "../types/bits.hpp"
#include "../view.hpp"
#include <cstdint>
#include <cstdlib>
@ -25,31 +26,24 @@
///////////////////////////////////////////////////////////////////////////////
namespace util::hash {
template <typename DIGEST>
template <typename DigestT>
class fletcher {
public:
using digest_t = DIGEST;
using digest_t = DigestT;
using part_t = typename bytes_type<sizeof (digest_t) / 2>::uint;
fletcher (part_t modulus, part_t a, part_t b);
void update (const void *restrict, size_t) noexcept;
void update (const uint8_t *restrict first, const uint8_t *restrict last) noexcept;
void finish (void);
digest_t digest (void) const;
void reset (void);
digest_t
operator() (util::view<const std::uint8_t*>) const noexcept;
private:
const digest_t m_modulus;
struct state_t {
part_t a, b;
};
const digest_t m_modulus;
const state_t m_initial;
state_t m_state;
};
}

View File

@ -16,6 +16,8 @@
#include "fnv1a.hpp"
using util::hash::fnv1a;
///////////////////////////////////////////////////////////////////////////////
// Prime is,
@ -28,48 +30,41 @@
//
// Bias is the FNV-0 hash of "chongo <Landon Curt Noll> /\\../\\"
template <size_t B>
template <typename DigestT>
struct constants { };
template <> struct constants<32> {
static constexpr uint32_t PRIME = 16777619u;
static constexpr uint32_t BIAS = 2166136261u;
};
template <> struct constants<64> {
static constexpr uint64_t PRIME = 1099511628211u;
static constexpr uint64_t BIAS = 14695981039346656037u;
//-----------------------------------------------------------------------------
template <>
struct constants<uint32_t> {
static constexpr uint32_t prime = 16777619u;
static constexpr uint32_t bias = 2166136261u;
};
//-----------------------------------------------------------------------------
template <typename T>
T
fnv1a (const void *restrict _data, size_t len)
{
auto *data = static_cast<const uint8_t *restrict> (_data);
T result = constants<sizeof(T)*8>::BIAS;
template <>
struct constants<uint64_t> {
static constexpr uint64_t prime = 1099511628211u;
static constexpr uint64_t bias = 14695981039346656037u;
};
for (size_t i = 0; i < len; ++i) {
result ^= data[i];
result *= constants<sizeof(T)*8>::PRIME;
///////////////////////////////////////////////////////////////////////////////
template <typename DigestT>
typename fnv1a<DigestT>::digest_t
fnv1a<DigestT>::operator() (const util::view<const uint8_t*> data) const noexcept
{
auto result = constants<DigestT>::bias;
for (auto i: data) {
result ^= i;
result *= constants<DigestT>::prime;
}
return result;
}
///////////////////////////////////////////////////////////////////////////////
uint32_t
util::hash::fnv1a32 (const void *data, size_t len)
{
return fnv1a<uint32_t> (data, len);
}
//-----------------------------------------------------------------------------
uint64_t
util::hash::fnv1a64 (const void *data, size_t len)
{
return fnv1a<uint64_t> (data, len);
}
template struct util::hash::fnv1a<uint32_t>;
template struct util::hash::fnv1a<uint64_t>;

View File

@ -17,13 +17,19 @@
#ifndef __UTIL_HASH_FNV1A_HPP
#define __UTIL_HASH_FNV1A_HPP
#include "view.hpp"
#include <cstdint>
#include <cstddef>
namespace util::hash {
// Fast and general hashing using FNV-1a
uint32_t fnv1a32 (const void *restrict, size_t);
uint64_t fnv1a64 (const void *restrict, size_t);
template <typename DigestT>
struct fnv1a {
using digest_t = DigestT;
digest_t operator() (util::view<const uint8_t*>) const noexcept;
};
}
#endif

View File

@ -16,109 +16,5 @@
#include "hmac.hpp"
#include "../debug.hpp"
#include <algorithm>
using util::hash::HMAC;
//-----------------------------------------------------------------------------
static const uint8_t IFILL = 0x36;
static const uint8_t OFILL = 0x5C;
//-----------------------------------------------------------------------------
template <class T>
HMAC<T>::HMAC (const uint8_t *restrict key, size_t len)
{
CHECK (key);
static_assert (sizeof (m_ikey) == sizeof (m_okey), "key padding must match");
// If the key is larger than the blocklength, use the hash of the key
if (len > T::BLOCK_SIZE) {
m_hash.update (key, len);
m_hash.finish ();
auto d = m_hash.digest ();
m_hash.reset ();
std::copy (d.begin (), d.end (), m_ikey.begin ());
len = d.size ();
// Use the key directly
} else {
std::copy (key, key + len, m_ikey.begin ());
}
std::fill (m_ikey.begin () + len,
m_ikey.end (),
0);
m_okey = m_ikey;
std::transform (m_ikey.begin (),
m_ikey.end (),
m_ikey.begin (),
[] (auto v) { return v ^ IFILL; });
std::transform (m_okey.begin (),
m_okey.end (),
m_okey.begin (),
[] (auto v) { return v ^ OFILL; });
m_hash.update (m_ikey.data (), m_ikey.size ());
}
//-----------------------------------------------------------------------------
template <class T>
void
HMAC<T>::update (const void *restrict data, size_t len)
{
m_hash.update ((const uint8_t*)data, len);
}
//-----------------------------------------------------------------------------
template <class T>
void
HMAC<T>::finish (void)
{
m_hash.finish ();
auto d = m_hash.digest ();
m_hash.reset ();
m_hash.update (m_okey.data (), m_okey.size ());
m_hash.update (d.data (), d.size ());
m_hash.finish ();
}
//-----------------------------------------------------------------------------
template <class T>
void
HMAC<T>::reset (void)
{
m_hash.reset ();
m_hash.update (m_ikey.data (), m_ikey.size ());
}
//-----------------------------------------------------------------------------
template <class T>
typename HMAC<T>::digest_t
HMAC<T>::digest (void)
{
return m_hash.digest ();
}
//-----------------------------------------------------------------------------
#include "md5.hpp"
#include "sha1.hpp"
namespace util::hash {
template class HMAC<MD5>;
template class HMAC<SHA1>;
}

View File

@ -14,33 +14,75 @@
* Copyright 2015 Danny Robson <danny@nerdcruft.net>
*/
#ifndef __UTIL_HASH_HMAC_HPP
#define __UTIL_HASH_HMAC_HPP
#ifndef CRUFT_UTIL_HASH_HMAC_HPP
#define CRUFT_UTIL_HASH_HMAC_HPP
#include "../debug.hpp"
#include "../view.hpp"
#include <algorithm>
#include <utility>
#include <array>
#include <cstdint>
#include <cstdlib>
namespace util::hash {
template <class T>
template <class HashT>
/// RFC 2104 key-hashing for message authentication
class HMAC {
public:
using digest_t = typename T::digest_t;
using digest_t = typename HashT::digest_t;
HMAC (const uint8_t *key, size_t);
void update (const void *restrict, size_t);
void finish (void);
void reset (void);
//---------------------------------------------------------------------
HMAC (util::view<const std::uint8_t*> key)
{
CHECK (!key.empty ());
static_assert (sizeof (m_ikey) == sizeof (m_okey), "key padding must match");
// If the key is larger than the blocklength, use the hash of the key
if (key.size () > HashT::BLOCK_SIZE) {
auto d = HashT{} (key);
auto tail = std::copy (d.begin (), d.end (), m_ikey.begin ());
std::fill (tail, std::end (m_ikey), 0);
// Use the key directly
} else {
auto tail = std::copy (key.begin (), key.end (), m_ikey.begin ());
std::fill (tail, m_ikey.end (), 0);
}
// copy and xor the key data to the okey
std::transform (
std::begin (m_ikey),
std::end (m_ikey),
std::begin (m_okey),
[] (auto v) { return v ^ OFILL; });
// just xor the ikey in place
std::transform (
m_ikey.begin (),
m_ikey.end (),
m_ikey.begin (),
[] (auto v) { return v ^ IFILL; });
}
//---------------------------------------------------------------------
template <typename ...DataT>
digest_t
operator() (DataT&&...data) const noexcept
{
HashT h;
return h (m_okey, h (m_ikey, std::forward<DataT> (data)...));
};
digest_t digest (void);
private:
std::array<uint8_t,T::BLOCK_SIZE> m_ikey;
std::array<uint8_t,T::BLOCK_SIZE> m_okey;
//---------------------------------------------------------------------
static constexpr uint8_t IFILL = 0x36;
static constexpr uint8_t OFILL = 0x5C;
T m_hash;
std::array<uint8_t,HashT::BLOCK_SIZE> m_ikey;
std::array<uint8_t,HashT::BLOCK_SIZE> m_okey;
};
}

View File

@ -11,7 +11,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright 2015 Danny Robson <danny@nerdcruft.net>
* Copyright 2015-2018 Danny Robson <danny@nerdcruft.net>
*/
#include "hotp.hpp"
@ -23,35 +23,28 @@
using util::hash::HOTP;
//-----------------------------------------------------------------------------
HOTP::HOTP (const char *_key, uint64_t _counter):
HOTP (_key, strlen (_key), _counter)
{ ; }
//-----------------------------------------------------------------------------
HOTP::HOTP (const void *_key, size_t _len, uint64_t _counter):
///////////////////////////////////////////////////////////////////////////////
HOTP::HOTP (util::view<const char*> _key, uint64_t _counter):
m_counter (_counter),
m_hash ((const uint8_t*)_key, _len)
m_hash (_key.template cast<const uint8_t> ())
{ ; }
//-----------------------------------------------------------------------------
unsigned
HOTP::value (void)
{
auto c = htob (m_counter);
union {
uint64_t number;
uint8_t bytes[8];
};
m_hash.update (&c, sizeof (c));
m_hash.finish ();
number = htob (m_counter);
auto h = m_hash.digest ();
auto t = truncate (h);
m_hash.reset ();
auto res = truncate (m_hash (util::make_cview (bytes)));
++m_counter;
return t % 1000000;
return res % 1'000'000;
}

View File

@ -11,11 +11,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright 2015 Danny Robson <danny@nerdcruft.net>
* Copyright 2015-2018 Danny Robson <danny@nerdcruft.net>
*/
#ifndef __UTIL_HASH_HOTP_HPP
#define __UTIL_HASH_HOTP_HPP
#ifndef CRUFT_UTIL_HASH_HOTP_HPP
#define CRUFT_UTIL_HASH_HOTP_HPP
#include "../view.hpp"
#include "hmac.hpp"
#include "sha1.hpp"
@ -26,8 +28,7 @@ namespace util::hash {
/// HMAC one-time password (RFC 4226)
class HOTP {
public:
HOTP (const char *key, uint64_t counter);
HOTP (const void *key, size_t len, uint64_t counter);
HOTP (util::view<const char*> key, uint64_t counter);
unsigned value (void);
uint64_t counter (void) const;

View File

@ -1,484 +0,0 @@
#include "keccak.hpp"
#include "../endian.hpp"
#include "../maths.hpp"
#include "../bitwise.hpp"
#include <cstdlib>
#include <cstdint>
#include <array>
#include <iostream>
#include <algorithm>
#if 0
#define FOR(i,n) for(i=0; i<n; ++i)
int LFSR86540(u8 *R) { (*R)=((*R)<<1)^(((*R)&0x80)?0x71:0); return ((*R)&2)>>1; }
#define ROL(a,o) ((((u64)a)<<o)^(((u64)a)>>(64-o)))
static u64 load64(const u8 *x) { ui i; u64 u=0; FOR(i,8) { u<<=8; u|=x[7-i]; } return u; }
static void store64(u8 *x, u64 u) { ui i; FOR(i,8) { x[i]=u; u>>=8; } }
static void xor64(u8 *x, u64 u) { ui i; FOR(i,8) { x[i]^=u; u>>=8; } }
#define rL(x,y) load64((u8*)s+8*(x+5*y))
#define wL(x,y,l) store64((u8*)s+8*(x+5*y),l)
#define XL(x,y,l) xor64((u8*)s+8*(x+5*y),l)
void KeccakF1600(void *s)
{
ui r,x,y,i,j,Y; u8 R=0x01; u64 C[5],D;
for(i=0; i<24; i++) {
/*θ*/ FOR(x,5) C[x]=rL(x,0)^rL(x,1)^rL(x,2)^rL(x,3)^rL(x,4); FOR(x,5) { D=C[(x+4)%5]^ROL(C[(x+1)%5],1); FOR(y,5) XL(x,y,D); }
/*ρπ*/ x=1; y=r=0; D=rL(x,y); FOR(j,24) { r+=j+1; Y=(2*x+3*y)%5; x=y; y=Y; C[0]=rL(x,y); wL(x,y,ROL(D,r%64)); D=C[0]; }
/*χ*/ FOR(y,5) { FOR(x,5) C[x]=rL(x,y); FOR(x,5) wL(x,y,C[x]^((~C[(x+1)%5])&C[(x+2)%5])); }
/*ι*/ FOR(j,7) if (LFSR86540(&R)) XL(0,0,(u64)1<<((1<<j)-1));
}
}
void Keccak(ui r, ui c, const u8 *in, u64 inLen, u8 sfx, u8 *out, u64 outLen)
{
/*initialize*/ u8 s[200]; ui R=r/8; ui i,b=0; FOR(i,200) s[i]=0;
/*absorb*/ while(inLen>0) { b=(inLen<R)?inLen:R; FOR(i,b) s[i]^=in[i]; in+=b; inLen-=b; if (b==R) { KeccakF1600(s); b=0; } }
/*pad*/ s[b]^=sfx; if((sfx&0x80)&&(b==(R-1))) KeccakF1600(s); s[R-1]^=0x80; KeccakF1600(s);
/*squeeze*/ while(outLen>0) { b=(outLen<R)?outLen:R; FOR(i,b) out[i]=s[i]; out+=b; outLen-=b; if(outLen>0) KeccakF1600(s); }
}
#else
// derived from Keccak (KCP) readable-and-compact C implementation
/*
Implementation by the Keccak, Keyak and Ketje Teams, namely, Guido Bertoni,
Joan Daemen, Michaël Peeters, Gilles Van Assche and Ronny Van Keer, hereby
denoted as "the implementer".
For more information, feedback or questions, please refer to our websites:
http://keccak.noekeon.org/
http://keyak.noekeon.org/
http://ketje.noekeon.org/
To the extent possible under law, the implementer has waived all copyright
and related or neighboring rights to the source code in this file.
http://creativecommons.org/publicdomain/zero/1.0/
*/
/*
================================================================
The purpose of this source file is to demonstrate a readable and compact
implementation of all the Keccak instances approved in the FIPS 202 standard,
including the hash functions and the extendable-output functions (XOFs).
We focused on clarity and on source-code compactness,
rather than on the performance.
The advantages of this implementation are:
+ The source code is compact, after removing the comments, that is. :-)
+ There are no tables with arbitrary constants.
+ For clarity, the comments link the operations to the specifications using
the same notation as much as possible.
+ There is no restriction in cryptographic features. In particular,
the SHAKE128 and SHAKE256 XOFs can produce any output length.
+ The code does not use much RAM, as all operations are done in place.
The drawbacks of this implementation are:
- There is no message queue. The whole message must be ready in a buffer.
- It is not optimized for peformance.
The implementation is even simpler on a little endian platform. Just define the
LITTLE_ENDIAN symbol in that case.
For a more complete set of implementations, please refer to
the Keccak Code Package at https://github.com/gvanas/KeccakCodePackage
For more information, please refer to:
* [Keccak Reference] http://keccak.noekeon.org/Keccak-reference-3.0.pdf
* [Keccak Specifications Summary] http://keccak.noekeon.org/specs_summary.html
This file uses UTF-8 encoding, as some comments use Greek letters.
================================================================
*/
/**
* Function to compute the Keccak[r, c] sponge function over a given input.
* @param rate The value of the rate r.
* @param capacity The value of the capacity c.
* @param input Pointer to the input message.
* @param inputByteLen The number of input bytes provided in the input message.
* @param delimitedSuffix Bits that will be automatically appended to the end
* of the input message, as in domain separation.
* This is a byte containing from 0 to 7 bits
* These <i>n</i> bits must be in the least significant bit positions
* and must be delimited with a bit 1 at position <i>n</i>
* (counting from 0=LSB to 7=MSB) and followed by bits 0
* from position <i>n</i>+1 to position 7.
* Some examples:
* - If no bits are to be appended, then @a delimitedSuffix must be 0x01.
* - If the 2-bit sequence 0,1 is to be appended (as for SHA3-*), @a delimitedSuffix must be 0x06.
* - If the 4-bit sequence 1,1,1,1 is to be appended (as for SHAKE*), @a delimitedSuffix must be 0x1F.
* - If the 7-bit sequence 1,1,0,1,0,0,0 is to be absorbed, @a delimitedSuffix must be 0x8B.
* @param output Pointer to the buffer where to store the output.
* @param outputByteLen The number of output bytes desired.
* @pre One must have r+c=1600 and the rate a multiple of 8 bits in this implementation.
*/
/**
* Function to compute SHAKE128 on the input message with any output length.
*/
void
FIPS202_SHAKE128(const uint8_t *input, size_t inputByteLen, uint8_t *output, size_t outputByteLen)
{
keccak k (1344, 256, 0x1f);
k.update (input, inputByteLen);
k.digest (output, outputByteLen);
}
/**
* Function to compute SHAKE256 on the input message with any output length.
*/
void FIPS202_SHAKE256(const uint8_t *input, size_t inputByteLen, uint8_t *output, size_t outputByteLen)
{
keccak k (1088, 512, 0x1f);
k.update (input, inputByteLen);
k.digest (output, outputByteLen);
}
/**
* Function to compute SHA3-224 on the input message. The output length is fixed to 28 bytes.
*/
void FIPS202_SHA3_224(const uint8_t *input, size_t inputByteLen, uint8_t *output)
{
keccak k (1152, 448, 0x06);
for (unsigned int i = 0; i < inputByteLen; ++i)
k.update (&input[i], 1);
//k.update (input, inputByteLen);
k.digest (output, 28);
}
/**
* Function to compute SHA3-256 on the input message. The output length is fixed to 32 bytes.
*/
void FIPS202_SHA3_256(const uint8_t *input, size_t inputByteLen, uint8_t *output)
{
keccak k (1088, 512, 0x06);
k.update (input, inputByteLen);
k.digest (output, 32);
}
/**
* Function to compute SHA3-384 on the input message. The output length is fixed to 48 bytes.
*/
void FIPS202_SHA3_384(const uint8_t *input, size_t inputByteLen, uint8_t *output)
{
keccak k (832, 768, 0x06);
k.update (input, inputByteLen);
k.digest (output, 48);
}
/**
* Function to compute SHA3-512 on the input message. The output length is fixed to 64 bytes.
*/
void FIPS202_SHA3_512(const uint8_t *input, size_t inputByteLen, uint8_t *output)
{
keccak k (576, 1024, 0x06);
k.update (input, inputByteLen);
k.digest (output, 64);
}
/*
================================================================
A readable and compact implementation of the Keccak-f[1600] permutation.
================================================================
*/
//static constexpr
//size_t
//i (size_t x, size_t y)
//{
// return x + 5 * y;
//}
/**
* Function that computes the linear feedback shift register (LFSR) used to
* define the round constants (see [Keccak Reference, Section 1.2]).
*/
class lfsr86540 {
public:
lfsr86540 ():
value (0x1)
{ ; }
bool
update (void)
{
bool result = value & 0x01;
if (value & 0x80)
// Primitive polynomial over GF(2): x^8+x^6+x^5+x^4+1
value = (value << 1) ^ 0x71;
else
value <<= 1;
return result;
}
private:
uint8_t value;
};
// θ step, see [Keccak Reference, Section 2.3.2]
static void
permute_theta (uint64_t m_words[5][5])
{
uint64_t C[5], D;
// Compute the parity of the columns
for (unsigned x = 0; x < 5; ++x)
C[x] = m_words[0][x] ^ m_words[1][x] ^ m_words[2][x] ^ m_words[3][x] ^ m_words[4][x];
for (unsigned x = 0; x < 5; ++x) {
// Compute the θ effect for a given column
D = C[(x+4)%5] ^ util::rotatel (C[(x+1)%5], 1);
// Add the θ effect to the whole column
for (unsigned y = 0; y < 5; ++y)
m_words[y][x] ^= D;
}
}
void
permute_rho (uint64_t m_words[5][5])
{
m_words[0][1] = util::rotatel (m_words[0][1], 1);
m_words[0][2] = util::rotatel (m_words[0][2], 62);
m_words[0][3] = util::rotatel (m_words[0][3], 28);
m_words[0][4] = util::rotatel (m_words[0][4], 27);
m_words[1][0] = util::rotatel (m_words[1][0], 36);
m_words[1][1] = util::rotatel (m_words[1][1], 44);
m_words[1][2] = util::rotatel (m_words[1][2], 6);
m_words[1][3] = util::rotatel (m_words[1][3], 55);
m_words[1][4] = util::rotatel (m_words[1][4], 20);
m_words[2][0] = util::rotatel (m_words[2][0], 3);
m_words[2][1] = util::rotatel (m_words[2][1], 10);
m_words[2][2] = util::rotatel (m_words[2][2], 43);
m_words[2][3] = util::rotatel (m_words[2][3], 25);
m_words[2][4] = util::rotatel (m_words[2][4], 39);
m_words[3][0] = util::rotatel (m_words[3][0], 41);
m_words[3][1] = util::rotatel (m_words[3][1], 45);
m_words[3][2] = util::rotatel (m_words[3][2], 15);
m_words[3][3] = util::rotatel (m_words[3][3], 21);
m_words[3][4] = util::rotatel (m_words[3][4], 8);
m_words[4][0] = util::rotatel (m_words[4][0], 18);
m_words[4][1] = util::rotatel (m_words[4][1], 2);
m_words[4][2] = util::rotatel (m_words[4][2], 61);
m_words[4][3] = util::rotatel (m_words[4][3], 56);
m_words[4][4] = util::rotatel (m_words[4][4], 14);
return;
for (size_t i = 1; i < 25; ++i) {
//unsigned r = ((t+1)*(t+2)/2)%64;
unsigned r = ((i + 1) * (i + 2) / 2) % 64;
m_words[i/5][i%5] = util::rotatel (m_words[i/5][i%5], r);
}
}
void
permute_pi (uint64_t m_words[5][5])
{
//auto A = reinterpret_cast<uint64_t*> (m_words);
//uint64_t A1;
//A1 = A[1];
//A[ 1] = A[ 6];
//A[ 6] = A[ 9];
//A[ 9] = A[22];
//A[22] = A[14];
//A[14] = A[20];
//A[20] = A[ 2];
//A[ 2] = A[12];
//A[12] = A[13];
//A[13] = A[19];
//A[19] = A[23];
//A[23] = A[15];
//A[15] = A[ 4];
//A[ 4] = A[24];
//A[24] = A[21];
//A[21] = A[ 8];
//A[ 8] = A[16];
//A[16] = A[ 5];
//A[ 5] = A[ 3];
//A[ 3] = A[18];
//A[18] = A[17];
//A[17] = A[11];
//A[11] = A[ 7];
//A[ 7] = A[10];
//A[10] = A1;
//return;
unsigned x = 1, y = 0;
uint64_t current = m_words[y][x];
uint64_t temp;
// Iterate over ((0 1)(2 3))^t * (1 0) for 0 ≤ t ≤ 23
for (unsigned t = 0; t < 24; ++t) {
unsigned int Y = (2*x+3*y)%5;
x = y;
y = Y;
temp = m_words[y][x];
m_words[y][x] = current;
current = temp;
}
//for (unsigned int i = 0; i < 5; ++i)
// for (unsigned int j = 0; j < 5; ++j)
// m_words[j][(2*i+3*j)%5] = m_words[i][j];
}
/**
* Function that computes the Keccak-f[1600] permutation on the given state.
*/
void
keccak::permute (void)
{
for (size_t i = 0; i < m_bitrate/64; ++i)
m_words[i/5][i%5] = util::ltoh (m_words[i/5][i%5]);
lfsr86540 shift;
for (unsigned round = 0; round < 24; ++round) {
permute_theta (m_words);
permute_rho (m_words);
permute_pi (m_words);
if (0) { // === ρ and π steps (see [Keccak Reference, Sections 2.3.3 and 2.3.4]) ===
uint64_t current, temp;
// Start at coordinates (1 0)
unsigned x = 1, y = 0;
current = m_words[y][x];
// Iterate over ((0 1)(2 3))^t * (1 0) for 0 ≤ t ≤ 23
for (unsigned t = 0; t < 24; ++t) {
// Compute the rotation constant r = (t+1)(t+2)/2
unsigned int r = ((t+1)*(t+2)/2)%64;
// Compute ((0 1)(2 3)) * (x y)
unsigned int Y = (2*x+3*y)%5; x = y; y = Y;
// Swap current and state(x,y), and rotate
temp = m_words[y][x];
m_words[y][x] = util::rotatel (current, r);
current = temp;
}
}
{ // === χ step (see [Keccak Reference, Section 2.3.1]) ===
uint64_t temp[5];
for (unsigned y = 0; y < 5; ++y) {
// Take a copy of the plane
for (unsigned x = 0; x < 5; ++x)
temp[x] = m_words[y][x];
// Compute χ on the plane
for(unsigned x = 0; x < 5; ++x)
m_words[y][x] = temp[x] ^((~temp[(x+1)%5]) & temp[(x+2)%5]);
}
}
{ // === ι step (see [Keccak Reference, Section 2.3.5]) ===
for (unsigned j = 0; j < 7; ++j) {
unsigned int bitPosition = (1 << j) - 1; //2^j-1
if (shift.update ())
m_words[0][0] ^= uint64_t{1} << bitPosition;
}
}
}
}
/*
================================================================
A readable and compact implementation of the Keccak sponge functions
that use the Keccak-f[1600] permutation.
================================================================
*/
void
keccak::update (
const uint8_t *input,
size_t len
) {
unsigned int byterate = m_bitrate / 8;
while (len) {
auto chunk = util::min (len, byterate - m_cursor);
for (unsigned i = 0; i < chunk; ++i)
m_bytes[m_cursor++] ^= *input++;
len -= chunk;
if (m_cursor == byterate) {
permute ();
m_cursor = 0;
}
}
}
void
keccak::digest (
uint8_t *output,
size_t len
) {
unsigned byterate = m_bitrate / 8u;
// === Do the padding and switch to the squeezing phase ===
// Absorb the last few bits and add the first bit of padding (which
// coincides with the delimiter in delimitedSuffix)
m_bytes[m_cursor] ^= m_suffix;
// If the first bit of padding is at position rate-1, we need a whole new
// block for the second bit of padding
if (m_suffix & 0x80 && m_cursor == byterate - 1)
permute ();
// Add the second bit of padding
m_bytes[byterate - 1] ^= 0x80;
// === Squeeze out all the output blocks ===
while (len) {
permute ();
auto chunk = util::min (len, byterate);
std::copy_n (m_bytes.begin (), chunk, output);
output += chunk;
len -= chunk;
}
}
keccak::keccak (unsigned _bitrate,
unsigned _capacity,
uint8_t _suffix):
m_bitrate (_bitrate),
m_capacity (_capacity),
m_suffix (_suffix),
m_cursor (0)
{
// we could support bitrates that are multiples of 8, but 64 simplifies
// some state handling, and the SHA-3 constants are all multiples of 64
// bits anyway.
if ((m_bitrate + m_capacity) / 8 != sizeof (m_bytes) || m_bitrate % 64 != 0)
throw "error";
std::fill (std::begin (m_bytes), std::end (m_bytes), 0);
}
#endif

View File

@ -1,53 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright 2016 Danny Robson <danny@nerdcruft.net>
*/
#ifndef __HASH_KECCAK_HPP
#define __HASH_KECCAK_HPP
#include <array>
#include <cstdint>
class keccak {
public:
keccak (unsigned bitrate, unsigned capacity, uint8_t suffix);
void update (const uint8_t *input, size_t len);
void digest (uint8_t *output, size_t len);
private:
void permute (void);
const unsigned m_bitrate;
const unsigned m_capacity;
const uint8_t m_suffix;
size_t m_cursor;
union {
std::array<uint8_t, 200> m_bytes;
uint64_t m_words[5][5];
};
};
void FIPS202_SHAKE128(const uint8_t *input, size_t inputByteLen, uint8_t *output, size_t outputByteLen);
void FIPS202_SHAKE256(const uint8_t *input, size_t inputByteLen, uint8_t *output, size_t outputByteLen);
void FIPS202_SHA3_224(const uint8_t *input, size_t inputByteLen, uint8_t *output);
void FIPS202_SHA3_256(const uint8_t *input, size_t inputByteLen, uint8_t *output);
void FIPS202_SHA3_384(const uint8_t *input, size_t inputByteLen, uint8_t *output);
void FIPS202_SHA3_512(const uint8_t *input, size_t inputByteLen, uint8_t *output);
#endif

View File

@ -27,14 +27,9 @@
///////////////////////////////////////////////////////////////////////////////
using util::hash::MD2;
using std::array;
using std::begin;
using std::end;
using std::fill;
///////////////////////////////////////////////////////////////////////////////
static array<uint8_t,256> S = { {
static constexpr std::array<uint8_t,256> S = { {
41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6, 19,
98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188, 76, 130, 202,
30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24, 138, 23, 229, 18,
@ -54,88 +49,16 @@ static array<uint8_t,256> S = { {
} };
///////////////////////////////////////////////////////////////////////////////
MD2::MD2 ()
{
reset ();
}
//-----------------------------------------------------------------------------
void
MD2::reset (void)
{
m_total = 0;
fill (begin (C), end (C), 0);
fill (begin (X), end (X), 0);
}
///////////////////////////////////////////////////////////////////////////////
void
MD2::update (const uint8_t *restrict first, const uint8_t *restrict last) noexcept
{
CHECK_LE (first, last);
update (first, last - first);
}
//-----------------------------------------------------------------------------
static const size_t M_OFFSET = 16;
static const size_t M_LENGTH = 16;
//-----------------------------------------------------------------------------
void
MD2::update (const void *restrict _data, size_t size) noexcept
{
auto data = static_cast<const uint8_t *restrict> (_data);
uint8_t *M = X + M_OFFSET;
size_t offset = m_total % M_LENGTH;
size_t remain = M_LENGTH - offset;
if (size > remain) {
memcpy (M + offset, data, remain);
transform ();
m_total += remain;
size -= remain;
data += remain;
while (size >= M_LENGTH) {
memcpy (M, data, M_LENGTH);
transform ();
m_total += M_LENGTH;
size -= M_LENGTH;
data += M_LENGTH;
}
offset = 0;
}
memcpy (M + offset, data, size);
m_total += size;
}
///////////////////////////////////////////////////////////////////////////////
MD2::digest_t
MD2::digest (void) const
static void
transform (std::array<uint8_t,16> &C, std::array<uint8_t,48> &X) noexcept
{
digest_t d;
memcpy (d.data (), X, sizeof (d));
return d;
}
///////////////////////////////////////////////////////////////////////////////
void
MD2::transform (void)
{
uint8_t *M = X + M_OFFSET;
util::view M { X.data () + M_OFFSET, M_LENGTH };
// Update the checksum.
// XXX: I can't see why we need the xor-assign from the spec, but it's the
@ -145,16 +68,15 @@ MD2::transform (void)
for (size_t i = 0; i < std::size (C); ++i)
L = C[i] ^= S[M[i] ^ L];
uint8_t t = 0;
// Setup the blocks
for (size_t i = 0; i < 16; ++i)
X[32 + i] = X[16 + i] ^ X[i];
X[32 + i] = X[16 + i] ^ X[i];
// Perform the processing rounds
for (size_t i = 0; i < 18; ++i) {
for (size_t i = 0, t = 0; i < 18; ++i) {
for (size_t j = 0; j < 48; ++j)
t = X[j] = X[j] ^ S[t];
t = X[j] ^= S[t];
t = (t + i) % 256;
}
@ -162,21 +84,40 @@ MD2::transform (void)
///////////////////////////////////////////////////////////////////////////////
void
MD2::finish (void)
MD2::digest_t
MD2::operator() (const util::view<const uint8_t*> data) const noexcept
{
uint8_t *M = X + M_OFFSET;
// zero initialise the state vectors, and create a simple window `M' into
// the middle of the `X' state vector.
std::array<uint8_t,16> C {};
std::array<uint8_t,48> X {};
const util::view M { std::begin (X) + M_OFFSET, M_LENGTH };
// Append the padding bytes
size_t offset = m_total % M_LENGTH;
size_t remain = M_LENGTH - offset;
// process each complete block by copying to the window `M' and
// transforming X and C.
//
// leave the remainder of the data in `M' for subsequent padding.
auto remain = data;
while (remain.size () >= M_LENGTH) {
std::copy_n (std::begin (remain), M_LENGTH, std::begin (M));
transform (C, X);
for (size_t i = 0; i < remain; ++i)
M[offset + i] = remain;
transform ();
remain = { remain.begin () + M_LENGTH, remain.end () };
};
// Append the checksum
memcpy (M, C, sizeof (C));
transform ();
// Copying the remaining data then append the padding bytes. Padding
// _must_ be performed even if we have an evenly divisible input buffer.
auto tail = std::copy (remain.begin (), remain.end (), std::begin (M));
auto unused = std::distance (tail, std::end (M));
std::fill (tail, std::end (M), unused);
transform (C, X);
// Append the checksum and transform once more.
std::copy (std::begin (C), std::end (C), std::begin (M));
transform (C, X);
// The final digest is the first `n' bytes of the state vector.
digest_t d;
std::copy_n (std::begin (X), d.size (), std::begin (d));
return d;
}

View File

@ -17,6 +17,8 @@
#ifndef __UTIL_HASH_MD2_HPP
#define __UTIL_HASH_MD2_HPP
#include "../view.hpp"
#include <array>
#include <cstdint>
@ -24,26 +26,11 @@
///////////////////////////////////////////////////////////////////////////////
namespace util::hash {
class MD2 {
public:
typedef std::array<uint8_t,16> digest_t;
public:
typedef std::array<uint8_t,16> digest_t;
public:
MD2 ();
void update (const void *restrict data, size_t len) noexcept;
void update (const uint8_t *restrict first, const uint8_t *restrict last) noexcept;
void finish (void);
digest_t digest (void) const;
void reset (void);
private:
void transform (void);
uint64_t m_total;
uint8_t C[16];
uint8_t X[48];
public:
digest_t operator() (util::view<const uint8_t*>) const noexcept;
};
}

View File

@ -27,7 +27,7 @@ using util::hash::MD4;
///////////////////////////////////////////////////////////////////////////////
// Auxiliary functions for each set of rounds
static constexpr
static inline constexpr
uint32_t
F (uint32_t X, uint32_t Y, uint32_t Z)
{
@ -36,7 +36,7 @@ F (uint32_t X, uint32_t Y, uint32_t Z)
//-----------------------------------------------------------------------------
static constexpr
static inline constexpr
uint32_t
G (uint32_t X, uint32_t Y, uint32_t Z)
{
@ -45,7 +45,7 @@ G (uint32_t X, uint32_t Y, uint32_t Z)
//-----------------------------------------------------------------------------
static constexpr
static inline constexpr
uint32_t
H (uint32_t X, uint32_t Y, uint32_t Z)
{
@ -54,105 +54,24 @@ H (uint32_t X, uint32_t Y, uint32_t Z)
//-----------------------------------------------------------------------------
static constexpr uint32_t DEFAULT_A = 0x67452301;
static constexpr uint32_t DEFAULT_B = 0xefcdab89;
static constexpr uint32_t DEFAULT_C = 0x98badcfe;
static constexpr uint32_t DEFAULT_D = 0x10325476;
static constexpr uint32_t INITIAL_A = 0x67452301;
static constexpr uint32_t INITIAL_B = 0xefcdab89;
static constexpr uint32_t INITIAL_C = 0x98badcfe;
static constexpr uint32_t INITIAL_D = 0x10325476;
///////////////////////////////////////////////////////////////////////////////
MD4::MD4 ()
{
reset ();
static_assert (sizeof (MD4::X) == sizeof (MD4::Xb),
"Byte and word buffer size must match exactly");
static_assert (sizeof (MD4::ABCD) == sizeof (MD4::digest_t),
"Internal state must match the size of the digest");
}
//-----------------------------------------------------------------------------
void
MD4::reset (void)
{
m_total = 0;
ABCD[0] = DEFAULT_A;
ABCD[1] = DEFAULT_B;
ABCD[2] = DEFAULT_C;
ABCD[3] = DEFAULT_D;
memset (Xb, 0, sizeof (Xb));
}
///////////////////////////////////////////////////////////////////////////////
void
MD4::update (const uint8_t *restrict first, const uint8_t *restrict last) noexcept
{
CHECK_LE (first, last);
update (first, last - first);
}
//-----------------------------------------------------------------------------
void
MD4::update (const void *restrict _data, size_t size) noexcept
{
CHECK (_data);
auto data = static_cast<const uint8_t *restrict> (_data);
size_t offset = m_total % sizeof (Xb);
size_t remain = sizeof (Xb) - offset;
if (size > remain) {
memcpy (Xb + offset, data, remain);
transform ();
m_total += remain;
size -= remain;
data += remain;
while (size >= sizeof (Xb)) {
memcpy (Xb, data, sizeof (Xb));
transform ();
m_total += sizeof (Xb);
size -= sizeof (Xb);
data += sizeof (Xb);
}
offset = 0;
}
memcpy (Xb + offset, data, size);
m_total += size;
}
///////////////////////////////////////////////////////////////////////////////
MD4::digest_t
MD4::digest (void) const
{
digest_t d;
memcpy (d.data (), ABCD.data(), sizeof (ABCD));
return d;
}
///////////////////////////////////////////////////////////////////////////////
void
MD4::transform (void)
static void
transform (std::array<uint32_t,4> &ABCD, const std::array<uint32_t,16> &X) noexcept
{
uint32_t A = ABCD[0],
B = ABCD[1],
C = ABCD[2],
D = ABCD[3];
B = ABCD[1],
C = ABCD[2],
D = ABCD[3];
#define ROUND1(a,b,c,d,k,s) do { \
#define ROUND1(a,b,c,d,k,s) do { \
(a) += F((b), (c), (d)) + X[k]; \
(a) = rotatel ((a), (s)); \
(a) = util::rotatel ((a), (s)); \
} while (0)
ROUND1(A,B,C,D, 0, 3);
@ -175,9 +94,9 @@ MD4::transform (void)
ROUND1(C,D,A,B, 14, 11);
ROUND1(B,C,D,A, 15, 19);
#define ROUND2(a,b,c,d,k,s) do { \
#define ROUND2(a,b,c,d,k,s) do { \
(a) += G((b),(c),(d)) + X[k] + 0x5A827999u; \
(a) = rotatel ((a), (s)); \
(a) = util::rotatel ((a), (s)); \
} while (0)
ROUND2(A,B,C,D, 0, 3);
@ -200,9 +119,9 @@ MD4::transform (void)
ROUND2(C,D,A,B, 11, 9);
ROUND2(B,C,D,A, 15, 13);
#define ROUND3(a,b,c,d,k,s) do { \
#define ROUND3(a,b,c,d,k,s) do { \
(a) += H((b),(c),(d)) + X[k] + 0x6ED9EBA1u; \
(a) = rotatel ((a), (s)); \
(a) = util::rotatel ((a), (s)); \
} while (0)
ROUND3(A,B,C,D, 0, 3);
@ -233,30 +152,64 @@ MD4::transform (void)
///////////////////////////////////////////////////////////////////////////////
void
MD4::finish (void)
MD4::digest_t
MD4::operator() (util::view<const uint8_t*> data) noexcept
{
uint64_t bits = m_total * 8;
/* RESET */
uint64_t total = 0;
std::array<uint32_t,4> ABCD {
INITIAL_A,
INITIAL_B,
INITIAL_C,
INITIAL_D,
};
union {
std::array<uint32_t,16> X;
std::array<uint8_t, 64> Xb;
};
static_assert (sizeof (X) == sizeof (Xb));
static_assert (sizeof (ABCD) == sizeof (digest_t));
std::fill (std::begin (X), std::end (X), 0);
/* UPDATE */
{
auto remain = data;
while (remain.size () >= sizeof (Xb)) {
std::copy_n (std::begin (remain), sizeof (Xb), std::begin (Xb));
transform (ABCD, X);
remain = { remain.begin () + sizeof (Xb), remain.end () };
total += sizeof (Xb);
}
std::copy (std::begin (remain), std::end (remain), std::begin (Xb));
total += remain.size ();
}
uint64_t bits = total * 8;
/* FINISH */
{
// Pad with the mandatory 1 bit
size_t offset = m_total % sizeof (Xb);
size_t offset = total % sizeof (Xb);
Xb[offset] = 0x80;
}
{
// Pad the remainder with 0's, until 56 bytes
size_t offset = (m_total + 1) % sizeof (Xb);
size_t offset = (total + 1) % sizeof (Xb);
size_t remain = (56 - offset % sizeof (Xb)) % sizeof (Xb);
if (offset > 56) {
memset (Xb + offset, 0, sizeof (Xb) - offset);
transform ();
std::fill_n (std::begin (Xb) + offset, sizeof (Xb) - offset, 0);
transform (ABCD, X);
remain -= sizeof (Xb) - offset;
offset = 0;
}
memset (Xb + offset, 0, remain);
std::fill (std::begin (Xb) + offset, std::end (Xb), 0);
// Put in the length (in bits) least significant first
for (size_t i = 0; i < sizeof (bits); ++i) {
@ -264,6 +217,11 @@ MD4::finish (void)
bits >>= 8;
}
transform ();
transform (ABCD, X);
}
/* DIGEEST */
digest_t d;
memcpy (d.data (), ABCD.data(), sizeof (ABCD));
return d;
}

View File

@ -17,6 +17,8 @@
#ifndef __UTIL_HASH_MD4_HPP
#define __UTIL_HASH_MD4_HPP
#include "view.hpp"
#include <array>
#include <cstdint>
@ -25,28 +27,9 @@
namespace util::hash {
class MD4 {
public:
typedef std::array<uint8_t,16> digest_t;
using digest_t = std::array<uint8_t,16>;
public:
MD4();
void update (const void *restrict data, size_t len) noexcept;
void update (const uint8_t *restrict first, const uint8_t *restrict last) noexcept;
void finish (void);
digest_t digest (void) const;
void reset (void);
private:
void transform (void);
uint64_t m_total;
std::array<uint32_t,4> ABCD;
union {
uint32_t X [16];
uint8_t Xb[64];
};
digest_t operator() (util::view<const uint8_t*>) noexcept;
};
}

View File

@ -16,6 +16,7 @@
#include "md5.hpp"
#include "../iterator.hpp"
#include "../bitwise.hpp"
#include "../debug.hpp"
@ -95,131 +96,107 @@ const std::array<uint32_t, 65> T = { {
//-----------------------------------------------------------------------------
static const uint32_t DEFAULT_A = 0x67452301;
static const uint32_t DEFAULT_B = 0xefcdab89;
static const uint32_t DEFAULT_C = 0x98badcfe;
static const uint32_t DEFAULT_D = 0x10325476;
static constexpr uint32_t INITIAL_A = 0x67452301;
static constexpr uint32_t INITIAL_B = 0xefcdab89;
static constexpr uint32_t INITIAL_C = 0x98badcfe;
static constexpr uint32_t INITIAL_D = 0x10325476;
///////////////////////////////////////////////////////////////////////////////
MD5::MD5()
typename MD5::digest_t
MD5::operator() (util::view<const uint8_t*> a) const noexcept
{
reset ();
return (*this)(a, nullptr);
}
//-----------------------------------------------------------------------------
void
MD5::reset (void)
typename MD5::digest_t
MD5::operator() (util::view<const uint8_t*> a, util::view<const uint8_t*> b) const noexcept
{
m_total = 0;
ABCD[0] = DEFAULT_A;
ABCD[1] = DEFAULT_B;
ABCD[2] = DEFAULT_C;
ABCD[3] = DEFAULT_D;
return (*this)(a, b, nullptr);
}
///////////////////////////////////////////////////////////////////////////////
void
MD5::update (const uint8_t *restrict first, const uint8_t *restrict last) noexcept
{
CHECK_LE (first, last);
typename MD5::digest_t
MD5::operator() (
const util::view<const uint8_t*> data_a,
const util::view<const uint8_t*> data_b,
const util::view<const uint8_t*> data_c
) const noexcept {
union {
std::array<uint32_t,16> w;
std::array<uint8_t, 64> b;
} X;
update (first, last - first);
}
static_assert (sizeof (X.w) == BLOCK_SIZE);
static_assert (X.w.size () == DIGEST_SIZE);
static_assert (sizeof (X.b) == BLOCK_SIZE);
static_assert (X.b.size () == BLOCK_SIZE);
//-----------------------------------------------------------------------------
void
MD5::update (const void *restrict _data, size_t size) noexcept
{
CHECK (_data);
auto data = static_cast<const uint8_t *restrict> (_data);
static_assert ((void*)&X.w == (void*)&X.b);
size_t offset = m_total % sizeof (Xb);
size_t remain = sizeof (Xb) - offset;
uint64_t m_total = 0;
std::array<uint32_t,4> ABCD = {
INITIAL_A,
INITIAL_B,
INITIAL_C,
INITIAL_D
};
if (size >= remain) {
memcpy (Xb + offset, data, remain);
transform ();
// note we pass in a windowed view of the state block, not the whole
// thing.
util::transform_by_block (
util::view {X.b},
[&,this] (auto) { ABCD = transform (ABCD, X.w); },
data_a, data_b, data_c
);
m_total = data_a.size () + data_b.size () + data_c.size ();
m_total += remain;
size -= remain;
data += remain;
uint64_t bits = m_total * 8;
while (size >= sizeof (Xb)) {
memcpy (Xb, data, sizeof (Xb));
transform ();
// Pad with the mandatory 1 bit
X.b[m_total % BLOCK_SIZE] = 0x80;
m_total += sizeof (Xb);
size -= sizeof (Xb);
data += sizeof (Xb);
{
// Pad the remainder with 0's, until 56 bytes
size_t offset = (m_total + 1) % BLOCK_SIZE;
size_t remain = (56 - offset % BLOCK_SIZE) % BLOCK_SIZE;
if (offset > 56) {
std::fill (&X.b[offset], X.b.end (), 0);
ABCD = transform (ABCD, X.w);
remain -= sizeof (X.b) - offset;
offset = 0;
}
offset = 0;
memset (&X.b[offset], 0, remain);
// Put in the length (in bits) least significant first
for (size_t i = 0; i < sizeof (bits); ++i) {
X.b[56 + i] = bits & 0xFF;
bits >>= 8;
}
ABCD = transform (ABCD, X.w);
}
memcpy (Xb + offset, data, size);
m_total += size;
}
///////////////////////////////////////////////////////////////////////////////
MD5::digest_t
MD5::digest (void) const
{
static_assert (sizeof (ABCD) == sizeof (digest_t),
"Hash state must be the same size as the final digest");
digest_t d;
memcpy (d.data (), ABCD.data (), sizeof (ABCD));
return d;
}
};
///////////////////////////////////////////////////////////////////////////////
void
MD5::finish (void)
std::array<uint32_t,4>
MD5::transform (
const std::array<uint32_t,4> &ABCD,
const std::array<uint32_t,16> &X) const noexcept
{
uint64_t bits = m_total * 8;
{
// Pad with the mandatory 1 bit
size_t offset = m_total % sizeof (Xb);
Xb[offset] = 0x80;
}
{
// Pad the remainder with 0's, until 56 bytes
size_t offset = (m_total + 1) % sizeof (Xb);
size_t remain = (56 - offset % sizeof (Xb)) % sizeof (Xb);
if (offset > 56) {
memset (Xb + offset, 0, sizeof (Xb) - offset);
transform ();
remain -= sizeof (Xb) - offset;
offset = 0;
}
memset (Xb + offset, 0, remain);
// Put in the length (in bits) least significant first
for (size_t i = 0; i < sizeof (bits); ++i) {
Xb[56 + i] = bits & 0xFF;
bits >>= 8;
}
transform ();
}
}
///////////////////////////////////////////////////////////////////////////////
void
MD5::transform (void)
{
uint32_t A = ABCD[0],
B = ABCD[1],
C = ABCD[2],
@ -315,8 +292,10 @@ MD5::transform (void)
ROUNDx(C,D,A,B, 2, 15, 63, I);
ROUNDx(B,C,D,A, 9, 21, 64, I);
ABCD[0] += A;
ABCD[1] += B;
ABCD[2] += C;
ABCD[3] += D;
return {
ABCD[0] + A,
ABCD[1] + B,
ABCD[2] + C,
ABCD[3] + D,
};
}

View File

@ -17,6 +17,8 @@
#ifndef __UTIL_HASH_MD5_HPP
#define __UTIL_HASH_MD5_HPP
#include "../view.hpp"
#include <array>
#include <cstdint>
#include <cstdlib>
@ -26,30 +28,21 @@
namespace util::hash {
class MD5 {
public:
typedef std::array<uint8_t,16> digest_t;
using digest_t = std::array<uint8_t,16>;
static const size_t BLOCK_SIZE = 64;
static const size_t DIGEST_SIZE = 16;
public:
MD5();
void update (const void *restrict data, size_t len) noexcept;
void update (const uint8_t *restrict first, const uint8_t *restrict last) noexcept;
void finish (void);
digest_t digest (void) const;
void reset (void);
digest_t operator() (util::view<const uint8_t*>) const noexcept;
digest_t operator() (util::view<const uint8_t*>,util::view<const uint8_t*>) const noexcept;
digest_t operator() (util::view<const uint8_t*>,util::view<const uint8_t*>,util::view<const uint8_t*>) const noexcept;
private:
void transform (void);
uint64_t m_total;
std::array<uint32_t, 4> ABCD;
union {
uint32_t X [16];
uint8_t Xb[64];
};
std::array<uint32_t,4>
transform (
const std::array<uint32_t,4> &ABCD,
const std::array<uint32_t,16> &X
) const noexcept;
};
}

View File

@ -19,40 +19,25 @@
#include "common.hpp"
#include "../../debug.hpp"
//-----------------------------------------------------------------------------
using util::hash::murmur1;
///////////////////////////////////////////////////////////////////////////////
uint32_t
util::hash::murmur1::mix (uint32_t h, uint32_t k)
murmur1::operator() (util::view<const uint8_t*> data) const noexcept
{
static const uint32_t m = 0xc6a4a793;
h += k;
h *= m;
h ^= h >> 16;
return h;
}
//-----------------------------------------------------------------------------
uint32_t
util::hash::murmur1::hash_32 (const void *restrict data,
size_t len,
uint32_t seed)
{
CHECK (data);
static const uint32_t m = 0xc6a4a793;
uint32_t h = seed ^ ((len & 0xffffffff) * m);
uint32_t h = m_seed ^ ((data.size () & 0xffffffff) * m);
// mix the body
auto cursor = reinterpret_cast<const uint32_t*> (data);
auto last = cursor + len / sizeof (uint32_t);
auto cursor = reinterpret_cast<const uint32_t*> (data.data ());
auto last = cursor + data.size () / sizeof (uint32_t);
for (; cursor < last; ++cursor)
h = mix (h, *cursor);
// mix the tail
if (len % sizeof (uint32_t))
h = mix (h, murmur::tail<uint32_t> (reinterpret_cast<const uint8_t*> (cursor), len));
if (data.size () % sizeof (uint32_t))
h = mix (h, murmur::tail<uint32_t> (reinterpret_cast<const uint8_t*> (cursor), data.size ()));
// finalise
h *= m; h ^= h >> 10;

View File

@ -17,13 +17,38 @@
#ifndef __UTIL_HASH_MURMUR_MURMUR1_HPP
#define __UTIL_HASH_MURMUR_MURMUR1_HPP
#include "../../view.hpp"
#include <cstdint>
#include <cstddef>
// Austin Appleby's MumurHash1
namespace util::hash::murmur1 {
uint32_t mix (uint32_t, uint32_t);
uint32_t hash_32 (const void *restrict data, size_t len, uint32_t seed);
namespace util::hash {
class murmur1 {
public:
using digest_t = uint32_t;
using seed_t = uint32_t;
murmur1 (seed_t _seed):
m_seed (_seed)
{ ; }
static constexpr uint32_t mix (uint32_t h, uint32_t k)
{
constexpr uint32_t m = 0xc6a4a793;
h += k;
h *= m;
h ^= h >> 16;
return h;
}
digest_t operator() (util::view<const uint8_t*> data) const noexcept;
private:
seed_t m_seed;
};
}
#endif

View File

@ -20,28 +20,28 @@
#include "../../debug.hpp"
#include "common.hpp"
using util::hash::murmur2;
///////////////////////////////////////////////////////////////////////////////
uint32_t
util::hash::murmur2::hash_32 (const void *restrict key,
size_t len,
uint32_t seed)
static uint32_t
hash_32 (const void *restrict key,
size_t len,
uint32_t seed)
{
CHECK (key);
// setup
constexpr auto m = detail::constants<uint32_t>::m;
constexpr auto m = util::hash::detail::murmur2::constants<uint32_t>::m;
uint32_t h = seed ^ (len & 0xffffffff);
// body
auto cursor = reinterpret_cast<const uint32_t*> (key);
auto last = cursor + len / sizeof (uint32_t);
for (; cursor < last; ++cursor)
h = mix (h, *cursor);
h = util::hash::murmur2<uint32_t>::mix (h, *cursor);
// tail
if (len % sizeof (uint32_t)) {
h ^= murmur::tail<uint32_t> (reinterpret_cast<const uint8_t*> (cursor), len);
h ^= util::hash::murmur::tail<uint32_t> (reinterpret_cast<const uint8_t*> (cursor), len);
h *= m;
}
@ -55,14 +55,14 @@ util::hash::murmur2::hash_32 (const void *restrict key,
//-----------------------------------------------------------------------------
uint64_t
util::hash::murmur2::hash_64 (const void *restrict key,
size_t len,
uint64_t seed)
static uint64_t
hash_64 (const void *restrict key,
size_t len,
uint64_t seed)
{
// setup
constexpr auto m = detail::constants<uint64_t>::m;
constexpr auto r = detail::constants<uint64_t>::r;
constexpr auto m = util::hash::detail::murmur2::constants<uint64_t>::m;
constexpr auto r = util::hash::detail::murmur2::constants<uint64_t>::r;
uint64_t h = seed ^ (len * m);
@ -70,11 +70,11 @@ util::hash::murmur2::hash_64 (const void *restrict key,
auto cursor = reinterpret_cast<const uint64_t*> (key);
auto last = cursor + len / sizeof (uint64_t);
for (; cursor < last; ++cursor)
h = mix (h, *cursor);
h = util::hash::murmur2<uint64_t>::mix (h, *cursor);
// tail
if (len % sizeof (uint64_t)) {
h ^= murmur::tail<uint64_t> (reinterpret_cast<const uint8_t*> (cursor), len);
h ^= util::hash::murmur::tail<uint64_t> (reinterpret_cast<const uint8_t*> (cursor), len);
h *= m;
}
@ -87,3 +87,23 @@ util::hash::murmur2::hash_64 (const void *restrict key,
}
///////////////////////////////////////////////////////////////////////////////
template <typename DigestT>
typename murmur2<DigestT>::digest_t
murmur2<DigestT>::operator() (util::view<const uint8_t*> data) const noexcept
{
static_assert (std::is_same_v<DigestT,uint32_t> || std::is_same_v<DigestT,uint64_t>);
if constexpr (std::is_same_v<DigestT,uint32_t>)
return hash_32 (data.data (), data.size (), m_seed);
if constexpr (std::is_same_v<DigestT,uint64_t>)
return hash_64 (data.data (), data.size (), m_seed);
unreachable ();
}
///////////////////////////////////////////////////////////////////////////////
template class util::hash::murmur2<uint32_t>;
template class util::hash::murmur2<uint64_t>;

View File

@ -17,21 +17,77 @@
#ifndef __UTIL_HASH_MURMUR_MURMUR2_HPP
#define __UTIL_HASH_MURMUR_MURMUR2_HPP
#include "../../view.hpp"
#include <cstddef>
#include <cstdint>
// Austin Appleby's MumurHash2, and MurmurHash64A. The exhaustive list of
// variants is deliberately not provided. You can damn well align your data or
// fix the algorithm.
namespace util::hash::murmur2 {
constexpr uint32_t mix (uint32_t, uint32_t);
constexpr uint64_t mix (uint64_t, uint64_t);
namespace util::hash {
namespace detail::murmur2 {
template <typename T> struct constants;
uint32_t hash_32 (const void *restrict data, size_t len, uint32_t seed);
uint64_t hash_64 (const void *restrict data, size_t len, uint64_t seed);
template <>
struct constants<uint32_t> {
static const uint32_t m = 0x5bd1e995;
static const int32_t r = 24;
};
template <>
struct constants<uint64_t> {
static const uint64_t m = 0xc6a4a7935bd1e995;
static const int64_t r = 47;
};
}
template <typename DigestT>
class murmur2 {
public:
using digest_t = DigestT;
using seed_t = DigestT;
murmur2 (seed_t _seed):
m_seed (_seed)
{ ; }
static constexpr uint32_t mix (uint32_t h, uint32_t k)
{
constexpr uint32_t m = detail::murmur2::constants<uint32_t>::m;
constexpr uint32_t r = detail::murmur2::constants<uint32_t>::r;
k *= m;
k ^= k >> r;
k *= m;
h *= m;
h ^= k;
return h;
}
static constexpr uint64_t mix (uint64_t h, uint64_t k)
{
constexpr uint64_t m = detail::murmur2::constants<uint64_t>::m;
constexpr uint64_t r = detail::murmur2::constants<uint64_t>::r;
k *= m;
k ^= k >> r;
k *= m;
h ^= k;
h *= m;
return h;
}
digest_t operator() (util::view<const uint8_t*> data) const noexcept;
private:
seed_t m_seed;
};
}
#include "murmur2.ipp"
#endif

View File

@ -1,78 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright 2016 Danny Robson <danny@nerdcruft.net>
*/
#if defined(__UTIL_HASH_MURMUR_MURMUR2_IPP)
#error
#endif
#define __UTIL_HASH_MURMUR_MURMUR2_IPP
///////////////////////////////////////////////////////////////////////////////
namespace util::hash::murmur2::detail {
template <typename T> struct constants;
template <>
struct constants<uint32_t> {
static const uint32_t m = 0x5bd1e995;
static const int32_t r = 24;
};
template <>
struct constants<uint64_t> {
static const uint64_t m = 0xc6a4a7935bd1e995;
static const int64_t r = 47;
};
}
/////////////////////////////////////////////////////////////////////
constexpr uint32_t
util::hash::murmur2::mix (uint32_t h, uint32_t k)
{
constexpr uint32_t m = detail::constants<uint32_t>::m;
constexpr uint32_t r = detail::constants<uint32_t>::r;
k *= m;
k ^= k >> r;
k *= m;
h *= m;
h ^= k;
return h;
}
//-----------------------------------------------------------------------------
// 64 bit murmur2 mixing function. Note the last two lines are swapped
// compared with 32 bit murmur2_mix. It's not clear if this is deliberate
// in the canonical implementation, so we just leave it to help compatibility.
constexpr uint64_t
util::hash::murmur2::mix (uint64_t h, uint64_t k)
{
constexpr uint64_t m = detail::constants<uint64_t>::m;
constexpr uint64_t r = detail::constants<uint64_t>::r;
k *= m;
k ^= k >> r;
k *= m;
h ^= k;
h *= m;
return h;
}

View File

@ -21,6 +21,8 @@
#include <algorithm>
using util::hash::murmur3;
///////////////////////////////////////////////////////////////////////////////
static
@ -36,8 +38,9 @@ read_u32 (const uint8_t *bytes)
///////////////////////////////////////////////////////////////////////////////
// Finalization mix - force all bits of a hash block to avalanche
template <size_t DigestBits, size_t ArchBits>
uint32_t
util::hash::murmur3::mix (uint32_t h)
murmur3<DigestBits,ArchBits>::mix (uint32_t h)
{
h ^= h >> 16;
h *= 0x85ebca6b;
@ -50,8 +53,9 @@ util::hash::murmur3::mix (uint32_t h)
//-----------------------------------------------------------------------------
template <size_t DigestBits, size_t ArchBits>
uint64_t
util::hash::murmur3::mix (uint64_t k)
murmur3<DigestBits,ArchBits>::mix (uint64_t k)
{
k ^= k >> 33;
k *= 0xff51afd7ed558ccd;
@ -63,56 +67,60 @@ util::hash::murmur3::mix (uint64_t k)
}
///////////////////////////////////////////////////////////////////////////////
template <size_t DigestBits, size_t ArchBits>
struct hash { };
//-----------------------------------------------------------------------------
uint32_t
util::hash::murmur3::hash_32(const void *restrict key,
size_t len,
uint32_t seed)
{
auto data = reinterpret_cast<const uint8_t*> (key);
auto nblocks = len / sizeof (uint32_t);
template <size_t ArchBits>
struct hash<32,ArchBits> {
static auto eval (util::view<const uint8_t*> data, uint32_t seed)
{
auto nblocks = data.size () / sizeof (uint32_t);
uint32_t h1 = seed;
uint32_t h1 = seed;
static const uint32_t c1 = 0xcc9e2d51;
static const uint32_t c2 = 0x1b873593;
static const uint32_t c1 = 0xcc9e2d51;
static const uint32_t c2 = 0x1b873593;
//----------
// body
auto cursor = data;
auto last = cursor + nblocks * sizeof (uint32_t);
for (; cursor < last; cursor += sizeof (uint32_t)) {
uint32_t k1 = read_u32 (cursor);
//----------
// body
auto cursor = data.begin ();
auto last = cursor + nblocks * sizeof (uint32_t);
for (; cursor < last; cursor += sizeof (uint32_t)) {
uint32_t k1 = read_u32 (cursor);
k1 *= c1;
k1 = util::rotatel (k1, 15);
k1 *= c2;
h1 ^= k1;
k1 *= c1;
k1 = util::rotatel (k1, 15);
k1 *= c2;
h1 ^= k1;
h1 = util::rotatel (h1, 13);
h1 += 0;
h1 = h1 * 5 + 0xe6546b64;
h1 = util::rotatel (h1, 13);
h1 += 0;
h1 = h1 * 5 + 0xe6546b64;
}
//----------
// tail
if (data.size () % sizeof (uint32_t)) {
uint32_t k1 = 0 ^ util::hash::murmur::tail<uint32_t> (cursor, data.size ());
k1 *= c1;
k1 = util::rotatel (k1, 15);
k1 *= c2;
h1 ^= k1;
}
//----------
// finalization
h1 ^= data.size ();
h1 = util::hash::murmur3<32,ArchBits>::mix (h1);
return h1;
}
//----------
// tail
if (len % sizeof (uint32_t)) {
uint32_t k1 = 0 ^ murmur::tail<uint32_t> (cursor, len);
k1 *= c1;
k1 = util::rotatel (k1, 15);
k1 *= c2;
h1 ^= k1;
}
//----------
// finalization
h1 ^= len;
h1 = mix (h1);
return h1;
}
};
///////////////////////////////////////////////////////////////////////////////
@ -237,7 +245,7 @@ hash_128 (const void *restrict key,
for (size_t i = 1; i < traits<T>::COMPONENTS; ++i) h[i] += h[0];
for (auto &v: h)
v = util::hash::murmur3::mix (v);
v = util::hash::murmur3<128,sizeof(T)*8>::mix (v);
for (size_t i = 1; i < traits<T>::COMPONENTS; ++i) h[0] += h[i];
for (size_t i = 1; i < traits<T>::COMPONENTS; ++i) h[i] += h[0];
@ -246,21 +254,33 @@ hash_128 (const void *restrict key,
}
///////////////////////////////////////////////////////////////////////////////
std::array<uint32_t,4>
util::hash::murmur3::hash_128_x86 (const void *restrict key,
const size_t len,
const uint32_t seed)
{
return ::hash_128<uint32_t> (key, len, seed);
}
template <>
struct hash<128,32> {
static auto eval (util::view<const uint8_t*> data, uint32_t seed)
{
return ::hash_128<uint32_t> (data.data (), data.size (), seed);
}
};
template <>
struct hash<128,64> {
static auto eval (util::view<const uint8_t*> data, uint32_t seed)
{
return ::hash_128<uint64_t> (data.data (), data.size (), seed);
}
};
//-----------------------------------------------------------------------------
std::array<uint64_t,2>
util::hash::murmur3::hash_128_x64 (const void *restrict key,
size_t len,
const uint32_t seed)
template <size_t DigestBits, size_t ArchBits>
typename murmur3<DigestBits,ArchBits>::digest_t
murmur3<DigestBits,ArchBits>::operator() (util::view<const uint8_t*> data) const noexcept
{
return ::hash_128<uint64_t> (key, len, seed);
return ::hash<DigestBits,ArchBits>::eval (data, m_seed);
}
///////////////////////////////////////////////////////////////////////////////
template class util::hash::murmur3<32, 32>;
template class util::hash::murmur3<128,32>;
template class util::hash::murmur3<128,64>;

View File

@ -17,18 +17,47 @@
#ifndef __UTIL_HASH_MURMUR_MURMUR3_HPP
#define __UTIL_HASH_MURMUR_MURMUR3_HPP
#include "../../view.hpp"
#include <array>
#include <cstddef>
#include <cstdint>
// Austin Appleby's MurmurHash3
namespace util::hash::murmur3 {
uint32_t mix (uint32_t);
uint64_t mix (uint64_t);
namespace util::hash {
namespace detail::murmur3 {
template <size_t DigestBits, size_t ArchBits>
struct digest_type { };
uint32_t hash_32 (const void *restrict data, size_t len, uint32_t seed);
std::array<uint32_t,4> hash_128_x86 (const void *restrict data, size_t len, uint32_t seed);
std::array<uint64_t,2> hash_128_x64 (const void *restrict data, size_t len, uint32_t seed);
template <> struct digest_type< 32,32> { using type = uint32_t; };
template <> struct digest_type<128,32> { using type = std::array<uint32_t,4>; };
template <> struct digest_type<128,64> { using type = std::array<uint64_t,2>; };
};
template <size_t DigestBits, size_t ArchBits>
class murmur3 {
public:
murmur3 (uint32_t _seed):
m_seed (_seed)
{ ; }
static_assert (DigestBits % 8 == 0);
using digest_t = typename detail::murmur3::digest_type<DigestBits,ArchBits>::type;
static uint32_t mix (uint32_t);
static uint64_t mix (uint64_t);
digest_t
operator() (util::view<const uint8_t*> data) const noexcept;
private:
uint32_t m_seed;
};
using murmur3_32 = murmur3< 32,32>;
using murmur3_128_x86 = murmur3<128,32>;
using murmur3_128_x64 = murmur3<128,64>;
}
#endif

View File

@ -1,17 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright 2013 Danny Robson <danny@nerdcruft.net>
*/
#include "pbkdf1.hpp"

View File

@ -1,28 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright 2013 Danny Robson <danny@nerdcruft.net>
*/
#ifndef __UTIL_HASH_PBKDF1_HPP
#define __UTIL_HASH_PBKDF1_HPP
namespace util {
namespace hash {
class PBKDF1 {
};
}
}
#endif

View File

@ -1,17 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright 2013 Danny Robson <danny@nerdcruft.net>
*/
#include "pbkdf2.hpp"

View File

@ -1,28 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright 2013 Danny Robson <danny@nerdcruft.net>
*/
#ifndef __UTIL_HASH_PBKDF2_HPP
#define __UTIL_HASH_PBKDF2_HPP
namespace util {
namespace hash {
class PBKDF2 {
};
}
}
#endif

View File

@ -12,7 +12,7 @@
* limitations under the License.
*
* Copyright:
* 2014, Danny Robson <danny@nerdcruft.net>
* 2014-2018, Danny Robson <danny@nerdcruft.net>
*/
#include "ripemd.hpp"
@ -25,70 +25,6 @@
using util::hash::RIPEMD;
///////////////////////////////////////////////////////////////////////////////
RIPEMD::RIPEMD()
{
reset ();
}
//-----------------------------------------------------------------------------
void
RIPEMD::reset (void)
{
m_state[0] = 0x67452301u;
m_state[1] = 0xEFCDAB89u;
m_state[2] = 0x98BADCFEu;
m_state[3] = 0x10325476u;
m_state[4] = 0xC3D2E1F0u;
m_length = 0;
m_buffer.size = 0;
}
///////////////////////////////////////////////////////////////////////////////
void
RIPEMD::update (
const uint8_t *restrict first,
const uint8_t *restrict last) noexcept
{
CHECK_LE (first, last);
update (first, last - first);
}
//-----------------------------------------------------------------------------
void
RIPEMD::update (const void *restrict _data, size_t len) noexcept
{
CHECK (_data);
auto data = static_cast<const uint8_t *restrict> (_data);
size_t cursor = 0;
while (cursor < len) {
size_t width = sizeof (m_buffer.d08) - m_buffer.size;
size_t chunk = min (width, len - cursor);
memcpy (m_buffer.d08 + m_buffer.size, data + cursor, chunk);
m_length += chunk;
m_buffer.size += chunk;
if (m_buffer.size == sizeof (m_buffer.d08))
transform ();
cursor += chunk;
}
if (m_length >> sizeof (m_length) * 8 - 3 != 0)
panic ("exceeded maximum message length");
}
///////////////////////////////////////////////////////////////////////////////
static constexpr
uint32_t
@ -135,11 +71,9 @@ f5 (uint32_t x, uint32_t y, uint32_t z)
///////////////////////////////////////////////////////////////////////////////
void
RIPEMD::transform (void)
static void
transform (uint32_t state[5], const uint32_t d32[16])
{
CHECK_EQ (m_buffer.size, sizeof (m_buffer.d32));
// Use: boolean function f
// state parameters a, b, c, d, e
// offset value o
@ -148,10 +82,10 @@ RIPEMD::transform (void)
#define ROUND(f,a,b,c,d,e,o,x,s) { \
a += f(b, c, d); \
a += m_buffer.d32[x] + o; \
a = rotatel (a, s); \
a += d32[x] + o; \
a = util::rotatel (a, s); \
a += e; \
c = rotatel (c, 10); \
c = util::rotatel (c, 10); \
}
#define R1a(a,b,c,d,e,x,s) ROUND(f1,a,b,c,d,e,0x00000000u,x,s)
@ -169,11 +103,11 @@ RIPEMD::transform (void)
uint32_t a1, b1, c1, d1, e1;
uint32_t a2, b2, c2, d2, e2;
a1 = a2 = m_state[0];
b1 = b2 = m_state[1];
c1 = c2 = m_state[2];
d1 = d2 = m_state[3];
e1 = e2 = m_state[4];
a1 = a2 = state[0];
b1 = b2 = state[1];
c1 = c2 = state[2];
d1 = d2 = state[3];
e1 = e2 = state[4];
// Left round 1
R1a(a1, b1, c1, d1, e1, 0, 11);
@ -356,61 +290,119 @@ RIPEMD::transform (void)
R5b(b2, c2, d2, e2, a2, 11 , 11);
// Finalise state
d2 = m_state[1] + c1 + d2;
m_state[1] = m_state[2] + d1 + e2;
m_state[2] = m_state[3] + e1 + a2;
m_state[3] = m_state[4] + a1 + b2;
m_state[4] = m_state[0] + b1 + c2;
m_state[0] = d2;
m_buffer.size = 0;
}
///////////////////////////////////////////////////////////////////////////////
void
RIPEMD::finish (void)
{
// Ensure the length wouldn't overflow if converted to bits. We need to
// grab this before there's a chance it gets overwritten.
CHECK_EQ (m_length >> sizeof(m_length) * 8 - 3, 0u);
uint64_t length = m_length * 8;
// Push a padding byte into the buffer
uint8_t padding = 0x80;
update (&padding, 1);
// Pad out the line if we couldn't fit a length at the end
size_t remaining = sizeof (m_buffer.d32) - m_buffer.size;
if (remaining < 8) {
static const uint8_t ZEROES[8] = { 0 };
update (ZEROES, remaining);
CHECK_EQ (m_buffer.size, 0u);
remaining = sizeof (m_buffer.d08);
}
// Write the length to the end of the buffer
union {
uint32_t d32[16];
uint8_t d08[64];
};
memset (d32, 0, sizeof (d32));
d32[14] = length & 0xFFFFFFFF;
d32[15] = length >> 32u;
// Do the final update
size_t offset = sizeof(d08) - remaining;
update (d08 + offset, remaining);
d2 = state[1] + c1 + d2;
state[1] = state[2] + d1 + e2;
state[2] = state[3] + e1 + a2;
state[3] = state[4] + a1 + b2;
state[4] = state[0] + b1 + c2;
state[0] = d2;
}
///////////////////////////////////////////////////////////////////////////////
RIPEMD::digest_t
RIPEMD::digest (void) const
RIPEMD::operator() (const util::view<const uint8_t*> data)
{
struct {
union {
uint32_t d32[16];
uint8_t d08[64];
};
size_t size;
} m_buffer;
/* INIT */
uint32_t m_state[5] {
0x67452301u,
0xEFCDAB89u,
0x98BADCFEu,
0x10325476u,
0xC3D2E1F0u,
};
uint64_t m_length = 0;
m_buffer.size = 0;
/* UPDATE */
{
auto len = data.size ();
auto base = data.begin ();
size_t cursor = 0;
while (cursor < len) {
size_t width = sizeof (m_buffer.d08) - m_buffer.size;
size_t chunk = min (width, len - cursor);
memcpy (m_buffer.d08 + m_buffer.size, base + cursor, chunk);
m_length += chunk;
m_buffer.size += chunk;
if (m_buffer.size == sizeof (m_buffer.d08)) {
transform (m_state, m_buffer.d32);
m_buffer.size = 0;
}
cursor += chunk;
}
if (m_length >> sizeof (m_length) * 8 - 3 != 0)
panic ("exceeded maximum message length");
}
/* FINISH */
{
// Ensure the length wouldn't overflow if converted to bits. We need to
// grab this before there's a chance it gets overwritten.
CHECK_EQ (m_length >> sizeof(m_length) * 8 - 3, 0u);
uint64_t length = m_length * 8;
// Push a padding byte into the buffer
uint8_t padding = 0x80;
assert (m_buffer.size != sizeof (m_buffer.d08));
m_buffer.d08[m_buffer.size] = padding;
m_buffer.size++;
m_length++;
if (m_buffer.size == sizeof (m_buffer.d08)) {
transform (m_state, m_buffer.d32);
m_buffer.size = 0;
}
// Pad out the line if we couldn't fit a length at the end
size_t remaining = sizeof (m_buffer.d32) - m_buffer.size;
if (remaining < 8) {
std::fill_n (m_buffer.d08 + m_buffer.size, remaining, 0);
m_buffer.size += remaining;
transform (m_state, m_buffer.d32);
m_buffer.size = 0;
m_length += remaining;
CHECK_EQ (m_buffer.size, 0u);
remaining = sizeof (m_buffer.d08);
}
// Write the length to the end of the buffer
union {
uint32_t d32[16];
uint8_t d08[64];
};
std::fill (std::begin (d32), std::end (d32), 0);
d32[14] = length & 0xFFFFFFFF;
d32[15] = length >> 32u;
// Do the final update
size_t offset = sizeof(d08) - remaining;
std::copy_n (d08 + offset, remaining, m_buffer.d08 + offset);
m_length += remaining;
m_buffer.size += remaining;
transform (m_state, m_buffer.d32);
m_buffer.size = 0;
}
/* DIGEST */
digest_t d;
memcpy (d.data (), m_state, sizeof (m_state));
return d;

View File

@ -12,11 +12,13 @@
* limitations under the License.
*
* Copyright:
* 2014, Danny Robson <danny@nerdcruft.net>
* 2014-2018, Danny Robson <danny@nerdcruft.net>
*/
#ifndef __UTIL_HASH_RIPEMD_HPP
#define __UTIL_HASH_RIPEMD_HPP
#ifndef CRUFT_UTIL_HASH_RIPEMD_HPP
#define CRUFT_UTIL_HASH_RIPEMD_HPP
#include "../view.hpp"
#include <array>
@ -24,34 +26,10 @@
///////////////////////////////////////////////////////////////////////////////
namespace util::hash {
class RIPEMD {
public:
typedef std::array<uint8_t,20> digest_t;
public:
typedef std::array<uint8_t,20> digest_t;
public:
RIPEMD();
void update (const void *restrict, size_t) noexcept;
void update (const uint8_t *restrict first, const uint8_t *restrict last) noexcept;
digest_t digest (void) const;
void finish (void);
void reset (void);
protected:
void transform ();
bool m_finished;
uint32_t m_state[5];
uint64_t m_length;
struct {
union {
uint32_t d32[16];
uint8_t d08[64];
};
size_t size;
} m_buffer;
digest_t operator() (util::view<const uint8_t*>);
};
}

View File

@ -16,6 +16,7 @@
#include "sha1.hpp"
#include "../iterator.hpp"
#include "../bitwise.hpp"
#include "../debug.hpp"
#include "../endian.hpp"
@ -24,23 +25,33 @@
#include <algorithm>
#include <cstdint>
#include <limits>
#include <ostream>
using util::hash::SHA1;
///////////////////////////////////////////////////////////////////////////////
std::ostream&
operator<< (std::ostream &os, SHA1::state_t t)
{
switch (t) {
case SHA1::READY: return os << "READY";
case SHA1::FINISHED: return os << "FINISHED";
}
// Constant words for sequence of rounds
static constexpr uint32_t K_00 = 0x5A827999;
static constexpr uint32_t K_20 = 0x6ED9EBA1;
static constexpr uint32_t K_40 = 0x8F1BBCDC;
static constexpr uint32_t K_60 = 0xCA62C1D6;
unreachable ();
}
//-----------------------------------------------------------------------------
static constexpr
std::array<uint32_t,5> INITIAL_H = {
0x67452301,
0xEFCDAB89,
0x98BADCFE,
0x10325476,
0xC3D2E1F0
};
//-----------------------------------------------------------------------------
static constexpr size_t BLOCK_WORDS = 16;
static constexpr size_t BLOCK_BYTES = BLOCK_WORDS * sizeof (uint32_t);
///////////////////////////////////////////////////////////////////////////////
@ -81,118 +92,35 @@ f_60 (uint32_t B, uint32_t C, uint32_t D)
///////////////////////////////////////////////////////////////////////////////
// Constant words for sequence of rounds
static constexpr uint32_t K_00 = 0x5A827999;
static constexpr uint32_t K_20 = 0x6ED9EBA1;
static constexpr uint32_t K_40 = 0x8F1BBCDC;
static constexpr uint32_t K_60 = 0xCA62C1D6;
static
constexpr uint32_t DEFAULT_H[] = {
0x67452301,
0xEFCDAB89,
0x98BADCFE,
0x10325476,
0xC3D2E1F0
};
static constexpr size_t BLOCK_WORDS = 16;
static constexpr size_t BLOCK_BYTES = BLOCK_WORDS * sizeof (uint32_t);
///////////////////////////////////////////////////////////////////////////////
SHA1::SHA1()
static void
process (std::array<uint32_t,5> &H, std::array<uint32_t,16+64> &W)
{
reset ();
}
//-----------------------------------------------------------------------------
void
SHA1::reset (void)
{
total = 0;
state = READY;
std::copy (std::begin (DEFAULT_H), std::end (DEFAULT_H), H);
}
///////////////////////////////////////////////////////////////////////////////
void
SHA1::update (
const uint8_t *restrict first,
const uint8_t *restrict last) noexcept
{
CHECK_LE (first, last);
update (first, last - first);
}
//-----------------------------------------------------------------------------
void
SHA1::update (const void *restrict _data, size_t size) noexcept
{
CHECK (_data);
CHECK_EQ (+state, +READY);
CHECK_GE (std::numeric_limits<decltype(total)>::max () - total, size);
auto data = static_cast<const uint8_t *restrict> (_data);
while (size > 0) {
// Copy the data into the remaining available buffer slots
const size_t offset = total % BLOCK_BYTES;
const size_t chunk = std::min (BLOCK_BYTES - offset, size);
std::copy (data, data + chunk, c + offset);
total += chunk;
// Attempt to process if full
if (total % BLOCK_BYTES == 0)
process ();
size -= chunk;
data += chunk;
}
}
///////////////////////////////////////////////////////////////////////////////
void
SHA1::process (void)
{
CHECK_EQ (total % BLOCK_BYTES, 0u);
// Byteswap the raw input we have buffered ready for arithmetic
std::transform (std::begin (W),
std::end (W),
std::begin (W),
[] (uint32_t x) {
return ntoh (x);
});
return util::ntoh (x);
});
// Initialise the work buffer and the state variables
for (size_t t = 16; t < 80; ++t)
W[t] = rotatel (W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16], 1);
W[t] = util::rotatel (W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16], 1);
uint32_t A = H[0],
B = H[1],
C = H[2],
D = H[3],
E = H[4];
B = H[1],
C = H[2],
D = H[3],
E = H[4];
// Perform each of the four rounds
#define ROTATE_STATE(i) do { \
uint32_t temp = rotatel (A, 5) + f_##i (B, C, D) + E + W[t] + K_##i; \
E = D; \
D = C; \
C = rotatel (B, 30); \
B = A; \
A = temp; \
#define ROTATE_STATE(i) do { \
uint32_t temp = util::rotatel (A, 5) + f_##i (B, C, D) + E + W[t] + K_##i; \
E = D; \
D = C; \
C = util::rotatel (B, 30); \
B = A; \
A = temp; \
} while (0)
for (size_t t = 0; t < 20; ++t) ROTATE_STATE(00);
@ -210,72 +138,80 @@ SHA1::process (void)
///////////////////////////////////////////////////////////////////////////////
void
SHA1::finish (void)
SHA1::digest_t
SHA1::operator() (util::view<const uint8_t*> data) noexcept
{
size_t offset = total % BLOCK_BYTES;
size_t used = total * 8;
return (*this) (data, nullptr);
}
// Append a single one bit
c[offset++] = 0x80;
total += 1;
// Zero fill if we can't append length
size_t chunk = BLOCK_BYTES - offset;
if (chunk < sizeof (total)) {
std::fill_n (c + offset, chunk, 0);
//-----------------------------------------------------------------------------
SHA1::digest_t
SHA1::operator() (
util::view<const uint8_t*> data_a,
util::view<const uint8_t*> data_b
) noexcept {
/* RESET */
uint64_t total = 0;
union {
std::array<uint32_t,5> w;
std::array<uint8_t,20> b;
} H;
union {
std::array<uint8_t, 16*4+64*4> c;
std::array<uint32_t, 16 +64 > W;
} state;
H.w = INITIAL_H;
/* UPDATE */
transform_by_block (
util::view {state.c.data (), BLOCK_BYTES },
[&] (auto) { process (H.w, state.W); },
data_a, data_b
);
total += data_a.size () + data_b.size ();
/* FINISH */
{
size_t offset = total % BLOCK_BYTES;
size_t used = total * 8;
// Append a single one bit
state.c[offset++] = 0x80;
total += 1;
// Zero fill if we can't append length
size_t chunk = BLOCK_BYTES - offset;
if (chunk < sizeof (total)) {
std::fill_n (std::begin (state.c) + offset, chunk, 0);
total += chunk;
process (H.w, state.W);
chunk = BLOCK_BYTES;
offset = 0;
}
// Zero fill and append total length
std::fill_n (std::begin (state.c) + offset, chunk - sizeof (total), 0);
state.c[BLOCK_BYTES - 1] = used & 0xFF; used >>= 8;
state.c[BLOCK_BYTES - 2] = used & 0xFF; used >>= 8;
state.c[BLOCK_BYTES - 3] = used & 0xFF; used >>= 8;
state.c[BLOCK_BYTES - 4] = used & 0xFF; used >>= 8;
state.c[BLOCK_BYTES - 5] = used & 0xFF; used >>= 8;
state.c[BLOCK_BYTES - 6] = used & 0xFF; used >>= 8;
state.c[BLOCK_BYTES - 7] = used & 0xFF; used >>= 8;
state.c[BLOCK_BYTES - 8] = used & 0xFF;
total += chunk;
process ();
chunk = BLOCK_BYTES;
offset = 0;
process (H.w, state.W);
}
// Zero fill and append total length
std::fill_n (c + offset, chunk - sizeof (total), 0);
c[BLOCK_BYTES - 1] = used & 0xFF; used >>= 8;
c[BLOCK_BYTES - 2] = used & 0xFF; used >>= 8;
c[BLOCK_BYTES - 3] = used & 0xFF; used >>= 8;
c[BLOCK_BYTES - 4] = used & 0xFF; used >>= 8;
c[BLOCK_BYTES - 5] = used & 0xFF; used >>= 8;
c[BLOCK_BYTES - 6] = used & 0xFF; used >>= 8;
c[BLOCK_BYTES - 7] = used & 0xFF; used >>= 8;
c[BLOCK_BYTES - 8] = used & 0xFF;
total += chunk;
process ();
state = FINISHED;
}
///////////////////////////////////////////////////////////////////////////////
SHA1::digest_t
SHA1::digest (void) const
{
CHECK_EQ (+state, +FINISHED);
return { {
trunc_cast<uint8_t> ((H[0] >> 24u) & 0xFF),
trunc_cast<uint8_t> ((H[0] >> 16u) & 0xFF),
trunc_cast<uint8_t> ((H[0] >> 8u) & 0xFF),
trunc_cast<uint8_t> ((H[0] ) & 0xFF),
trunc_cast<uint8_t> ((H[1] >> 24u) & 0xFF),
trunc_cast<uint8_t> ((H[1] >> 16u) & 0xFF),
trunc_cast<uint8_t> ((H[1] >> 8u) & 0xFF),
trunc_cast<uint8_t> ((H[1] ) & 0xFF),
trunc_cast<uint8_t> ((H[2] >> 24u) & 0xFF),
trunc_cast<uint8_t> ((H[2] >> 16u) & 0xFF),
trunc_cast<uint8_t> ((H[2] >> 8u) & 0xFF),
trunc_cast<uint8_t> ((H[2] ) & 0xFF),
trunc_cast<uint8_t> ((H[3] >> 24u) & 0xFF),
trunc_cast<uint8_t> ((H[3] >> 16u) & 0xFF),
trunc_cast<uint8_t> ((H[3] >> 8u) & 0xFF),
trunc_cast<uint8_t> ((H[3] ) & 0xFF),
trunc_cast<uint8_t> ((H[4] >> 24u) & 0xFF),
trunc_cast<uint8_t> ((H[4] >> 16u) & 0xFF),
trunc_cast<uint8_t> ((H[4] >> 8u) & 0xFF),
trunc_cast<uint8_t> ((H[4] ) & 0xFF)
} };
/* DIGEST */
std::transform (std::begin (H.w), std::end (H.w), std::begin (H.w), util::hton<uint32_t>);
return H.b;
}

View File

@ -11,53 +11,33 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright 2013 Danny Robson <danny@nerdcruft.net>
* Copyright 2013-2018 Danny Robson <danny@nerdcruft.net>
*/
#ifndef __UTIL_HASH_SHA1_HPP
#define __UTIL_HASH_SHA1_HPP
#ifndef CRUFT_UTIL_HASH_SHA1_HPP
#define CRUFT_UTIL_HASH_SHA1_HPP
#include <cstdint>
#include <cstdlib>
#include "../view.hpp"
#include <array>
#include <cstdint>
#include <cstdlib>
///////////////////////////////////////////////////////////////////////////////
namespace util::hash {
class SHA1 {
public:
typedef std::array<uint8_t,20> digest_t;
using digest_t = std::array<uint8_t,20>;
static const size_t BLOCK_SIZE = 64;
static const size_t DIGEST_SIZE = 20;
public:
SHA1();
void update (const void *restrict, size_t) noexcept;
void update (const uint8_t *restrict first, const uint8_t *restrict last) noexcept;
//template <typename ...DataT>
//digest_t
//operator() (DataT&&...);
void finish (void);
digest_t digest (void) const;
void reset (void);
enum state_t {
READY,
FINISHED
};
protected:
void process (void);
state_t state;
uint64_t total;
uint32_t H[5];
union {
uint8_t c[16*4+64*4];
uint32_t W[16 +64 ];
};
digest_t operator() (util::view<const uint8_t*>, util::view<const uint8_t*>) noexcept;
digest_t operator() (util::view<const uint8_t*>) noexcept;
};
}

View File

@ -182,87 +182,86 @@ H_512[] = {
///////////////////////////////////////////////////////////////////////////////
SHA256::SHA256 ():
m_total (0)
SHA256::digest_t
SHA256::operator() (util::view<const uint8_t*> data) noexcept
{
(void)K_80;
(void)H_224;
(void)H_384;
(void)H_512;
/* INIT */
m_total = 0;
std::copy (std::begin (H_256), std::end (H_256), std::begin (H));
}
/* UPDATE */
{
auto cursor = data.begin ();
auto length = data.size ();
//-----------------------------------------------------------------------------
void
SHA256::update (const uint8_t *restrict first, const uint8_t *restrict last) noexcept
{
CHECK_LE (first, last);
while (length) {
size_t buffered = m_total % sizeof (M);
size_t chunk = std::min (sizeof (M) - buffered, length);
std::copy (cursor, cursor + chunk, C.begin () + buffered);
update (first, last - first);
}
length -= chunk;
m_total += chunk;
//-----------------------------------------------------------------------------
void
SHA256::update (const void *restrict _data, size_t length) noexcept
{
CHECK (_data);
auto data = static_cast<const uint8_t *restrict> (_data);
while (length) {
size_t buffered = m_total % sizeof (M);
size_t chunk = std::min (sizeof (M) - buffered, length);
std::copy (data, data + chunk, C.begin () + buffered);
length -= chunk;
m_total += chunk;
if (m_total % sizeof (M) == 0)
process ();
if (m_total % sizeof (M) == 0)
process ();
}
}
}
/* FINISH */
{
// Append a single 1 bit followed by 0s.
auto buffered = m_total % sizeof (M);
auto used = m_total * 8u;
///////////////////////////////////////////////////////////////////////////////
void
SHA256::finish (void)
{
// Append a single 1 bit followed by 0s.
auto buffered = m_total % sizeof (M);
auto used = m_total * 8u;
C[buffered++] = 0x80;
++m_total;
C[buffered++] = 0x80;
++m_total;
// Pad out to 56 byte length
if (buffered > 56) {
size_t chunk = sizeof (M) - buffered;
std::fill_n (C.begin () + buffered, chunk, 0);
m_total += chunk;
process ();
// Pad out to 56 byte length
if (buffered > 56) {
size_t chunk = sizeof (M) - buffered;
buffered = 0;
}
size_t chunk = sizeof (M) - sizeof (uint64_t) - buffered;
std::fill_n (C.begin () + buffered, chunk, 0);
m_total += chunk;
process ();
buffered = 0;
// Finish with the m_total size
C[56] = (used >> 56) & 0xFF;
C[57] = (used >> 48) & 0xFF;
C[58] = (used >> 40) & 0xFF;
C[59] = (used >> 32) & 0xFF;
C[60] = (used >> 24) & 0xFF;
C[61] = (used >> 16) & 0xFF;
C[62] = (used >> 8) & 0xFF;
C[63] = (used >> 0) & 0xFF;
m_total += 8;
// Reprocess
process ();
}
size_t chunk = sizeof (M) - sizeof (uint64_t) - buffered;
std::fill_n (C.begin () + buffered, chunk, 0);
m_total += chunk;
/* DIGEST */
digest_t out;
// Finish with the m_total size
C[56] = (used >> 56) & 0xFF;
C[57] = (used >> 48) & 0xFF;
C[58] = (used >> 40) & 0xFF;
C[59] = (used >> 32) & 0xFF;
C[60] = (used >> 24) & 0xFF;
C[61] = (used >> 16) & 0xFF;
C[62] = (used >> 8) & 0xFF;
C[63] = (used >> 0) & 0xFF;
m_total += 8;
auto cursor = out.begin ();
for (auto i: H) {
*cursor++ = (i >> 24) & 0xFF;
*cursor++ = (i >> 16) & 0xFF;
*cursor++ = (i >> 8) & 0xFF;
*cursor++ = (i >> 0) & 0xFF;
}
// Reprocess
process ();
return out;
}
@ -312,21 +311,3 @@ SHA256::process (void)
H[6] += g;
H[7] += h;
}
///////////////////////////////////////////////////////////////////////////////
SHA256::digest_t
SHA256::digest (void) const
{
digest_t out;
auto cursor = out.begin ();
for (auto i: H) {
*cursor++ = (i >> 24) & 0xFF;
*cursor++ = (i >> 16) & 0xFF;
*cursor++ = (i >> 8) & 0xFF;
*cursor++ = (i >> 0) & 0xFF;
}
return out;
}

View File

@ -18,6 +18,8 @@
#ifndef __UTIL_HASH_SHA2_HPP
#define __UTIL_HASH_SHA2_HPP
#include "view.hpp"
#include <array>
#include <cstdint>
@ -29,15 +31,7 @@ namespace util::hash {
typedef std::array<uint8_t,32> digest_t;
public:
SHA256();
void update (const void *restrict, size_t) noexcept;
void update (const uint8_t *restrict first, const uint8_t *restrict last) noexcept;
void finish (void);
digest_t digest (void) const;
void reset (void);
digest_t operator() (util::view<const uint8_t*>) noexcept;
private:
void process (void);

View File

@ -1,17 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright 2016, Danny Robson <danny@nerdcruft.net>
*/
#include "simple.hpp"

View File

@ -1,43 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright 2016, Danny Robson <danny@nerdcruft.net>
*/
#ifndef __UTIL_HASH_SIMPLE_HPP
#define __UTIL_HASH_SIMPLE_HPP
#include <cstdint>
#include <utility>
///////////////////////////////////////////////////////////////////////////////
namespace util { namespace hash {
//template <class H, class InputT>
template <class H, typename ...Args>
typename H::digest_t
simple (const void *restrict first, const void *restrict last, Args&&...args) noexcept
{
H h (std::forward<Args> (args)...);
h.update (
static_cast<const uint8_t*restrict> (first),
static_cast<const uint8_t*restrict> (last)
);
h.finish ();
return h.digest ();
}
} }
#endif

View File

@ -21,10 +21,35 @@
namespace util::hash {
// Thomas Wang's integer mixing functions, ca 2007
constexpr uint32_t wang (uint32_t);
constexpr uint64_t wang (uint64_t);
//-------------------------------------------------------------------------
constexpr uint32_t
util::hash::wang (uint32_t key) {
// a prime or an odd constant
constexpr uint32_t c2 = 0x27d4eb2d;
key = (key ^ 61) ^ (key >> 16);
key = key + (key << 3);
key = key ^ (key >> 4);
key = key * c2;
key = key ^ (key >> 15);
return key;
}
//-------------------------------------------------------------------------
constexpr uint64_t
util::hash::wang (uint64_t key) {
key = ~key + (key << 21); // key = (key << 21) - key - 1;
key = key ^ (key >> 24);
key = (key + (key << 3)) + (key << 8); // key * 265
key = key ^ (key >> 14);
key = (key + (key << 2)) + (key << 4); // key * 21
key = key ^ (key >> 28);
key = key + (key << 31);
return key;
}
}
#include "wang.ipp"
#endif

View File

@ -1,51 +0,0 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright 2010-2015 Danny Robson <danny@nerdcruft.net>
*/
#ifdef __UTIL_HASH_WANG_IPP
#error
#endif
#define __UTIL_HASH_WANG_IPP
//-----------------------------------------------------------------------------
constexpr uint32_t
util::hash::wang (uint32_t key) {
// a prime or an odd constant
constexpr uint32_t c2 = 0x27d4eb2d;
key = (key ^ 61) ^ (key >> 16);
key = key + (key << 3);
key = key ^ (key >> 4);
key = key * c2;
key = key ^ (key >> 15);
return key;
}
//-----------------------------------------------------------------------------
constexpr uint64_t
util::hash::wang (uint64_t key) {
key = ~key + (key << 21); // key = (key << 21) - key - 1;
key = key ^ (key >> 24);
key = (key + (key << 3)) + (key << 8); // key * 265
key = key ^ (key >> 14);
key = (key + (key << 2)) + (key << 4); // key * 21
key = key ^ (key >> 28);
key = key + (key << 31);
return key;
}

View File

@ -78,40 +78,6 @@ const uint64_t
constants<uint64_t>::round_rotl = 31;
///////////////////////////////////////////////////////////////////////////////
constexpr uint32_t DEFAULT_SEED = 0;
//-----------------------------------------------------------------------------
template <typename T>
xxhash<T>::xxhash (void):
xxhash (DEFAULT_SEED)
{ ; }
//-----------------------------------------------------------------------------
template <typename T>
xxhash<T>::xxhash (uint32_t _seed):
m_seed (_seed)
{
reset ();
}
//-----------------------------------------------------------------------------
template <typename T>
void
xxhash<T>::reset (void)
{
memset (&m_state, 0, sizeof (m_state));
m_state.v1 = m_seed + constants<T>::prime[0] + constants<T>::prime[1];
m_state.v2 = m_seed + constants<T>::prime[1];
m_state.v3 = m_seed;
m_state.v4 = m_seed - constants<T>::prime[0];
}
///////////////////////////////////////////////////////////////////////////////
template <typename T>
static
@ -128,117 +94,142 @@ round (T seed, T input)
///////////////////////////////////////////////////////////////////////////////
template <typename T>
void
xxhash<T>::update (const uint8_t *restrict first, const uint8_t *restrict last)
{
CHECK (first);
CHECK (last);
CHECK_LE (first, last);
//auto endian = XXH_littleEndian;
size_t len = last - first;
auto input = (const void*)first;
auto p = reinterpret_cast<const uint8_t*> (input);
auto const bEnd = p + len;
constexpr auto CHUNK = 4 * sizeof (T);
m_state.total_len_32 += (unsigned)len;
m_state.large_len |= (len >= CHUNK) | (m_state.total_len_32 >= CHUNK);
if (m_state.memsize + len < CHUNK) { /* fill in tmp buffer */
memcpy ((uint8_t*)(m_state.mem32) + m_state.memsize, input, len);
m_state.memsize += (unsigned)len;
return;
}
if (m_state.memsize) { /* some data left from previous update */
memcpy ((uint8_t*)(m_state.mem32) + m_state.memsize, input, CHUNK - m_state.memsize);
{ const uint32_t* p32 = m_state.mem32;
m_state.v1 = round<T> (m_state.v1, ltoh (*p32)); p32++;
m_state.v2 = round<T> (m_state.v2, ltoh (*p32)); p32++;
m_state.v3 = round<T> (m_state.v3, ltoh (*p32)); p32++;
m_state.v4 = round<T> (m_state.v4, ltoh (*p32)); p32++;
}
p += CHUNK - m_state.memsize;
m_state.memsize = 0;
}
if (p <= bEnd - CHUNK * sizeof (T)) {
const uint8_t* const limit = bEnd - 4 * sizeof (T);
T v1 = m_state.v1;
T v2 = m_state.v2;
T v3 = m_state.v3;
T v4 = m_state.v4;
do {
v1 = round<T> (v1, read_le<T> (p)); p += sizeof (T);
v2 = round<T> (v2, read_le<T> (p)); p += sizeof (T);
v3 = round<T> (v3, read_le<T> (p)); p += sizeof (T);
v4 = round<T> (v4, read_le<T> (p)); p += sizeof (T);
} while (p <= limit);
m_state.v1 = v1;
m_state.v2 = v2;
m_state.v3 = v3;
m_state.v4 = v4;
}
if (p < bEnd) {
memcpy (m_state.mem32, p, (size_t)(bEnd-p));
m_state.memsize = (unsigned)(bEnd-p);
}
}
///////////////////////////////////////////////////////////////////////////////
template <typename T>
void
xxhash<T>::finish (void)
{
;
}
xxhash<T>::xxhash (uint32_t _seed):
m_seed (_seed)
{ ; }
///////////////////////////////////////////////////////////////////////////////
template <typename T>
typename xxhash<T>::digest_t
xxhash<T>::digest (void) const
xxhash<T>::operator() (const util::view<const uint8_t*> data)
{
auto p = reinterpret_cast<const uint8_t*> (m_state.mem32);
auto last = p + m_state.memsize;
struct {
uint32_t total_len_32;
uint32_t large_len;
T h;
T v1, v2, v3, v4;
uint32_t mem32[4];
uint32_t memsize;
uint32_t reserved;
if (m_state.large_len) {
h = rotatel (m_state.v1, T{ 1}) +
rotatel (m_state.v2, T{ 7}) +
rotatel (m_state.v3, T{12}) +
rotatel (m_state.v4, T{18});
} else {
h = m_state.v3 /* == seed */ + constants<T>::prime[4];
//uint64_t length;
//T v[4];
//T mem[4];
//unsigned memsize;
} m_state;
/* RESET */
memset (&m_state, 0, sizeof (m_state));
m_state.v1 = m_seed + constants<T>::prime[0] + constants<T>::prime[1];
m_state.v2 = m_seed + constants<T>::prime[1];
m_state.v3 = m_seed;
m_state.v4 = m_seed - constants<T>::prime[0];
/* UPDATE */
do {
auto first = data.begin ();
auto last = data.end ();
if (first == last)
break;
CHECK (first);
CHECK (last);
CHECK_LE (first, last);
//auto endian = XXH_littleEndian;
size_t len = last - first;
auto input = (const void*)first;
auto p = reinterpret_cast<const uint8_t*> (input);
auto const bEnd = p + len;
constexpr auto CHUNK = 4 * sizeof (T);
m_state.total_len_32 += (unsigned)len;
m_state.large_len |= (len >= CHUNK) | (m_state.total_len_32 >= CHUNK);
if (m_state.memsize + len < CHUNK) { /* fill in tmp buffer */
memcpy ((uint8_t*)(m_state.mem32) + m_state.memsize, input, len);
m_state.memsize += (unsigned)len;
break;
}
if (m_state.memsize) { /* some data left from previous update */
memcpy ((uint8_t*)(m_state.mem32) + m_state.memsize, input, CHUNK - m_state.memsize);
{ const uint32_t* p32 = m_state.mem32;
m_state.v1 = round<T> (m_state.v1, ltoh (*p32)); p32++;
m_state.v2 = round<T> (m_state.v2, ltoh (*p32)); p32++;
m_state.v3 = round<T> (m_state.v3, ltoh (*p32)); p32++;
m_state.v4 = round<T> (m_state.v4, ltoh (*p32)); p32++;
}
p += CHUNK - m_state.memsize;
m_state.memsize = 0;
}
if (p <= bEnd - CHUNK * sizeof (T)) {
const uint8_t* const limit = bEnd - 4 * sizeof (T);
T v1 = m_state.v1;
T v2 = m_state.v2;
T v3 = m_state.v3;
T v4 = m_state.v4;
do {
v1 = round<T> (v1, read_le<T> (p)); p += sizeof (T);
v2 = round<T> (v2, read_le<T> (p)); p += sizeof (T);
v3 = round<T> (v3, read_le<T> (p)); p += sizeof (T);
v4 = round<T> (v4, read_le<T> (p)); p += sizeof (T);
} while (p <= limit);
m_state.v1 = v1;
m_state.v2 = v2;
m_state.v3 = v3;
m_state.v4 = v4;
}
if (p < bEnd) {
memcpy (m_state.mem32, p, (size_t)(bEnd-p));
m_state.memsize = (unsigned)(bEnd-p);
}
} while (0);
/* DIGEST */
{
auto p = reinterpret_cast<const uint8_t*> (m_state.mem32);
auto last = p + m_state.memsize;
T h;
if (m_state.large_len) {
h = rotatel (m_state.v1, T{ 1}) +
rotatel (m_state.v2, T{ 7}) +
rotatel (m_state.v3, T{12}) +
rotatel (m_state.v4, T{18});
} else {
h = m_state.v3 /* == seed */ + constants<T>::prime[4];
}
h += m_state.total_len_32;
while (p + sizeof (T) <= last) {
h += read_le<T> (p) * constants<T>::prime[2];
h = rotatel (h, 17) * constants<T>::prime[3];
p += 4;
}
while (p < last) {
h += (*p) * constants<T>::prime[4];
h = rotatel (h, 11) * constants<T>::prime[0];
p++;
}
h ^= h >> 15; h *= constants<T>::prime[1];
h ^= h >> 13; h *= constants<T>::prime[2];
h ^= h >> 16;
return h;
}
h += m_state.total_len_32;
while (p + sizeof (T) <= last) {
h += read_le<T> (p) * constants<T>::prime[2];
h = rotatel (h, 17) * constants<T>::prime[3];
p += 4;
}
while (p < last) {
h += (*p) * constants<T>::prime[4];
h = rotatel (h, 11) * constants<T>::prime[0];
p++;
}
h ^= h >> 15; h *= constants<T>::prime[1];
h ^= h >> 13; h *= constants<T>::prime[2];
h ^= h >> 16;
return h;
}

View File

@ -17,6 +17,8 @@
#ifndef CRUFT_UTIL_HASH_XXHASH_HPP
#define CRUFT_UTIL_HASH_XXHASH_HPP
#include "../view.hpp"
#include <cstdint>
#include <type_traits>
@ -27,33 +29,14 @@ namespace util::hash {
static_assert (std::is_same<T,uint32_t>::value || std::is_same<T,uint64_t>::value);
using digest_t = T;
xxhash (void);
xxhash (uint32_t seed);
static constexpr uint32_t DEFAULT_SEED = 0;
void update (const uint8_t *restrict first, const uint8_t *restrict last);
void finish (void);
digest_t digest (void) const;
xxhash (uint32_t seed = DEFAULT_SEED);
void reset (void);
digest_t operator() (const util::view<const uint8_t*> data);
private:
uint32_t m_seed;
struct {
uint32_t total_len_32;
uint32_t large_len;
T v1, v2, v3, v4;
uint32_t mem32[4];
uint32_t memsize;
uint32_t reserved;
//uint64_t length;
//T v[4];
//T mem[4];
//unsigned memsize;
} m_state;
};
using xxhash32 = xxhash<uint32_t>;

2
io.hpp
View File

@ -90,7 +90,7 @@ namespace util {
{
auto remain = src;
while (!remain.empty ())
remain = src - dst.write (remain);
remain = remain.consume (dst.write (remain));
return src;
}

View File

@ -31,6 +31,14 @@ namespace util {
namespace detail::posix {
class mapped_file {
public:
using value_type = uint8_t;
using reference = value_type&;
using const_reference = const value_type&;
using iterator = value_type*;
using const_iterator = const value_type*;
using difference_type = std::iterator_traits<iterator>::difference_type;
using size_type = size_t;
mapped_file (const std::experimental::filesystem::path&,
int fflags = O_RDONLY | O_BINARY,
int mflags = PROT_READ);
@ -50,27 +58,19 @@ namespace util {
/// often use this in conjunction with sizeof and packed structure.
/// it is greatly simpler to cast to signed where it's actually
/// required rather than the other way around.
size_t size (void) const;
size_type size (void) const;
const uint8_t* data (void) const &;
uint8_t* data (void) &;
const_iterator data (void) const &;
iterator data (void) &;
uint8_t* begin (void) &;
uint8_t* end (void) &;
iterator begin (void) &;
iterator end (void) &;
const uint8_t* begin (void) const &;
const uint8_t* end (void) const &;
const_iterator begin (void) const &;
const_iterator end (void) const &;
const uint8_t* cbegin (void) const &;
const uint8_t* cend (void) const &;
template <typename T>
util::view<std::add_const_t<T>*>
as_view () const &;
template <typename T>
util::view<T*>
as_view () &;
const_iterator cbegin (void) const &;
const_iterator cend (void) const &;
private:
uint8_t *m_data;

View File

@ -21,27 +21,3 @@
#define __UTIL_IO_POSIX_IPP
#include "pointer.hpp"
///////////////////////////////////////////////////////////////////////////////
template <typename T>
util::view<std::add_const_t<T>*>
util::detail::posix::mapped_file::as_view (void) const&
{
return {
reinterpret_cast<const T*> (cbegin ()),
reinterpret_cast<const T*> (align (cend (), alignof (T)))
};
}
//-----------------------------------------------------------------------------
template <typename T>
util::view<T*>
util::detail::posix::mapped_file::as_view (void) &
{
return {
reinterpret_cast<T *> (begin ()),
reinterpret_cast<T *> (align (end (), alignof(T)))
};
}

View File

@ -516,6 +516,75 @@ namespace util {
) {
return false;
}
///////////////////////////////////////////////////////////////////////////
template <typename OutputIt, typename FunctionT>
OutputIt
_transform_by_block (
const util::view<OutputIt> &,
OutputIt cursor,
FunctionT &&
) {
return cursor;
}
//-------------------------------------------------------------------------
template <typename OutputIt, typename FunctionT, typename InputT, typename ...TailT>
OutputIt
_transform_by_block (
const util::view<OutputIt> &dst,
OutputIt cursor,
FunctionT &&func,
const InputT &_src,
TailT &&...tail
) {
auto remain = _src;
if (cursor != dst.begin ()) {
auto infill = std::distance (cursor, dst.end ());
if (remain.size () < static_cast<size_t> (infill)) {
return _transform_by_block (
dst,
std::copy_n (remain.begin (), remain.size (), cursor),
std::forward<FunctionT> (func),
std::forward<TailT> (tail)...
);
}
std::copy_n (remain.begin (), infill, cursor);
func (dst);
cursor = dst.begin ();
remain = { remain.begin () + infill, remain.end () };
}
while (remain.size () >= dst.size ()) {
std::copy_n (remain.begin (), dst.size (), dst.begin ());
func (dst);
remain = { remain.begin () + dst.size (), remain.end () };
}
return _transform_by_block (
dst,
std::copy (remain.begin (), remain.end (), cursor),
std::forward<FunctionT> (func),
std::forward<TailT> (tail)...
);
}
//-------------------------------------------------------------------------
template <typename OutputIt, typename FunctionT, typename ...Args>
OutputIt
transform_by_block (const util::view<OutputIt> &dst, FunctionT &&func, Args &&...src)
{
return _transform_by_block (
dst,
dst.begin (),
std::forward<FunctionT> (func),
std::forward<Args> (src)...
);
}
};
#endif

View File

@ -52,7 +52,8 @@ namespace json::flat {
};
template <typename T>
std::vector<item<T>> parse (util::view<T> data);
std::vector<item<T>>
parse (util::view<T> data);
std::ostream& operator<< (std::ostream&, type);
}

View File

@ -535,6 +535,6 @@ json::schema::validate (json::tree::node &data,
const std::experimental::filesystem::path &schema_path)
{
const util::mapped_file schema_data (schema_path);
auto schema_object = json::tree::parse (schema_data.as_view<char> ());
auto schema_object = json::tree::parse (util::view{schema_data}.cast<const char> ());
validate (data, schema_object->as_object ());
}

View File

@ -210,7 +210,7 @@ parse (typename std::vector<json::flat::item<T>>::const_iterator first,
///////////////////////////////////////////////////////////////////////////////
template <typename T>
std::unique_ptr<json::tree::node>
json::tree::parse (const util::view<T> src)
json::tree::parse (const util::view<T> &src)
{
std::unique_ptr<json::tree::node> output;
auto data = json::flat::parse (src);
@ -225,7 +225,7 @@ json::tree::parse (const util::view<T> src)
#define INSTANTIATE(KLASS) \
template \
std::unique_ptr<json::tree::node> \
json::tree::parse (util::view<KLASS>);
json::tree::parse (const util::view<KLASS>&);
MAP0(INSTANTIATE,
std::string::iterator,
@ -244,7 +244,7 @@ std::unique_ptr<json::tree::node>
json::tree::parse (const std::experimental::filesystem::path &src)
{
const util::mapped_file data (src);
return parse (data.as_view<char> ());
return parse (util::view{data}.cast<const char> ());
}
@ -443,6 +443,15 @@ namespace json::tree {
{
return static_cast<int64_t> (as_sint ());
}
//-------------------------------------------------------------------------
template <>
const std::string&
json::tree::node::as (void) const
{
return as_string ().native ();
}
#if defined(__clang__)
#elif defined(__GNUC__)
#pragma GCC diagnostic pop

View File

@ -46,7 +46,7 @@ namespace json::tree {
/// Parse an encoded form of JSON into a tree structure
template <typename T>
std::unique_ptr<node>
parse (util::view<T> data);
parse (const util::view<T> &data);
std::unique_ptr<node>
parse (const std::experimental::filesystem::path &);

View File

@ -225,7 +225,7 @@ namespace util {
typename BaseT,
typename ExponentT,
typename = std::enable_if_t<
std::is_unsigned_v<ExponentT>,
std::is_integral_v<ExponentT>,
void
>
>

View File

@ -11,27 +11,63 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright 2014 Danny Robson <danny@nerdcruft.net>
* Copyright 2014-2018 Danny Robson <danny@nerdcruft.net>
*/
#ifndef __UTIL_STRINGID_HPP
#define __UTIL_STRINGID_HPP
#ifndef CRUFT_UTIL_STRINGID_HPP
#define CRUFT_UTIL_STRINGID_HPP
#include "view.hpp"
#include <map>
#include <string>
namespace util {
class stringid {
public:
typedef size_t id_t;
public:
typedef size_t id_t;
id_t add (std::string);
id_t find (const std::string&) const;
void clear (void);
///////////////////////////////////////////////////////////////////////
id_t add (std::string);
private:
std::map<const std::string, id_t> m_map;
//---------------------------------------------------------------------
template <typename T>
id_t add (util::view<T> key)
{
return add (
std::string{
std::cbegin (key),
std::cend (key)
}
);
}
///////////////////////////////////////////////////////////////////////
id_t find (const std::string&) const;
//---------------------------------------------------------------------
template <typename T>
id_t find (util::view<T> key) const
{
return find (
std::string {
std::cbegin (key),
std::cend (key)
}
);
}
///////////////////////////////////////////////////////////////////////
void clear (void);
private:
std::map<const std::string, id_t> m_map;
};
}

View File

@ -1,7 +1,6 @@
#include "hash/adler.hpp"
#include "hash/bsdsum.hpp"
#include "hash/simple.hpp"
#include "types.hpp"
#include "tap.hpp"
@ -35,19 +34,19 @@ int
main (int, char**) {
util::TAP::logger tap;
util::hash::adler32 a;
util::hash::bsdsum b;
for (const auto &t: TESTS) {
tap.expect_eq (
t.adler,
util::hash::simple<util::hash::adler32> (
(const void*)t.data,
(const void*)(t.data + strlen (t.data))
),
a (util::view {t.data}.template cast<const uint8_t> ()),
"adler checksum: %s", t.msg
);
tap.expect_eq (
t.bsd,
util::hash::simple<util::hash::bsdsum> (t.data, t.data + strlen (t.data)),
b (util::view {t.data}.template cast<const uint8_t> ()),
"bsdsum checksum: %s", t.msg);
}

View File

@ -1,6 +1,5 @@
#include "tap.hpp"
#include "hash/crc.hpp"
#include "hash/simple.hpp"
#include <cstdint>
#include <utility>
@ -34,15 +33,8 @@ main (int, char**)
util::TAP::logger tap;
for (const auto &t: TESTS) {
auto first = t.dat;
auto last = first + strlen (t.dat);
#define TEST(KLASS) do { \
auto computed = \
util::hash::simple< \
util::hash::KLASS \
> (first, last); \
\
#define TEST(KLASS) do { \
auto computed = util::hash::KLASS{}(util::view {t.dat}.template cast<const uint8_t> ()); \
tap.expect_eq (t.result.KLASS, computed, "%s: %s", #KLASS, t.msg); \
} while (0)

View File

@ -3,6 +3,19 @@
#include <cstring>
///////////////////////////////////////////////////////////////////////////////
std::vector<uint8_t>
operator"" _u8s (const char *str, size_t len)
{
std::vector<uint8_t> res;
res.resize (len);
std::copy_n (str, len, std::begin (res));
return res;
}
///////////////////////////////////////////////////////////////////////////////
int
main (void)
{
@ -13,23 +26,26 @@ main (void)
uint32_t hash32;
uint64_t seed64;
uint64_t hash64;
const char *str;
std::vector<uint8_t> data;
} TESTS[] = {
{ 0x00000000, 0x00000000, 0x0000000000000000, 0x0000000000000000, "" },
{ 0x00000001, 0xd30ac4de, 0x0000000000000001, 0x2127599bf4321e79, "" },
{ 0xffffffff, 0xf5c7b4b0, 0xffffffffffffffff, 0x9b4792000001368f, "" },
{ 0xf5c7b4b0, 0x228128b7, 0x9b4792000001368f, 0x67a642098cc81da6, "a" },
{ 0x228128b7, 0x8400568d, 0x67a642098cc81da6, 0xc906440e03ce99a8, "abc" },
{ 0x8400568d, 0x12b4858b, 0x67a642098cc81da6, 0x1a36fbf3d71b0737, "message digest" },
{ 0x12b4858b, 0x730b822e, 0x1a36fbf3d71b0737, 0x7b48e31e3ac40a0f, "abcdefghijklmnopqrstuvwxyz" },
{ 0x00000000, 0x00000000, 0x0000000000000000, 0x0000000000000000, ""_u8s },
{ 0x00000001, 0xd30ac4de, 0x0000000000000001, 0x2127599bf4321e79, ""_u8s },
{ 0xffffffff, 0xf5c7b4b0, 0xffffffffffffffff, 0x9b4792000001368f, ""_u8s },
{ 0xf5c7b4b0, 0x228128b7, 0x9b4792000001368f, 0x67a642098cc81da6, "a"_u8s },
{ 0x228128b7, 0x8400568d, 0x67a642098cc81da6, 0xc906440e03ce99a8, "abc"_u8s },
{ 0x8400568d, 0x12b4858b, 0x67a642098cc81da6, 0x1a36fbf3d71b0737, "message digest"_u8s },
{ 0x12b4858b, 0x730b822e, 0x1a36fbf3d71b0737, 0x7b48e31e3ac40a0f, "abcdefghijklmnopqrstuvwxyz"_u8s },
};
bool success32 = true;
bool success64 = true;
util::hash::fasthash<uint32_t> h32{};
util::hash::fasthash<uint64_t> h64{};
for (const auto &t: TESTS) {
success32 = success32 && t.hash32 == util::hash::fasthash::hash32 (t.str, strlen (t.str), t.seed32);
success64 = success64 && t.hash64 == util::hash::fasthash::hash64 (t.str, strlen (t.str), t.seed64);
success32 = success32 && t.hash32 == h32 (t.seed32, t.data);
success64 = success64 && t.hash64 == h64 (t.seed64, t.data);
}
tap.expect (success32, "fasthash32");

32
test/hash/fnv1a.cpp Normal file
View File

@ -0,0 +1,32 @@
#include "hash/fnv1a.hpp"
#include "tap.hpp"
///////////////////////////////////////////////////////////////////////////////
static const struct {
uint32_t h32;
uint64_t h64;
const char *data;
} TESTS[] = {
{ 0x811c9dc5, 0xcbf29ce484222325, "" },
{ 0xe40c292c, 0xaf63dc4c8601ec8c, "a" },
{ 0xbf9cf968, 0x85944171f73967e8, "foobar" },
};
///////////////////////////////////////////////////////////////////////////////
int
main (void)
{
util::TAP::logger tap;
const util::hash::fnv1a<uint32_t> h32;
const util::hash::fnv1a<uint64_t> h64;
for (const auto &t: TESTS) {
tap.expect_eq (h32 (util::view{t.data}.cast <const uint8_t> ()), t.h32, "fnv1a32: '%s'", t.data);
tap.expect_eq (h64 (util::view{t.data}.cast <const uint8_t> ()), t.h64, "fnv1a64: '%s'", t.data);
}
return tap.status ();
}

View File

@ -28,11 +28,8 @@ test_hmac (const std::vector<uint8_t> &key,
const std::vector<uint8_t> &dat,
const std::vector<uint8_t> &res)
{
util::hash::HMAC<T> h (key.data (), key.size ());
h.update (dat.data (), dat.size ());
h.finish ();
auto ours = h.digest ();
util::hash::HMAC<T> h (key);
const auto ours = h (dat);
return std::equal (ours.begin (), ours.end (), res.begin ());
}
@ -49,7 +46,7 @@ static const struct {
std::vector<uint8_t> res;
test_func fun;
} TESTS[] = {
// RFC 2104 test data, MD5
// 1: RFC 2104 test data, MD5
{
{ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b },
@ -59,6 +56,7 @@ static const struct {
&test_hmac<util::hash::MD5>
},
// 2:
{
to_vector ("Jefe"),
to_vector ("what do ya want for nothing?"),
@ -67,6 +65,7 @@ static const struct {
&test_hmac<util::hash::MD5>
},
// 3:
{
{ 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA },
@ -82,7 +81,7 @@ static const struct {
&test_hmac<util::hash::MD5>
},
// RFC 2202 test data, MD5
// 4: RFC 2202 test data, MD5
{
{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
@ -100,6 +99,7 @@ static const struct {
&test_hmac<util::hash::MD5>
},
// 5:
{
{ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c },
@ -110,6 +110,7 @@ static const struct {
// digest-96: 0x56461ef2342edc00f9bab995
},
// 6:
{
{ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
@ -127,6 +128,7 @@ static const struct {
&test_hmac<util::hash::MD5>
},
// 7:
{
{ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
@ -144,7 +146,7 @@ static const struct {
&test_hmac<util::hash::MD5>
},
// RFC 2202 test data, SHA1
// 8: RFC 2202 test data, SHA1
{
{ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,

View File

@ -1,180 +0,0 @@
#include "hash/keccak.hpp"
#include "debug.hpp"
#include "tap.hpp"
#include <iomanip>
#include <vector>
static constexpr
uint8_t
from_hex (char c) {
switch (c) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
return c - '0';
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
return 10 + c - 'A';
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
return 10 + c - 'a';
}
unreachable ();
}
template <size_t S>
static constexpr
std::array<uint8_t,S/2>
make_array (const char (&str)[S])
{
if (S < 1)
return {};
static_assert (S % 2 == 1, "requires full bytes + null");
std::array<uint8_t,S/2> out {};
for (size_t i = 0; i < S - 1; i+=2) {
out[i / 2] = +from_hex (str[i]) << 4 | +from_hex (str[i+1]);
}
return out;
}
template <size_t S>
static
std::vector<uint8_t>
make_vector (const char (&str)[S]) {
const auto arr = make_array (str);
return std::vector<uint8_t> (arr.cbegin (), arr.cend ());
}
int
main (int, char**)
{
util::TAP::logger tap;
static const struct {
const char *msg;
std::vector<uint8_t> data;
std::array<uint8_t, 28> sha3_224;
std::array<uint8_t, 32> sha3_256;
std::array<uint8_t, 48> sha3_384;
std::array<uint8_t, 64> sha3_512;
} TESTS[] = {
{
" empty string",
{ },
make_array ("6B4E03423667DBB73B6E15454F0EB1ABD4597F9A1B078E3F5B5A6BC7"),
make_array ("A7FFC6F8BF1ED76651C14756A061D662F580FF4DE43B49FA82D80A4B80F8434A"),
make_array ("0C63A75B845E4F7D01107D852E4C2485C51A50AAAA94FC61995E71BBEE983A2AC3713831264ADB47FB6BD1E058D5F004"),
make_array ("A69F73CCA23A9AC5C8B567DC185A756E97C982164FE25859E0D1DCC1475C80A615B2123AF1F5F94C11E3E9402C3AC558F500199D95B6D3E301758586281DCD26"),
},
{
" 8-bit string",
make_vector ("CC"),
make_array ("DF70ADC49B2E76EEE3A6931B93FA41841C3AF2CDF5B32A18B5478C39"),
make_array ("677035391CD3701293D385F037BA32796252BB7CE180B00B582DD9B20AAAD7F0"),
make_array ("5EE7F374973CD4BB3DC41E3081346798497FF6E36CB9352281DFE07D07FC530CA9AD8EF7AAD56EF5D41BE83D5E543807"),
make_array ("3939FCC8B57B63612542DA31A834E5DCC36E2EE0F652AC72E02624FA2E5ADEECC7DD6BB3580224B4D6138706FC6E80597B528051230B00621CC2B22999EAA205"),
},
{
" 16-bit string",
make_vector ("41FB"),
make_array ("BFF295861DAEDF33E70519B1E2BCB4C2E9FE3364D789BC3B17301C15"),
make_array ("39F31B6E653DFCD9CAED2602FD87F61B6254F581312FB6EEEC4D7148FA2E72AA"),
make_array ("1DD81609DCC290EFFD7AC0A95D4A20821580E56BD50DBD843920650BE7A80A1719577DA337CFDF86E51C764CAA2E10BD"),
make_array ("AA092865A40694D91754DBC767B5202C546E226877147A95CB8B4C8F8709FE8CD6905256B089DA37896EA5CA19D2CD9AB94C7192FC39F7CD4D598975A3013C69"),
},
{
"224-bit string",
make_vector ("0F8B2D8FCFD9D68CFFC17CCFB117709B53D26462A3F346FB7C79B85E"),
make_array ("1E693B0BCE2372550DAEF35B14F13AB43441ED6742DEE3E86FD1D8EF"),
make_array ("6DE164A9626D5A4F54D854AC158994F35A8E362ECC753F55182790934A2E0D06"),
make_array ("641A7AF13B889D1A0F1AA3E4E4FF8CC5903C47E1A52BDEA257D80E37E596564AB33EEAD06717CDB6B706CB6986293D4F"),
make_array ("21132FC11F6040AD493D627027C752CE29816589DE7BE78562914B63D1A9219803DDBD9673AA749F37FF4D6E1B5AE2A12633BA8B0C9994E031EBF6C42E58A793"),
},
{
"256-bit string",
make_vector ("9F2FCC7C90DE090D6B87CD7E9718C1EA6CB21118FC2D5DE9F97E5DB6AC1E9C10"),
make_array ("887921848AD98458F3DB3E0ECD5AD5DB1F0BF9F2D0CA08601074D597"),
make_array ("2F1A5F7159E34EA19CDDC70EBF9B81F1A66DB40615D7EAD3CC1F1B954D82A3AF"),
make_array ("BAAE7AAED4FBF42F9316C7E8F722EEB06A598B509F184B22FBD5A81C93D95FFF711F5DE90847B3248B6DF76CABCE07EE"),
make_array ("B087C90421AEBF87911647DE9D465CBDA166B672EC47CCD4054A7135A1EF885E7903B52C3F2C3FE722B1C169297A91B82428956A02C631A2240F12162C7BC726"),
},
{
"384-bit string",
make_vector ("D8FABA1F5194C4DB5F176FABFFF856924EF627A37CD08CF55608BBA8F1E324D7C7F157298EABC4DCE7D89CE5162499F9"),
make_array ("B7A51FBB084DEEB55136EFD7260E5B112E3C40D1A2D14B142DF930DF"),
make_array ("34F8607EC10C092C1BA0B6565CE6197062C4E1A35A8E8C723E48A2D2416C3790"),
make_array ("A127FEFCDD240F762CCE3F5F1551FC7E1CDEBC7950D1CD94C6888F490CB2285A10FD0EE797B168C5CA4761FA232AAF05"),
make_array ("7EF3A2894C6ECBC4201B15348F90671515ACCBA3C8166621F864A9184BF08C3F5A895F6B599D3CB41F20A8A1DF25AE84F1A6D7C8DE74FB7CEF48F7E96FDE8D43"),
},
{
"512-bit string",
make_vector ("E926AE8B0AF6E53176DBFFCC2A6B88C6BD765F939D3D178A9BDE9EF3AA131C61E31C1E42CDFAF4B4DCDE579A37E150EFBEF5555B4C1CB40439D835A724E2FAE7"),
make_array ("C154607F986F9BF902D831293C8386D36B201EABA6F6FB0B678B4B81"),
make_array ("27A6441EE939B46E2C378D7AFEB0E891C47A28120E488EFF0AB71AF08788CEB3"),
make_array ("423BA134D3BCB5E440AC83372C7EDDBA3AE3BDDF1222F505C19CDE246AD76A2B0D07239A54E1D0934C9B3D29D49E5FBD"),
make_array ("EB5067BF762A291CF258AD69A816A0B089E0BD44F8E5B74CF60BCE64734E59853CCB8D091CD2E33F90AA063FB7942CF5965D459200144C1A0801ABD69A9A094A"),
},
{
"520-bit string",
make_vector ("16E8B3D8F988E9BB04DE9C96F2627811C973CE4A5296B4772CA3EEFEB80A652BDF21F50DF79F32DB23F9F73D393B2D57D9A0297F7A2F2E79CFDA39FA393DF1AC00"),
make_array ("95E87AC90F541AB90CBCF7FD7E0E0C152CEF78D5EE1830E9ED8A1ED7"),
make_array ("C4BB067383002DB44CA773918BB74104B604A583E12B06BE56C270F8B43512F2"),
make_array ("662C4851D311A786DE4CDA7E9EA1EFF0BFA462761FF6CF804E591ED9A15B0DC93A2BB6A6CFFDC8D7D23A233A52C86EAD"),
make_array ("B0E23D600BA4215F79D50047BBFED50DF7D6E769514D796AFD166DEECA88BD1CBE0AFC72A41E0317A223225B4F5882F723AFCBA3AF7C457EB525946DA6C53BB0"),
},
{
"1,000,000 'a' string",
std::vector<uint8_t> (1'000'000, 'a'),
make_array ("d69335b93325192e516a912e6d19a15cb51c6ed5c15243e7a7fd653c"),
make_array ("5c8875ae474a3634ba4fd55ec85bffd661f32aca75c6d699d0cdcb6c115891c1"),
make_array ("eee9e24d78c1855337983451df97c8ad9eedf256c6334f8e948d252d5e0e76847aa0774ddb90a842190d2c558b4b8340"),
make_array ("3c3a876da14034ab60627c077bb98f7e120a2a5370212dffb3385a18d4f38859ed311d0a9d5141ce9cc5c66ee689b266a8aa18ace8282a0e0db596c90b0a7b87"),
}
};
for (const auto &t: TESTS) {
#define TEST(WIDTH) \
do { \
std::array<uint8_t, WIDTH/8> sha3_##WIDTH; \
std::fill (std::begin (sha3_##WIDTH), std::end (sha3_##WIDTH), 0); \
\
FIPS202_SHA3_##WIDTH (t.data.data (), t.data.size (), &sha3_##WIDTH[0]); \
\
tap.expect_eq (t.sha3_##WIDTH, sha3_##WIDTH, "sha3-%u %s", unsigned (WIDTH), t.msg); \
} while (0)
TEST(224);
TEST(256);
TEST(384);
TEST(512);
}
return tap.status ();
}

View File

@ -46,13 +46,9 @@ main (int, char **) {
bool success = true;
const MD2 h;
for (const auto &i: TESTS) {
MD2 h;
h.update (i.input, strlen (i.input));
h.finish ();
auto out = h.digest ();
if (out != i.output) {
if (h (util::view{i.input}.cast<const uint8_t> ()) != i.output) {
std::cerr << "Failed on " << i.input << "\n";
success = false;
}

View File

@ -54,13 +54,9 @@ main (int, char**) {
bool success = true;
MD4 h;
for (auto i: TESTS) {
MD4 h;
h.update (i.input, strlen (i.input));
h.finish ();
auto out = h.digest ();
if (out != i.output) {
if (h (util::view{i.input}.cast <const uint8_t> ()) != i.output) {
std::cerr << "Failed on '" << i.input << "'\n";
success = false;
}

View File

@ -7,57 +7,87 @@
using util::hash::MD5;
///////////////////////////////////////////////////////////////////////////////
std::vector<uint8_t>
operator"" _u8s (const char *str, size_t len)
{
std::vector<uint8_t> res;
res.resize (len);
std::copy_n (str, len, std::begin (res));
return res;
}
///////////////////////////////////////////////////////////////////////////////
int
main (int, char**) {
static const struct {
const char *input;
std::vector<uint8_t> input;
MD5::digest_t output;
const char *msg;
} TESTS[] = {
{ "",
{ {},
{ { 0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04,
0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e } }
0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e } },
"empty"
},
{ "a",
{ "a"_u8s,
{ { 0x0c, 0xc1, 0x75, 0xb9, 0xc0, 0xf1, 0xb6, 0xa8,
0x31, 0xc3, 0x99, 0xe2, 0x69, 0x77, 0x26, 0x61 } }
0x31, 0xc3, 0x99, 0xe2, 0x69, 0x77, 0x26, 0x61 } },
"1 byte"
},
{ "abc",
{ "abc"_u8s,
{ { 0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0,
0xd6, 0x96, 0x3f, 0x7d, 0x28, 0xe1, 0x7f, 0x72 } }
0xd6, 0x96, 0x3f, 0x7d, 0x28, 0xe1, 0x7f, 0x72 } },
"3 bytes"
},
{ "message digest",
{ "message digest"_u8s,
{ { 0xf9, 0x6b, 0x69, 0x7d, 0x7c, 0xb7, 0x93, 0x8d,
0x52, 0x5a, 0x2f, 0x31, 0xaa, 0xf1, 0x61, 0xd0 } }
0x52, 0x5a, 0x2f, 0x31, 0xaa, 0xf1, 0x61, 0xd0 } },
"14 bytes, text"
},
{ "abcdefghijklmnopqrstuvwxyz",
{ "abcdefghijklmnopqrstuvwxyz"_u8s,
{ { 0xc3, 0xfc, 0xd3, 0xd7, 0x61, 0x92, 0xe4, 0x00,
0x7d, 0xfb, 0x49, 0x6c, 0xca, 0x67, 0xe1, 0x3b } }
0x7d, 0xfb, 0x49, 0x6c, 0xca, 0x67, 0xe1, 0x3b } },
"14 bytes, alphabet"
},
{ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
{ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"_u8s,
{ { 0xd1, 0x74, 0xab, 0x98, 0xd2, 0x77, 0xd9, 0xf5,
0xa5, 0x61, 0x1c, 0x2c, 0x9f, 0x41, 0x9d, 0x9f } }
0xa5, 0x61, 0x1c, 0x2c, 0x9f, 0x41, 0x9d, 0x9f } },
"62 bytes"
},
{ "12345678901234567890123456789012345678901234567890123456789012345678901234567890",
{ "12345678901234567890123456789012345678901234567890123456789012345678901234567890"_u8s,
{ { 0x57, 0xed, 0xf4, 0xa2, 0x2b, 0xe3, 0xc9, 0x55,
0xac, 0x49, 0xda, 0x2e, 0x21, 0x07, 0xb6, 0x7a } }
0xac, 0x49, 0xda, 0x2e, 0x21, 0x07, 0xb6, 0x7a } },
"80 bytes"
}
};
bool success = true;
util::TAP::logger tap;
for (auto i: TESTS) {
MD5 h;
h.update (i.input, strlen (i.input));
h.finish ();
auto out = h.digest ();
MD5 h;
for (const auto &t: TESTS)
tap.expect_eq (h (t.input), t.output, "%s", t.msg);
if (out != i.output) {
std::cerr << "Failed on '" << i.input << "'\n";
success = false;
}
// check that appending or prepending an empty data view doesn't change
// the hash for a couple of data lengths
for (auto l: { 0, 1, 8, 64, 80}) {
std::vector<uint8_t> data (l);
std::iota (std::begin (data), std::end (data), 0);
tap.expect_eq (h (nullptr, data), h (data), "empty-full vs full hash equality, %! bytes", l);
tap.expect_eq (h (data, nullptr), h (data), "full-empty vs full hash equality, %! bytes", l);
}
util::TAP::logger tap;
tap.expect (success, "test vectors");
for (auto l: { 2, 64, 80}) {
std::vector<uint8_t> data (l);
std::iota (std::begin (data), std::end (data), 0);
util::view root {data};
auto [a,b] = root.split (root.begin () + l / 2);
tap.expect_eq (h (a,b), h(root), "split data hash equality, %! bytes", l);
};
return tap.status ();
}

View File

@ -6,11 +6,35 @@
#include <cstring>
///////////////////////////////////////////////////////////////////////////////
std::vector<uint8_t>
operator"" _u8s (const char *str, size_t len)
{
std::vector<uint8_t> res;
res.resize (len);
std::copy_n (str, len, std::begin (res));
return res;
}
///////////////////////////////////////////////////////////////////////////////
template <size_t N>
std::ostream&
operator<< (std::ostream &os, std::array<uint8_t,N> &val)
{
for (auto c: val)
os << c;
return os;
}
///////////////////////////////////////////////////////////////////////////////
void
test (util::TAP::logger &tap)
{
struct {
const char *key;
std::vector<uint8_t> data;
const char *msg;
struct { uint32_t seed; uint32_t hash; } m1_32;
struct { uint32_t seed; uint32_t hash; } m2_32;
@ -21,7 +45,8 @@ test (util::TAP::logger &tap)
struct { uint32_t seed; std::array<uint64_t,2> hash; } m3_128_x64;
} TESTS[] = {
{ "",
{ ""_u8s,
"empty, zero seed",
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
@ -30,7 +55,8 @@ test (util::TAP::logger &tap)
{ 0, 0 },
},
{ "",
{ ""_u8s,
"empty, nonzero seed",
{ 0x00000001, 0x8f5a8d63 },
{ 0x00000001, 0x5bd15e36 },
{ 1, 0xc6a4a7935bd064dc },
@ -39,7 +65,8 @@ test (util::TAP::logger &tap)
{ 1, { 0x4610abe56eff5cb5, 0x51622daa78f83583 } },
},
{ "",
{ ""_u8s,
"empty, max seed",
{ 0xffffffff, 0x7a3f4f7e },
{ 0xffffffff, 0xb35966b0 },
{ uint64_t(-1), 0xb0d9485c2cd761b2 },
@ -48,7 +75,8 @@ test (util::TAP::logger &tap)
{ 0xffffffff, { 0x6af1df4d9d3bc9ec, 0x857421121ee6446b } },
},
{ "a",
{ "a"_u8s,
"1 byte",
{ 0x7a3f4f7e, 0x18abad09 },
{ 0xb35966b0, 0x1eea8b10 },
{ 0xb0d9485c2cd761b2, 0x0a9b4c93b35b1b9f },
@ -56,7 +84,8 @@ test (util::TAP::logger &tap)
{ 0x051e08a9, { 0x08e91d27, 0x12c6d92a, 0x12c6d92a, 0x12c6d92a } },
{ 0x9d3bc9ec, { 0xf79489c9f1a785de, 0xf6486d31835a9c7f } },
},
{ "abc",
{ "abc"_u8s,
"3 byte",
{ 0x18abad09, 0x1defb5e9 },
{ 0x1eea8b10, 0x72cac527 },
{ 0x0a9b4c93b35b1b9f, 0x2ffdf3214d9a4fa4 },
@ -64,7 +93,8 @@ test (util::TAP::logger &tap)
{ 0x08e91d27, { 0xc11cc883, 0xb5d7f69a, 0xb5d7f69a, 0xb5d7f69a } },
{ 0xf1a785de, { 0x946e5ee63ce3b80e, 0xadb7d6d0e2558c3c } },
},
{ "message digest",
{ "message digest"_u8s,
"14 byte",
{ 0x1defb5e9, 0x7b3ea4bd },
{ 0x72cac527, 0x68563c37 },
{ 0x2ffdf3214d9a4fa4, 0x9a83e79336350cee },
@ -73,7 +103,8 @@ test (util::TAP::logger &tap)
{ 0x3ce3b80e, { 0x2c91b16326bf5f7f, 0xa21acf13c39485bc } },
},
{ "abcdefghijklmnopqrstuvwxyz",
{ "abcdefghijklmnopqrstuvwxyz"_u8s,
"26 byte",
{ 0x7b3ea4bd, 0xd94ee9ea },
{ 0x68563c37, 0x0473b699 },
{ 0x9a83e79336350cee, 0x1f256c898952ae12 },
@ -83,42 +114,29 @@ test (util::TAP::logger &tap)
}
};
bool m1_32 = true;
bool m2_32 = true;
bool m2_64 = true;
bool m3_32 = true;
bool m3_128_x86 = true;
bool m3_128_x64 = true;
for (const auto &t: TESTS) {
m1_32 = m1_32 && (t.m1_32.hash == util::hash::murmur1::hash_32 (t.key, strlen (t.key), t.m1_32.seed));
m2_32 = m2_32 && (t.m2_32.hash == util::hash::murmur2::hash_32 (t.key, strlen (t.key), t.m2_32.seed));
m2_64 = m2_64 && (t.m2_64.hash == util::hash::murmur2::hash_64 (t.key, strlen (t.key), t.m2_64.seed));
m3_32 = m3_32 && (t.m3_32.hash == util::hash::murmur3::hash_32 (t.key, strlen (t.key), t.m3_32.seed));
const util::hash::murmur1 h1 (t.m1_32.seed);
{
auto result = util::hash::murmur3::hash_128_x86 (t.key, strlen (t.key), t.m3_128_x86.seed);
bool success = t.m3_128_x86.hash == result;
m3_128_x86 = m3_128_x86 && success;
}
const util::hash::murmur2<uint32_t> h2_32 (t.m2_32.seed);
const util::hash::murmur2<uint64_t> h2_64 (t.m2_64.seed);
{
auto result = util::hash::murmur3::hash_128_x64 (t.key, strlen (t.key), t.m3_128_x64.seed);
bool success = t.m3_128_x64.hash == result;
m3_128_x64 = m3_128_x64 && success;
}
const util::hash::murmur3_32 h3 (t.m3_32.seed);
const util::hash::murmur3_128_x86 h3_x86 (t.m3_128_x86.seed);
const util::hash::murmur3_128_x64 h3_x64 (t.m3_128_x64.seed);
tap.expect_eq (h1 (t.data), t.m1_32.hash, "murmur1_32, '%s'", t.msg);
tap.expect_eq (h2_32 (t.data), t.m2_32.hash, "murmur2_32, '%s'", t.msg);
tap.expect_eq (h2_64 (t.data), t.m2_64.hash, "murmur2_64, '%s'", t.msg);
tap.expect_eq (h3 (t.data), t.m3_32.hash, "murmur3_32, '%s'", t.msg);
tap.expect_eq (h3_x86 (t.data), t.m3_128_x86.hash, "murmur3_128_x86, '%s'", t.msg);
tap.expect_eq (h3_x64 (t.data), t.m3_128_x64.hash, "murmur3_128_x64, '%s'", t.msg);
}
tap.expect (m1_32, "murmur1_32");
tap.expect (m2_32, "murmur2_32");
tap.expect (m2_64, "murmur2_64");
tap.expect (m3_32, "murmur3_32");
tap.expect (m3_128_x86, "murmur3_128_x86");
tap.expect (m3_128_x64, "murmur3_128_x64");
}
///////////////////////////////////////////////////////////////////////////////
int
main (void)
{

View File

@ -5,15 +5,28 @@
#include <cstring>
///////////////////////////////////////////////////////////////////////////////
std::vector<uint8_t>
operator"" _u8s (const char *str, size_t len)
{
std::vector<uint8_t> res;
res.resize (len);
std::copy_n (str, len, std::begin (res));
return res;
}
///////////////////////////////////////////////////////////////////////////////
static const
struct {
const char *msg;
const char *data;
std::vector<uint8_t> data;
util::hash::RIPEMD::digest_t output;
} TESTS[] = {
{
"empty",
"",
""_u8s,
{ 0x9c, 0x11, 0x85, 0xa5, 0xc5, 0xe9, 0xfc, 0x54, 0x61, 0x28,
0x08, 0x97, 0x7e, 0xe8, 0xf5, 0x48, 0xb2, 0x25, 0x8d, 0x31 },
// 128: cdf26213a150dc3ecb610f18f6b38b46
@ -24,7 +37,7 @@ struct {
{
"a",
"a",
"a"_u8s,
{ 0x0b, 0xdc, 0x9d, 0x2d, 0x25, 0x6b, 0x3e, 0xe9, 0xda, 0xae,
0x34, 0x7b, 0xe6, 0xf4, 0xdc, 0x83, 0x5a, 0x46, 0x7f, 0xfe },
// 128: 86be7afa339d0fc7cfc785e72f578d33
@ -35,7 +48,7 @@ struct {
{
"abc",
"abc",
"abc"_u8s,
{ 0x8e, 0xb2, 0x08, 0xf7, 0xe0, 0x5d, 0x98, 0x7a, 0x9b, 0x04,
0x4a, 0x8e, 0x98, 0xc6, 0xb0, 0x87, 0xf1, 0x5a, 0x0b, 0xfc },
// 128: c14a12199c66e4ba84636b0f69144c77
@ -46,7 +59,7 @@ struct {
{
"message digest",
"message digest",
"message digest"_u8s,
{ 0x5d, 0x06, 0x89, 0xef, 0x49, 0xd2, 0xfa, 0xe5, 0x72, 0xb8,
0x81, 0xb1, 0x23, 0xa8, 0x5f, 0xfa, 0x21, 0x59, 0x5f, 0x36 },
// 128: 9e327b3d6e523062afc1132d7df9d1b8
@ -57,7 +70,7 @@ struct {
{
"26 characters",
"abcdefghijklmnopqrstuvwxyz",
"abcdefghijklmnopqrstuvwxyz"_u8s,
{ 0xf7, 0x1c, 0x27, 0x10, 0x9c, 0x69, 0x2c, 0x1b, 0x56, 0xbb,
0xdc, 0xeb, 0x5b, 0x9d, 0x28, 0x65, 0xb3, 0x70, 0x8d, 0xbc },
// 128: fd2aa607f71dc8f510714922b371834e
@ -68,7 +81,7 @@ struct {
{
"57 characters",
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"_u8s,
{ 0x12, 0xa0, 0x53, 0x38, 0x4a, 0x9c, 0x0c, 0x88, 0xe4, 0x05,
0xa0, 0x6c, 0x27, 0xdc, 0xf4, 0x9a, 0xda, 0x62, 0xeb, 0x2b },
// 128: a1aa0689d0fafa2ddc22e88b49133a06
@ -79,7 +92,7 @@ struct {
{
"63 characters",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"_u8s,
{ 0xb0, 0xe2, 0x0b, 0x6e, 0x31, 0x16, 0x64, 0x02, 0x86, 0xed,
0x3a, 0x87, 0xa5, 0x71, 0x30, 0x79, 0xb2, 0x1f, 0x51, 0x89 },
// 128: d1e959eb179c911faea4624c60c5c702
@ -90,7 +103,7 @@ struct {
{
"81 digits",
"12345678901234567890123456789012345678901234567890123456789012345678901234567890",
"12345678901234567890123456789012345678901234567890123456789012345678901234567890"_u8s,
{ 0x9b, 0x75, 0x2e, 0x45, 0x57, 0x3d, 0x4b, 0x39, 0xf4, 0xdb,
0xd3, 0x32, 0x3c, 0xab, 0x82, 0xbf, 0x63, 0x32, 0x6b, 0xfb }
// 128: 3f45ef194732c2dbb2c4a2c769795fa3
@ -100,43 +113,33 @@ struct {
}
};
// 1 million times "a"
// 128: 4a7f5723f954eba1216c9d8f6320431f
// 160: 52783243c1697bdbe16d37f97f68f08325dc1528
// 256: ac953744e10e31514c150d4d8d7b677342e33399788296e43ae4850ce4f97978
// 320: bdee37f4371e20646b8b0d862dda16292ae36f40965e8c8509e63d1dbddecc503e2b63eb9245bb66
int
main(int, char**) {
util::TAP::logger tap;
// Check against simple test vectors
for (const auto &i: TESTS) {
for (const auto &t: TESTS) {
util::hash::RIPEMD obj;
obj.update (reinterpret_cast<const uint8_t*> (i.data), strlen (i.data));
obj.finish ();
tap.expect_eq (obj.digest (), i.output, "%s", i.msg);
tap.expect_eq (obj (t.data), t.output, "%s", t.msg);
}
// Perform 'million-a' check
static const size_t CHUNK_WIDTH = 1'000;
util::hash::RIPEMD obj;
for (size_t i = 0; i < 1'000'000; i += CHUNK_WIDTH) {
uint8_t data[CHUNK_WIDTH];
memset (data, 'a', sizeof (data));
// 1 million times "a"
// 128: 4a7f5723f954eba1216c9d8f6320431f
// 160: 52783243c1697bdbe16d37f97f68f08325dc1528
// 256: ac953744e10e31514c150d4d8d7b677342e33399788296e43ae4850ce4f97978
// 320: bdee37f4371e20646b8b0d862dda16292ae36f40965e8c8509e63d1dbddecc503e2b63eb9245bb66
obj.update (data, sizeof (data));
}
obj.finish ();
std::vector<uint8_t> data (1'000'000, 'a');
static const util::hash::RIPEMD::digest_t MILLION {
0x52, 0x78, 0x32, 0x43, 0xc1, 0x69, 0x7b, 0xdb, 0xe1, 0x6d,
0x37, 0xf9, 0x7f, 0x68, 0xf0, 0x83, 0x25, 0xdc, 0x15, 0x28
};
tap.expect_eq (obj.digest (), MILLION, "million 'a'");
tap.expect_eq (obj (data), MILLION, "million 'a'");
return tap.status ();
}

View File

@ -10,6 +10,18 @@
#include <iostream>
///////////////////////////////////////////////////////////////////////////////
std::vector<uint8_t>
operator"" _u8s (const char *str, size_t len)
{
std::vector<uint8_t> res;
res.resize (len);
std::copy_n (str, len, std::begin (res));
return res;
}
///////////////////////////////////////////////////////////////////////////////
int
main (int, char**)
{
@ -17,43 +29,58 @@ main (int, char**)
static const struct {
const char *msg;
const char *input;
std::vector<uint8_t> input;
util::hash::SHA1::digest_t output;
} TESTS[] = {
{
"empty string",
"",
""_u8s,
{ { 0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55,
0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09 } }
},
{
"single a",
"a",
"a"_u8s,
{ { 0x86, 0xf7, 0xe4, 0x37, 0xfa, 0xa5, 0xa7, 0xfc, 0xe1, 0x5d,
0x1d, 0xdc, 0xb9, 0xea, 0xea, 0xea, 0x37, 0x76, 0x67, 0xb8 } }
},
{
"abc",
"abc",
"abc"_u8s,
{ { 0xA9, 0x99, 0x3E, 0x36, 0x47, 0x06, 0x81, 0x6A, 0xBA, 0x3E,
0x25, 0x71, 0x78, 0x50, 0xC2, 0x6C, 0x9C, 0xD0, 0xD8, 0x9D } }
},
{
"abc...opq",
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"_u8s,
{ { 0x84, 0x98, 0x3E, 0x44, 0x1C, 0x3B, 0xD2, 0x6E, 0xBA, 0xAE,
0x4A, 0xA1, 0xF9, 0x51, 0x29, 0xE5, 0xE5, 0x46, 0x70, 0xF1 } }
},
{
"896 bit alphabet",
"abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn"
"hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"_u8s,
{ 0xa4, 0x9b, 0x24, 0x46,
0xa0, 0x2c, 0x64, 0x5b,
0xf4, 0x19, 0xf9, 0x95,
0xb6, 0x70, 0x91, 0x25,
0x3a, 0x04, 0xa2, 0x59, }
}
// 1'000'000 * 'a'
//{ "a",
// { { 0x34, 0xAA, 0x97, 0x3C, 0xD4, 0xC4, 0xDA, 0xA4, 0xF6, 0x1E,
// 0xEB, 0x2B, 0xDB, 0xAD, 0x27, 0x31, 0x65, 0x34, 0x01, 0x6F } }
//},
// 16'777'216 x "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmno"
// about 1GiB of text
// 7789f0c9 ef7bfc40 d9331114 3dfbe69e 2017f592
// 80 repetitions of 01234567
//{ "0123456701234567012345670123456701234567012345670123456701234567",
// { { 0xDE, 0xA3, 0x56, 0xA2, 0xCD, 0xDD, 0x90, 0xC7, 0xA7, 0xEC,
@ -61,12 +88,21 @@ main (int, char**)
//}
};
for (const auto &i: TESTS) {
util::hash::SHA1 obj;
obj.update (reinterpret_cast<const uint8_t*> (i.input), strlen (i.input));
obj.finish ();
util::hash::SHA1 obj;
tap.expect_eq (obj.digest (), i.output, "%s", i.msg);
for (const auto &t: TESTS)
tap.expect_eq (obj (t.input), t.output, "%s", t.msg);
{
std::vector<uint8_t> data (1'000'000, 0x61);
util::hash::SHA1::digest_t result {
0x34, 0xaa, 0x97, 0x3c,
0xd4, 0xc4, 0xda, 0xa4,
0xf6, 0x1e, 0xeb, 0x2b,
0xdb, 0xad, 0x27, 0x31,
0x65, 0x34, 0x01, 0x6f,
};
tap.expect_eq (obj (data), result, "1'000'000 a's");
}
return tap.status ();

View File

@ -9,6 +9,7 @@ static const struct {
const char *input;
util::hash::SHA256::digest_t output;
} TESTS[] = {
{
"empty",
"",
@ -52,10 +53,7 @@ main (int, char **) {
for (const auto &i: TESTS) {
util::hash::SHA256 obj;
obj.update (reinterpret_cast<const uint8_t*> (i.input), strlen (i.input));
obj.finish ();
tap.expect_eq (obj.digest (), i.output, "%s", i.msg);
tap.expect_eq (obj (util::view{i.input}.cast<const uint8_t> ()), i.output, "%s", i.msg);
}
return tap.status ();

View File

@ -17,9 +17,21 @@
#include "tap.hpp"
#include "hash/simple.hpp"
#include "hash/xxhash.hpp"
///////////////////////////////////////////////////////////////////////////////
std::vector<uint8_t>
operator"" _u8s (const char *str, size_t len)
{
std::vector<uint8_t> res;
res.resize (len);
std::copy_n (str, len, std::begin (res));
return res;
}
///////////////////////////////////////////////////////////////////////////////
int
main (int, char **)
{
@ -29,29 +41,27 @@ main (int, char **)
uint32_t hash32;
uint64_t hash64;
unsigned seed;
std::string data;
std::string msg;
std::vector<uint8_t> data;
const char *msg;
} TESTS[] = {
{ 0x02CC5D05, 0xef46db3751d8e999, 0, "", "empty string, 0 seed" },
{ 0x0b2cb792, 0xd5afba1336a3be4b, 1, "", "empty string, 1 seed" },
{ 0x550d7456, 0xd24ec4f1a98c6e5b, 0, "a", "single a, 0 seed" },
{ 0xf514706f, 0xdec2bc81c3cd46c6, 1, "a", "single a, 1 seed" },
{ 0x32d153ff, 0x44bc2cf5ad770999, 0, "abc", "abc, 0 seed" },
{ 0xaa3da8ff, 0xbea9ca8199328908, 1, "abc", "abc, 1 seed" },
{ 0x02CC5D05, 0xef46db3751d8e999, 0, ""_u8s, "empty string, 0 seed" },
{ 0x0b2cb792, 0xd5afba1336a3be4b, 1, ""_u8s, "empty string, 1 seed" },
{ 0x550d7456, 0xd24ec4f1a98c6e5b, 0, "a"_u8s, "single a, 0 seed" },
{ 0xf514706f, 0xdec2bc81c3cd46c6, 1, "a"_u8s, "single a, 1 seed" },
{ 0x32d153ff, 0x44bc2cf5ad770999, 0, "abc"_u8s, "abc, 0 seed" },
{ 0xaa3da8ff, 0xbea9ca8199328908, 1, "abc"_u8s, "abc, 1 seed" },
{ 0x54ca7e46, 0x892a0760a6343391, 0x1234,
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+",
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()_+"_u8s,
"long alphabet" }
};
for (const auto &t: TESTS) {
auto first = t.data.data ();
auto last = first + t.data.size ();
util::hash::xxhash32 h32 (t.seed);
//util::hash::xxhash32 h64 (t.seed);
auto digest32 = util::hash::simple<util::hash::xxhash32> (first, last, t.seed);
//auto digest64 = util::hash::simple<util::hash::xxhash64> (first, last, t.seed);
tap.expect_eq (digest32, t.hash32, "xxhash32 %s", t.msg);
//tap.expect_eq (digest64, t.hash64, "xxhash64 %s", t.msg);
tap.expect_eq (h32 (t.data), t.hash32, "xxhash32 %s", t.msg);
//tap.expect_eq (h64 (t.data), t.hash64, "xxhash64 %s", t.msg);
}
return tap.status ();

View File

@ -97,7 +97,6 @@ to_epoch (const tm &t)
// similar. in the future we can account for this
CHECK_SANITY (t);
constexpr int
cumulative_days [12] = {
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334

View File

@ -22,8 +22,6 @@
#include "io.hpp"
#include "stream.hpp"
#include "hash/simple.hpp"
#include "hash/adler.hpp"
#include "hash/bsdsum.cpp"
#include "hash/crc.hpp"
@ -74,34 +72,30 @@ print_digest (std::ostream &os, std::array<uint8_t,S> digest)
///////////////////////////////////////////////////////////////////////////////
static
void
compute (const std::string &name,
const unsigned char *restrict first,
const unsigned char *restrict last)
static void
compute (const std::string &name, const util::view<const uint8_t*> data)
{
#define stream(TYPE, ...) do { \
if (name != #TYPE) \
break; \
\
auto sum = util::hash::simple<util::hash::TYPE> ( \
first, last, ##__VA_ARGS__ \
); \
\
print_digest (std::cout, sum) << '\n'; \
return; \
#define stream(TYPE, ...) do { \
if (name != #TYPE) \
break; \
\
print_digest ( \
std::cout, \
util::hash::TYPE{} (data) \
) << '\n'; \
return; \
} while (0);
stream (adler32);
stream (bsdsum);
stream (crc32);
//stream (bsdsum);
//stream (crc32);
stream (MD2);
stream (MD4);
stream (MD5);
stream (RIPEMD);
stream (SHA1);
stream (SHA256);
//stream (MD2);
//stream (MD4);
//stream (MD5);
//stream (RIPEMD);
//stream (SHA1);
//stream (SHA256);
#undef stream
}
@ -140,8 +134,8 @@ main (int argc, char **argv)
}
if (strcmp (argv[ARG_INPUT], "-")) {
util::mapped_file src (argv[ARG_INPUT]);
compute (argv[ARG_HASH], src.cbegin (), src.cend ());
const util::mapped_file src (argv[ARG_INPUT]);
compute (argv[ARG_HASH], util::view{src});
return EXIT_SUCCESS;
} else {

View File

@ -51,7 +51,7 @@ main (int argc, char **argv)
try {
const util::mapped_file src (argv[ARG_INPUT]);
std::cout << *json::tree::parse (src.as_view<const char> ()) << '\n';
std::cout << *json::tree::parse (util::view{src}.cast<const char> ()) << '\n';
} catch (const json::error& err) {
std::cerr << err.what () << "\n";
return EXIT_FAILURE;

View File

@ -50,8 +50,8 @@ main (int argc, char **argv) {
const util::mapped_file schema_src (argv[ARG_SCHEMA]);
const util::mapped_file input_src (argv[ARG_INPUT]);
auto schema = json::tree::parse (schema_src.as_view<const char> ());
auto input = json::tree::parse (input_src.as_view<const char> ());
auto schema = json::tree::parse (util::view{schema_src}.cast<const char>());
auto input = json::tree::parse (util::view{input_src} .cast<const char>());
json::schema::validate (*input, schema->as_object ());
} catch (const json::error &e) {

View File

@ -42,7 +42,7 @@ main (int argc, char ** argv) {
try {
const util::mapped_file data (argv[ARG_PATH]);
json::flat::parse (data.as_view<const char> ());
json::flat::parse (util::view{data}.cast<const char> ());
} catch (const json::error &x) {
std::cerr << "error: " << x.what () << '\n';
return EXIT_FAILURE;

View File

@ -270,6 +270,29 @@ template <std::size_t N, typename FuncT>
using nth_argument_t = typename nth_argument<N, FuncT>::type;
///////////////////////////////////////////////////////////////////////////////
template <typename T, typename = std::void_t<>>
struct is_container : public std::false_type {};
template <typename T>
struct is_container<
T,
std::void_t<
typename T::value_type,
typename T::reference,
typename T::const_reference,
typename T::iterator,
typename T::const_iterator,
typename T::difference_type,
typename T::size_type
>
> : public std::true_type {};
template <typename T>
constexpr auto is_container_v = is_container<T>::value;
///////////////////////////////////////////////////////////////////////////////
#include <string>
#include <array>

View File

@ -43,7 +43,7 @@ namespace util {
public:
explicit uri (std::string &&);
explicit uri (const std::string&);
explicit uri (const char *str);
explicit uri (const char *);
explicit uri (util::view<const char *>);
class parse_error : public std::runtime_error

View File

@ -45,6 +45,23 @@ namespace util {
m_end (last)
{ ; }
template <
typename ContainerT,
typename = std::enable_if_t<is_container_v<std::decay_t<ContainerT>>,void>
>
view (ContainerT &rhs):
view (rhs.begin (), rhs.end ())
{ ; }
template <
typename ContainerT,
typename = std::enable_if_t<is_container_v<std::decay_t<ContainerT>>,void>
>
view (const ContainerT &rhs):
view (rhs.begin (), rhs.end ())
{ ; }
//---------------------------------------------------------------------
// cosntruction from pointer/size represenations for ease of use with
@ -179,6 +196,20 @@ namespace util {
{ ; }
//---------------------------------------------------------------------
template <typename ValueT, std::size_t N>
view (std::array<ValueT,N> &rhs):
view (std::data (rhs), std::data (rhs) + std::size (rhs))
{ ; }
//---------------------------------------------------------------------
template <typename ValueT, std::size_t N>
view (const std::array<ValueT,N> &rhs):
view (std::data (rhs), std::data (rhs) + std::size (rhs))
{ ; }
//---------------------------------------------------------------------
view&
operator= (const view &rhs) noexcept
@ -253,11 +284,44 @@ namespace util {
//---------------------------------------------------------------------
util::view<BeginT,EndT>
operator- (util::view<BeginT,BeginT> prefix) const
constexpr std::tuple<
util::view<BeginT,BeginT>,
util::view<BeginT,EndT>
>
split (BeginT pos)
{
assert (prefix.begin () == begin ());
return { prefix.end (), end () };
return {
{ m_begin, pos },
{ pos, m_end }
};
}
//---------------------------------------------------------------------
constexpr auto
split (int pos)
{
auto last = m_begin;
std::advance (last, pos);
return split (last);
}
//---------------------------------------------------------------------
constexpr auto
consume (int count)
{
auto [a,b] = split (count);
return b;
}
//---------------------------------------------------------------------
constexpr util::view<BeginT,EndT>
consume (util::view<BeginT,BeginT> val)
{
assert (val.begin () == begin ());
return { val.end (), end () };
}
@ -321,7 +385,7 @@ namespace util {
//-------------------------------------------------------------------------
view (const char*) -> view<const char*>;
view (const char*) -> view<const char*, const char*>;
view (char*) -> view<char*>;
@ -351,6 +415,20 @@ namespace util {
template <typename ValueT, typename AllocatorT>
view (const std::vector<ValueT,AllocatorT>&) -> view<typename AllocatorT::const_pointer>;
template <typename ValueT, std::size_t N>
view (std::array<ValueT,N>) -> view<ValueT*>;
template <typename ContainerT>
view (ContainerT&) -> view<
typename ContainerT::iterator
>;
template <typename ContainerT>
view (const ContainerT&) -> view<
typename ContainerT::const_iterator
>;
///////////////////////////////////////////////////////////////////////////
template <typename ValueT, size_t N>