// Boost CRC test program file ---------------------------------------------// // (C) Copyright Daryle Walker 2001. Permission to copy, use, modify, sell // and distribute this software is granted provided this copyright notice // appears in all copies. This software is provided "as is" without express or // implied warranty, and with no claim as to its suitability for any purpose. // Revision History // 10 May 01 Fixed VC6/gcc/Borland support. // 06 May 01 Make compatiblity fixes (Daryle Walker). // 19 Apr 01 Quick-fix endian problems in augmented-CRC tests (Daryle Walker). // 18 Apr 01 Use the Boost test tools (Daryle Walker). // 17 Apr 01 Make compatiblity fixes (Daryle Walker). // 28 Mar 01 Reword timing test messages (Daryle Walker). // 26 Mar 01 Corrected problem with error_test declaration (Daryle Walker). // 24 Mar 01 Adjust to header changes. Add test for messages that have their // CRC augumented at the message's end (Daryle Walker). // 23 Mar 01 Adjust to header changes; add error test (Daryle Walker). // 20 Mar 01 Add comparison with customized CRC function (Daryle Walker). // 18 Mar 01 Adjust to header changes (Daryle Walker). // 16 Mar 01 Refactor code; Adjust to header changes (Daryle Walker). // 02 Mar 01 Enforce 8-bits per byte restriction (Daryle Walker). // Add test for the basic (x + 1) CRC (Daryle Walker). // 13 Feb 01 Trust to #include for std::endl since // GCC is broken in that it doesn't have (Daryle Walker). // Added tests for single-byte and the new buffer-and-length // processing (Daryle Walker). // 11 Feb 01 Initial version (Daryle Walker). // Use the full Boost test tools in this file #define BOOST_INCLUDE_MAIN #include #include // for BOOST_MSVC, etc. #include // for boost::crc_basic, etc. #include // for boost::uint16_t, etc. #include // for boost::exit_success #include // for boost::uint_t #include // for boost::minstd_rand #include // for boost::timer #include // for std::for_each, std::generate_n, std::count #include // for CHAR_BIT #include // for std::size_t #include // for std::cout (std::ostream and std::endl indirectly) #if CHAR_BIT != 8 #error The expected results assume an eight-bit byte. #endif #ifndef BOOST_NO_DEPENDENT_TYPES_IN_TEMPLATE_VALUE_PARAMETERS #define CRC_PARM_TYPE typename boost::uint_t::fast #else #define CRC_PARM_TYPE unsigned long #endif #if !defined(BOOST_MSVC) && !defined(__GNUC__) #define PRIVATE_DECLARE_BOOST( TypeName ) using boost:: TypeName #else #define PRIVATE_DECLARE_BOOST( TypeName ) typedef boost:: TypeName TypeName #endif // Types template < std::size_t Bits, CRC_PARM_TYPE TrPo, CRC_PARM_TYPE InRe, CRC_PARM_TYPE FiXo, bool ReIn, bool ReRe > class crc_tester { public: // All the following were separate function templates, but they have // been moved to class-static member functions of a class template // because MS VC++ 6 can't handle function templates that can't // deduce all their template arguments from their function arguments. typedef typename boost::uint_t::fast value_type; static void master_test( char const *test_name, value_type expected ); private: typedef boost::crc_optimal optimal_crc_type; typedef boost::crc_basic basic_crc_type; static void compute_test( value_type expected ); static void interrupt_test( value_type expected ); static void error_test(); }; // crc_tester // Global data unsigned char const std_data[] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39 }; std::size_t const std_data_len = sizeof( std_data ) / sizeof( std_data[0] ); boost::uint16_t const std_crc_ccitt_result = 0x29B1; boost::uint16_t const std_crc_16_result = 0xBB3D; boost::uint32_t const std_crc_32_result = 0xCBF43926; // Function prototypes void timing_test(); boost::uint32_t basic_crc32( void const *buffer, std::size_t byte_count ); boost::uint32_t optimal_crc32( void const *buffer, std::size_t byte_count ); boost::uint32_t quick_crc32( void const *buffer, std::size_t byte_count ); boost::uint32_t quick_reflect( boost::uint32_t value, std::size_t bits ); double time_trial( char const *name, boost::uint32_t (*crc_func)(void const *, std::size_t), boost::uint32_t expected, void const *data, std::size_t length ); void augmented_tests(); boost::uint32_t native_to_big( boost::uint32_t x ); boost::uint32_t big_to_native( boost::uint32_t x ); // Macro to compact code #define PRIVATE_TESTER_NAME crc_tester // Run a test on slow and fast CRC computers and function template < std::size_t Bits, CRC_PARM_TYPE TrPo, CRC_PARM_TYPE InRe, CRC_PARM_TYPE FiXo, bool ReIn, bool ReRe > void PRIVATE_TESTER_NAME::compute_test ( typename PRIVATE_TESTER_NAME::value_type expected ) { std::cout << "\tDoing computation tests." << std::endl; optimal_crc_type fast_crc; basic_crc_type slow_crc( TrPo, InRe, FiXo, ReIn, ReRe ); value_type const func_result = boost::crc( std_data, std_data_len ); fast_crc.process_bytes( std_data, std_data_len ); slow_crc.process_bytes( std_data, std_data_len ); BOOST_TEST( fast_crc.checksum() == expected ); BOOST_TEST( slow_crc.checksum() == expected ); BOOST_TEST( func_result == expected ); } // Run a test in two runs, and check all the inspectors template < std::size_t Bits, CRC_PARM_TYPE TrPo, CRC_PARM_TYPE InRe, CRC_PARM_TYPE FiXo, bool ReIn, bool ReRe > void PRIVATE_TESTER_NAME::interrupt_test ( typename PRIVATE_TESTER_NAME::value_type expected ) { std::cout << "\tDoing interrupt tests." << std::endl; // Process the first half of the data (also test accessors) optimal_crc_type fast_crc1; basic_crc_type slow_crc1( fast_crc1.get_truncated_polynominal(), fast_crc1.get_initial_remainder(), fast_crc1.get_final_xor_value(), fast_crc1.get_reflect_input(), fast_crc1.get_reflect_remainder() ); BOOST_TEST( fast_crc1.get_interim_remainder() == slow_crc1.get_initial_remainder() ); std::size_t const mid_way = std_data_len / 2; unsigned char const * const std_data_end = std_data + std_data_len; fast_crc1.process_bytes( std_data, mid_way ); slow_crc1.process_bytes( std_data, mid_way ); BOOST_TEST( fast_crc1.checksum() == slow_crc1.checksum() ); // Process the second half of the data (also test accessors) boost::crc_optimal fast_crc2( fast_crc1.get_interim_remainder() ); boost::crc_basic slow_crc2( slow_crc1.get_truncated_polynominal(), slow_crc1.get_interim_remainder(), slow_crc1.get_final_xor_value(), slow_crc1.get_reflect_input(), slow_crc1.get_reflect_remainder() ); fast_crc2.process_block( std_data + mid_way, std_data_end ); slow_crc2.process_block( std_data + mid_way, std_data_end ); BOOST_TEST( fast_crc2.checksum() == slow_crc2.checksum() ); BOOST_TEST( fast_crc2.checksum() == expected ); BOOST_TEST( slow_crc2.checksum() == expected ); } // Run a test to see if a single-bit error is detected template < std::size_t Bits, CRC_PARM_TYPE TrPo, CRC_PARM_TYPE InRe, CRC_PARM_TYPE FiXo, bool ReIn, bool ReRe > void PRIVATE_TESTER_NAME::error_test ( ) { PRIVATE_DECLARE_BOOST( uint32_t ); // A single-bit error is ensured to be detected if the polynominal // has at least two bits set. The highest bit is what is removed // to give the truncated polynominal, and it is always set. This // means that the truncated polynominal needs at least one of its // bits set, which implies that it cannot be zero. if ( !(TrPo & boost::detail::mask_uint_t::sig_bits_fast) ) { BOOST_CRITICAL_ERROR( "truncated CRC polymonial is zero" ); } std::cout << "\tDoing error tests." << std::endl; // Create a random block of data uint32_t ran_data[ 256 ]; std::size_t const ran_length = sizeof(ran_data) / sizeof(ran_data[0]); std::generate_n( ran_data, ran_length, boost::minstd_rand() ); // Create computers and compute the checksum of the data optimal_crc_type fast_tester; basic_crc_type slow_tester( TrPo, InRe, FiXo, ReIn, ReRe ); fast_tester.process_bytes( ran_data, sizeof(ran_data) ); slow_tester.process_bytes( ran_data, sizeof(ran_data) ); uint32_t const fast_checksum = fast_tester.checksum(); uint32_t const slow_checksum = slow_tester.checksum(); BOOST_TEST( fast_checksum == slow_checksum ); // Do the checksum again (and test resetting ability) fast_tester.reset(); slow_tester.reset( InRe ); fast_tester.process_bytes( ran_data, sizeof(ran_data) ); slow_tester.process_bytes( ran_data, sizeof(ran_data) ); BOOST_TEST( fast_tester.checksum() == slow_tester.checksum() ); BOOST_TEST( fast_tester.checksum() == fast_checksum ); BOOST_TEST( slow_tester.checksum() == slow_checksum ); // Produce a single-bit error ran_data[ ran_data[0] % ran_length ] ^= ( 1 << (ran_data[1] % 32) ); // Compute the checksum of the errorenous data // (and continue testing resetting ability) fast_tester.reset( InRe ); slow_tester.reset(); fast_tester.process_bytes( ran_data, sizeof(ran_data) ); slow_tester.process_bytes( ran_data, sizeof(ran_data) ); BOOST_TEST( fast_tester.checksum() == slow_tester.checksum() ); BOOST_TEST( fast_tester.checksum() != fast_checksum ); BOOST_TEST( slow_tester.checksum() != slow_checksum ); } // Run the other CRC object tests template < std::size_t Bits, CRC_PARM_TYPE TrPo, CRC_PARM_TYPE InRe, CRC_PARM_TYPE FiXo, bool ReIn, bool ReRe > void PRIVATE_TESTER_NAME::master_test ( char const * test_name, typename PRIVATE_TESTER_NAME::value_type expected ) { std::cout << "Doing test suite for " << test_name << '.' << std::endl; compute_test( expected ); interrupt_test( expected ); error_test(); } // Undo limited macros #undef PRIVATE_TESTER_NAME // A CRC-32 computer based on crc_basic, for timing boost::uint32_t basic_crc32 ( void const * buffer, std::size_t byte_count ) { static boost::crc_basic<32> computer( 0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF, true, true ); computer.reset(); computer.process_bytes( buffer, byte_count ); return computer.checksum(); } // A CRC-32 computer based on crc_optimal, for timing inline boost::uint32_t optimal_crc32 ( void const * buffer, std::size_t byte_count ) { static boost::crc_32_type computer; computer.reset(); computer.process_bytes( buffer, byte_count ); return computer.checksum(); } // Reflect the lower "bits" bits of a "value" boost::uint32_t quick_reflect ( boost::uint32_t value, std::size_t bits ) { boost::uint32_t reflection = 0; for ( std::size_t i = 0 ; i < bits ; ++i ) { if ( value & (1u << i) ) { reflection |= 1 << ( bits - 1 - i ); } } return reflection; } // A customized CRC-32 computer, for timing boost::uint32_t quick_crc32 ( void const * buffer, std::size_t byte_count ) { PRIVATE_DECLARE_BOOST( uint32_t ); typedef unsigned char byte_type; // Compute the CRC table (first run only) static bool did_init = false; static uint32_t crc_table[ 1ul << CHAR_BIT ]; if ( !did_init ) { uint32_t const value_high_bit = static_cast(1) << 31u; byte_type dividend = 0; do { uint32_t remainder = 0; for ( byte_type mask = 1u << (CHAR_BIT - 1u) ; mask ; mask >>= 1 ) { if ( dividend & mask ) { remainder ^= value_high_bit; } if ( remainder & value_high_bit ) { remainder <<= 1; remainder ^= 0x04C11DB7u; } else { remainder <<= 1; } } crc_table[ quick_reflect(dividend, CHAR_BIT) ] = quick_reflect( remainder, 32 ); } while ( ++dividend ); did_init = true; } // Compute the CRC of the data uint32_t rem = 0xFFFFFFFF; byte_type const * const b_begin = static_cast( buffer ); byte_type const * const b_end = b_begin + byte_count; for ( byte_type const *p = b_begin ; p < b_end ; ++p ) { byte_type const byte_index = *p ^ rem; rem >>= CHAR_BIT; rem ^= crc_table[ byte_index ]; } return ~rem; } // Run an individual timing trial double time_trial ( char const * name, boost::uint32_t (*crc_func)(void const *, std::size_t), boost::uint32_t expected, void const * data, std::size_t length ) { PRIVATE_DECLARE_BOOST( uint32_t ); using std::cout; // Limits of a trial static uint32_t const max_count = 1L << 16; // ~square-root of max static double const max_time = 3.14159; // easy as pi(e) // Mark the trial cout << '\t' << name << " CRC-32: "; // Trial loop uint32_t trial_count = 0, wrong_count = 0; double elapsed_time = 0.0; boost::timer t; do { uint32_t const scratch = (*crc_func)( data, length ); if ( scratch != expected ) { ++wrong_count; } elapsed_time = t.elapsed(); ++trial_count; } while ( (trial_count < max_count) && (elapsed_time < max_time) ); if ( wrong_count ) { BOOST_ERROR( "at least one time trial didn't match expected" ); } // Report results double const rate = trial_count / elapsed_time; cout << trial_count << " runs, " << elapsed_time << " s, " << rate << " run/s" << std::endl; return rate; } // Time runs of Boost CRCs vs. a customized CRC function void timing_test ( ) { PRIVATE_DECLARE_BOOST( uint32_t ); using std::cout; using std::endl; cout << "Doing timing tests." << endl; // Create a random block of data boost::int32_t ran_data[ 256 ]; std::size_t const ran_length = sizeof(ran_data) / sizeof(ran_data[0]); std::generate_n( ran_data, ran_length, boost::minstd_rand() ); // Use the first runs as a check. This gives a chance for first- // time static initialization to not interfere in the timings. uint32_t const basic_result = basic_crc32( ran_data, sizeof(ran_data) ); uint32_t const optimal_result = optimal_crc32( ran_data, sizeof(ran_data) ); uint32_t const quick_result = quick_crc32( ran_data, sizeof(ran_data) ); BOOST_TEST( basic_result == optimal_result ); BOOST_TEST( optimal_result == quick_result ); BOOST_TEST( quick_result == basic_result ); // Run trials double const basic_rate = time_trial( "Boost-Basic", basic_crc32, basic_result, ran_data, sizeof(ran_data) ); double const optimal_rate = time_trial( "Boost-Optimal", optimal_crc32, optimal_result, ran_data, sizeof(ran_data) ); double const quick_rate = time_trial( "Reference", quick_crc32, quick_result, ran_data, sizeof(ran_data) ); // Report results cout << "\tThe optimal Boost version is " << (quick_rate - optimal_rate) / quick_rate * 100.0 << "% slower than the reference version.\n"; cout << "\tThe basic Boost version is " << (quick_rate - basic_rate) / quick_rate * 100.0 << "% slower than the reference version.\n"; cout << "\tThe basic Boost version is " << (optimal_rate - basic_rate) / optimal_rate * 100.0 << "% slower than the optimal Boost version." << endl; } // Reformat an integer to the big-endian storage format boost::uint32_t native_to_big ( boost::uint32_t x ) { boost::uint32_t temp; unsigned char * tp = reinterpret_cast( &temp ); for ( std::size_t i = sizeof(x) ; i > 0 ; --i ) { tp[ i - 1 ] = static_cast( x ); x >>= CHAR_BIT; } return temp; } // Restore an integer from the big-endian storage format boost::uint32_t big_to_native ( boost::uint32_t x ) { boost::uint32_t temp = 0; unsigned char * xp = reinterpret_cast( &x ); for ( std::size_t i = 0 ; i < sizeof(x) ; ++i ) { temp <<= CHAR_BIT; temp |= xp[ i ]; } return temp; } // Run tests on using CRCs on augmented messages void augmented_tests ( ) { #define PRIVATE_ACRC_FUNC boost::augmented_crc<32, 0x04C11DB7> using std::size_t; PRIVATE_DECLARE_BOOST( uint32_t ); std::cout << "Doing CRC-augmented message tests." << std::endl; // Create a random block of data, with space for a CRC. uint32_t ran_data[ 257 ]; size_t const ran_length = sizeof(ran_data) / sizeof(ran_data[0]); size_t const data_length = ran_length - 1; std::generate_n( ran_data, data_length, boost::minstd_rand() ); // When creating a CRC for an augmented message, use // zeros in the appended CRC spot for the first run. uint32_t & ran_crc = ran_data[ data_length ]; ran_crc = 0; // Compute the CRC with augmented-CRC computing function typedef boost::uint_t<32>::fast return_type; ran_crc = PRIVATE_ACRC_FUNC( ran_data, sizeof(ran_data) ); // With the appended CRC set, running the checksum again should get zero. // NOTE: CRC algorithm assumes numbers are in big-endian format ran_crc = native_to_big( ran_crc ); uint32_t ran_crc_check = PRIVATE_ACRC_FUNC( ran_data, sizeof(ran_data) ); BOOST_TEST( 0 == ran_crc_check ); // Compare that result with other CRC computing functions // and classes, which don't accept augmented messages. typedef boost::crc_optimal<32, 0x04C11DB7> fast_crc_type; typedef boost::crc_basic<32> slow_crc_type; fast_crc_type fast_tester; slow_crc_type slow_tester( 0x04C11DB7 ); size_t const data_size = data_length * sizeof(ran_data[0]); uint32_t const func_tester = boost::crc<32, 0x04C11DB7, 0, 0, false, false>( ran_data, data_size ); fast_tester.process_bytes( ran_data, data_size ); slow_tester.process_bytes( ran_data, data_size ); BOOST_TEST( fast_tester.checksum() == slow_tester.checksum() ); ran_crc = big_to_native( ran_crc ); BOOST_TEST( fast_tester.checksum() == ran_crc ); BOOST_TEST( func_tester == ran_crc ); // Do a single-bit error test ran_crc = native_to_big( ran_crc ); ran_data[ ran_data[0] % ran_length ] ^= ( 1 << (ran_data[1] % 32) ); ran_crc_check = PRIVATE_ACRC_FUNC( ran_data, sizeof(ran_data) ); BOOST_TEST( 0 != ran_crc_check ); // Run a version of these tests with a nonzero initial remainder. uint32_t const init_rem = ran_data[ ran_data[2] % ran_length ]; ran_crc = 0; ran_crc = PRIVATE_ACRC_FUNC( ran_data, sizeof(ran_data), init_rem ); // Have some fun by processing data in two steps. size_t const mid_index = ran_length / 2; ran_crc = native_to_big( ran_crc ); ran_crc_check = PRIVATE_ACRC_FUNC( ran_data, mid_index * sizeof(ran_data[0]), init_rem ); ran_crc_check = PRIVATE_ACRC_FUNC( &ran_data[mid_index], sizeof(ran_data) - mid_index * sizeof(ran_data[0]), ran_crc_check ); BOOST_TEST( 0 == ran_crc_check ); // This substep translates an augmented-CRC initial // remainder to an unaugmented-CRC initial remainder. uint32_t const zero = 0; uint32_t const new_init_rem = PRIVATE_ACRC_FUNC( &zero, sizeof(zero), init_rem ); slow_crc_type slow_tester2( 0x04C11DB7, new_init_rem ); slow_tester2.process_bytes( ran_data, data_size ); ran_crc = big_to_native( ran_crc ); BOOST_TEST( slow_tester2.checksum() == ran_crc ); // Redo single-bit error test ran_data[ ran_data[3] % ran_length ] ^= ( 1 << (ran_data[4] % 32) ); ran_crc_check = PRIVATE_ACRC_FUNC( ran_data, sizeof(ran_data), init_rem ); BOOST_TEST( 0 != ran_crc_check ); #undef PRIVATE_ACRC_FUNC } // Explicit template instantiations // (needed to fix a link error in Metrowerks CodeWarrior Pro 5.3) #ifndef BOOST_MSVC template class crc_tester<16, 0x1021, 0xFFFF, 0, false, false>; template class crc_tester<16, 0x8005, 0, 0, true, true>; template class crc_tester<32, 0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF, true, true>; #endif // Main testing function int test_main ( int , // "argc" is unused char * [] // "argv" is unused ) { using std::cout; using std::endl; // Run simulations on some CRC types typedef crc_tester<16, 0x1021, 0xFFFF, 0, false, false> crc_ccitt_tester; typedef crc_tester<16, 0x8005, 0, 0, true, true> crc_16_tester; typedef crc_tester<32, 0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF, true, true> crc_32_tester; crc_ccitt_tester::master_test( "CRC-CCITT", std_crc_ccitt_result ); crc_16_tester::master_test( "CRC-16", std_crc_16_result ); crc_32_tester::master_test( "CRC-32", std_crc_32_result ); // Run a timing comparison test timing_test(); // Test using augmented messages augmented_tests(); // Try a CRC based on the (x + 1) polynominal, which is a factor in // many real-life polynominals and doesn't fit evenly in a byte. cout << "Doing one-bit polynominal CRC test." << endl; boost::crc_basic<1> crc_1( 1 ); crc_1.process_bytes( std_data, std_data_len ); BOOST_TEST( crc_1.checksum() == 1 ); // Test the function object interface cout << "Doing functional object interface test." << endl; boost::crc_optimal<16, 0x8005, 0, 0, true, true> crc_16; crc_16 = std::for_each( std_data, std_data + std_data_len, crc_16 ); BOOST_TEST( crc_16() == std_crc_16_result ); return boost::exit_success; }