Я пытаюсь реализовать простой POSIX cksum
используя Boost.CRC.
Код, который я использую, сводится к следующему:
for(int i = 1; i<argc; ++i)
{
support::file current(argv[i], support::file::access::read);
size_t octets = 0;
boost::crc_32_type crc;
while(true)
{
size_t bytes_read = current.read_some(buffer_size, buffer);
octets += bytes_read;
crc.process_bytes(&buffer[0], bytes_read);
if(bytes_read < buffer_size)
break;
}
if(i>1)
support::print("\n");
support::print(boost::lexical_cast<string>(crc.checksum()) + " " + boost::lexical_cast<string>(octets) + " " + argv[i]);
}
куда support::file
это простой fopen
/fread
бинарный файл ввода / вывода, который я успешно использовал для cat
реализация. support::print
дает тот же результат, что и std::cout
, но мне это нужно для надежного вывода без ASCII на Windows.
Заголовок Boost имеет это:
typedef crc_optimal<32, 0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF, true, true> crc_32_type;
как единственный 32-битный CRC typedef. Это дает неправильный ответ (проверено с помощью GNUWin32 coreutils cksum
) для пустого файла (touch test && cksum test
). Я попытался использовать вышеупомянутый typedef и изменить один или оба из 0xFFFFFFFF
значения до 0, я получаю правильный результат для пустого файла, но любой другой файл по-прежнему дает другие результаты.
Как Boost.CRC связан с POSIX cksum
Спецификация?
На самом деле это не ответ на ваш вопрос, а результаты проведенного мною расследования. Я имел обыкновение бороться с CRC, когда я создавал простую реализацию контроллера Ethernet в VHDL, и я делать Следует понимать, что реализации могут иногда быть очень разными по неизвестным причинам.
Хорошо, начнем. typedef
что вы нашли в boost/crc.hpp
:
typedef crc_optimal<32, 0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF, true, true> crc_32_type;
Это простое объявление генератора CRC, который будет генерировать CRC для Ethernet. Параметры шаблона следующие: Bits
(количество битов, выводимых генератором), TruncPoly
(полином, используемый генератором), InitRem
(начальный остаток для подачи в алгоритм перед обработкой первого байта ввода), FinalXor
(значение, которое должно быть на выходе XORed
после обработки всех байтов ввода), ReflectIn
а также ReflectRem
(если входные байты и / или выходные данные должны отражаться в битах, например, бит 0 становится битом 7 и т. д.). Ethernet не только требует, чтобы выходной сигнал был рассчитан с заданным полиномом, но также требует ограничений, которые вы можете прочитать из этого typedef.
Согласно спецификации cksum
, typedef для генератора CRC для него должен выглядеть примерно так:
typedef crc_optimal<32, 0x04C11DB7, 0, 0xFFFFFFFF, false, false> cksum_crc_type;
Это потому что :
Однако, когда дело доходит до cksum
в отличие от обычных CRC:
(…) сопровождаемый одним или несколькими октетами, представляющими длину
файл в виде двоичного значения, наименее значимый октет первым. Наименьший
число октетов, которые могут представлять это целое число.
Обычные генераторы CRC не учитывают количество октетов, которые были обработаны. Это также объясняет, почему вы получили хороший результат при обработке файла нулевой длины, но плохой при обработке больших файлов.
К сожалению, я не вижу простого решения этой проблемы. Я думаю, что вы могли бы сделать, это изменить process_bytes
метод следующим образом:
template < std::size_t Bits, BOOST_CRC_PARM_TYPE TruncPoly,
BOOST_CRC_PARM_TYPE InitRem, BOOST_CRC_PARM_TYPE FinalXor,
bool ReflectIn, bool ReflectRem >
inline
void
BOOST_CRC_OPTIMAL_NAME::process_bytes
(
void const * buffer,
std::size_t byte_count
)
{
unsigned char const * const b = static_cast<unsigned char const *>(
buffer );
process_block( b, b + byte_count );
for(; byte_count; byte_count >>= 8)
rem_ = (rem_ << 8) ^ crc_table_type::table_[((rem_ >> 24) ^ byte_count) & 0xFF];
}
При такой реализации метод дает тот же результат, что и cksum
, for
Любезность цикла GNU coreutils.
Надеюсь, я помог.
Других решений пока нет …