diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ad9082d --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/.idea/ +/cmake-build*/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..335eee4 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "third_party/googletest"] + path = third_party/googletest + url = https://github.com/google/googletest.git +[submodule "third_party/benchmark"] + path = third_party/benchmark + url = https://github.com/google/benchmark.git diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..6af91ad --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,26 @@ +cmake_minimum_required(VERSION 3.5) +project(md5sum LANGUAGES CXX) + +option(MD5_ENABLE_TESTING "Enable testing of the md5sum library." ON) +option(MD5_ENABLE_BENCHMARK "Enable benchmark of the md5sum library." ON) + +set(CMAKE_CXX_STANDARD 20) + +add_compile_options(-fno-rtti -fno-exceptions -Wall -Wextra) + +add_library(md5sum STATIC src/md5_core.cc) +target_include_directories(md5sum INTERFACE src/) + +include(third_party/ThirdParty.cmake) +add_library(md5sum::md5sum ALIAS md5sum) + +if (MD5_ENABLE_TESTING) + enable_testing() + add_executable(md5_test test/md5_update.cc) + target_link_libraries(md5_test PRIVATE md5sum::md5sum GTest::gtest_main) +endif() + +if (MD5_ENABLE_BENCHMARK) + add_executable(md5_benchmark benchmark.cc) + target_link_libraries(md5_benchmark PRIVATE md5sum::md5sum benchmark::benchmark_main) +endif() diff --git a/benchmark.cc b/benchmark.cc new file mode 100644 index 0000000..cecebf6 --- /dev/null +++ b/benchmark.cc @@ -0,0 +1,26 @@ +#include + +#include "md5.h" + +std::string test_data() { + char data[64]; + for (char i = 0; i < 64; ++i) { + data[i] = i; + } + return {data, data + 64}; +} + +static void BM_MD5_NEXT(benchmark::State &state) { + const auto data = test_data(); + + md5::MD5::md5_ctx c; + md5::MD5::md5_reset(&c); + + for (auto _ : state) { + md5::MD5::md5_update(&c, data.c_str(), 64); + } +} + +BENCHMARK(BM_MD5_NEXT); + +BENCHMARK_MAIN(); diff --git a/md5_core.h b/md5_core.h deleted file mode 100644 index 165e497..0000000 --- a/md5_core.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include - -namespace md5 { - -constexpr uint32_t MD5_A = 0x67452301; -constexpr uint32_t MD5_B = 0xefcdab89; -constexpr uint32_t MD5_C = 0x98badcfe; -constexpr uint32_t MD5_D = 0x10325476; - -struct md5_ctx { - uint32_t A = MD5_A; - uint32_t B = MD5_B; - uint32_t C = MD5_C; - uint32_t D = MD5_D; - uint64_t size = 0; -}; - -/// Reset md5 ctx with algorithm constants. -void md5_reset(md5_ctx *ctx); - -/// Update md5 ctx with specified data, note that `len` is a multiple of 64. -void md5_update(md5_ctx *ctx, const void *buffer, uint64_t len); - -/// Update and end the md5 hash with the specified data, the value of `len` has no limit. -void md5_final(md5_ctx *ctx, const void *buffer, uint64_t len); - -} // namespace md5 diff --git a/src/md5.h b/src/md5.h new file mode 100644 index 0000000..62e786f --- /dev/null +++ b/src/md5.h @@ -0,0 +1,40 @@ +#pragma once + +#include + +namespace md5 { + +class MD5 { +public: + MD5() = default; + +public: + static constexpr uint32_t MD5_A = 0x67452301; + static constexpr uint32_t MD5_B = 0xefcdab89; + static constexpr uint32_t MD5_C = 0x98badcfe; + static constexpr uint32_t MD5_D = 0x10325476; + + struct md5_ctx { + uint32_t A = MD5_A; + uint32_t B = MD5_B; + uint32_t C = MD5_C; + uint32_t D = MD5_D; + uint64_t size = 0; + }; + + /// Reset md5 ctx with algorithm constants. + static void md5_reset(md5_ctx *ctx); + + /// Update md5 ctx with specified data, note that `len` is a multiple of 64. + static void md5_update(md5_ctx *ctx, const void *buffer, uint64_t len); + + /// Update and end the md5 hash with the specified data, the value of `len` has no limit. + static void md5_final(md5_ctx *ctx, const void *buffer, uint64_t len); + +private: + md5_ctx ctx_; + char buffer_[64] = {}; + char buffer_size_ = 0; +}; + +} // namespace md5 diff --git a/md5_core.cc b/src/md5_core.cc similarity index 94% rename from md5_core.cc rename to src/md5_core.cc index 35cb60c..05f8e61 100644 --- a/md5_core.cc +++ b/src/md5_core.cc @@ -1,5 +1,6 @@ #include -#include "md5_core.h" + +#include "md5.h" #include "md5_math.h" #define RA A, B, C, D @@ -58,7 +59,7 @@ consteval uint32_t T(int index) { // index -> [0, 64) return static_cast(std::abs(val) * 0x100000000); } -void md5_reset(md5_ctx *ctx) { +void MD5::md5_reset(md5_ctx *ctx) { ctx->A = MD5_A; ctx->B = MD5_B; ctx->C = MD5_C; @@ -66,7 +67,7 @@ void md5_reset(md5_ctx *ctx) { ctx->size = 0; } -void md5_update(md5_ctx *ctx, const void *data, uint64_t len) { +void MD5::md5_update(md5_ctx *ctx, const void *data, uint64_t len) { auto *block = reinterpret_cast(data); auto *limit = block + (len >> 2); auto A = ctx->A; @@ -97,7 +98,7 @@ void md5_update(md5_ctx *ctx, const void *data, uint64_t len) { ctx->size += len; // processed size in byte } -void md5_final(md5_ctx *ctx, const void *data, uint64_t len) { +void MD5::md5_final(md5_ctx *ctx, const void *data, uint64_t len) { if (len >= 120) { // len -> [64 + 56, INF) auto size = len & ~(uint64_t)0b111111; md5_update(ctx, data, size); diff --git a/md5_math.h b/src/md5_math.h similarity index 100% rename from md5_math.h rename to src/md5_math.h diff --git a/main.cc b/test/md5_update.cc similarity index 75% rename from main.cc rename to test/md5_update.cc index 3a010d1..f99b626 100644 --- a/main.cc +++ b/test/md5_update.cc @@ -1,6 +1,6 @@ -#include +#include -#include "md5_core.h" +#include "md5.h" std::string test_data() { char data[64]; @@ -10,7 +10,7 @@ std::string test_data() { return {data, data + 64}; } -void dump_ctx(const md5::md5_ctx *c) { +void dump_ctx(const md5::MD5::md5_ctx *c) { std::cout << std::hex << c->A << std::endl; std::cout << std::hex << c->B << std::endl; std::cout << std::hex << c->C << std::endl; @@ -18,10 +18,10 @@ void dump_ctx(const md5::md5_ctx *c) { std::cout << std::dec << c->size << std::endl; } -int main() { +TEST(md5sum, main) { auto data = test_data() + test_data() + test_data() + test_data(); - md5::md5_ctx c; + md5::MD5::md5_ctx c; dump_ctx(&c); // md5::md5_update(&c, data.c_str(), data.size()); @@ -33,10 +33,11 @@ int main() { // md5::md5_update(&c, data.c_str(), data.size()); // dump_ctx(&c); - md5::md5_final(&c, data.c_str(), 0); + md5::MD5::md5_final(&c, data.c_str(), 0); dump_ctx(&c); - md5::md5_reset(&c); - md5::md5_final(&c, data.c_str(), data.size()); + md5::MD5::md5_reset(&c); + md5::MD5::md5_final(&c, data.c_str(), data.size()); dump_ctx(&c); + } diff --git a/third_party/ThirdParty.cmake b/third_party/ThirdParty.cmake new file mode 100644 index 0000000..8356d91 --- /dev/null +++ b/third_party/ThirdParty.cmake @@ -0,0 +1,10 @@ +if (MD5_ENABLE_TESTING) + add_subdirectory(third_party/googletest/ EXCLUDE_FROM_ALL) +endif() + +if (MD5_ENABLE_BENCHMARK) + set(BENCHMARK_ENABLE_LTO ON) + set(BENCHMARK_ENABLE_TESTING OFF) + set(BENCHMARK_ENABLE_EXCEPTIONS OFF) + add_subdirectory(third_party/benchmark/ EXCLUDE_FROM_ALL) +endif() diff --git a/third_party/benchmark b/third_party/benchmark new file mode 160000 index 0000000..3441176 --- /dev/null +++ b/third_party/benchmark @@ -0,0 +1 @@ +Subproject commit 344117638c8ff7e239044fd0fa7085839fc03021 diff --git a/third_party/googletest b/third_party/googletest new file mode 160000 index 0000000..f8d7d77 --- /dev/null +++ b/third_party/googletest @@ -0,0 +1 @@ +Subproject commit f8d7d77c06936315286eb55f8de22cd23c188571