refactor: extract shared SipHash state into SipHashState

Split the repeated `SipHash` v[0..3] initialization into a small `SipHashState` helper that is used by both `CSipHasher` and `PresaltedSipHasher`.

Added explanatory comments to clarify behavior, documenting the equivalence of `PresaltedSipHasher` `operator()` overloads to `CSipHasher` usage.

Co-authored-by: Ryan Ofsky <ryan@ofsky.org>
This commit is contained in:
LőrincandRyan Ofsky committed 2025-12-09 17:17:47 +01:00
1 parent 118d22ddb4
commit 6eb5ba5691
2 files changed
+47 -48

No files matched your search

+21 -29
View File
@@ -19,41 +19,33 @@
v2 = std::rotl(v2, 32); \ v2 = std::rotl(v2, 32); \
} while (0) } while (0)
CSipHasher::CSipHasher(uint64_t k0, uint64_t k1) CSipHasher::CSipHasher(uint64_t k0, uint64_t k1) : m_state{k0, k1} {}
{
v[0] = C0 ^ k0;
v[1] = C1 ^ k1;
v[2] = C2 ^ k0;
v[3] = C3 ^ k1;
count = 0;
tmp = 0;
}
CSipHasher& CSipHasher::Write(uint64_t data) CSipHasher& CSipHasher::Write(uint64_t data)
{ {
uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3]; uint64_t v0 = m_state.v[0], v1 = m_state.v[1], v2 = m_state.v[2], v3 = m_state.v[3];
assert(count % 8 == 0); assert(m_count % 8 == 0);
v3 ^= data; v3 ^= data;
SIPROUND; SIPROUND;
SIPROUND; SIPROUND;
v0 ^= data; v0 ^= data;
v[0] = v0; m_state.v[0] = v0;
v[1] = v1; m_state.v[1] = v1;
v[2] = v2; m_state.v[2] = v2;
v[3] = v3; m_state.v[3] = v3;
count += 8; m_count += 8;
return *this; return *this;
} }
CSipHasher& CSipHasher::Write(std::span<const unsigned char> data) CSipHasher& CSipHasher::Write(std::span<const unsigned char> data)
{ {
uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3]; uint64_t v0 = m_state.v[0], v1 = m_state.v[1], v2 = m_state.v[2], v3 = m_state.v[3];
uint64_t t = tmp; uint64_t t = m_tmp;
uint8_t c = count; uint8_t c = m_count;
while (data.size() > 0) { while (data.size() > 0) {
t |= uint64_t{data.front()} << (8 * (c % 8)); t |= uint64_t{data.front()} << (8 * (c % 8));
@@ -68,21 +60,21 @@ CSipHasher& CSipHasher::Write(std::span<const unsigned char> data)
data = data.subspan(1); data = data.subspan(1);
} }
v[0] = v0; m_state.v[0] = v0;
v[1] = v1; m_state.v[1] = v1;
v[2] = v2; m_state.v[2] = v2;
v[3] = v3; m_state.v[3] = v3;
count = c; m_count = c;
tmp = t; m_tmp = t;
return *this; return *this;
} }
uint64_t CSipHasher::Finalize() const uint64_t CSipHasher::Finalize() const
{ {
uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3]; uint64_t v0 = m_state.v[0], v1 = m_state.v[1], v2 = m_state.v[2], v3 = m_state.v[3];
uint64_t t = tmp | (((uint64_t)count) << 56); uint64_t t = m_tmp | (((uint64_t)m_count) << 56);
v3 ^= t; v3 ^= t;
SIPROUND; SIPROUND;
@@ -98,7 +90,7 @@ uint64_t CSipHasher::Finalize() const
uint64_t PresaltedSipHasher::operator()(const uint256& val) const noexcept uint64_t PresaltedSipHasher::operator()(const uint256& val) const noexcept
{ {
uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3]; uint64_t v0 = m_state.v[0], v1 = m_state.v[1], v2 = m_state.v[2], v3 = m_state.v[3];
uint64_t d = val.GetUint64(0); uint64_t d = val.GetUint64(0);
v3 ^= d; v3 ^= d;
@@ -135,7 +127,7 @@ uint64_t PresaltedSipHasher::operator()(const uint256& val) const noexcept
/** Specialized implementation for efficiency */ /** Specialized implementation for efficiency */
uint64_t PresaltedSipHasher::operator()(const uint256& val, uint32_t extra) const noexcept uint64_t PresaltedSipHasher::operator()(const uint256& val, uint32_t extra) const noexcept
{ {
uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3]; uint64_t v0 = m_state.v[0], v1 = m_state.v[1], v2 = m_state.v[2], v3 = m_state.v[3];
uint64_t d = val.GetUint64(0); uint64_t d = val.GetUint64(0);
v3 ^= d; v3 ^= d;
SIPROUND; SIPROUND;
+26 -19
View File
@@ -5,28 +5,34 @@
#ifndef BITCOIN_CRYPTO_SIPHASH_H #ifndef BITCOIN_CRYPTO_SIPHASH_H
#define BITCOIN_CRYPTO_SIPHASH_H #define BITCOIN_CRYPTO_SIPHASH_H
#include <array>
#include <cstdint> #include <cstdint>
#include <span> #include <span>
class uint256; class uint256;
/** SipHash-2-4 */ /** Shared SipHash internal state v[0..3], initialized from (k0, k1). */
class CSipHasher class SipHashState
{ {
private: static constexpr uint64_t C0{0x736f6d6570736575ULL}, C1{0x646f72616e646f6dULL}, C2{0x6c7967656e657261ULL}, C3{0x7465646279746573ULL};
uint64_t v[4];
uint64_t tmp;
uint8_t count; // Only the low 8 bits of the input size matter.
public: public:
static constexpr uint64_t C0{0x736f6d6570736575ULL}; explicit SipHashState(uint64_t k0, uint64_t k1) noexcept : v{C0 ^ k0, C1 ^ k1, C2 ^ k0, C3 ^ k1} {}
static constexpr uint64_t C1{0x646f72616e646f6dULL};
static constexpr uint64_t C2{0x6c7967656e657261ULL};
static constexpr uint64_t C3{0x7465646279746573ULL};
/** Construct a SipHash calculator initialized with 128-bit key (k0, k1) */ std::array<uint64_t, 4> v{};
};
/** General SipHash-2-4 implementation. */
class CSipHasher
{
SipHashState m_state;
uint64_t m_tmp{0};
uint8_t m_count{0}; //!< Only the low 8 bits of the input size matter.
public:
/** Construct a SipHash calculator initialized with 128-bit key (k0, k1). */
CSipHasher(uint64_t k0, uint64_t k1); CSipHasher(uint64_t k0, uint64_t k1);
/** Hash a 64-bit integer worth of data /** Hash a 64-bit integer worth of data.
* It is treated as if this was the little-endian interpretation of 8 bytes. * It is treated as if this was the little-endian interpretation of 8 bytes.
* This function can only be used when a multiple of 8 bytes have been written so far. * This function can only be used when a multiple of 8 bytes have been written so far.
*/ */
@@ -48,17 +54,18 @@ public:
*/ */
class PresaltedSipHasher class PresaltedSipHasher
{ {
uint64_t v[4]; const SipHashState m_state;
public: public:
explicit PresaltedSipHasher(uint64_t k0, uint64_t k1) noexcept { explicit PresaltedSipHasher(uint64_t k0, uint64_t k1) noexcept : m_state{k0, k1} {}
v[0] = CSipHasher::C0 ^ k0;
v[1] = CSipHasher::C1 ^ k1;
v[2] = CSipHasher::C2 ^ k0;
v[3] = CSipHasher::C3 ^ k1;
}
/** Equivalent to CSipHasher(k0, k1).Write(val).Finalize(). */
uint64_t operator()(const uint256& val) const noexcept; uint64_t operator()(const uint256& val) const noexcept;
/**
* Equivalent to CSipHasher(k0, k1).Write(val).Write(extra).Finalize(),
* with `extra` encoded as 4 little-endian bytes.
*/
uint64_t operator()(const uint256& val, uint32_t extra) const noexcept; uint64_t operator()(const uint256& val, uint32_t extra) const noexcept;
}; };