/*
								+----------------------------------+
								|                                  |
								| ***  Universal CRC template  *** |
								|                                  |
								|   Copyright © -tHE SWINe- 2008   |
								|                                  |
								|              Crc.h               |
								|                                  |
								+----------------------------------+
*/

#pragma once
#ifndef __CRC_INCLUDED
#define __CRC_INCLUDED

/**
 *	@file Crc.h
 *	@author -tHE SWINe-
 *	@date 2008
 *	@brief Universal CRC template implementation
 *
 *	@date 2010-07-31
 *
 *	Slightly changed ordering of operations in CRC processing loop, in order to make it more efficient.
 *	Changed algorithm from normal to reversed, changed polynomial roots and starting values accordingly.
 *	Added CCrc::n_Start() and CCrc::n_Finalize() functions to make implementation more clear. Preferred
 *	use is now this:
 *@code
 *	uint32_t n_crc = CCrc_32::n_Start();
 *	// start CRC calculation
 *
 *	for(;;) {
 *		size_t n_size = ...;
 *		const void *p_data = ...;
 *		// read some data
 *
 *		n_crc = CCrc_32::n_Crc(n_size, p_data, n_crc);
 *		// calculate CRC
 *	}
 *	// calculate CRC of one (or more) chunks of data
 *
 *	n_crc = CCrc_32::n_Finalize(n_crc);
 *	// do the final XOR@endcode
 *
 *	@date 2012-06-19
 *
 *	Moved multiple inclusion guard before file documentation comment.
 *
 *	Added a single-step crc function to be able to easily calculate CRC of a single block of data that
 *	completely fits in the memory at once (a convenience function).
 *
 */

#include "Integer.h"

/**
 *	@brief CRC calculation template
 *
 *	@param TScalar is CRC scalar type (uint16_t for crc16, uint32_t for crc32)
 *	@param n_polynom is CRC polynom
 *	@param n_start is CRC starting value
 *	@param n_final_xor is final reflection value (usually all 0's or all 1's)
 *
 *	@note It is possible to use predefined specializations CCrc_16 and CCrc_32,
 *		implementing standard CRC16 and CRC32 algorithms, respectively.
 */
template<class TScalar, const TScalar n_polynom, const TScalar n_start, const TScalar n_final_xor>
class CCrc {
public:
	/**
	 *	@brief gets CRC starting value
	 *	@return Returns starting value of CRC.
	 */
	static inline TScalar n_Start()
	{
		return n_start;
	}

	/**
	 *	@brief calculates final xor
	 *
	 *	@param[in] n_prev_crc is either n_Start() (CRC of empty buffer),
	 *		or result of previous call to n_Crc() (when calculating CRC of one or more
	 *		concatenated buffers)
	 *
	 *	@return Returns final value of CRC.
	 */
	static inline TScalar n_Finalize(TScalar n_prev_crc)
	{
		return n_prev_crc ^ n_final_xor;
	}

	/**
	 *	@brief calculates CRC on a stream of data
	 *
	 *	Calculates CRC of n_size bytes from buffer p_data.
	 *
	 *	@param[in] n_size is size of input data, in bytes
	 *	@param[in] p_data is input data buffer, contains n_size bytes
	 *	@param[in] n_prev_crc is either n_Start() (starting new CRC calculation),
	 *		or result of previous call to n_Crc() (when calculating CRC of more
	 *		concatenated buffers)
	 *
	 *	@return Returns CRC value, no data reflection, no final xor.
	 */
	static TScalar n_Crc(size_t n_size, const void *p_data, TScalar n_prev_crc /*= n_start*/) // otherwise ambiguous with the default param
	{
		const uint8_t *_p_data = (const uint8_t*)p_data;

		static TScalar p_crc_table[256];
		static bool b_crc_table_ready = false;
		if(!b_crc_table_ready) {
			b_crc_table_ready = true;
			for(int i = 0; i < 256; ++ i) {
				TScalar n_root = TScalar(i);
				for(int j = 0; j < 8; ++ j) {
					if(n_root & 1)
						n_root = (n_root >> 1) ^ n_polynom;
					else
						n_root >>= 1;
				}
				p_crc_table[i] = n_root;
			}
		}
		// prepare the table

		TScalar n_crc = n_prev_crc;
		for(const uint8_t *p_end = _p_data + n_size; _p_data != p_end; ++ _p_data)
			n_crc = p_crc_table[(n_crc ^ *_p_data) & 0xff] ^ (n_crc >> 8);
		// calculate CRC

		return n_crc;
	}

	/**
	 *	@brief calculates CRC
	 *
	 *	Calculates CRC of n_size bytes from buffer p_data.
	 *
	 *	@param[in] n_size is size of input data, in bytes
	 *	@param[in] p_data is input data buffer, contains n_size bytes
	 *
	 *	@return Returns the final CRC value.
	 */
	static inline TScalar n_Crc(size_t n_size, const void *p_data)
	{
		return n_Finalize(n_Crc(n_size, p_data, n_Start()));
	}
};

typedef CCrc<uint16_t, 0xA001, 0xffff, 0xffff> CCrc_16; /**< CRC-16, as defined by ANSI (X3.28) */
typedef CCrc<uint32_t, 0xedb88320U, 0xffffffffU, 0xffffffffU> CCrc_32; /**< CRC-32, as defined by POSIX */

#endif // !__CRC_INCLUDED
