Click here to Skip to main content
15,894,405 members
Articles / Programming Languages / C++

CRC_32

Rate me:
Please Sign up or sign in to vote.
4.50/5 (9 votes)
9 Oct 2001CPOL3 min read 148.8K   5.5K   55  
A class that implements the CRC-32 Cyclic Redundancy Check Algorithm (MultiThreaded with Progress Bar support)
/****************************************************************************
CRC_32.cpp : implementation file for the CRC_32 class
written by PJ Arends
pja@telus.net

based on the CRC-32 code found at
http://www.createwindow.com/programming/crc32/crcfile.htm

For updates check http://www3.telus.net/pja/crc32.htm

-----------------------------------------------------------------------------
This code is provided as is, with no warranty as to it's suitability or usefulness
in any application in which it may be used. This code has not been tested for
UNICODE builds, nor has it been tested on a network ( with UNC paths ).

This code may be used in any way you desire. This file may be redistributed by any
means as long as it is not sold for profit, and providing that this notice and the
authors name are included.

If any bugs are found and fixed, a note to the author explaining the problem and
fix would be nice.
-----------------------------------------------------------------------------

created : October 2001
      
Revision History:

October 11, 2001   - changed SendMessage to PostMessage in CRC32ThreadProc

****************************************************************************/

#include "CRC_32.h"
#include <io.h>         // _close(), _read()
#include <fcntl.h>      // _O_* flags
#include <share.h>      // _SH_DENYWR
#include <tchar.h>      // UNICODE compatibility
#include <commctrl.h>   // PBM_* progress bar messages

// use a 100 KB buffer (a larger buffer does not seem to run
// significantly faster, but takes more RAM)
#define BUFFERSIZE 102400

/////////////////////////////////////////////////////////////////////////////
//
//  CRC_32::CRC32ThreadProc  (static private member function)
//    Calculates the CRC-32 value for a file or data buffer.
//
//  Parameters :
//    lpVoid [in] : A pointer to a CRCStruct structure, type casted as a void pointer
//
//  Returns :
//    The CRC-32 value of the supplied buffer or file
//
//  Note :
//    If this function is started as a thread, it will send PBM_* messages to the supplied
//    progress bar control. When the thread is finished, this function will send a 
//    WM_CRC_THREAD_DONE message to the parent window of the progress bar. the WPARAM parameter
//    will contain the HANDLE of the thread, and the LPARAM will contain the CRC-32 value
//    of the supplied buffer or file.
//
/////////////////////////////////////////////////////////////////////////////

DWORD WINAPI CRC_32::CRC32ThreadProc(LPVOID lpVoid)
{
    LPCRCStruct pCRCSt = (LPCRCStruct)lpVoid;
    ULONG CRC = 0xFFFFFFFF;
    HWND Progress = NULL;
    if (::IsWindow(pCRCSt->hWnd))
    {   // setup the progress bar
        Progress = pCRCSt->hWnd;
        ::PostMessage(Progress, PBM_SETPOS, 0, 0);
        ::PostMessage(Progress, PBM_SETRANGE32, 0, 100);
    }
    if (pCRCSt->pByte)
    {   // calculate CRC for a buffer
        for (UINT offset = 0; offset < pCRCSt->size; offset += BUFFERSIZE)
        {
            pCRCSt->pCRC_32->Calculate(pCRCSt->pByte + offset,
                                       (pCRCSt->size - offset > BUFFERSIZE) ? BUFFERSIZE : (pCRCSt->size - offset),
                                       CRC);
            if(::IsWindow(Progress))
            {
                int percent = offset > pCRCSt->size ? 100 : (int)(((double)offset / (double)pCRCSt->size) * 100);
                ::PostMessage(Progress, PBM_SETPOS, percent, 0);
            }
        }
    }
    else if (pCRCSt->FileName)
    {   // calculate CRC for a file
        LONGLONG done = 0;
        UINT size = BUFFERSIZE;
        BYTE buffer[BUFFERSIZE];

        // Open the file
        int theFile = _tsopen(pCRCSt->FileName, _O_RDONLY | _O_SEQUENTIAL | _O_BINARY, _SH_DENYWR);
        if (theFile != -1)
        {   // Get the file size
            _lseeki64(theFile, 0L, SEEK_SET);
            LONGLONG length = _lseeki64(theFile, 0L, SEEK_END);
            _lseeki64(theFile, 0L, SEEK_SET);
        
            // process the file
            while (size == BUFFERSIZE)
            {
                size = _read(theFile, buffer, BUFFERSIZE);
                if (size)
                {
                    pCRCSt->pCRC_32->Calculate(buffer, size, CRC);
                    if (::IsWindow(Progress))
                    {   // update the progress bar
                        done += size;
                        int percent = (int)(((long double)done / (long double)length) * 100);
                        ::PostMessage(Progress, PBM_SETPOS, percent, 0);
                    }
                }
            }
            _close(theFile);
        }
    }

    CRC ^= 0xFFFFFFFF;
    
    if(IsWindow(Progress))
        // notify dialog that we are done
        ::PostMessage(::GetParent(Progress), WM_CRC_THREAD_DONE, (WPARAM)pCRCSt->Thread, CRC);
    
    // clean up
    // Known Problem : If the thread is prematurely terminated,
    // this cleanup code is never run, resulting in a memory leak.
    delete pCRCSt->pByte;
    delete pCRCSt;

    // return our CRC-32 value
    return CRC;
}

