// 直接贴代码了,具体原理有很多地方都有描述,这里给出实现方便对比自己的算法到底什么地方错,算法是绝对没有问题的,计算大型文件MD5的效率还可以优化的。
struct IIStream
{
    virtual size_t GetLength() = 0;
    virtual size_t Read(size_t size_,unsigned char * buff_out_) = 0;
    virtual bool   Eof() = 0;
    virtual bool   Valid() = 0;
    virtual void   Release() = 0;
};

struct IOStream
{
    virtual void Init(const char * param) = 0;
    virtual void OnRecvData(unsigned char * data_,size_t size_) = 0;
    virtual bool Valid() = 0;
    virtual void Release() = 0;
};

IIStream * CreateStdFileStream(const char * szPath );
IIStream * CreateMemoryStream(unsigned char * pData,size_t size);


struct SimpleProgressMotintor:public IProgress
{
    void OnProcess(size_t bytes_total,size_t bytes_processed,size_t bytes_got);
};

struct StdFileStream:public IIStream
{
    FILE * m_file;
    unsigned long m_iLen;
    StdFileStream(const char * szPath);

    bool Valid();
    size_t GetLength();
    size_t Read(size_t size_,unsigned char * buff_out_);

    bool Eof();
    void Release();
};



struct FileWriter:public IOStream
{
    FILE *      m_file;
    FileWriter();

    void Init(const char * param);
    virtual void OnRecvData(unsigned char * data_,size_t size_);
    bool Valid();
    void Release();
};

#include "ZUtils.h"

void * AllocateMem(size_t size_)
{
    return malloc(size_);
}

void RecycleMem(void * p)
{
    free(p);
}

struct MemoryStream:public IIStream
{
    unsigned char *          m_pData;
    unsigned char *          m_pEnd;
    unsigned char *          m_pCurr;

    MemoryStream( unsigned char * pData, size_t size)
    {
        m_pData = pData;
        m_pEnd = pData + size;
        m_pCurr = m_pData;
    }

    bool Valid()
    {
        return m_pData;
    }

    size_t GetLength()
    {
        return m_pEnd - m_pData;
    }

    size_t Read(size_t size_,unsigned char * buff_out_)
    {
        unsigned long left = m_pEnd - m_pCurr;
        if(left > size_)
        {
            memcpy(buff_out_,m_pCurr,size_);
            m_pCurr += size_;
            return size_;
        }
        else
        {
            memcpy(buff_out_,m_pCurr,left);
            m_pCurr += left;
            return left;
        }
    }

    bool Eof()
    {
        return (m_pCurr >= m_pEnd)?true:false;
    }
    void Release()
    {
        delete this;
    }
};

IIStream * CreateMemoryStream(unsigned char * pData,size_t size)
{
    MemoryStream * pObj = new MemoryStream(pData,size);
    return pObj;
}

IIStream * CreateStdFileStream(const char * szPath )
{
    return new StdFileStream(szPath);
}

void SimpleProgressMotintor::OnProcess(size_t bytes_total,size_t bytes_processed,size_t bytes_got)
{
    printf("bytes total : %d bytes processed : %d bytes got : %d",bytes_total,bytes_processed,bytes_got);
}


StdFileStream::StdFileStream(const char * szPath)
{
    m_file = fopen(szPath,"rb+");
    if(m_file)
    {
        m_iLen = ftell(m_file);
        fseek(m_file,0,SEEK_END);
        m_iLen = ftell(m_file) - m_iLen;
        fseek(m_file,0,SEEK_SET);
    }
    else
    {
        m_iLen = 0;
    }
}

bool StdFileStream::Valid()
{
    return m_iLen > 0;
}
size_t StdFileStream::GetLength()
{
    return m_iLen;
}
size_t StdFileStream::Read(size_t size_,unsigned char * buff_out_)
{
    return fread(buff_out_,1,size_,m_file);
}

bool StdFileStream::Eof()
{
    return feof(m_file);
}

void StdFileStream::Release()
{
    if(m_file)
        fclose(m_file);
    delete this;
}

