#include "stdafx.h"
#include <stdio.h>
#include <string.h>
#include "MakeCRC.h"
#include "MakeMD5.h"
#include "MakeSHA1.h"
#include "MakeSHA256.h"
#include "MakeSHA384.h"
#include "MakeSHA512.h"
#include "MakeRIPEMD128.h"
#include "MakeRIPEMD160.h"
#include "MakeRIPEMD256.h"
#include "MakeRIPEMD320.h"

// ハッシュの速度を計測する場合はWindows.hをインクルード(Windows版専用)
#include "Windows.h"

void HashSpeed();
BOOL HashTest();


void main(int argc, char *argv[])
{
    FILE *pFile;
    size_t size;
    char buf[32*1024];
    CMakeMD5 md5;


#ifdef _DEBUG
    if ( !HashTest() ) {
        printf("ハッシュ関数が正しい値を算出できませんでした\n");
        return;
    }
#endif


    if ( argc == 1 )
        return;

    // コマンドラインに /s /S が指定されている場合は、ハッシュ速度を測定
    if ( strcmp(argv[1],"/s")==0 || strcmp(argv[1],"/S")==0 ) {
        HashSpeed();
        return;
    }

    pFile = fopen(argv[1], "rb");
    if ( pFile == NULL ) {
        printf("ファイルがOpenできません\n");
        return;
    }

    // 32*1024＝32KB
    // ハッシュを計算するときはできるだけ2の乗数バイト数
    // （GetHashBlockSize()の倍数でも可）にすること
    // そうしないと計算速度が遅くなります
    size = fread(buf, 1, 32*1024, pFile);
    while ( size > 0 ) {
        md5.ComputeHash(buf, size);
        size = fread(buf, 1, 32*1024, pFile);
    }
    fclose(pFile);
    md5.GetHashString(buf, TRUE);
    printf("MD5sum = %s\n", buf);
}


BOOL HashTest()
{
    CMakeCRC16 crc16;
    CMakeCRC32 crc32;
    CMakeMD5 md5;
    CMakeSHA1 sha1;
    CMakeSHA256 sha256;
    CMakeSHA384 sha384;
    CMakeSHA512 sha512;
    CMakeRIPEMD128 rmd128;
    CMakeRIPEMD160 rmd160;
    CMakeRIPEMD256 rmd256;
    CMakeRIPEMD320 rmd320;
    int i=0;
    CMakeHashAlgorithm *pHash[] = { &crc16, &crc32, &md5, &sha1, &sha256, &sha384, &sha512,
                                    &rmd128, &rmd160, &rmd256, &rmd320, NULL };

    while ( pHash[i] ) {
        if ( !pHash[i]->Test() )
            return FALSE;
        i++;
    }

    return TRUE;
}


void HashSpeed()
{
// ハッシュを計算する速度を測定
// この測定関数はWindows専用です
// Windows以外の場合はタイマ等を適宜変更してください

    CMakeCRC16 crc16;
    CMakeCRC32 crc32;
    CMakeMD5 md5;
    CMakeSHA1 sha1;
    CMakeSHA256 sha256;
    CMakeSHA384 sha384;
    CMakeSHA512 sha512;
    CMakeRIPEMD128 rmd128;
    CMakeRIPEMD160 rmd160;
    CMakeRIPEMD256 rmd256;
    CMakeRIPEMD320 rmd320;
    CMakeHashAlgorithm *pHash[] = { &crc16, &crc32, &md5, &sha1, &sha256, &sha384, &sha512,
                                    &rmd128, &rmd160, &rmd256, &rmd320, NULL };

    __int64 start_time, end_time, freq_time, mbps, ms, hash_time[32];
    char *hash_name[32];
    DWORD hash_cmp;
    int i;
    BYTE *pBuffer, hashbuf[128];

    // タイマの分解能を取得
    BOOL ok=QueryPerformanceFrequency((LARGE_INTEGER*)&freq_time);
    if ( !ok ) {
        printf("Timerの作成に失敗しました\n");
        return;
    }

    i = 0;
    while ( pHash[i] ) {
        hash_name[i++] = pHash[i]->GetHashName();
    }
    hash_name[i] = NULL;

    // 10MBのバッファ作成
    pBuffer = new BYTE[10*1024*1024];
    memset(pBuffer, 0, 10*1024*1024);

    // プロセスの優先度を最高にする
#ifdef _DEBUG
    SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
#else
    SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
#endif

    i = 0;
    while ( pHash[i] ) {
        pHash[i]->ComputeHash(pBuffer, 10*1024*1024);
        pHash[i]->GetHashValue(hashbuf);
        pHash[i]->Initialize();
        QueryPerformanceCounter((LARGE_INTEGER*)&start_time);
        pHash[i]->ComputeHash(pBuffer, 10*1024*1024);
        pHash[i]->GetHashValue(hashbuf);
        QueryPerformanceCounter((LARGE_INTEGER*)&end_time);
        hash_time[i] = end_time - start_time;
        ms = (hash_time[i]*10000) / freq_time;
        mbps = ((__int64)(10*1024*1024)*freq_time) / hash_time[i];
        printf("%s : %.1fMB/s  [%.1fms]\n", hash_name[i], ((double)mbps)/(1024*1024), ((double)ms)/10);
        i++;
    }

    printf("\n比較 (sec)\n");
    i = 0;
    while ( pHash[i] ) {
        hash_cmp = (DWORD)((hash_time[i]*100)/hash_time[0]);
        if ( pHash[i+1] )
            printf("%u : ", hash_cmp);
        else
            printf("%u\n", hash_cmp);
        i++;
    }

    printf("\n比較 (MB/s)\n");
    i = 0;
    while ( pHash[i] ) {
        hash_cmp = (DWORD)((hash_time[0]*100)/hash_time[i]);
        if ( pHash[i+1] )
            printf("%u : ", hash_cmp);
        else
            printf("%u\n", hash_cmp);
        i++;
    }

    delete [] pBuffer;
    SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
}

