Files
rippled/include/xrpl/beast/hash/hash_append.h
Bart 1d42c4f6de refactor: Remove unnecessary copyright notices already covered by LICENSE.md (#5929)
Per XLS-0095, we are taking steps to rename ripple(d) to xrpl(d).

This change specifically removes all copyright notices referencing Ripple, XRPLF, and certain affiliated contributors upon mutual agreement, so the notice in the LICENSE.md file applies throughout. Copyright notices referencing external contributions remain as-is. Duplicate verbiage is also removed.
2025-11-04 08:33:42 +00:00

484 lines
13 KiB
C++

#ifndef BEAST_HASH_HASH_APPEND_H_INCLUDED
#define BEAST_HASH_HASH_APPEND_H_INCLUDED
#include <boost/container/flat_set.hpp>
#include <boost/endian/conversion.hpp>
#include <array>
#include <chrono>
#include <cstring>
#include <functional>
#include <memory>
#include <string>
#include <system_error>
#include <tuple>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
namespace beast {
namespace detail {
template <class T>
/*constexpr*/
inline void
reverse_bytes(T& t)
{
unsigned char* bytes = static_cast<unsigned char*>(
std::memmove(std::addressof(t), std::addressof(t), sizeof(T)));
for (unsigned i = 0; i < sizeof(T) / 2; ++i)
std::swap(bytes[i], bytes[sizeof(T) - 1 - i]);
}
template <class T>
/*constexpr*/
inline void
maybe_reverse_bytes(T& t, std::false_type)
{
}
template <class T>
/*constexpr*/
inline void
maybe_reverse_bytes(T& t, std::true_type)
{
reverse_bytes(t);
}
template <class T, class Hasher>
/*constexpr*/
inline void
maybe_reverse_bytes(T& t, Hasher&)
{
maybe_reverse_bytes(
t,
std::integral_constant<
bool,
Hasher::endian != boost::endian::order::native>{});
}
} // namespace detail
// is_uniquely_represented<T>
// A type T is contiguously hashable if for all combinations of two values of
// a type, say x and y, if x == y, then it must also be true that
// memcmp(addressof(x), addressof(y), sizeof(T)) == 0. I.e. if x == y,
// then x and y have the same bit pattern representation.
template <class T>
struct is_uniquely_represented
: public std::integral_constant<
bool,
std::is_integral<T>::value || std::is_enum<T>::value ||
std::is_pointer<T>::value>
{
explicit is_uniquely_represented() = default;
};
template <class T>
struct is_uniquely_represented<T const> : public is_uniquely_represented<T>
{
explicit is_uniquely_represented() = default;
};
template <class T>
struct is_uniquely_represented<T volatile> : public is_uniquely_represented<T>
{
explicit is_uniquely_represented() = default;
};
template <class T>
struct is_uniquely_represented<T const volatile>
: public is_uniquely_represented<T>
{
explicit is_uniquely_represented() = default;
};
// is_uniquely_represented<std::pair<T, U>>
template <class T, class U>
struct is_uniquely_represented<std::pair<T, U>>
: public std::integral_constant<
bool,
is_uniquely_represented<T>::value &&
is_uniquely_represented<U>::value &&
sizeof(T) + sizeof(U) == sizeof(std::pair<T, U>)>
{
explicit is_uniquely_represented() = default;
};
// is_uniquely_represented<std::tuple<T...>>
template <class... T>
struct is_uniquely_represented<std::tuple<T...>>
: public std::integral_constant<
bool,
std::conjunction_v<is_uniquely_represented<T>...> &&
sizeof(std::tuple<T...>) == (sizeof(T) + ...)>
{
explicit is_uniquely_represented() = default;
};
// is_uniquely_represented<T[N]>
template <class T, std::size_t N>
struct is_uniquely_represented<T[N]> : public is_uniquely_represented<T>
{
explicit is_uniquely_represented() = default;
};
// is_uniquely_represented<std::array<T, N>>
template <class T, std::size_t N>
struct is_uniquely_represented<std::array<T, N>>
: public std::integral_constant<
bool,
is_uniquely_represented<T>::value &&
sizeof(T) * N == sizeof(std::array<T, N>)>
{
explicit is_uniquely_represented() = default;
};
/** Metafunction returning `true` if the type can be hashed in one call.
For `is_contiguously_hashable<T>::value` to be true, then for every
combination of possible values of `T` held in `x` and `y`,
if `x == y`, then it must be true that `memcmp(&x, &y, sizeof(T))`
return 0; i.e. that `x` and `y` are represented by the same bit pattern.
For example: A two's complement `int` should be contiguously hashable.
Every bit pattern produces a unique value that does not compare equal to
any other bit pattern's value. A IEEE floating point should not be
contiguously hashable because -0. and 0. have different bit patterns,
though they compare equal.
*/
/** @{ */
template <class T, class HashAlgorithm>
struct is_contiguously_hashable
: public std::integral_constant<
bool,
is_uniquely_represented<T>::value &&
(sizeof(T) == 1 ||
HashAlgorithm::endian == boost::endian::order::native)>
{
explicit is_contiguously_hashable() = default;
};
template <class T, std::size_t N, class HashAlgorithm>
struct is_contiguously_hashable<T[N], HashAlgorithm>
: public std::integral_constant<
bool,
is_uniquely_represented<T[N]>::value &&
(sizeof(T) == 1 ||
HashAlgorithm::endian == boost::endian::order::native)>
{
explicit is_contiguously_hashable() = default;
};
/** @} */
//------------------------------------------------------------------------------
/** Logically concatenate input data to a `Hasher`.
Hasher requirements:
`X` is the type `Hasher`
`h` is a value of type `x`
`p` is a value convertible to `void const*`
`n` is a value of type `std::size_t`, greater than zero
Expression:
`h.append (p, n);`
Throws:
Never
Effect:
Adds the input data to the hasher state.
Expression:
`static_cast<std::size_t>(j)`
Throws:
Never
Effect:
Returns the reslting hash of all the input data.
*/
/** @{ */
// scalars
template <class Hasher, class T>
inline std::enable_if_t<is_contiguously_hashable<T, Hasher>::value>
hash_append(Hasher& h, T const& t) noexcept
{
h(std::addressof(t), sizeof(t));
}
template <class Hasher, class T>
inline std::enable_if_t<
!is_contiguously_hashable<T, Hasher>::value &&
(std::is_integral<T>::value || std::is_pointer<T>::value ||
std::is_enum<T>::value)>
hash_append(Hasher& h, T t) noexcept
{
detail::reverse_bytes(t);
h(std::addressof(t), sizeof(t));
}
template <class Hasher, class T>
inline std::enable_if_t<std::is_floating_point<T>::value>
hash_append(Hasher& h, T t) noexcept
{
if (t == 0)
t = 0;
detail::maybe_reverse_bytes(t, h);
h(&t, sizeof(t));
}
template <class Hasher>
inline void
hash_append(Hasher& h, std::nullptr_t) noexcept
{
void const* p = nullptr;
detail::maybe_reverse_bytes(p, h);
h(&p, sizeof(p));
}
// Forward declarations for ADL purposes
template <class Hasher, class T, std::size_t N>
std::enable_if_t<!is_contiguously_hashable<T, Hasher>::value>
hash_append(Hasher& h, T (&a)[N]) noexcept;
template <class Hasher, class CharT, class Traits, class Alloc>
std::enable_if_t<!is_contiguously_hashable<CharT, Hasher>::value>
hash_append(
Hasher& h,
std::basic_string<CharT, Traits, Alloc> const& s) noexcept;
template <class Hasher, class CharT, class Traits, class Alloc>
std::enable_if_t<is_contiguously_hashable<CharT, Hasher>::value>
hash_append(
Hasher& h,
std::basic_string<CharT, Traits, Alloc> const& s) noexcept;
template <class Hasher, class T, class U>
std::enable_if_t<!is_contiguously_hashable<std::pair<T, U>, Hasher>::value>
hash_append(Hasher& h, std::pair<T, U> const& p) noexcept;
template <class Hasher, class T, class Alloc>
std::enable_if_t<!is_contiguously_hashable<T, Hasher>::value>
hash_append(Hasher& h, std::vector<T, Alloc> const& v) noexcept;
template <class Hasher, class T, class Alloc>
std::enable_if_t<is_contiguously_hashable<T, Hasher>::value>
hash_append(Hasher& h, std::vector<T, Alloc> const& v) noexcept;
template <class Hasher, class T, std::size_t N>
std::enable_if_t<!is_contiguously_hashable<std::array<T, N>, Hasher>::value>
hash_append(Hasher& h, std::array<T, N> const& a) noexcept;
template <class Hasher, class... T>
std::enable_if_t<!is_contiguously_hashable<std::tuple<T...>, Hasher>::value>
hash_append(Hasher& h, std::tuple<T...> const& t) noexcept;
template <class Hasher, class Key, class T, class Hash, class Pred, class Alloc>
void
hash_append(Hasher& h, std::unordered_map<Key, T, Hash, Pred, Alloc> const& m);
template <class Hasher, class Key, class Hash, class Pred, class Alloc>
void
hash_append(Hasher& h, std::unordered_set<Key, Hash, Pred, Alloc> const& s);
template <class Hasher, class Key, class Compare, class Alloc>
std::enable_if_t<!is_contiguously_hashable<Key, Hasher>::value>
hash_append(
Hasher& h,
boost::container::flat_set<Key, Compare, Alloc> const& v) noexcept;
template <class Hasher, class Key, class Compare, class Alloc>
std::enable_if_t<is_contiguously_hashable<Key, Hasher>::value>
hash_append(
Hasher& h,
boost::container::flat_set<Key, Compare, Alloc> const& v) noexcept;
template <class Hasher, class T0, class T1, class... T>
void
hash_append(Hasher& h, T0 const& t0, T1 const& t1, T const&... t) noexcept;
// c-array
template <class Hasher, class T, std::size_t N>
std::enable_if_t<!is_contiguously_hashable<T, Hasher>::value>
hash_append(Hasher& h, T (&a)[N]) noexcept
{
for (auto const& t : a)
hash_append(h, t);
}
// basic_string
template <class Hasher, class CharT, class Traits, class Alloc>
inline std::enable_if_t<!is_contiguously_hashable<CharT, Hasher>::value>
hash_append(
Hasher& h,
std::basic_string<CharT, Traits, Alloc> const& s) noexcept
{
for (auto c : s)
hash_append(h, c);
hash_append(h, s.size());
}
template <class Hasher, class CharT, class Traits, class Alloc>
inline std::enable_if_t<is_contiguously_hashable<CharT, Hasher>::value>
hash_append(
Hasher& h,
std::basic_string<CharT, Traits, Alloc> const& s) noexcept
{
h(s.data(), s.size() * sizeof(CharT));
hash_append(h, s.size());
}
// pair
template <class Hasher, class T, class U>
inline std::enable_if_t<
!is_contiguously_hashable<std::pair<T, U>, Hasher>::value>
hash_append(Hasher& h, std::pair<T, U> const& p) noexcept
{
hash_append(h, p.first, p.second);
}
// vector
template <class Hasher, class T, class Alloc>
inline std::enable_if_t<!is_contiguously_hashable<T, Hasher>::value>
hash_append(Hasher& h, std::vector<T, Alloc> const& v) noexcept
{
for (auto const& t : v)
hash_append(h, t);
hash_append(h, v.size());
}
template <class Hasher, class T, class Alloc>
inline std::enable_if_t<is_contiguously_hashable<T, Hasher>::value>
hash_append(Hasher& h, std::vector<T, Alloc> const& v) noexcept
{
h(v.data(), v.size() * sizeof(T));
hash_append(h, v.size());
}
// array
template <class Hasher, class T, std::size_t N>
std::enable_if_t<!is_contiguously_hashable<std::array<T, N>, Hasher>::value>
hash_append(Hasher& h, std::array<T, N> const& a) noexcept
{
for (auto const& t : a)
hash_append(h, t);
}
template <class Hasher, class Key, class Compare, class Alloc>
std::enable_if_t<!is_contiguously_hashable<Key, Hasher>::value>
hash_append(
Hasher& h,
boost::container::flat_set<Key, Compare, Alloc> const& v) noexcept
{
for (auto const& t : v)
hash_append(h, t);
}
template <class Hasher, class Key, class Compare, class Alloc>
std::enable_if_t<is_contiguously_hashable<Key, Hasher>::value>
hash_append(
Hasher& h,
boost::container::flat_set<Key, Compare, Alloc> const& v) noexcept
{
h(&(v.begin()), v.size() * sizeof(Key));
}
// tuple
namespace detail {
inline void
for_each_item(...) noexcept
{
}
template <class Hasher, class T>
inline int
hash_one(Hasher& h, T const& t) noexcept
{
hash_append(h, t);
return 0;
}
template <class Hasher, class... T, std::size_t... I>
inline void
tuple_hash(
Hasher& h,
std::tuple<T...> const& t,
std::index_sequence<I...>) noexcept
{
for_each_item(hash_one(h, std::get<I>(t))...);
}
} // namespace detail
template <class Hasher, class... T>
inline std::enable_if_t<
!is_contiguously_hashable<std::tuple<T...>, Hasher>::value>
hash_append(Hasher& h, std::tuple<T...> const& t) noexcept
{
detail::tuple_hash(h, t, std::index_sequence_for<T...>{});
}
// shared_ptr
template <class Hasher, class T>
inline void
hash_append(Hasher& h, std::shared_ptr<T> const& p) noexcept
{
hash_append(h, p.get());
}
// chrono
template <class Hasher, class Rep, class Period>
inline void
hash_append(Hasher& h, std::chrono::duration<Rep, Period> const& d) noexcept
{
hash_append(h, d.count());
}
template <class Hasher, class Clock, class Duration>
inline void
hash_append(
Hasher& h,
std::chrono::time_point<Clock, Duration> const& tp) noexcept
{
hash_append(h, tp.time_since_epoch());
}
// variadic
template <class Hasher, class T0, class T1, class... T>
inline void
hash_append(Hasher& h, T0 const& t0, T1 const& t1, T const&... t) noexcept
{
hash_append(h, t0);
hash_append(h, t1, t...);
}
// error_code
template <class HashAlgorithm>
inline void
hash_append(HashAlgorithm& h, std::error_code const& ec)
{
hash_append(h, ec.value(), &ec.category());
}
} // namespace beast
#endif