mirror of
https://github.com/mhx/dwarfs.git
synced 2025-09-14 06:48:39 -04:00
feat(checksum): add hexdigest() method
This commit is contained in:
parent
b54347d341
commit
7ded26d6a3
@ -60,6 +60,8 @@ class checksum {
|
|||||||
|
|
||||||
size_t digest_size() const { return impl_->digest_size(); }
|
size_t digest_size() const { return impl_->digest_size(); }
|
||||||
|
|
||||||
|
std::string hexdigest() const { return impl_->hexdigest(); }
|
||||||
|
|
||||||
class impl {
|
class impl {
|
||||||
public:
|
public:
|
||||||
virtual ~impl() = default;
|
virtual ~impl() = default;
|
||||||
@ -67,6 +69,7 @@ class checksum {
|
|||||||
virtual void update(void const* data, size_t size) = 0;
|
virtual void update(void const* data, size_t size) = 0;
|
||||||
virtual bool finalize(void* digest) = 0;
|
virtual bool finalize(void* digest) = 0;
|
||||||
virtual size_t digest_size() = 0;
|
virtual size_t digest_size() = 0;
|
||||||
|
virtual std::string hexdigest() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <cassert>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
@ -30,6 +31,8 @@
|
|||||||
|
|
||||||
#include <xxhash.h>
|
#include <xxhash.h>
|
||||||
|
|
||||||
|
#include <boost/algorithm/hex.hpp>
|
||||||
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
|
||||||
#include "dwarfs/checksum.h"
|
#include "dwarfs/checksum.h"
|
||||||
@ -44,25 +47,45 @@ std::unordered_set<std::string> supported_algorithms{
|
|||||||
"xxh3-128",
|
"xxh3-128",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::string make_hexdigest(checksum::impl& cs) {
|
||||||
|
std::array<char, EVP_MAX_MD_SIZE> tmp;
|
||||||
|
auto dig_size = cs.digest_size();
|
||||||
|
assert(dig_size <= tmp.size());
|
||||||
|
if (!cs.finalize(tmp.data())) {
|
||||||
|
throw std::runtime_error("failed to finalize digest");
|
||||||
|
}
|
||||||
|
std::string result;
|
||||||
|
result.resize(dig_size * 2);
|
||||||
|
boost::algorithm::hex_lower(tmp.begin(), tmp.begin() + dig_size,
|
||||||
|
result.begin());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
class checksum_evp : public checksum::impl {
|
class checksum_evp : public checksum::impl {
|
||||||
public:
|
public:
|
||||||
explicit checksum_evp(::EVP_MD const* evp)
|
explicit checksum_evp(::EVP_MD const* evp)
|
||||||
: context_(::EVP_MD_CTX_new())
|
: context_{::EVP_MD_CTX_new(), &::EVP_MD_CTX_free}
|
||||||
, dig_size_(::EVP_MD_size(evp)) {
|
, dig_size_(::EVP_MD_size(evp)) {
|
||||||
DWARFS_CHECK(::EVP_DigestInit(context_, evp), "EVP_DigestInit() failed");
|
DWARFS_CHECK(::EVP_DigestInit(context_.get(), evp),
|
||||||
|
"EVP_DigestInit() failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
~checksum_evp() override { ::EVP_MD_CTX_destroy(context_); }
|
|
||||||
|
|
||||||
void update(void const* data, size_t size) override {
|
void update(void const* data, size_t size) override {
|
||||||
DWARFS_CHECK(::EVP_DigestUpdate(context_, data, size),
|
assert(context_);
|
||||||
|
DWARFS_CHECK(::EVP_DigestUpdate(context_.get(), data, size),
|
||||||
"EVP_DigestUpdate() failed");
|
"EVP_DigestUpdate() failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool finalize(void* digest) override {
|
bool finalize(void* digest) override {
|
||||||
|
if (!context_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
unsigned int dig_size = 0;
|
unsigned int dig_size = 0;
|
||||||
bool rv = ::EVP_DigestFinal_ex(
|
bool rv = ::EVP_DigestFinal_ex(
|
||||||
context_, reinterpret_cast<unsigned char*>(digest), &dig_size);
|
context_.get(), reinterpret_cast<unsigned char*>(digest), &dig_size);
|
||||||
|
|
||||||
|
context_.reset();
|
||||||
|
|
||||||
if (rv) {
|
if (rv) {
|
||||||
DWARFS_CHECK(
|
DWARFS_CHECK(
|
||||||
@ -73,6 +96,8 @@ class checksum_evp : public checksum::impl {
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string hexdigest() override { return make_hexdigest(*this); }
|
||||||
|
|
||||||
static std::vector<std::string> available_algorithms() {
|
static std::vector<std::string> available_algorithms() {
|
||||||
std::vector<std::string> available;
|
std::vector<std::string> available;
|
||||||
::EVP_MD_do_all(
|
::EVP_MD_do_all(
|
||||||
@ -93,7 +118,7 @@ class checksum_evp : public checksum::impl {
|
|||||||
if (auto md = ::EVP_get_digestbyname(algo.c_str())) {
|
if (auto md = ::EVP_get_digestbyname(algo.c_str())) {
|
||||||
::EVP_MD_CTX* cx = ::EVP_MD_CTX_new();
|
::EVP_MD_CTX* cx = ::EVP_MD_CTX_new();
|
||||||
bool success = ::EVP_DigestInit(cx, md);
|
bool success = ::EVP_DigestInit(cx, md);
|
||||||
::EVP_MD_CTX_destroy(cx);
|
::EVP_MD_CTX_free(cx);
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -102,69 +127,64 @@ class checksum_evp : public checksum::impl {
|
|||||||
size_t digest_size() override { return dig_size_; }
|
size_t digest_size() override { return dig_size_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
::EVP_MD_CTX* context_;
|
std::unique_ptr<::EVP_MD_CTX, decltype(&::EVP_MD_CTX_free)> context_;
|
||||||
size_t const dig_size_;
|
size_t const dig_size_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class checksum_xxh3_64 : public checksum::impl {
|
struct xxh3_64_policy {
|
||||||
|
using result_type = XXH64_hash_t;
|
||||||
|
static constexpr auto reset = XXH3_64bits_reset;
|
||||||
|
static constexpr auto update = XXH3_64bits_update;
|
||||||
|
static constexpr auto digest = XXH3_64bits_digest;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct xxh3_128_policy {
|
||||||
|
using result_type = XXH128_hash_t;
|
||||||
|
static constexpr auto reset = XXH3_128bits_reset;
|
||||||
|
static constexpr auto update = XXH3_128bits_update;
|
||||||
|
static constexpr auto digest = XXH3_128bits_digest;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Policy>
|
||||||
|
class checksum_xxh3 : public checksum::impl {
|
||||||
public:
|
public:
|
||||||
checksum_xxh3_64()
|
checksum_xxh3()
|
||||||
: state_(XXH3_createState()) {
|
: state_{XXH3_createState(), &XXH3_freeState} {
|
||||||
DWARFS_CHECK(XXH3_64bits_reset(state_) == XXH_OK,
|
DWARFS_CHECK(Policy::reset(state_.get()) == XXH_OK, "XXH3 reset failed");
|
||||||
"XXH3_64bits_reset() failed");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~checksum_xxh3_64() override { XXH3_freeState(state_); }
|
|
||||||
|
|
||||||
void update(void const* data, size_t size) override {
|
void update(void const* data, size_t size) override {
|
||||||
auto err = XXH3_64bits_update(state_, data, size);
|
assert(state_);
|
||||||
DWARFS_CHECK(err == XXH_OK, fmt::format("XXH3_64bits_update() failed: {}",
|
auto err = Policy::update(state_.get(), data, size);
|
||||||
static_cast<int>(err)));
|
DWARFS_CHECK(err == XXH_OK,
|
||||||
|
fmt::format("XXH3 update failed: {}", static_cast<int>(err)));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool finalize(void* digest) override {
|
bool finalize(void* digest) override {
|
||||||
auto hash = XXH3_64bits_digest(state_);
|
if (!state_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
auto hash = Policy::digest(state_.get());
|
||||||
|
state_.reset();
|
||||||
::memcpy(digest, &hash, sizeof(hash));
|
::memcpy(digest, &hash, sizeof(hash));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string hexdigest() override { return make_hexdigest(*this); }
|
||||||
|
|
||||||
size_t digest_size() override {
|
size_t digest_size() override {
|
||||||
return sizeof(decltype(std::function{XXH3_64bits_digest})::result_type);
|
static_assert(
|
||||||
|
sizeof(typename Policy::result_type) ==
|
||||||
|
sizeof(typename decltype(std::function{Policy::digest})::result_type));
|
||||||
|
return sizeof(typename Policy::result_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
XXH3_state_t* state_;
|
std::unique_ptr<XXH3_state_t, decltype(&XXH3_freeState)> state_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class checksum_xxh3_128 : public checksum::impl {
|
using checksum_xxh3_64 = checksum_xxh3<xxh3_64_policy>;
|
||||||
public:
|
using checksum_xxh3_128 = checksum_xxh3<xxh3_128_policy>;
|
||||||
checksum_xxh3_128()
|
|
||||||
: state_(XXH3_createState()) {
|
|
||||||
DWARFS_CHECK(XXH3_128bits_reset(state_) == XXH_OK,
|
|
||||||
"XXH3_128bits_reset() failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
~checksum_xxh3_128() override { XXH3_freeState(state_); }
|
|
||||||
|
|
||||||
void update(void const* data, size_t size) override {
|
|
||||||
auto err = XXH3_128bits_update(state_, data, size);
|
|
||||||
DWARFS_CHECK(err == XXH_OK, fmt::format("XXH3_128bits_update() failed: {}",
|
|
||||||
static_cast<int>(err)));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool finalize(void* digest) override {
|
|
||||||
auto hash = XXH3_128bits_digest(state_);
|
|
||||||
::memcpy(digest, &hash, sizeof(hash));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t digest_size() override {
|
|
||||||
return sizeof(decltype(std::function{XXH3_128bits_digest})::result_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
XXH3_state_t* state_;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
bool verify_impl(T&& alg, void const* data, size_t size, const void* digest,
|
bool verify_impl(T&& alg, void const* data, size_t size, const void* digest,
|
||||||
|
@ -28,6 +28,8 @@
|
|||||||
|
|
||||||
#include <boost/algorithm/hex.hpp>
|
#include <boost/algorithm/hex.hpp>
|
||||||
|
|
||||||
|
#include <folly/String.h>
|
||||||
|
|
||||||
#include "dwarfs/checksum.h"
|
#include "dwarfs/checksum.h"
|
||||||
|
|
||||||
using namespace dwarfs;
|
using namespace dwarfs;
|
||||||
@ -94,17 +96,32 @@ TEST_P(checksum_test_str, end_to_end) {
|
|||||||
checksum cs(alg);
|
checksum cs(alg);
|
||||||
cs.update(payload.data(), payload.size());
|
cs.update(payload.data(), payload.size());
|
||||||
digest.resize(cs.digest_size());
|
digest.resize(cs.digest_size());
|
||||||
cs.finalize(digest.data());
|
ASSERT_TRUE(cs.finalize(digest.data()));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto hexdigest =
|
std::string hexdigest;
|
||||||
boost::algorithm::hex(std::string(digest.begin(), digest.end()));
|
|
||||||
|
{
|
||||||
|
checksum cs(alg);
|
||||||
|
cs.update(payload.data(), payload.size());
|
||||||
|
hexdigest = cs.hexdigest();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string hexdigest_upper;
|
||||||
|
boost::algorithm::hex(digest.begin(), digest.end(),
|
||||||
|
std::back_inserter(hexdigest_upper));
|
||||||
|
|
||||||
|
std::string hexdigest_lower;
|
||||||
|
boost::algorithm::hex_lower(digest.begin(), digest.end(),
|
||||||
|
std::back_inserter(hexdigest_lower));
|
||||||
|
|
||||||
|
EXPECT_EQ(hexdigest_lower, hexdigest);
|
||||||
|
|
||||||
EXPECT_TRUE(checksum::verify(alg, payload.data(), payload.size(),
|
EXPECT_TRUE(checksum::verify(alg, payload.data(), payload.size(),
|
||||||
digest.data(), digest.size()));
|
digest.data(), digest.size()));
|
||||||
|
|
||||||
if (auto it = ref_digests_str.find(alg); it != ref_digests_str.end()) {
|
if (auto it = ref_digests_str.find(alg); it != ref_digests_str.end()) {
|
||||||
EXPECT_EQ(it->second, hexdigest) << alg;
|
EXPECT_EQ(it->second, hexdigest_upper) << alg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,11 +140,12 @@ TEST_P(checksum_test_enum, end_to_end) {
|
|||||||
checksum cs(alg);
|
checksum cs(alg);
|
||||||
cs.update(payload.data(), payload.size());
|
cs.update(payload.data(), payload.size());
|
||||||
digest.resize(cs.digest_size());
|
digest.resize(cs.digest_size());
|
||||||
cs.finalize(digest.data());
|
ASSERT_TRUE(cs.finalize(digest.data()));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto hexdigest =
|
std::string hexdigest;
|
||||||
boost::algorithm::hex(std::string(digest.begin(), digest.end()));
|
boost::algorithm::hex(digest.begin(), digest.end(),
|
||||||
|
std::back_inserter(hexdigest));
|
||||||
|
|
||||||
auto it = ref_digests_enum.find(alg);
|
auto it = ref_digests_enum.find(alg);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user