+#include <cmath>
+#include <stdexcept>
+#include "md5.h"
+
+using namespace std;
+
+namespace Msp {
+namespace Crypto {
+
+namespace {
+
+template<unsigned N>
+inline UInt32 func(UInt32, UInt32, UInt32);
+
+template<>
+UInt32 func<1>(UInt32 x, UInt32 y, UInt32 z)
+{
+ return (y&x) | (z&~x);
+}
+
+template<>
+UInt32 func<2>(UInt32 x, UInt32 y, UInt32 z)
+{
+ return (x&z) | (y&~z);
+}
+
+template<>
+UInt32 func<3>(UInt32 x, UInt32 y, UInt32 z)
+{
+ return x^y^z;
+}
+
+template<>
+UInt32 func<4>(UInt32 x, UInt32 y, UInt32 z)
+{
+ return y^(x|~z);
+}
+
+inline UInt32 rotate_left(UInt32 x, UInt32 b)
+{
+ return (x<<b) | (x>>(32-b));
+}
+
+}
+
+
+UInt32 MD5::sin_table[64] = { 0 };
+unsigned MD5::rotate_table[16] =
+{
+ 7, 12, 17, 22,
+ 5, 9, 14, 20,
+ 4, 11, 16, 23,
+ 6, 10, 15, 21
+};
+
+MD5::MD5()
+{
+ init();
+}
+
+MD5::MD5(const char *data, unsigned len)
+{
+ init();
+ update(data, len);
+}
+
+MD5::MD5(const string &str)
+{
+ init();
+ update(str);
+}
+
+void MD5::init()
+{
+ buffer[0] = 0x67452301;
+ buffer[1] = 0xefcdab89;
+ buffer[2] = 0x98badcfe;
+ buffer[3] = 0x10325476;
+ processed_bytes = 0;
+ unprocessed_bytes = 0;
+
+ if(!sin_table[0])
+ for(unsigned i=0; i<64; ++i)
+ sin_table[i] = 4294967296.0*abs(sin((i+1)*1.0));
+
+ if(!rotate_table[0])
+ {
+ for(unsigned i=0; i<4; ++i)
+ rotate_table[i] = 7+i*5;
+ for(unsigned i=0; i<4; ++i)
+ rotate_table[4+i] = 5+i*4+i*i/3;
+ for(unsigned i=0; i<4; ++i)
+ rotate_table[8+i] = 4+i*7;
+ for(unsigned i=0; i<4; ++i)
+ rotate_table[12+i] = 6+i*4+i*i/3;
+ }
+}
+
+void MD5::update(const char *data, unsigned len)
+{
+ if(unprocessed_bytes && unprocessed_bytes+len>=64)
+ {
+ unsigned needed = 64-unprocessed_bytes;
+ copy(data, data+needed, unprocessed+unprocessed_bytes);
+ process_block(unprocessed);
+ data += needed;
+ len -= needed;
+ unprocessed_bytes = 0;
+ }
+
+ while(len>=64)
+ {
+ process_block(data);
+ data += 64;
+ len -= 64;
+ }
+
+ if(len>0)
+ {
+ copy(data, data+len, unprocessed+unprocessed_bytes);
+ unprocessed_bytes += len;
+ }
+}
+
+unsigned MD5::get_digest(char *digest, unsigned len) const
+{
+ if(len<16)
+ throw invalid_argument("MD5::get_digest");
+
+ MD5 padded = *this;
+
+ char padding[64] = { static_cast<char>(0x80) };
+ padded.update(padding, 64-(unprocessed_bytes+8)%64);
+
+ UInt64 message_length = (processed_bytes+unprocessed_bytes)*8;
+ for(unsigned i=0; i<8; ++i)
+ padding[i] = message_length>>(i*8);
+ padded.update(padding, 8);
+
+ for(unsigned i=0; i<16; ++i)
+ digest[i] = padded.buffer[i/4]>>((i%4)*8);
+
+ return 16;
+}
+
+void MD5::process_block(const char *data)
+{
+ UInt32 input_words[16];
+
+ const UInt8 *u8data = reinterpret_cast<const UInt8 *>(data);
+ for(unsigned i=0; i<16; ++i)
+ input_words[i] = (u8data[i*4+3]<<24) | (u8data[i*4+2]<<16) | (u8data[i*4+1]<<8) | u8data[i*4];
+
+ UInt32 work_buffer[4];
+ copy(buffer, buffer+4, work_buffer);
+
+ perform_round<1, 0, 1>(work_buffer, input_words);
+ perform_round<2, 1, 5>(work_buffer, input_words);
+ perform_round<3, 5, 3>(work_buffer, input_words);
+ perform_round<4, 0, 7>(work_buffer, input_words);
+
+ for(unsigned i=0; i<4; ++i)
+ buffer[i] += work_buffer[i];
+
+ processed_bytes += 64;
+}
+
+template<unsigned N, unsigned START, unsigned DELTA>
+inline void MD5::perform_round(UInt32 *work_buffer, const UInt32 *input_words)
+{
+ for(unsigned i=0; i<4; ++i)
+ for(unsigned j=0; j<4; ++j)
+ {
+ unsigned k = i*4+j;
+ UInt32 &a = work_buffer[(4-j)&3];
+ UInt32 &b = work_buffer[(5-j)&3];
+ UInt32 &c = work_buffer[(6-j)&3];
+ UInt32 &d = work_buffer[(7-j)&3];
+ UInt32 sum = a + func<N>(b, c, d) + input_words[(START+k*DELTA)&15] + sin_table[(N-1)*16+k];
+ a = b + rotate_left(sum, rotate_table[(N-1)*4+j]);
+ }
+}
+
+} // namespace Crypto
+} // namespace Msp