FileWriter::FileWriter()
{

}

void FileWriter::Init(const char * param)
{
    m_file = fopen(param,"wb+");
}
void FileWriter::OnRecvData(unsigned char * data_,size_t size_)
{
    fwrite(data_,1,size_,m_file);
}
bool FileWriter::Valid()
{
    return !!m_file;
}
void FileWriter::Release()
{
    fclose(m_file);
    delete this;
}

#ifndef MD5_H_INCLUDED
#define MD5_H_INCLUDED

#include <cassert>

#ifndef uint
typedef unsigned int uint;
#endif

const extern size_t align_size;
const extern size_t align_mod_size;
const extern size_t align_end_size; // 8 ×ú 64

struct MD5Calc
{
    char m_helpBuff[128];

	uint A,B,C,D;
	uint a,b,c,d;

	uint F(uint& x,uint& y,uint& z);
	uint G(uint& x,uint& y,uint& z);
	uint H(uint& x,uint& y,uint& z);
	uint I(uint& x,uint& y,uint& z);

	void FF(uint& a,uint& b,uint& c,uint& d,uint& x,uint y,uint& z);
	void GG(uint& a,uint& b,uint& c,uint& d,uint& x,uint y,uint& z);
	void HH(uint& a,uint& b,uint& c,uint& d,uint& x,uint y,uint& z);
	void II(uint& a,uint& b,uint& c,uint& d,uint& x,uint y,uint& z);
    MD5Calc();

    void MD5(const char * szFile);
	void Turn( uint * chunk);
	void PrintMD5();
};

size_t calc_md5_fill_n( size_t nData );

#endif // MD5_H_INCLUDED

#include "md5.h"
#include "ZUtils.h"
#include <cmath>
#include <cstdio>

struct MD5IStream:public IIStream
{
    IIStream * m_FileStream;
    IIStream * m_MemStream;
    unsigned char m_helpBuff[128];

    MD5IStream(const char * szFilepath)
    {
        m_FileStream = CreateStdFileStream(szFilepath);
        long long datalen = m_FileStream->GetLength();
        long long md5_fill_n = calc_md5_fill_n(m_FileStream->GetLength());
        long long buffsize = md5_fill_n + align_end_size;
        m_helpBuff[0] = 128;
        memset(m_helpBuff + 1,0,md5_fill_n -1);
        long long datalenbytes = datalen * 8;
        memcpy(m_helpBuff + md5_fill_n,&datalenbytes,sizeof(datalenbytes));
        m_MemStream = CreateMemoryStream(m_helpBuff,buffsize);
    }

    size_t GetLength()
    {
        return m_FileStream->GetLength() + m_MemStream->GetLength();
    }
    virtual size_t Read(size_t size_,unsigned char * buff_out_)
    {
        size_t read = 0;
        if(!m_FileStream->Eof())
        {
            read += m_FileStream->Read(size_,buff_out_);
            if(read < size_)
            {
                read += m_MemStream->Read(size_ - read,buff_out_ + read);
            }
            return read;
        }
        else
        {
            return m_MemStream->Read(size_,buff_out_);
        }
    }
    virtual bool   Eof()
    {
        return m_FileStream->Eof() && m_MemStream->Eof();
    }
    virtual bool   Valid()
    {
        return m_FileStream && m_MemStream;
    }
    virtual void   Release()
    {
        m_FileStream->Release();
        m_MemStream->Release();
    }
};

char chain_values[] =
{
    0x01,0x23,0x45,0x67,
    0x89,0xab,0xcd,0xef,
    0xfe,0xdc,0xba,0x98,
    0x76,0x54,0x32,0x10
};