/////////////////////////////////////////////////////////////////////////////
//
//  CRC_32 constructor  (public member function)
//    Sets up the CRC-32 reference table
//
//  Parameters :
//    None
//
//  Returns :
//    Nothing
//
/////////////////////////////////////////////////////////////////////////////

CRC_32::CRC_32()
{
    // This is the official polynomial used by CRC-32 
    // in PKZip, WinZip and Ethernet. 
    ULONG ulPolynomial = 0x04C11DB7;

    // 256 values representing ASCII character codes.
    for (int i = 0; i <= 0xFF; i++)
    {
        Table[i] = Reflect(i, 8) << 24;
        for (int j = 0; j < 8; j++)
            Table[i] = (Table[i] << 1) ^ (Table[i] & (1 << 31) ? ulPolynomial : 0);
        Table[i] = Reflect(Table[i], 32);
    }
}

/////////////////////////////////////////////////////////////////////////////
//
//  CRC_32::Reflect  (private member function)
//    used by the constructor to help set up the CRC-32 reference table
//
//  Parameters :
//    ref [in] : the value to be reflected
//    ch  [in] : the number of bits to move
//
//  Returns :
//    the new value
//
/////////////////////////////////////////////////////////////////////////////

ULONG CRC_32::Reflect(ULONG ref, char ch)
{
    ULONG value = 0;

    // Swap bit 0 for bit 7
    // bit 1 for bit 6, etc.
    for(int i = 1; i < (ch + 1); i++)
    {
        if (ref & 1)
            value |= 1 << (ch - i);
        ref >>= 1;
    }
    return value;
}

/////////////////////////////////////////////////////////////////////////////
//
//  CRC_32::Calculate  (private member function)
//    Calculates the CRC-32 value for the given buffer
//
//  Parameters :
//    buffer [in] : pointer to the data bytes
//    size   [in] : the size of the buffer
//    CRC    [in] : the initial CRC-32 value
//          [out] : the new CRC-32 value
//
//  Returns :
//    Nothing
//
/////////////////////////////////////////////////////////////////////////////

void CRC_32::Calculate(const LPBYTE buffer, UINT size, ULONG &CRC)
{   // calculate the CRC
    LPBYTE pbyte = buffer;

    while (size--)
        CRC = (CRC >> 8) ^ Table[(CRC & 0xFF) ^ *pbyte++];
}

/////////////////////////////////////////////////////////////////////////////
//
//  CRC_32::CalcCRC  (public member function)
//    calculates the CRC-32 value for the given buffer
//
//  Parameters :
//    buffer      [in] : a pointer to the data bytes
//    size        [in] : the size of the buffer
//    ProgressWnd [in] : the HWND of the progress bar
//
//  Returns :
//    if ProgressWnd is not a window returns the CRC-32 value of the buffer
//    if ProgressWnd is a window, returns the HANDLE of the created thread
//    returns NULL if an error occurs
//
//  Note :
//    ProgressWnd is passed through the IsWindow() API function. If IsWindow()
//    returns zero, CalcCRC() will calculate the CRC-32 value directly. if IsWindow()
//    returns nonzero, CalcCRC() will start a seperate thread. The thread will send 
//    PBM_* progress bar messages to the ProgressWnd. When the thread is finished,
//    the thread will send a WM_CRC_THREAD_DONE message to the parent window of the
//    ProgressWnd. the WPARAM parameter will contain the HANDLE of the thread, and
//    the LPARAM will contain the CRC-32 value of the buffer.
//
/////////////////////////////////////////////////////////////////////////////

