diff --git a/src/rhash.hpp b/src/rhash.hpp new file mode 100644 index 0000000..cde203e --- /dev/null +++ b/src/rhash.hpp @@ -0,0 +1,137 @@ +/* +=========================================================================== + * RHash - wrapper for C++ +Copyright (C) 2024 Poul Sander + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see http://www.gnu.org/licenses/ + +Source information and contacts persons can be found at +https://github.com/sago007/saland +=========================================================================== +*/ + +#pragma once + +#include +#include +#include + +class RHash { +public: + RHash(unsigned hash_mask) { + hash_ids.push_back(hash_mask); + } + + RHash(std::initializer_list hash_list) : RHash(0) { + for (unsigned hash_id : hash_list) { + add_hash(hash_id); + } + } + + ~RHash() { + if (ctx_) { + rhash_free(ctx_); + ctx_ = nullptr; + } + } + + void reinit() { + if (ctx_) { + rhash_free(ctx_); + ctx_ = nullptr; + } + unsigned hash_mask = 0; + for (unsigned hash_id : hash_ids) { + hash_mask |= hash_id; + } + ctx_ = rhash_init(hash_mask); + if (ctx_ == nullptr) { + throw std::runtime_error("Failed to initialize RHash context"); + } + done = false; + } + + void add_hash(unsigned hash_id) { + hash_ids.push_back(hash_id); + } + + void update(const std::string& data) { + if (!ctx_) { + reinit(); + } + if (done) { + throw std::runtime_error("\"update\" called on a finalized rhash context"); + } + rhash_update(ctx_, data.c_str(), data.length()); + length += data.length(); + } + + void finish() { + rhash_final(ctx_, nullptr); + done = true; + } + + std::string hash_as_string(unsigned hash_id, int flags) { + if (!done) { + finish(); + } + size_t length = rhash_get_hash_length(hash_id); + std::vector buffer(length); + rhash_print(buffer.data(), ctx_, hash_id, flags); + return std::string(buffer.data(), buffer.size()); + } + + std::string hex(unsigned hash_id) { + return hash_as_string(hash_id, RHPR_HEX); + } + + std::string HEX(unsigned hash_id) { + return hash_as_string(hash_id, RHPR_HEX | RHPR_UPPERCASE); + } + + std::string base32(unsigned hash_id) { + return hash_as_string(hash_id, RHPR_BASE32); + } + + std::string base64(unsigned hash_id) { + return hash_as_string(hash_id, RHPR_BASE64); + } + + size_t size() { + return length; + } + + void hash_file(const std::string& filename) { + FILE* file = fopen(filename.c_str(), "rb"); + if (file == nullptr) { + throw std::runtime_error("Failed to open file for hashing: " + filename); + } + reinit(); //Unconditionally reinit when loading a new file + char buffer[1024]; + size_t bytes_read; + while ((bytes_read = fread(buffer, 1, sizeof(buffer), file)) > 0) { + rhash_update(ctx_, buffer, bytes_read); + length += bytes_read; + } + + fclose(file); + rhash_final(ctx_, nullptr); + } + +private: + struct rhash_context* ctx_ = nullptr; + std::vector hash_ids; + size_t length = 0; + bool done = false; +}; \ No newline at end of file