uint T[] =
{
	0x00000000,
	0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,
	0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,
	0x698098d8,0x8b44f7af,0xffff5bb1,0x895cd7be,
	0x6b901122,0xfd987193,0xa679438e,0x49b40821,

	0xf61e2562,0xc040b340,0x265e5a51,0xe9b6c7aa,
	0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8,
	0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,
	0xa9e3e905,0xfcefa3f8,0x676f02d9,0x8d2a4c8a,

	0xfffa3942,0x8771f681,0x6d9d6122,0xfde5380c,
	0xa4beea44,0x4bdecfa9,0xf6bb4b60,0xbebfbc70,
	0x289b7ec6,0xeaa127fa,0xd4ef3085,0x04881d05,
	0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,

	0xf4292244,0x432aff97,0xab9423a7,0xfc93a039,
	0x655b59c3,0x8f0ccc92,0xffeff47d,0x85845dd1,
	0x6fa87e4f,0xfe2ce6e0,0xa3014314,0x4e0811a1,
	0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391
};

/*
unsigned int T(float i)
{
    double s = sin(i);
    if (s > 0) {
        return 4294967296.f * s;
    }
    else
    {
        return 4294967296.f * -s;
    }
}
*/

const size_t align_size = 64;
const size_t align_mod_size = 56;
const size_t align_end_size = sizeof(long long int); //

unsigned int CLS_32( unsigned int i , unsigned int offset)
{
    assert(offset < 32 && offset > 0);
    return i<<offset|i>>(32-offset);
}

MD5Calc::MD5Calc()
{
}

uint MD5Calc::F(uint& x,uint& y,uint& z)
{
	return (x&y)|((~x)&z);
}
uint MD5Calc::G(uint& x,uint& y,uint& z)
{
	return (x&z)|(y&(~z));
}
uint MD5Calc::H(uint& x,uint& y,uint& z)
{
	return x^y^z;
}
uint MD5Calc::I(uint& x,uint& y,uint& z)
{
	return y^(x|(~z));
}

void MD5Calc::FF(uint& a,uint& b,uint& c,uint& d,uint& x,uint y,uint& z)
{
	a = b+CLS_32(a+F(b,c,d)+x+z,y);
}
void MD5Calc::GG(uint& a,uint& b,uint& c,uint& d,uint& x,uint y,uint& z)
{
	a = b+CLS_32(a+G(b,c,d)+x+z,y);
}
void MD5Calc::HH(uint& a,uint& b,uint& c,uint& d,uint& x,uint y,uint& z)
{
	a = b+CLS_32(a+H(b,c,d)+x+z,y);
}
void MD5Calc::II(uint& a,uint& b,uint& c,uint& d,uint& x,uint y,uint& z)
{
	a = b+CLS_32(a+I(b,c,d)+x+z,y);
}

void MD5Calc::MD5(const char * szFile)
{
    A = *(uint*)chain_values;
    B = *((uint*)chain_values + 1);
    C = *((uint*)chain_values + 2);
    D = *((uint*)chain_values + 3);

    MD5IStream md5IStream(szFile);
    unsigned char buff[64];
    while(!md5IStream.Eof())
    {
        md5IStream.Read(64,buff);
        this->Turn((uint *)&buff[0]);
    }
    md5IStream.Release();
}

