diff --git a/src/impl/algorithm.inc b/src/impl/algorithm.inc index f099f28..db4b341 100644 --- a/src/impl/algorithm.inc +++ b/src/impl/algorithm.inc @@ -12,11 +12,11 @@ #define MD5_CYC(w, s) (w = (w << s) | (w >> (32 - s))) -#define MD5_STEP(i, f, a, b, c, d) \ - do { \ - a += f(b, c, d) + MD5_DATA(i) + MD5_T(i); \ - MD5_CYC(a, MD5_S(i)); \ - a += b; \ +#define MD5_STEP(i, f, a, b, c, d) \ + do { \ + a += f(b, c, d) + block[K(i)] + T(i); \ + MD5_CYC(a, S(i)); \ + a += b; \ } while (0) #define MD5_FF(i, ...) MD5_STEP((0x00 | i), MD5_F, __VA_ARGS__) @@ -33,18 +33,34 @@ #define MD5_UPDATE \ MD5_ROUND(MD5_FF) MD5_ROUND(MD5_GG) MD5_ROUND(MD5_HH) MD5_ROUND(MD5_II) -namespace md5::value { +#include "sine.inc" + +namespace md5 { + +/// MD5 fixed constants in little endian. +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; // processed size in byte +}; /// MD5 data block index, input between 0 and 63. consteval int K(int i) { - const int step[] = {1, 5, 3, 7}; - const int begin[] = {0, 1, 5, 0}; + const int step[4] = {1, 5, 3, 7}; + const int begin[4] = {0, 1, 5, 0}; return (begin[i >> 4] + step[i >> 4] * i) & 0b1111; } /// MD5 circular shift times, input between 0 and 63. consteval int S(int i) { - const int shift[][4] = { + const int shift[4][4] = { {7, 12, 17, 22}, {5, 9, 14, 20}, {4, 11, 16, 23}, @@ -59,4 +75,4 @@ consteval uint32_t T(int i) { return static_cast(std::abs(val) * 0x100000000); } -} // namespace md5::value +} // namespace md5 diff --git a/src/impl/constexpr.inc b/src/impl/constexpr.inc new file mode 100644 index 0000000..9820fdc --- /dev/null +++ b/src/impl/constexpr.inc @@ -0,0 +1,99 @@ +#pragma once + +#include + +namespace md5 { + +using BlockData = std::array; + +// TODO: test the compile speed of T function +//static constexpr std::array kT = { +// T(0x00), T(0x01), T(0x02), T(0x03), T(0x04), T(0x05), T(0x06), T(0x07), +// T(0x08), T(0x09), T(0x0a), T(0x0b), T(0x0c), T(0x0d), T(0x0e), T(0x0f), +// T(0x10), T(0x11), T(0x12), T(0x13), T(0x14), T(0x15), T(0x16), T(0x17), +// T(0x18), T(0x19), T(0x1a), T(0x1b), T(0x1c), T(0x1d), T(0x1e), T(0x1f), +// T(0x20), T(0x21), T(0x22), T(0x23), T(0x24), T(0x25), T(0x26), T(0x27), +// T(0x28), T(0x29), T(0x2a), T(0x2b), T(0x2c), T(0x2d), T(0x2e), T(0x2f), +// T(0x30), T(0x31), T(0x32), T(0x33), T(0x34), T(0x35), T(0x36), T(0x37), +// T(0x38), T(0x39), T(0x3a), T(0x3b), T(0x3c), T(0x3d), T(0x3e), T(0x3f), +//}; + +static constexpr void BlockUpdate(md5_ctx *ctx, const BlockData &block) { + auto A = ctx->A; + auto B = ctx->B; + auto C = ctx->C; + auto D = ctx->D; + + auto A_ = A; + auto B_ = B; + auto C_ = C; + auto D_ = D; + + MD5_UPDATE + + A += A_; + B += B_; + C += C_; + D += D_; + + ctx->A = A; + ctx->B = B; + ctx->C = C; + ctx->D = D; +} + +static constexpr uint8_t GetByte(const char *data, const uint64_t index, + const uint64_t len, const uint64_t padded_len) { + if (index < len) { + return data[index]; + } + if (index == len) { + return 0x80; + } + if (index >= padded_len - 8) { + const uint64_t i = index - (padded_len - 8); + return static_cast(((len * 8) >> (i * 8)) & 0xff); + } + return 0; +} + +static constexpr uint32_t GetWord(const char *data, const uint64_t index, + const uint64_t len, const uint64_t padded_len) { + auto b0 = static_cast(GetByte(data, index + 0, len, padded_len)); + auto b1 = static_cast(GetByte(data, index + 1, len, padded_len)); + auto b2 = static_cast(GetByte(data, index + 2, len, padded_len)); + auto b3 = static_cast(GetByte(data, index + 3, len, padded_len)); + return b0 | (b1 << 8) | (b2 << 16) | (b3 << 24); +} + +static constexpr BlockData GetBlock(const char *data, const uint64_t index, + const uint64_t len, const uint64_t padded_len) { + return BlockData { + GetWord(data, index + 0, len, padded_len), + GetWord(data, index + 4, len, padded_len), + GetWord(data, index + 8, len, padded_len), + GetWord(data, index + 12, len, padded_len), + GetWord(data, index + 16, len, padded_len), + GetWord(data, index + 20, len, padded_len), + GetWord(data, index + 24, len, padded_len), + GetWord(data, index + 28, len, padded_len), + GetWord(data, index + 32, len, padded_len), + GetWord(data, index + 36, len, padded_len), + GetWord(data, index + 40, len, padded_len), + GetWord(data, index + 44, len, padded_len), + GetWord(data, index + 48, len, padded_len), + GetWord(data, index + 52, len, padded_len), + GetWord(data, index + 56, len, padded_len), + }; +} + +inline constexpr CE MD5::HashCE(const char *data, uint64_t len) { + md5_ctx ctx; + const uint32_t padded_len = ((len + 64 + 8) / 64) * 64; + for (uint32_t index = 0; index < padded_len; index += 64) { + BlockUpdate(&ctx, GetBlock(data, index, len, padded_len)); + } + return {ctx.A, ctx.B, ctx.C, ctx.D}; +} + +} // namespace md5 diff --git a/src/impl/core.cc b/src/impl/core.cc index ce2b7f2..c6c7559 100644 --- a/src/impl/core.cc +++ b/src/impl/core.cc @@ -1,12 +1,6 @@ #include #include "md5.h" -#include "sine.inc" -#include "algorithm.inc" - -#define MD5_S(i) value::S(i) -#define MD5_T(i) value::T(i) -#define MD5_DATA(i) block[value::K(i)] namespace md5 { diff --git a/src/impl/inline.inc b/src/impl/inline.inc index e2641ae..f0eb470 100644 --- a/src/impl/inline.inc +++ b/src/impl/inline.inc @@ -31,4 +31,8 @@ inline std::string MD5::Hash(const void *data, uint64_t len) { return md5.Digest(); } +inline constexpr CE MD5::HashCE(const std::string_view &data) { + return HashCE(data.data(), data.size()); +} + } // namespace md5 diff --git a/src/md5.h b/src/md5.h index a7d682c..154115e 100644 --- a/src/md5.h +++ b/src/md5.h @@ -3,14 +3,22 @@ #include #include +// TODO: remove if not using tuple +#include + static_assert(sizeof(uintptr_t) == 8, "Project only works on 64-bits architecture."); static_assert(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__, "Project only works on little-endian architecture."); +#include "impl/algorithm.inc" + namespace md5 { +// TODO: return string or tuple result? +using CE = std::tuple; + class MD5 { public: MD5() = default; @@ -37,19 +45,12 @@ public: /// Calculate the md5 hash value of the specified data. static std::string Hash(const void *data, uint64_t len); -private: - 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; // processed size in byte - }; + /// Calculate the md5 hash value of the specified data with constexpr. + static constexpr CE HashCE(const std::string_view &data); + + /// Calculate the md5 hash value of the specified data with constexpr. + // TODO: using `const void *` or `const char *` + static constexpr CE HashCE(const char *data, uint64_t len); private: md5_ctx ctx_; @@ -66,3 +67,4 @@ private: } // namespace md5 #include "impl/inline.inc" +#include "impl/constexpr.inc"