#pragma once
#include <libsolutil/CommonData.h>
#include <libsolutil/Numeric.h>
#include <boost/functional/hash.hpp>
#include <boost/io/ios_state.hpp>
#include <array>
#include <cstdint>
#include <algorithm>
namespace solidity::util
{
template <unsigned N>
class FixedHash
{
public:
using Arith = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<N * 8, N * 8, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>;
enum { size = N };
static_assert(N != 0);
enum ConstructFromStringType { FromHex, FromBinary };
enum ConstructFromHashType { AlignLeft, AlignRight, FailIfDifferent };
explicit FixedHash() { m_data.fill(0); }
template <unsigned M> explicit FixedHash(FixedHash<M> const& _h, ConstructFromHashType _t = AlignLeft)
{
m_data.fill(0);
unsigned c = std::min(M, N);
for (unsigned i = 0; i < c; ++i)
m_data[_t == AlignRight ? N - 1 - i : i] = _h[_t == AlignRight ? M - 1 - i : i];
}
FixedHash(Arith const& _arith) { toBigEndian(_arith, m_data); }
explicit FixedHash(bytes const& _array, ConstructFromHashType _sizeMismatchBehavior = FailIfDifferent)
{
if (_array.size() == N)
memcpy(m_data.data(), _array.data(), _array.size());
else
{
m_data.fill(0);
if (_sizeMismatchBehavior != FailIfDifferent)
{
auto bytesToCopy = std::min<size_t>(_array.size(), N);
for (size_t i = 0; i < bytesToCopy; ++i)
if (_sizeMismatchBehavior == AlignRight)
m_data[N - 1 - i] = _array[_array.size() - 1 - i];
else
m_data[i] = _array[i];
}
}
}
explicit FixedHash(bytesConstRef _b, ConstructFromHashType _t = FailIfDifferent)
{
if (_b.size() == N)
memcpy(m_data.data(), _b.data(), std::min<size_t>(_b.size(), N));
else
{
m_data.fill(0);
if (_t != FailIfDifferent)
{
auto c = std::min<size_t>(_b.size(), N);
for (size_t i = 0; i < c; ++i)
if (_t == AlignRight)
m_data[N - 1 - i] = _b[_b.size() - 1 - i];
else
m_data[i] = _b[i];
}
}
}
explicit FixedHash(std::string const& _s, ConstructFromStringType _t = FromHex, ConstructFromHashType _ht = FailIfDifferent):
FixedHash(_t == FromHex ? fromHex(_s, WhenError::Throw) : solidity::util::asBytes(_s), _ht)
{}
operator Arith() const { return fromBigEndian<Arith>(m_data); }
bool operator==(FixedHash const& _c) const { return m_data == _c.m_data; }
bool operator!=(FixedHash const& _c) const { return m_data != _c.m_data; }
bool operator<(FixedHash const& _c) const {
for (unsigned i = 0; i < N; ++i)
{
if (m_data[i] < _c.m_data[i])
return true;
else if (m_data[i] > _c.m_data[i])
return false;
}
return false;
}
uint8_t& operator[](unsigned _i) { return m_data[_i]; }
uint8_t operator[](unsigned _i) const { return m_data[_i]; }
std::string hex() const { return toHex(asBytes()); }
bytesRef ref() { return bytesRef(m_data.data(), N); }
bytesConstRef ref() const { return bytesConstRef(m_data.data(), N); }
uint8_t* data() { return m_data.data(); }
uint8_t const* data() const { return m_data.data(); }
bytes asBytes() const { return bytes(data(), data() + N); }
private:
std::array<uint8_t, N> m_data; };
template <unsigned N>
inline std::ostream& operator<<(std::ostream& _out, FixedHash<N> const& _h)
{
boost::io::ios_all_saver guard(_out);
_out << std::noshowbase << std::hex << std::setfill('0');
for (unsigned i = 0; i < N; ++i)
_out << std::setw(2) << (int)_h[i];
_out << std::dec;
return _out;
}
using h256 = FixedHash<32>;
using h160 = FixedHash<20>;
}