void MD5Calc::Turn(uint * chunk)
{
	//
	a = A;
	b = B;
	c = C;
	d = D;

    FF(a,b,c,d,chunk[0],7,T[1]);FF(d,a,b,c,chunk[1],12,T[2]);FF(c,d,a,b,chunk[2],17,T[3]);FF(b,c,d,a,chunk[3],22,T[4]);
    FF(a,b,c,d,chunk[4],7,T[5]);FF(d,a,b,c,chunk[5],12,T[6]);FF(c,d,a,b,chunk[6],17,T[7]);FF(b,c,d,a,chunk[7],22,T[8]);
    FF(a,b,c,d,chunk[8],7,T[9]);FF(d,a,b,c,chunk[9],12,T[10]);FF(c,d,a,b,chunk[10],17,T[11]);FF(b,c,d,a,chunk[11],22,T[12]);
    FF(a,b,c,d,chunk[12],7,T[13]);FF(d,a,b,c,chunk[13],12,T[14]);FF(c,d,a,b,chunk[14],17,T[15]);FF(b,c,d,a,chunk[15],22,T[16]);
	//
	GG(a,b,c,d,chunk[1],5,T[17]);GG(d,a,b,c,chunk[6],9,T[18]);GG(c,d,a,b,chunk[11],14,T[19]);GG(b,c,d,a,chunk[0],20,T[20]);
	GG(a,b,c,d,chunk[5],5,T[21]);GG(d,a,b,c,chunk[10],9,T[22]);GG(c,d,a,b,chunk[15],14,T[23]);GG(b,c,d,a,chunk[4],20,T[24]);
	GG(a,b,c,d,chunk[9],5,T[25]);GG(d,a,b,c,chunk[14],9,T[26]);GG(c,d,a,b,chunk[3],14,T[27]);GG(b,c,d,a,chunk[8],20,T[28]);
	GG(a,b,c,d,chunk[13],5,T[29]);GG(d,a,b,c,chunk[2],9,T[30]);GG(c,d,a,b,chunk[7],14,T[31]);GG(b,c,d,a,chunk[12],20,T[32]);
	//
	HH(a,b,c,d,chunk[5],4,T[33]);HH(d,a,b,c,chunk[8],11,T[34]);HH(c,d,a,b,chunk[11],16,T[35]);HH(b,c,d,a,chunk[14],23,T[36]);
	HH(a,b,c,d,chunk[1],4,T[37]);HH(d,a,b,c,chunk[4],11,T[38]);HH(c,d,a,b,chunk[7],16,T[39]);HH(b,c,d,a,chunk[10],23,T[40]);
	HH(a,b,c,d,chunk[13],4,T[41]);HH(d,a,b,c,chunk[0],11,T[42]);HH(c,d,a,b,chunk[3],16,T[43]);HH(b,c,d,a,chunk[6],23,T[44]);
	HH(a,b,c,d,chunk[9],4,T[45]);HH(d,a,b,c,chunk[12],11,T[46]);HH(c,d,a,b,chunk[15],16,T[47]);HH(b,c,d,a,chunk[2],23,T[48]);
	//
	II(a,b,c,d,chunk[0],6,T[49]); II(d,a,b,c,chunk[7],10,T[50]); II(c,d,a,b,chunk[14],15,T[51]);II(b,c,d,a,chunk[5],21,T[52]);
	II(a,b,c,d,chunk[12],6,T[53]);II(d,a,b,c,chunk[3],10,T[54]); II(c,d,a,b,chunk[10],15,T[55]);II(b,c,d,a,chunk[1],21,T[56]);
	II(a,b,c,d,chunk[8],6,T[57]); II(d,a,b,c,chunk[15],10,T[58]);II(c,d,a,b,chunk[6], 15,T[59]); II(b,c,d,a,chunk[13],21,T[60]);
	II(a,b,c,d,chunk[4],6,T[61]); II(d,a,b,c,chunk[11],10,T[62]);II(c,d,a,b,chunk[2], 15,T[63]); II(b,c,d,a,chunk[9],21,T[64]);

    A += a;
    B += b;
    C += c;
    D += d;
}

void MD5Calc::PrintMD5()
{
    uint array[] = {A,B,C,D};
    for(int i = 0;i<sizeof(array);i++)
    {
        unsigned char cc = *(((unsigned char *)array)+i);
        printf("%02x",cc);
    }
}

size_t calc_md5_fill_n( size_t nData )
{
    if(nData & (align_size-1))
    {
        unsigned int t = (nData + align_size - 1) & ~(align_size-1);
        if (t - align_end_size > nData)
        {
            return  t - align_end_size - nData;
        }
        else
        {
            return t + align_mod_size;
        }
    }
    else //  64a¥–°μ’ ±
    {
        return align_mod_size;
    }
}

#include <iostream>
#include <cstring>
#include <cstdio>
#include "md5.h"

using namespace std;

int main()
{
    MD5Calc calc;

    calc.MD5("DG8Setup_1311Beta.exe");

    calc.PrintMD5();

    return 0;
}