Browse Source

feat: support compile-time calculation

master
Dnomd343 6 months ago
parent
commit
0da03cfb01
  1. 36
      src/impl/algorithm.inc
  2. 99
      src/impl/constexpr.inc
  3. 6
      src/impl/core.cc
  4. 4
      src/impl/inline.inc
  5. 28
      src/md5.h

36
src/impl/algorithm.inc

@ -12,11 +12,11 @@
#define MD5_CYC(w, s) (w = (w << s) | (w >> (32 - s))) #define MD5_CYC(w, s) (w = (w << s) | (w >> (32 - s)))
#define MD5_STEP(i, f, a, b, c, d) \ #define MD5_STEP(i, f, a, b, c, d) \
do { \ do { \
a += f(b, c, d) + MD5_DATA(i) + MD5_T(i); \ a += f(b, c, d) + block[K(i)] + T(i); \
MD5_CYC(a, MD5_S(i)); \ MD5_CYC(a, S(i)); \
a += b; \ a += b; \
} while (0) } while (0)
#define MD5_FF(i, ...) MD5_STEP((0x00 | i), MD5_F, __VA_ARGS__) #define MD5_FF(i, ...) MD5_STEP((0x00 | i), MD5_F, __VA_ARGS__)
@ -33,18 +33,34 @@
#define MD5_UPDATE \ #define MD5_UPDATE \
MD5_ROUND(MD5_FF) MD5_ROUND(MD5_GG) MD5_ROUND(MD5_HH) MD5_ROUND(MD5_II) 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. /// MD5 data block index, input between 0 and 63.
consteval int K(int i) { consteval int K(int i) {
const int step[] = {1, 5, 3, 7}; const int step[4] = {1, 5, 3, 7};
const int begin[] = {0, 1, 5, 0}; const int begin[4] = {0, 1, 5, 0};
return (begin[i >> 4] + step[i >> 4] * i) & 0b1111; return (begin[i >> 4] + step[i >> 4] * i) & 0b1111;
} }
/// MD5 circular shift times, input between 0 and 63. /// MD5 circular shift times, input between 0 and 63.
consteval int S(int i) { consteval int S(int i) {
const int shift[][4] = { const int shift[4][4] = {
{7, 12, 17, 22}, {7, 12, 17, 22},
{5, 9, 14, 20}, {5, 9, 14, 20},
{4, 11, 16, 23}, {4, 11, 16, 23},
@ -59,4 +75,4 @@ consteval uint32_t T(int i) {
return static_cast<uint32_t>(std::abs(val) * 0x100000000); return static_cast<uint32_t>(std::abs(val) * 0x100000000);
} }
} // namespace md5::value } // namespace md5

99
src/impl/constexpr.inc

@ -0,0 +1,99 @@
#pragma once
#include <array>
namespace md5 {
using BlockData = std::array<uint32_t, 16>;
// TODO: test the compile speed of T function
//static constexpr std::array<uint32_t, 64> 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<uint8_t>(((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<uint32_t>(GetByte(data, index + 0, len, padded_len));
auto b1 = static_cast<uint32_t>(GetByte(data, index + 1, len, padded_len));
auto b2 = static_cast<uint32_t>(GetByte(data, index + 2, len, padded_len));
auto b3 = static_cast<uint32_t>(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

6
src/impl/core.cc

@ -1,12 +1,6 @@
#include <cstring> #include <cstring>
#include "md5.h" #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 { namespace md5 {

4
src/impl/inline.inc

@ -31,4 +31,8 @@ inline std::string MD5::Hash(const void *data, uint64_t len) {
return md5.Digest(); return md5.Digest();
} }
inline constexpr CE MD5::HashCE(const std::string_view &data) {
return HashCE(data.data(), data.size());
}
} // namespace md5 } // namespace md5

28
src/md5.h

@ -3,14 +3,22 @@
#include <string> #include <string>
#include <cstdint> #include <cstdint>
// TODO: remove if not using tuple
#include <tuple>
static_assert(sizeof(uintptr_t) == 8, static_assert(sizeof(uintptr_t) == 8,
"Project only works on 64-bits architecture."); "Project only works on 64-bits architecture.");
static_assert(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__, static_assert(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__,
"Project only works on little-endian architecture."); "Project only works on little-endian architecture.");
#include "impl/algorithm.inc"
namespace md5 { namespace md5 {
// TODO: return string or tuple result?
using CE = std::tuple<uint32_t, uint32_t, uint32_t, uint32_t>;
class MD5 { class MD5 {
public: public:
MD5() = default; MD5() = default;
@ -37,19 +45,12 @@ public:
/// Calculate the md5 hash value of the specified data. /// Calculate the md5 hash value of the specified data.
static std::string Hash(const void *data, uint64_t len); static std::string Hash(const void *data, uint64_t len);
private: /// Calculate the md5 hash value of the specified data with constexpr.
static constexpr uint32_t MD5_A = 0x67452301; static constexpr CE HashCE(const std::string_view &data);
static constexpr uint32_t MD5_B = 0xefcdab89;
static constexpr uint32_t MD5_C = 0x98badcfe; /// Calculate the md5 hash value of the specified data with constexpr.
static constexpr uint32_t MD5_D = 0x10325476; // TODO: using `const void *` or `const char *`
static constexpr CE HashCE(const char *data, uint64_t len);
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
};
private: private:
md5_ctx ctx_; md5_ctx ctx_;
@ -66,3 +67,4 @@ private:
} // namespace md5 } // namespace md5
#include "impl/inline.inc" #include "impl/inline.inc"
#include "impl/constexpr.inc"

Loading…
Cancel
Save