Skip to content

Instantly share code, notes, and snippets.

@shavitush
Last active February 17, 2021 12:59
Show Gist options
  • Save shavitush/4dab72f2922806864d0ec2a0c94269a8 to your computer and use it in GitHub Desktop.
Save shavitush/4dab72f2922806864d0ec2a0c94269a8 to your computer and use it in GitHub Desktop.
Single header C++17 RAII wrapper for xxHash states and ultra fast file checksums
#pragma once
#include <any>
#include <cstdint>
#include <fstream>
#include <filesystem>
#include <xxhash.h>
class file_not_found_exception : public std::runtime_error
{
public:
file_not_found_exception(const char* message) : std::runtime_error(message)
{ }
};
template <const int nBits = 32, typename ret_t = uint32_t>
class xxhash
{
static_assert(nBits == 32 || nBits == 64, "xxHash only supports 32/64 bit");
static_assert(sizeof(ret_t) * 8 >= nBits, "ret_t's must be equal to or higher than nBits");
private:
std::any m_pState = nullptr;
public:
xxhash(uint32_t dwSeed = 0)
{
if(nBits == 32)
{
this->m_pState = XXH32_createState();
XXH32_reset(std::any_cast<XXH32_state_t*>(this->m_pState), dwSeed);
}
else
{
this->m_pState = XXH64_createState();
XXH64_reset(std::any_cast<XXH64_state_t*>(this->m_pState), dwSeed);
}
}
~xxhash()
{
if(nBits == 32) XXH32_freeState(std::any_cast<XXH32_state_t*>(this->m_pState));
else XXH64_freeState(std::any_cast<XXH64_state_t*>(this->m_pState));
}
// Feeds data in to the xxhash64 state
template <typename T>
auto feed(T* aBuffer, size_t dwSize) const -> void
{
if(nBits == 32) XXH32_update(std::any_cast<XXH32_state_t*>(this->m_pState), reinterpret_cast<const void*>(aBuffer), dwSize);
else XXH64_update(std::any_cast<XXH64_state_t*>(this->m_pState), reinterpret_cast<const void*>(aBuffer), dwSize);
}
// Gets a digest from the state
auto get() const -> ret_t
{
if(nBits == 32) return static_cast<ret_t>(XXH32_digest(std::any_cast<XXH32_state_t*>(this->m_pState)));
return static_cast<ret_t>(XXH64_digest(std::any_cast<XXH64_state_t*>(this->m_pState)));
}
// Gets a hash of the specified file from its path.
static auto hash_file(std::filesystem::path pFile, uint32_t dwSeed = 0) -> ret_t
{
if(!std::filesystem::exists(pFile))
{
throw file_not_found_exception("Specified file could not be found");
}
constexpr auto nBufferSize = 1024 * 1024; // 1 megabyte
auto aBuffer = std::make_unique<char[]>(nBufferSize);
std::ifstream pInput(pFile, std::ios::binary);
xxhash<nBits, ret_t> pHash(dwSeed);
while(pInput)
{
pInput.read(&aBuffer[0], nBufferSize);
pHash.feed(&aBuffer[0], nBufferSize);
}
return pHash.get();
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment