diff --git a/dshash/dshash b/dshash/dshash index 4c4fe94..7068766 100755 Binary files a/dshash/dshash and b/dshash/dshash differ diff --git a/dshash/dshash.o b/dshash/dshash.o index d65faa8..06e3fea 100644 Binary files a/dshash/dshash.o and b/dshash/dshash.o differ diff --git a/dshash/main.cpp b/dshash/main.cpp index dc828a4..46a964f 100644 --- a/dshash/main.cpp +++ b/dshash/main.cpp @@ -37,28 +37,20 @@ int main(int argc, char* argv[]) { return 1; } - DSHash::Hash hash; - - if (std::filesystem::is_regular_file(target)) { - if (verbose) { + if (verbose) { + if (std::filesystem::is_regular_file(target)) { std::cerr << "Processing file: " << target << std::endl; - } - hash = DSHash::hashFile(target); - } else if (std::filesystem::is_directory(target)) { - if (verbose) { + } else if (std::filesystem::is_directory(target)) { for (const auto& entry : std::filesystem::recursive_directory_iterator(target)) { if (entry.is_regular_file()) { std::cerr << "Processing: " << entry.path() << std::endl; } } } - hash = DSHash::hashDirectory(target); - } else { - std::cerr << "Error: Path is neither a file nor a directory: " << path << std::endl; - return 1; } - std::cout << DSHash::toString(hash) << std::endl; + DSHash hasher(target); + std::cout << hasher.toString() << std::endl; } catch (const std::exception& e) { std::cerr << "Error: " << e.what() << std::endl; diff --git a/dshash/main.o b/dshash/main.o index 83208fd..88c9feb 100644 Binary files a/dshash/main.o and b/dshash/main.o differ diff --git a/src/dshash.cpp b/src/dshash.cpp index 2d3b112..53e7fa5 100644 --- a/src/dshash.cpp +++ b/src/dshash.cpp @@ -52,34 +52,6 @@ static inline uint32_t gamma1(uint32_t x) { return rotr(x, 17) ^ rotr(x, 19) ^ (x >> 10); } -DSHash::DSHash() { - h[0] = 0x6a09e667; - h[1] = 0xbb67ae85; - h[2] = 0x3c6ef372; - h[3] = 0xa54ff53a; - h[4] = 0x510e527f; - h[5] = 0x9b05688c; - h[6] = 0x1f83d9ab; - h[7] = 0x5be0cd19; -} - -void DSHash::update(const uint8_t* data, size_t length) { - if (finalized) return; - - total_length += length; - - buffer.insert(buffer.end(), data, data + length); - - while (buffer.size() >= BLOCK_SIZE) { - processBlock(buffer.data()); - buffer.erase(buffer.begin(), buffer.begin() + BLOCK_SIZE); - } -} - -void DSHash::update(const std::string& str) { - update(reinterpret_cast(str.data()), str.size()); -} - void DSHash::processBlock(const uint8_t* block) { uint32_t w[64]; @@ -126,6 +98,23 @@ void DSHash::processBlock(const uint8_t* block) { h[7] += hh; } +void DSHash::update(const uint8_t* data, size_t length) { + if (finalized) return; + + total_length += length; + + buffer.insert(buffer.end(), data, data + length); + + while (buffer.size() >= BLOCK_SIZE) { + processBlock(buffer.data()); + buffer.erase(buffer.begin(), buffer.begin() + BLOCK_SIZE); + } +} + +void DSHash::update(const std::string& str) { + update(reinterpret_cast(str.data()), str.size()); +} + void DSHash::padMessage() { uint64_t bit_length = total_length * 8; @@ -140,9 +129,9 @@ void DSHash::padMessage() { } } -DSHash::Hash DSHash::finalize() { +tHash DSHash::finalize() { if (finalized) { - Hash result; + tHash result; for (int i = 0; i < 8; i++) { result[i * 4] = (h[i] >> 24) & 0xff; result[i * 4 + 1] = (h[i] >> 16) & 0xff; @@ -161,7 +150,7 @@ DSHash::Hash DSHash::finalize() { finalized = true; - Hash result; + tHash result; for (int i = 0; i < 8; i++) { result[i * 4] = (h[i] >> 24) & 0xff; result[i * 4 + 1] = (h[i] >> 16) & 0xff; @@ -172,35 +161,80 @@ DSHash::Hash DSHash::finalize() { return result; } -DSHash::Hash DSHash::hashString(const std::string& str) { - DSHash hasher; - hasher.update(str); - return hasher.finalize(); +std::string DSHash::hashString(const std::string& str) { + h[0] = 0x6a09e667; + h[1] = 0xbb67ae85; + h[2] = 0x3c6ef372; + h[3] = 0xa54ff53a; + h[4] = 0x510e527f; + h[5] = 0x9b05688c; + h[6] = 0x1f83d9ab; + h[7] = 0x5be0cd19; + buffer.clear(); + total_length = 0; + finalized = false; + + update(str); + tHash hash = finalize(); + + std::stringstream ss; + for (uint8_t byte : hash) { + ss << std::hex << std::setfill('0') << std::setw(2) << static_cast(byte); + } + return ss.str(); } -DSHash::Hash DSHash::hashFile(const std::filesystem::path& filepath) { +std::string DSHash::hashFile(const std::filesystem::path& filepath) { std::ifstream file(filepath, std::ios::binary); if (!file) { throw std::runtime_error("Cannot open file: " + filepath.string()); } - DSHash hasher; + h[0] = 0x6a09e667; + h[1] = 0xbb67ae85; + h[2] = 0x3c6ef372; + h[3] = 0xa54ff53a; + h[4] = 0x510e527f; + h[5] = 0x9b05688c; + h[6] = 0x1f83d9ab; + h[7] = 0x5be0cd19; + buffer.clear(); + total_length = 0; + finalized = false; + constexpr size_t BUFFER_SIZE = 8192; char buffer[BUFFER_SIZE]; while (file.read(buffer, BUFFER_SIZE) || file.gcount() > 0) { - hasher.update(reinterpret_cast(buffer), file.gcount()); + update(reinterpret_cast(buffer), file.gcount()); } - return hasher.finalize(); + tHash hash = finalize(); + + std::stringstream ss; + for (uint8_t byte : hash) { + ss << std::hex << std::setfill('0') << std::setw(2) << static_cast(byte); + } + return ss.str(); } -DSHash::Hash DSHash::hashDirectory(const std::filesystem::path& dirpath) { +std::string DSHash::hashDirectory(const std::filesystem::path& dirpath) { if (!std::filesystem::is_directory(dirpath)) { throw std::runtime_error("Not a directory: " + dirpath.string()); } - DSHash hasher; + h[0] = 0x6a09e667; + h[1] = 0xbb67ae85; + h[2] = 0x3c6ef372; + h[3] = 0xa54ff53a; + h[4] = 0x510e527f; + h[5] = 0x9b05688c; + h[6] = 0x1f83d9ab; + h[7] = 0x5be0cd19; + buffer.clear(); + total_length = 0; + finalized = false; + std::vector paths; for (const auto& entry : std::filesystem::recursive_directory_iterator(dirpath)) { @@ -213,19 +247,78 @@ DSHash::Hash DSHash::hashDirectory(const std::filesystem::path& dirpath) { for (const auto& path : paths) { std::string relative = std::filesystem::relative(path, dirpath).string(); - hasher.update(relative); + update(relative); - auto fileHash = hashFile(path); - hasher.update(fileHash.data(), fileHash.size()); + DSHash fileHasher(path); + std::string fileHashStr = fileHasher.toString(); + update(fileHashStr); } - return hasher.finalize(); -} - -std::string DSHash::toString(const Hash& hash) { + tHash hash = finalize(); + std::stringstream ss; for (uint8_t byte : hash) { ss << std::hex << std::setfill('0') << std::setw(2) << static_cast(byte); } return ss.str(); +} + +DSHash::DSHash(const std::filesystem::path& path) { + h[0] = 0x6a09e667; + h[1] = 0xbb67ae85; + h[2] = 0x3c6ef372; + h[3] = 0xa54ff53a; + h[4] = 0x510e527f; + h[5] = 0x9b05688c; + h[6] = 0x1f83d9ab; + h[7] = 0x5be0cd19; + + if (std::filesystem::is_regular_file(path)) { + hashFile(path); + } else if (std::filesystem::is_directory(path)) { + hashDirectory(path); + } else { + throw std::runtime_error("Path is neither file nor directory: " + path.string()); + } +} + +DSHash::DSHash(const std::string& str) { + h[0] = 0x6a09e667; + h[1] = 0xbb67ae85; + h[2] = 0x3c6ef372; + h[3] = 0xa54ff53a; + h[4] = 0x510e527f; + h[5] = 0x9b05688c; + h[6] = 0x1f83d9ab; + h[7] = 0x5be0cd19; + + hashString(str); +} + +std::string DSHash::toString() { + if (!finalized) { + finalize(); + } + + tHash hash = get(); + std::stringstream ss; + for (uint8_t byte : hash) { + ss << std::hex << std::setfill('0') << std::setw(2) << static_cast(byte); + } + return ss.str(); +} + +tHash DSHash::get() { + if (!finalized) { + return finalize(); + } + + tHash result; + for (int i = 0; i < 8; i++) { + result[i * 4] = (h[i] >> 24) & 0xff; + result[i * 4 + 1] = (h[i] >> 16) & 0xff; + result[i * 4 + 2] = (h[i] >> 8) & 0xff; + result[i * 4 + 3] = h[i] & 0xff; + } + return result; } \ No newline at end of file diff --git a/src/dshash.hpp b/src/dshash.hpp index a03241c..a8b09c1 100644 --- a/src/dshash.hpp +++ b/src/dshash.hpp @@ -7,23 +7,25 @@ #include #include +typedef std::array tHash; + class DSHash { -public: - using Hash = std::array; +public: + explicit DSHash(const std::filesystem::path& path); + explicit DSHash(const std::string& str); - DSHash(); + std::string toString(); + tHash get(); +private: + std::string hashString(const std::string& str); + std::string hashFile(const std::filesystem::path& filepath); + std::string hashDirectory(const std::filesystem::path& dirpath); + void update(const uint8_t* data, size_t length); void update(const std::string& str); - - Hash finalize(); - - static Hash hashString(const std::string& str); - static Hash hashFile(const std::filesystem::path& filepath); - static Hash hashDirectory(const std::filesystem::path& dirpath); - - static std::string toString(const Hash& hash); - + tHash finalize(); + private: void processBlock(const uint8_t* block); void padMessage(); diff --git a/tests/test_lib b/tests/test_lib index 116b94b..534c6d6 100755 Binary files a/tests/test_lib and b/tests/test_lib differ diff --git a/tests/test_lib.cpp b/tests/test_lib.cpp index 587e6b7..b0ffbe8 100644 --- a/tests/test_lib.cpp +++ b/tests/test_lib.cpp @@ -6,70 +6,52 @@ #include void test_string_hash() { - auto hash = DSHash::hashString("abc"); - std::string hashStr = DSHash::toString(hash); + DSHash hasher(std::string("abc")); + std::string hashStr = hasher.toString(); assert(hashStr == "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"); std::cout << "✓ String hash test passed" << std::endl; } void test_empty_string() { - auto hash = DSHash::hashString(""); - std::string hashStr = DSHash::toString(hash); + DSHash hasher(std::string("")); + std::string hashStr = hasher.toString(); assert(hashStr == "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); std::cout << "✓ Empty string hash test passed" << std::endl; } void test_long_string() { - auto hash = DSHash::hashString("The quick brown fox jumps over the lazy dog"); - std::string hashStr = DSHash::toString(hash); + DSHash hasher(std::string("The quick brown fox jumps over the lazy dog")); + std::string hashStr = hasher.toString(); assert(hashStr == "d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592"); std::cout << "✓ Long string hash test passed" << std::endl; } -void test_incremental_update() { - DSHash hasher; - hasher.update("a"); - hasher.update("b"); - hasher.update("c"); - auto hash = hasher.finalize(); - std::string hashStr = DSHash::toString(hash); - assert(hashStr == "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"); - std::cout << "✓ Incremental update test passed" << std::endl; -} - void test_file_hash() { std::string tempFile = "/tmp/test_hash_file.txt"; std::ofstream out(tempFile); out << "Test content for hashing"; out.close(); - auto hash = DSHash::hashFile(tempFile); - std::string hashStr = DSHash::toString(hash); + DSHash fileHasher{std::filesystem::path(tempFile)}; + std::string hashStr = fileHasher.toString(); - DSHash hasher; - hasher.update("Test content for hashing"); - auto expectedHash = hasher.finalize(); - std::string expectedHashStr = DSHash::toString(expectedHash); + DSHash stringHasher(std::string("Test content for hashing")); + std::string expectedHashStr = stringHasher.toString(); assert(hashStr == expectedHashStr); std::filesystem::remove(tempFile); std::cout << "✓ File hash test passed" << std::endl; } -void test_large_data() { - DSHash hasher; - std::string chunk(1000, 'a'); - for (int i = 0; i < 100; i++) { - hasher.update(chunk); - } - auto hash = hasher.finalize(); - std::string hashStr = DSHash::toString(hash); +void test_get_method() { + DSHash hasher(std::string("test")); + tHash hash = hasher.get(); + assert(hash.size() == 32); + + std::string hashStr = hasher.toString(); assert(hashStr.length() == 64); - for (char c : hashStr) { - assert((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')); - } - std::cout << "✓ Large data hash test passed" << std::endl; + std::cout << "✓ get() method test passed" << std::endl; } void test_known_vectors() { @@ -87,22 +69,36 @@ void test_known_vectors() { }; for (const auto& vec : vectors) { - auto hash = DSHash::hashString(vec.input); - std::string hashStr = DSHash::toString(hash); + DSHash hasher(std::string(vec.input)); + std::string hashStr = hasher.toString(); assert(hashStr == vec.expected); } std::cout << "✓ Known test vectors passed" << std::endl; } -void test_binary_data() { - uint8_t binaryData[] = {0x00, 0x01, 0x02, 0x03, 0xFF, 0xFE, 0xFD}; - DSHash hasher; - hasher.update(binaryData, sizeof(binaryData)); - auto hash = hasher.finalize(); - std::string hashStr = DSHash::toString(hash); +void test_directory_hash() { + std::filesystem::path tempDir = "/tmp/test_hash_dir"; + std::filesystem::create_directories(tempDir); + std::filesystem::create_directories(tempDir / "subdir"); + + std::ofstream file1(tempDir / "file1.txt"); + file1 << "content1"; + file1.close(); + + std::ofstream file2(tempDir / "subdir" / "file2.txt"); + file2 << "content2"; + file2.close(); + + DSHash dirHasher(tempDir); + std::string hashStr = dirHasher.toString(); assert(hashStr.length() == 64); - std::cout << "✓ Binary data hash test passed" << std::endl; + for (char c : hashStr) { + assert((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f')); + } + + std::filesystem::remove_all(tempDir); + std::cout << "✓ Directory hash test passed" << std::endl; } int main() { @@ -110,11 +106,10 @@ int main() { test_string_hash(); test_empty_string(); test_long_string(); - test_incremental_update(); test_file_hash(); - test_large_data(); + test_get_method(); test_known_vectors(); - test_binary_data(); + test_directory_hash(); std::cout << "\nAll library tests passed!" << std::endl; return 0;