Files
pip/libs/main/math/picrc.h
2026-03-12 14:46:57 +03:00

561 lines
19 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! \~\file picrc.h
//! \~\ingroup Math
//! \~\brief
//! \~english CRC checksum calculation
//! \~russian Вычисление CRC контрольной суммы
/*
PIP - Platform Independent Primitives
CRC checksum calculator
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PICRC_H
#define PICRC_H
#include "pistring.h"
//! \~\ingroup Math
//! \~\brief
//! \~english Fixed-width unsigned helper used by generic CRC implementations.
//! \~russian Вспомогательный беззнаковый тип фиксированной ширины для обобщенных реализаций CRC.
template<int L>
class PIP_EXPORT uint_cl {
public:
//! \~english Constructs a zero-filled value.
//! \~russian Создает значение, заполненное нулями.
uint_cl() {
for (int i = 0; i < L / 8; ++i)
data_[i] = 0;
}
//! \~english Constructs a copy.
//! \~russian Создает копию.
uint_cl(const uint_cl<L> & v) {
for (int i = 0; i < L / 8; ++i)
data_[i] = v.data_[i];
}
uint_cl(uchar v) {
for (int i = 0; i < L / 8; ++i)
data_[i] = (i == 0 ? v : 0);
}
uint_cl(char v) {
for (int i = 0; i < L / 8; ++i)
data_[i] = (i == 0 ? v : 0);
}
uint_cl(ushort v) {
int l = piMin<uint>(L / 8, sizeof(v));
memcpy(data_, &v, l);
for (int i = l; i < L / 8; ++i)
data_[i] = 0;
}
uint_cl(short v) {
int l = piMin<uint>(L / 8, sizeof(v));
memcpy(data_, &v, l);
for (int i = l; i < L / 8; ++i)
data_[i] = 0;
}
uint_cl(uint v) {
int l = piMin<uint>(L / 8, sizeof(v));
memcpy(data_, &v, l);
for (int i = l; i < L / 8; ++i)
data_[i] = 0;
}
uint_cl(int v) {
int l = piMin<uint>(L / 8, sizeof(v));
memcpy(data_, &v, l);
for (int i = l; i < L / 8; ++i)
data_[i] = 0;
}
uint_cl(ulong v) {
int l = piMin<uint>(L / 8, sizeof(v));
memcpy(data_, &v, l);
for (int i = l; i < L / 8; ++i)
data_[i] = 0;
}
uint_cl(long v) {
int l = piMin<uint>(L / 8, sizeof(v));
memcpy(data_, &v, l);
for (int i = l; i < L / 8; ++i)
data_[i] = 0;
}
uint_cl(ullong v) {
int l = piMin<uint>(L / 8, sizeof(v));
memcpy(data_, &v, l);
for (int i = l; i < L / 8; ++i)
data_[i] = 0;
}
uint_cl(llong v) {
int l = piMin<uint>(L / 8, sizeof(v));
memcpy(data_, &v, l);
for (int i = l; i < L / 8; ++i)
data_[i] = 0;
}
//! \~english Returns \c true when at least one bit is set.
//! \~russian Возвращает \c true, если установлен хотя бы один бит.
operator bool() {
for (int i = 0; i < L / 8; ++i)
if (data_[i] > 0) return true;
return false;
}
operator char() { return (char)data_[0]; }
operator short() {
short t(0);
int l = piMin<uint>(L / 8, sizeof(t));
memcpy(&t, data_, l);
return t;
}
operator int() {
int t(0);
int l = piMin<uint>(L / 8, sizeof(t));
memcpy(&t, data_, l);
return t;
}
operator long() {
long t(0);
int l = piMin<uint>(L / 8, sizeof(t));
memcpy(&t, data_, l);
return t;
}
operator llong() {
llong t(0);
int l = piMin<uint>(L / 8, sizeof(t));
memcpy(&t, data_, l);
return t;
}
operator uchar() { return data_[0]; }
operator ushort() {
ushort t(0);
int l = piMin<uint>(L / 8, sizeof(t));
memcpy(&t, data_, l);
return t;
}
operator uint() {
uint t(0);
int l = piMin<uint>(L / 8, sizeof(t));
memcpy(&t, data_, l);
return t;
}
operator ulong() {
ulong t(0);
int l = piMin<uint>(L / 8, sizeof(t));
memcpy(&t, data_, l);
return t;
}
operator ullong() {
ullong t(0);
int l = piMin<uint>(L / 8, sizeof(t));
memcpy(&t, data_, l);
return t;
}
//! \~english Adds two fixed-width values with carry propagation.
//! \~russian Складывает два значения фиксированной ширины с переносом.
uint_cl<L> operator+(const uint_cl<L> & v) {
uint_cl<L> t;
uint cv;
bool ov = false;
for (int i = 0; i < L / 8; ++i) {
cv = v.data_[i] + data_[i];
if (ov) ++cv;
ov = cv > 255;
t.data_[i] = ov ? cv - 256 : cv;
}
return t;
}
//! \~english Returns bitwise AND with another value.
//! \~russian Возвращает побитовое И с другим значением.
uint_cl<L> operator&(const uint_cl<L> & v) const {
uint_cl<L> t;
for (int i = 0; i < L / 8; ++i)
t.data_[i] = v.data_[i] & data_[i];
return t;
}
uint_cl<L> operator&(const uchar & v) const { return *this & uint_cl<L>(v); }
uint_cl<L> operator&(const ushort & v) const { return *this & uint_cl<L>(v); }
uint_cl<L> operator&(const uint & v) const { return *this & uint_cl<L>(v); }
uint_cl<L> operator&(const ulong & v) const { return *this & uint_cl<L>(v); }
uint_cl<L> operator&(const ullong & v) const { return *this & uint_cl<L>(v); }
uint_cl<L> operator&(const char & v) const { return *this & uint_cl<L>(v); }
uint_cl<L> operator&(const short & v) const { return *this & uint_cl<L>(v); }
uint_cl<L> operator&(const int & v) const { return *this & uint_cl<L>(v); }
uint_cl<L> operator&(const long & v) const { return *this & uint_cl<L>(v); }
uint_cl<L> operator&(const llong & v) const { return *this & uint_cl<L>(v); }
//! \~english Returns bitwise OR with another value.
//! \~russian Возвращает побитовое ИЛИ с другим значением.
uint_cl<L> operator|(const uint_cl<L> & v) const {
uint_cl<L> t;
for (int i = 0; i < L / 8; ++i)
t.data_[i] = v.data_[i] | data_[i];
return t;
}
uint_cl<L> operator|(const uchar & v) const { return *this | uint_cl<L>(v); }
uint_cl<L> operator|(const ushort & v) const { return *this | uint_cl<L>(v); }
uint_cl<L> operator|(const uint & v) const { return *this | uint_cl<L>(v); }
uint_cl<L> operator|(const ulong & v) const { return *this | uint_cl<L>(v); }
uint_cl<L> operator|(const ullong & v) const { return *this | uint_cl<L>(v); }
uint_cl<L> operator|(const char & v) const { return *this | uint_cl<L>(v); }
uint_cl<L> operator|(const short & v) const { return *this | uint_cl<L>(v); }
uint_cl<L> operator|(const int & v) const { return *this | uint_cl<L>(v); }
uint_cl<L> operator|(const long & v) const { return *this | uint_cl<L>(v); }
uint_cl<L> operator|(const llong & v) const { return *this | uint_cl<L>(v); }
//! \~english Returns bitwise XOR with another value.
//! \~russian Возвращает побитовое исключающее ИЛИ с другим значением.
uint_cl<L> operator^(const uint_cl<L> & v) const {
uint_cl<L> t;
for (int i = 0; i < L / 8; ++i)
t.data_[i] = v.data_[i] ^ data_[i];
return t;
}
uint_cl<L> operator^(const uchar & v) const { return *this ^ uint_cl<L>(v); }
uint_cl<L> operator^(const ushort & v) const { return *this ^ uint_cl<L>(v); }
uint_cl<L> operator^(const uint & v) const { return *this ^ uint_cl<L>(v); }
uint_cl<L> operator^(const ulong & v) const { return *this ^ uint_cl<L>(v); }
uint_cl<L> operator^(const ullong & v) const { return *this ^ uint_cl<L>(v); }
uint_cl<L> operator^(const char & v) const { return *this ^ uint_cl<L>(v); }
uint_cl<L> operator^(const short & v) const { return *this ^ uint_cl<L>(v); }
uint_cl<L> operator^(const int & v) const { return *this ^ uint_cl<L>(v); }
uint_cl<L> operator^(const long & v) const { return *this ^ uint_cl<L>(v); }
uint_cl<L> operator^(const llong & v) const { return *this ^ uint_cl<L>(v); }
//! \~english Less than operator
//! \~russian Оператор меньше
bool operator<(const uint_cl<L> & v) const {
for (int i = 0; i < L / 8; ++i) {
if (v.data_[i] > data_[i]) return true;
if (v.data_[i] < data_[i]) return false;
}
return false;
}
//! \~english Less than or equal operator
//! \~russian Оператор меньше или равно
bool operator<=(const uint_cl<L> & v) const {
for (int i = 0; i < L / 8; ++i) {
if (v.data_[i] > data_[i]) return true;
if (v.data_[i] < data_[i]) return false;
}
return true;
}
//! \~english Greater than operator
//! \~russian Оператор больше
bool operator>(const uint_cl<L> & v) const {
for (int i = 0; i < L / 8; ++i) {
if (v.data_[i] < data_[i]) return true;
if (v.data_[i] > data_[i]) return false;
}
return false;
}
//! \~english Greater than or equal operator
//! \~russian Оператор больше или равно
bool operator>=(const uint_cl<L> & v) const {
for (int i = 0; i < L / 8; ++i) {
if (v.data_[i] < data_[i]) return true;
if (v.data_[i] > data_[i]) return false;
}
return true;
}
//! \~english Equality operator
//! \~russian Оператор равенства
bool operator==(const uint_cl<L> & v) const {
for (int i = 0; i < L / 8; ++i)
if (v.data_[i] != data_[i]) return false;
return true;
}
//! \~english Inequality operator
//! \~russian Оператор неравенства
bool operator!=(const uint_cl<L> & v) const {
for (int i = 0; i < L / 8; ++i)
if (v.data_[i] != data_[i]) return true;
return false;
}
//! \~english Less than or equal operator (specialized for 8-bit)
//! \~russian Оператор меньше или равно (специализация для 8 бит)
bool operator<=(const uint_cl<8> & v1) { return (*(uchar *)data()) <= (*(uchar *)v1.data()); }
//! \~english Returns a value shifted right by the specified bit count.
//! \~russian Возвращает значение, сдвинутое вправо на указанное число битов.
uint_cl<L> operator>>(const int & c) const {
uint_cl<L> t;
int l = L - c;
bool bit;
if (l <= 0) return t;
for (int i = 0; i < l; ++i) {
bit = 1 & (data_[(i + c) / 8] >> ((i + c) % 8));
if (bit)
t.data_[i / 8] |= (1 << (i % 8));
else
t.data_[i / 8] &= ~(1 << (i % 8));
}
return t;
}
uint_cl<L> operator>>(const uint & c) const { return (*this << (int)c); }
//! \~english Returns a value shifted left by the specified bit count.
//! \~russian Возвращает значение, сдвинутое влево на указанное число битов.
uint_cl<L> operator<<(const int & c) const {
uint_cl<L> t;
int l = L - c;
bool bit;
if (l <= 0) return t;
for (int i = c; i < L; ++i) {
bit = 1 & (data_[(i - c) / 8] >> ((i - c) % 8));
if (bit)
t.data_[i / 8] |= (1 << (i % 8));
else
t.data_[i / 8] &= ~(1 << (i % 8));
}
return t;
}
uint_cl<L> operator<<(const uint & c) const { return (*this >> (int)c); }
//! \~english Inverts all bits in place.
//! \~russian Инвертирует все биты на месте.
uint_cl<L> & inverse() const {
for (int i = 0; i < L / 8; ++i)
data_[i] = ~data_[i];
return *this;
}
//! \~english Returns a copy with all bits inverted.
//! \~russian Возвращает копию с инвертированными битами.
uint_cl<L> inversed() const {
uint_cl<L> t(*this);
for (int i = 0; i < L / 8; ++i)
t.data_[i] = ~t.data_[i];
return t;
}
//! \~english Returns a copy with bit order reversed.
//! \~russian Возвращает копию с обратным порядком битов.
uint_cl<L> reversed() const {
uint_cl<L> t;
bool bit;
for (int i = 0; i < L; ++i) {
bit = 1 & (data_[(L - i - 1) / 8] >> ((L - i - 1) % 8));
if (bit)
t.data_[i / 8] |= (1 << (i % 8));
else
t.data_[i / 8] &= ~(1 << (i % 8));
}
return t;
}
//! \~english Returns raw byte storage.
//! \~russian Возвращает сырое байтовое представление.
const uchar * data() const { return data_; }
//! \~english Returns raw byte storage.
//! \~russian Возвращает сырое байтовое представление.
uchar * data() { return data_; }
//! \~english Returns storage length in bytes.
//! \~russian Возвращает длину хранения в байтах.
uint length() const { return L / 8; }
private:
uchar data_[L / 8];
};
//! \~english Returns a byte with reversed bit order.
//! \~russian Возвращает байт с обратным порядком битов.
inline uchar reverseByte(uchar b) {
uchar ret = 0;
bool bit;
for (int i = 0; i < 8; ++i) {
bit = 1 & (b >> (7 - i));
if (bit) ret |= (1 << i);
}
return ret;
}
//! \~\ingroup Math
//! \~\brief
//! \~english Generic table-driven CRC calculator for a polynomial width \a L.
//! \~russian Универсальный табличный калькулятор CRC для полинома ширины \a L.
template<uint L, typename N = uint_cl<L>>
class PIP_EXPORT PICRC {
public:
//! \~english Constructs a calculator with the specified polynomial and default CRC conventions.
//! \~russian Создает калькулятор с указанным полиномом и стандартными настройками CRC.
PICRC(const N & poly = N()) {
poly_ = poly;
reverse_poly = true;
init_ = inversed(N(0));
out_ = inversed(N(0));
reverse_before_xor = reverse_data = false;
initTable();
}
//! \~english Constructs a calculator with fully specified initialization parameters.
//! \~russian Создает калькулятор с полностью заданными параметрами инициализации.
PICRC(const N & poly, bool reverse_poly_, const N & initial, const N & out_xor) {
poly_ = poly;
reverse_poly = reverse_poly_;
init_ = initial;
out_ = out_xor;
reverse_before_xor = reverse_data = false;
initTable();
}
//! \~english Sets the initial CRC value.
//! \~russian Устанавливает начальное значение CRC.
void setInitial(const N & v) { init_ = v; }
//! \~english Sets the final XOR value.
//! \~russian Устанавливает финальное значение XOR.
void setOutXor(const N & v) { out_ = v; }
//! \~english Enables or disables polynomial bit reversal and rebuilds the lookup table.
//! \~russian Включает или отключает реверс полинома и перестраивает таблицу поиска.
void setReversePolynome(bool yes) {
reverse_poly = yes;
initTable();
}
//! \~english Reverses the resulting CRC before applying the output XOR.
//! \~russian Разворачивает итоговый CRC перед применением выходного XOR.
void setReverseOutBeforeXOR(bool yes) { reverse_before_xor = yes; }
//! \~english Reverses bits in each input byte before processing.
//! \~russian Разворачивает биты в каждом входном байте перед обработкой.
void setReverseDataBytes(bool yes) { reverse_data = yes; }
//! \~english Rebuilds the 256-entry lookup table for the current polynomial settings.
//! \~russian Перестраивает таблицу из 256 элементов для текущих настроек полинома.
void initTable() {
N tmp, pol = reverse_poly ? reversed(poly_) : poly_;
// cout << std::hex << "poly " << (uint)N(poly_) << " -> " << (uint)N(pol) << endl;
for (int i = 0; i < 256; ++i) {
tmp = uchar(i);
for (int j = 0; j < 8; ++j)
tmp = ((tmp & 1) ? ((tmp >> 1) ^ pol) : (tmp >> 1));
table[i] = tmp;
}
}
//! \~english Calculates CRC for a raw memory block.
//! \~russian Вычисляет CRC для блока памяти.
N calculate(const void * data, int size) {
N crc = init_;
uchar *data_ = (uchar *)data, cb;
// cout << "process " << size << endl;
uchar nTemp;
for (int i = 0; i < size; ++i) {
cb = data_[i];
if (reverse_data) cb = reverseByte(cb);
nTemp = cb ^ uchar(crc);
crc = crc >> 8;
crc = crc ^ table[nTemp];
}
if (reverse_before_xor) crc = reversed(crc);
return crc ^ out_;
}
//! \~english Calculates CRC for a byte array.
//! \~russian Вычисляет CRC для массива байтов.
N calculate(const PIByteArray & d) { return calculate(d.data(), d.size()); }
//! \~english Calculates CRC for a null-terminated string.
//! \~russian Вычисляет CRC для нуль-терминированной строки.
N calculate(const char * str) {
PIByteArray s(PIString(str).toByteArray());
return calculate(s.data(), s.size_s());
}
private:
inline N reversed(const N & v) { return v.reversed(); }
inline N inversed(const N & v) { return v.inversed(); }
N table[256];
N poly_, init_, out_;
bool reverse_poly, reverse_before_xor, reverse_data;
};
template<>
inline uchar PICRC<8, uchar>::reversed(const uchar & v) {
return reverseByte(v);
}
template<>
inline ushort PICRC<16, ushort>::reversed(const ushort & v) {
return uint_cl<16>(v).reversed();
}
template<>
inline uint PICRC<32, uint>::reversed(const uint & v) {
return uint_cl<32>(v).reversed();
}
template<>
inline uchar PICRC<8, uchar>::inversed(const uchar & v) {
return ~v;
}
template<>
inline ushort PICRC<16, ushort>::inversed(const ushort & v) {
return ~v;
}
template<>
inline uint PICRC<32, uint>::inversed(const uint & v) {
return ~v;
}
//! \~english CRC-32 calculator type.
//! \~russian Тип калькулятора CRC-32.
typedef PICRC<32, uint> CRC_32;
//! \~english CRC-24 calculator type.
//! \~russian Тип калькулятора CRC-24.
typedef PICRC<24> CRC_24;
//! \~english CRC-16 calculator type.
//! \~russian Тип калькулятора CRC-16.
typedef PICRC<16, ushort> CRC_16;
//! \~english CRC-8 calculator type.
//! \~russian Тип калькулятора CRC-8.
typedef PICRC<8, uchar> CRC_8;
//! \~english Create standard CRC-32 calculator
//! \~russian Создать стандартный калькулятор CRC-32
inline CRC_32 standardCRC_32() {
return CRC_32(0x04C11DB7, true, 0xFFFFFFFF, 0xFFFFFFFF);
}
//! \~english Create standard CRC-16 calculator
//! \~russian Создать стандартный калькулятор CRC-16
inline CRC_16 standardCRC_16() {
return CRC_16(0x8005, true, 0x0, 0x0);
}
//! \~english Create standard CRC-16 Modbus calculator
//! \~russian Создать стандартный калькулятор CRC-16 Modbus
inline CRC_16 standardCRC_16_Modbus() {
return CRC_16(0x8005, 0xFFFF, 0xFFFF, false);
}
//! \~english Create standard CRC-8 calculator
//! \~russian Создать стандартный калькулятор CRC-8
inline CRC_8 standardCRC_8() {
return CRC_8(0xD5, true, 0x0, 0x0);
}
#endif // CRC_H