/*=============================================================================
|
| NAME
|  
|      crcCode.cpp
|
| DESCRIPTION
|  
|      CRC code implementation.
|    
| AUTHOR
|
|      Sean O'Connor
|
| LEGAL
|
|     CRCDemo Version 2.1 - A Program for generating and checking CRC codes.
|     Copyright (C) 1999-2017 by Sean Erik O'Connor.  All Rights Reserved.
|
|     This program is free software: you can redistribute it and/or modify
|     it under the terms of the GNU General Public License as published by
|     the Free Software Foundation, either version 3 of the License, or
|     (at your option) any later version.
|
|     This program is distributed in the hope that it will be useful,
|     but WITHOUT ANY WARRANTY; without even the implied warranty of
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
|     GNU General Public License for more details.
|
|     You should have received a copy of the GNU General Public License
|     along with this program.  If not, see <http://www.gnu.org/licenses/>.
|     
|     The author's address is artificer!AT!seanerikoconnor!DOT!freeservers!DOT!com
|     with !DOT! replaced by . and the !AT! replaced by @
|
+============================================================================*/

#include "dataTypes.h"     // 
#include "shiftRegister.h" // Binary linear feedback shift register class.
#include "crcCode.h"       // CRC code class.


// Construct a CRC code object.

CRCCode::CRCCode( const string & g,
                  syndrome_t shiftRegisterPreset,
                  bool invertParity )
        : shiftRegister_( g )
        , shiftRegisterPreset_( shiftRegisterPreset )
        , invertParity_( invertParity )
{
    // For CRC codes, the number of parity bits is
    // n-k, which is the same as the degree of the
    // CRC generator polynomial.
    numParityBits_ = shiftRegister_.getDegreeOfGeneratorPoly() ;
}


CRCCode::~CRCCode()
{
}


// Return the number of parity bytes.
int CRCCode::getNumParityBytes( void ) const
{
    return numParityBits_ / 8 ;
}


// Return the parity bits computed on the data in the buffer.
// Parity bits are in the most significant bits of the buffer.

syndrome_t
CRCCode::parityBits( unsigned char * buffer,
                     long int        n )
{
    // Clear the shift register to its preset value.
    shiftRegister_.clear( shiftRegisterPreset_ ) ;

    // Shift message bytes into the shift register.
    for (long int i = 0 ;  i < n ;  ++i)
        shiftRegister_.shiftInByte( buffer[ i ] ) ;

    // Return the parity bits.
    if (invertParity_)
        return ~shiftRegister_.shiftRegContents() ;
    else
        return shiftRegister_.shiftRegContents() ;
}


// Append CRC parity bytes to the end of the buffer.
// The buffer must be long enough to accomodate the
// extra parity bits.  Buffer length is increased
// upon exit.
void CRCCode::addParityToBuffer( unsigned char * buffer, long int n )
{
    // Compute the parity bits from the data.
    syndrome_t parity = parityBits( buffer, n ) ;


    // Initial mask is 0xFF00...00, whatever the word size.
    int downShiftAmount = 8 * (sizeof( parity ) - 1) ;
    syndrome_t mask = 0xFF << downShiftAmount ;

    // Append parity bits to the end of the buffer.
    for (int i = 0 ;  i < numParityBits_ / 8 ;  ++i)
    {
        buffer[ n++ ] = (unsigned char)
                        ((mask & parity) >> downShiftAmount) ;
        mask >>= 8 ;
        downShiftAmount -= 8 ;
    }

    return ;
}


// Compute the shifted syndrome of the codeword.
// Zero indicates we have a codeword.
syndrome_t
CRCCode::shiftedSyndrome( unsigned char * buffer,
                          long int        n )
{
    // Clear the shift register to its preset value.
    shiftRegister_.clear( shiftRegisterPreset_ ) ;

    // Invert parity bytes if necessary.
    if (invertParity_)
    {
        for (long int j = 0 ;  j < numParityBits_ / 8 ;  ++j)
            buffer[ (n-1)-j ] = ~buffer[ (n-1)-j ] ;
    }

    // Shift codeword bytes into the shift register.
    for (long int i = 0 ;  i < n ;  ++i)
        shiftRegister_.shiftInByte( buffer[ i ] ) ;

    // Return the shifted syndrome.
    // Zero means we have a codeword.
    return shiftRegister_.shiftRegContents() ;
}