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őrinc
2025-09-30 19:03:02 -04:00
parent 118d22ddb4
commit 6eb5ba5691
2 changed files with 47 additions and 48 deletions

View File

@@ -19,41 +19,33 @@
v2 = std::rotl(v2, 32); \
} while (0)
CSipHasher::CSipHasher(uint64_t k0, uint64_t k1)
{
v[0] = C0 ^ k0;
v[1] = C1 ^ k1;
v[2] = C2 ^ k0;
v[3] = C3 ^ k1;
count = 0;
tmp = 0;
}
CSipHasher::CSipHasher(uint64_t k0, uint64_t k1) : m_state{k0, k1} {}
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;
SIPROUND;
SIPROUND;
v0 ^= data;
v[0] = v0;
v[1] = v1;
v[2] = v2;
v[3] = v3;
m_state.v[0] = v0;
m_state.v[1] = v1;
m_state.v[2] = v2;
m_state.v[3] = v3;
count += 8;
m_count += 8;
return *this;
}
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 t = tmp;
uint8_t c = count;
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 = m_tmp;
uint8_t c = m_count;
while (data.size() > 0) {
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);
}
v[0] = v0;
v[1] = v1;
v[2] = v2;
v[3] = v3;
count = c;
tmp = t;
m_state.v[0] = v0;
m_state.v[1] = v1;
m_state.v[2] = v2;
m_state.v[3] = v3;
m_count = c;
m_tmp = t;
return *this;
}
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;
SIPROUND;
@@ -98,7 +90,7 @@ uint64_t CSipHasher::Finalize() const
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);
v3 ^= d;
@@ -135,7 +127,7 @@ uint64_t PresaltedSipHasher::operator()(const uint256& val) const noexcept
/** Specialized implementation for efficiency */
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);
v3 ^= d;
SIPROUND;

View File

@@ -5,28 +5,34 @@
#ifndef BITCOIN_CRYPTO_SIPHASH_H
#define BITCOIN_CRYPTO_SIPHASH_H
#include <array>
#include <cstdint>
#include <span>
class uint256;
/** SipHash-2-4 */
class CSipHasher
/** Shared SipHash internal state v[0..3], initialized from (k0, k1). */
class SipHashState
{
private:
uint64_t v[4];
uint64_t tmp;
uint8_t count; // Only the low 8 bits of the input size matter.
static constexpr uint64_t C0{0x736f6d6570736575ULL}, C1{0x646f72616e646f6dULL}, C2{0x6c7967656e657261ULL}, C3{0x7465646279746573ULL};
public:
static constexpr uint64_t C0{0x736f6d6570736575ULL};
static constexpr uint64_t C1{0x646f72616e646f6dULL};
static constexpr uint64_t C2{0x6c7967656e657261ULL};
static constexpr uint64_t C3{0x7465646279746573ULL};
explicit SipHashState(uint64_t k0, uint64_t k1) noexcept : v{C0 ^ k0, C1 ^ k1, C2 ^ k0, C3 ^ k1} {}
/** 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);
/** 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.
* This function can only be used when a multiple of 8 bytes have been written so far.
*/
@@ -48,17 +54,18 @@ public:
*/
class PresaltedSipHasher
{
uint64_t v[4];
const SipHashState m_state;
public:
explicit PresaltedSipHasher(uint64_t k0, uint64_t k1) noexcept {
v[0] = CSipHasher::C0 ^ k0;
v[1] = CSipHasher::C1 ^ k1;
v[2] = CSipHasher::C2 ^ k0;
v[3] = CSipHasher::C3 ^ k1;
}
explicit PresaltedSipHasher(uint64_t k0, uint64_t k1) noexcept : m_state{k0, k1} {}
/** Equivalent to CSipHasher(k0, k1).Write(val).Finalize(). */
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;
};