mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-04 19:25:51 +00:00
Compare commits
9 Commits
develop
...
Bronek/pro
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e874c4061e | ||
|
|
892876af5e | ||
|
|
a2a5a97d70 | ||
|
|
096ab3a86d | ||
|
|
64f1f2d580 | ||
|
|
3f9b724ed8 | ||
|
|
cad9eba7ed | ||
|
|
dbb989921c | ||
|
|
a0cf51d454 |
@@ -24,32 +24,111 @@
|
||||
|
||||
#include <xxhash.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
|
||||
namespace beast {
|
||||
|
||||
class xxhasher
|
||||
{
|
||||
private:
|
||||
// requires 64-bit std::size_t
|
||||
static_assert(sizeof(std::size_t) == 8, "");
|
||||
public:
|
||||
using result_type = std::size_t;
|
||||
|
||||
XXH3_state_t* state_;
|
||||
private:
|
||||
static_assert(sizeof(std::size_t) == 8, "requires 64-bit std::size_t");
|
||||
|
||||
// Have an internal buffer to avoid the streaming API
|
||||
// A 64-byte buffer should to be big enough for us
|
||||
static constexpr std::size_t INTERNAL_BUFFER_SIZE = 64;
|
||||
|
||||
alignas(64) std::array<std::uint8_t, INTERNAL_BUFFER_SIZE> buffer_;
|
||||
std::span<std::uint8_t> readBuffer_;
|
||||
std::span<std::uint8_t> writeBuffer_;
|
||||
|
||||
std::optional<XXH64_hash_t> seed_;
|
||||
XXH3_state_t* state_ = nullptr;
|
||||
|
||||
void
|
||||
resetBuffers()
|
||||
{
|
||||
writeBuffer_ = std::span{buffer_};
|
||||
readBuffer_ = {};
|
||||
}
|
||||
|
||||
void
|
||||
updateHash(void const* data, std::size_t len)
|
||||
{
|
||||
if (writeBuffer_.size() < len)
|
||||
{
|
||||
flushToState(data, len);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::memcpy(writeBuffer_.data(), data, len);
|
||||
writeBuffer_ = writeBuffer_.subspan(len);
|
||||
readBuffer_ = std::span{
|
||||
std::begin(buffer_), buffer_.size() - writeBuffer_.size()};
|
||||
}
|
||||
}
|
||||
|
||||
static XXH3_state_t*
|
||||
allocState()
|
||||
{
|
||||
auto ret = XXH3_createState();
|
||||
if (ret == nullptr)
|
||||
throw std::bad_alloc();
|
||||
throw std::bad_alloc(); // LCOV_EXCL_LINE
|
||||
return ret;
|
||||
}
|
||||
|
||||
public:
|
||||
using result_type = std::size_t;
|
||||
void
|
||||
flushToState(void const* data, std::size_t len)
|
||||
{
|
||||
if (!state_)
|
||||
{
|
||||
state_ = allocState();
|
||||
if (seed_.has_value())
|
||||
{
|
||||
XXH3_64bits_reset_withSeed(state_, *seed_);
|
||||
}
|
||||
else
|
||||
{
|
||||
XXH3_64bits_reset(state_);
|
||||
}
|
||||
}
|
||||
XXH3_64bits_update(state_, readBuffer_.data(), readBuffer_.size());
|
||||
resetBuffers();
|
||||
if (data && len)
|
||||
{
|
||||
XXH3_64bits_update(state_, data, len);
|
||||
}
|
||||
}
|
||||
|
||||
result_type
|
||||
retrieveHash()
|
||||
{
|
||||
if (state_)
|
||||
{
|
||||
flushToState(nullptr, 0);
|
||||
return XXH3_64bits_digest(state_);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (seed_.has_value())
|
||||
{
|
||||
return XXH3_64bits_withSeed(
|
||||
readBuffer_.data(), readBuffer_.size(), *seed_);
|
||||
}
|
||||
else
|
||||
{
|
||||
return XXH3_64bits(readBuffer_.data(), readBuffer_.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
static constexpr auto const endian = boost::endian::order::native;
|
||||
|
||||
xxhasher(xxhasher const&) = delete;
|
||||
@@ -58,43 +137,43 @@ public:
|
||||
|
||||
xxhasher()
|
||||
{
|
||||
state_ = allocState();
|
||||
XXH3_64bits_reset(state_);
|
||||
resetBuffers();
|
||||
}
|
||||
|
||||
~xxhasher() noexcept
|
||||
{
|
||||
XXH3_freeState(state_);
|
||||
if (state_)
|
||||
{
|
||||
XXH3_freeState(state_);
|
||||
}
|
||||
}
|
||||
|
||||
template <
|
||||
class Seed,
|
||||
std::enable_if_t<std::is_unsigned<Seed>::value>* = nullptr>
|
||||
explicit xxhasher(Seed seed)
|
||||
explicit xxhasher(Seed seed) : seed_(seed)
|
||||
{
|
||||
state_ = allocState();
|
||||
XXH3_64bits_reset_withSeed(state_, seed);
|
||||
resetBuffers();
|
||||
}
|
||||
|
||||
template <
|
||||
class Seed,
|
||||
std::enable_if_t<std::is_unsigned<Seed>::value>* = nullptr>
|
||||
xxhasher(Seed seed, Seed)
|
||||
xxhasher(Seed seed, Seed) : seed_(seed)
|
||||
{
|
||||
state_ = allocState();
|
||||
XXH3_64bits_reset_withSeed(state_, seed);
|
||||
resetBuffers();
|
||||
}
|
||||
|
||||
void
|
||||
operator()(void const* key, std::size_t len) noexcept
|
||||
{
|
||||
XXH3_64bits_update(state_, key, len);
|
||||
updateHash(key, len);
|
||||
}
|
||||
|
||||
explicit
|
||||
operator std::size_t() noexcept
|
||||
operator result_type() noexcept
|
||||
{
|
||||
return XXH3_64bits_digest(state_);
|
||||
return retrieveHash();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
201
src/test/beast/xxhasher_test.cpp
Normal file
201
src/test/beast/xxhasher_test.cpp
Normal file
@@ -0,0 +1,201 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of rippled: https://github.com/ripple/rippled
|
||||
Copyright (c) 2025 Ripple Labs Inc.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include <xrpl/beast/hash/xxhasher.h>
|
||||
#include <xrpl/beast/unit_test.h>
|
||||
|
||||
namespace beast {
|
||||
|
||||
class XXHasher_test : public unit_test::suite
|
||||
{
|
||||
public:
|
||||
void
|
||||
testWithoutSeed()
|
||||
{
|
||||
testcase("Without Seed");
|
||||
|
||||
xxhasher hasher{};
|
||||
|
||||
std::string objectToHash{"Hello, xxHash!"};
|
||||
hasher(objectToHash.data(), objectToHash.size());
|
||||
|
||||
BEAST_EXPECT(
|
||||
static_cast<xxhasher::result_type>(hasher) ==
|
||||
16042857369214894119ULL);
|
||||
}
|
||||
|
||||
void
|
||||
testWithSeed()
|
||||
{
|
||||
testcase("With Seed");
|
||||
|
||||
xxhasher hasher{static_cast<std::uint32_t>(102)};
|
||||
|
||||
std::string objectToHash{"Hello, xxHash!"};
|
||||
hasher(objectToHash.data(), objectToHash.size());
|
||||
|
||||
BEAST_EXPECT(
|
||||
static_cast<xxhasher::result_type>(hasher) ==
|
||||
14440132435660934800ULL);
|
||||
}
|
||||
|
||||
void
|
||||
testWithTwoSeeds()
|
||||
{
|
||||
testcase("With Two Seeds");
|
||||
xxhasher hasher{
|
||||
static_cast<std::uint32_t>(102), static_cast<std::uint32_t>(103)};
|
||||
|
||||
std::string objectToHash{"Hello, xxHash!"};
|
||||
hasher(objectToHash.data(), objectToHash.size());
|
||||
|
||||
BEAST_EXPECT(
|
||||
static_cast<xxhasher::result_type>(hasher) ==
|
||||
14440132435660934800ULL);
|
||||
}
|
||||
|
||||
void
|
||||
testBigObjectWithMultiupleSmallUpdatesWithoutSeed()
|
||||
{
|
||||
testcase("Big Object With Multiuple Small Updates Without Seed");
|
||||
xxhasher hasher{};
|
||||
|
||||
std::string objectToHash{"Hello, xxHash!"};
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
hasher(objectToHash.data(), objectToHash.size());
|
||||
}
|
||||
|
||||
BEAST_EXPECT(
|
||||
static_cast<xxhasher::result_type>(hasher) ==
|
||||
15296278154063476002ULL);
|
||||
}
|
||||
|
||||
void
|
||||
testBigObjectWithMultiupleSmallUpdatesWithSeed()
|
||||
{
|
||||
testcase("Big Object With Multiuple Small Updates With Seed");
|
||||
xxhasher hasher{static_cast<std::uint32_t>(103)};
|
||||
|
||||
std::string objectToHash{"Hello, xxHash!"};
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
hasher(objectToHash.data(), objectToHash.size());
|
||||
}
|
||||
|
||||
BEAST_EXPECT(
|
||||
static_cast<xxhasher::result_type>(hasher) ==
|
||||
17285302196561698791ULL);
|
||||
}
|
||||
|
||||
void
|
||||
testBigObjectWithSmallAndBigUpdatesWithoutSeed()
|
||||
{
|
||||
testcase("Big Object With Small And Big Updates Without Seed");
|
||||
xxhasher hasher{};
|
||||
|
||||
std::string objectToHash{"Hello, xxHash!"};
|
||||
std::string bigObject;
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
bigObject += "Hello, xxHash!";
|
||||
}
|
||||
hasher(objectToHash.data(), objectToHash.size());
|
||||
hasher(bigObject.data(), bigObject.size());
|
||||
hasher(objectToHash.data(), objectToHash.size());
|
||||
|
||||
BEAST_EXPECT(
|
||||
static_cast<xxhasher::result_type>(hasher) ==
|
||||
1865045178324729219ULL);
|
||||
}
|
||||
|
||||
void
|
||||
testBigObjectWithSmallAndBigUpdatesWithSeed()
|
||||
{
|
||||
testcase("Big Object With Small And Big Updates With Seed");
|
||||
xxhasher hasher{static_cast<std::uint32_t>(103)};
|
||||
|
||||
std::string objectToHash{"Hello, xxHash!"};
|
||||
std::string bigObject;
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
bigObject += "Hello, xxHash!";
|
||||
}
|
||||
hasher(objectToHash.data(), objectToHash.size());
|
||||
hasher(bigObject.data(), bigObject.size());
|
||||
hasher(objectToHash.data(), objectToHash.size());
|
||||
|
||||
BEAST_EXPECT(
|
||||
static_cast<xxhasher::result_type>(hasher) ==
|
||||
16189862915636005281ULL);
|
||||
}
|
||||
|
||||
void
|
||||
testBigObjectWithOneUpdateWithoutSeed()
|
||||
{
|
||||
testcase("Big Object With One Update Without Seed");
|
||||
xxhasher hasher{};
|
||||
|
||||
std::string objectToHash;
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
objectToHash += "Hello, xxHash!";
|
||||
}
|
||||
hasher(objectToHash.data(), objectToHash.size());
|
||||
|
||||
BEAST_EXPECT(
|
||||
static_cast<xxhasher::result_type>(hasher) ==
|
||||
15296278154063476002ULL);
|
||||
}
|
||||
|
||||
void
|
||||
testBigObjectWithOneUpdateWithSeed()
|
||||
{
|
||||
testcase("Big Object With One Update With Seed");
|
||||
xxhasher hasher{static_cast<std::uint32_t>(103)};
|
||||
|
||||
std::string objectToHash;
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
objectToHash += "Hello, xxHash!";
|
||||
}
|
||||
hasher(objectToHash.data(), objectToHash.size());
|
||||
|
||||
BEAST_EXPECT(
|
||||
static_cast<xxhasher::result_type>(hasher) ==
|
||||
17285302196561698791ULL);
|
||||
}
|
||||
|
||||
void
|
||||
run() override
|
||||
{
|
||||
testWithoutSeed();
|
||||
testWithSeed();
|
||||
testWithTwoSeeds();
|
||||
testBigObjectWithMultiupleSmallUpdatesWithoutSeed();
|
||||
testBigObjectWithMultiupleSmallUpdatesWithSeed();
|
||||
testBigObjectWithSmallAndBigUpdatesWithoutSeed();
|
||||
testBigObjectWithSmallAndBigUpdatesWithSeed();
|
||||
testBigObjectWithOneUpdateWithoutSeed();
|
||||
testBigObjectWithOneUpdateWithSeed();
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(XXHasher, beast_core, beast);
|
||||
} // namespace beast
|
||||
Reference in New Issue
Block a user