DWORD CRC_32::CalcCRC(LPVOID buffer, UINT size, HWND ProgressWnd/*= NULL*/)
{   // check the validity of the data
    if (!buffer || !size)
        return 0;

    if (!IsWindow(ProgressWnd))
    {   // calculate CRC directly
        DWORD CRC = 0xFFFFFFFF;
        Calculate ((LPBYTE)buffer, size, CRC);
        return CRC ^ 0xFFFFFFFF;
    }

    // start the thread
    LPCRCStruct pCRCSt = new CRCStruct();
    DWORD ThreadID;
    HANDLE Handle = ::CreateThread(NULL, 0, CRC32ThreadProc, (LPVOID)pCRCSt, CREATE_SUSPENDED, &ThreadID);
    if (Handle)
    {   // thread successfully created
        pCRCSt->pCRC_32 = this;
        pCRCSt->FileName[0] = 0;
        pCRCSt->pByte = new BYTE[size];
        memcpy(pCRCSt->pByte, buffer, size);
        pCRCSt->size = size;
        pCRCSt->hWnd = ProgressWnd;
        pCRCSt->Thread = Handle;
        ::ResumeThread(Handle);
    }
    else // thread creation failed, clean up
        delete pCRCSt;
    return (DWORD)Handle;
}

/////////////////////////////////////////////////////////////////////////////
//
//  CRC_32::CalcCRC  (public member function)
//    calculates the CRC-32 value for the given buffer
//
//  Parameters :
//    FileName    [in] : the complete path to the file
//    ProgressWnd [in] : the HWND of the progress bar
//
//  Returns :
//    if ProgressWnd is not a window returns the CRC-32 value of the file
//    if ProgressWnd is a window, returns the HANDLE of the created thread
//    returns NULL if an error occurs
//
//  Note :
//    ProgressWnd is passed through the IsWindow() API function. If IsWindow()
//    returns zero, CalcCRC() will calculate the CRC-32 value directly. if IsWindow()
//    returns nonzero, CalcCRC() will start a seperate thread. The thread will send 
//    PBM_* progress bar messages to the ProgressWnd. When the thread is finished,
//    the thread will send a WM_CRC_THREAD_DONE message to the parent window of the
//    ProgressWnd. the WPARAM parameter will contain the HANDLE of the thread, and
//    the LPARAM will contain the CRC-32 value of the file.
//
/////////////////////////////////////////////////////////////////////////////

DWORD CRC_32::CalcCRC(LPCTSTR FileName, HWND ProgressWnd/*= NULL*/)
{   // make sure the file exists and is not a directory
    DWORD attrib = ::GetFileAttributes(FileName);
    if (attrib == 0xFFFFFFFF || attrib & FILE_ATTRIBUTE_DIRECTORY)
        return 0;

    // setup the CRCStruct
    LPCRCStruct pCRCSt = new CRCStruct();
    pCRCSt->pCRC_32 = this;
    _tcsncpy(pCRCSt->FileName, FileName, _MAX_PATH);
    pCRCSt->pByte = NULL;
    pCRCSt->size = 0;
    pCRCSt->hWnd = ProgressWnd;
    pCRCSt->Thread = NULL;

    if (!IsWindow(ProgressWnd))
    {   // calculate CRC directly
        return CRC32ThreadProc((LPVOID)pCRCSt);
    }

    // start the thread
    DWORD ThreadID;
    HANDLE Handle = ::CreateThread(NULL, 0, CRC32ThreadProc, (LPVOID)pCRCSt, CREATE_SUSPENDED, &ThreadID);
    if (Handle)
    {   // thread successfully created
        pCRCSt->Thread = Handle;
        ::ResumeThread(Handle);
    }
    else // thread creation failed, clean up
        delete pCRCSt;
    return (DWORD)Handle;
}

/////////////////////////////////////////////////////////////////////////////
//
//  End of CRC_32.cpp
//
/////////////////////////////////////////////////////////////////////////////



    

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
President
Canada Canada
Father of two, brother of two, child of two.
Spouse to one, uncle to many, friend to lots.
Farmer, carpenter, mechanic, electrician, but definitely not a plumber.
Likes walks with the wife, board games, card games, travel, and camping in the summer.
High school graduate, college drop-out.
Hobby programmer who knows C++ with MFC and the STL.
Has dabbled with BASIC, Pascal, Fortran, COBOL, C#, SQL, ASM, and HTML.
Realized long ago that programming is fun when there is nobody pressuring you with schedules and timelines.

Comments and Discussions