|
1 | | -// |
2 | | -// CRC.hpp |
3 | | -// Clock Signal |
4 | | -// |
5 | | -// Created by Thomas Harte on 18/09/2016. |
6 | | -// Copyright 2016 Thomas Harte. All rights reserved. |
7 | | -// |
8 | | - |
9 | 1 | #pragma once |
10 | 2 |
|
| 3 | +#include <array> |
| 4 | +#include <bit> |
11 | 5 | #include <cstddef> |
12 | 6 | #include <cstdint> |
| 7 | +#include <numeric> |
13 | 8 |
|
14 | 9 | namespace CRC { |
15 | | - |
16 | | -constexpr uint8_t reverse_byte(uint8_t byte) { |
17 | | - return |
18 | | - ((byte & 0x80) ? 0x01 : 0x00) | |
19 | | - ((byte & 0x40) ? 0x02 : 0x00) | |
20 | | - ((byte & 0x20) ? 0x04 : 0x00) | |
21 | | - ((byte & 0x10) ? 0x08 : 0x00) | |
22 | | - ((byte & 0x08) ? 0x10 : 0x00) | |
23 | | - ((byte & 0x04) ? 0x20 : 0x00) | |
24 | | - ((byte & 0x02) ? 0x40 : 0x00) | |
25 | | - ((byte & 0x01) ? 0x80 : 0x00); |
26 | | -} |
27 | | - |
28 | | -/*! Provides a class capable of generating a CRC from source data. */ |
29 | | -template <typename IntType, IntType reset_value, IntType output_xor, bool reflect_input, bool reflect_output> |
30 | | -class Generator { |
| 10 | +struct ByteSwapped16 { |
31 | 11 | public: |
32 | | - /*! |
33 | | - Instantiates a CRC16 that will compute the CRC16 specified by the supplied |
34 | | - @c polynomial and @c reset_value. |
35 | | - */ |
36 | | - constexpr Generator(IntType polynomial) noexcept: value_(reset_value) { |
37 | | - const IntType top_bit = IntType(~(IntType(~0) >> 1)); |
38 | | - for(int c = 0; c < 256; c++) { |
39 | | - IntType shift_value = IntType(c << multibyte_shift); |
40 | | - for(int b = 0; b < 8; b++) { |
41 | | - IntType exclusive_or = (shift_value&top_bit) ? polynomial : 0; |
42 | | - shift_value = IntType(shift_value << 1) ^ exclusive_or; |
43 | | - } |
44 | | - xor_table[c] = shift_value; |
45 | | - } |
| 12 | + explicit constexpr ByteSwapped16(uint16_t original) noexcept { |
| 13 | + value_ = std::rotl(original, 8); |
46 | 14 | } |
| 15 | + ByteSwapped16() = default; |
47 | 16 |
|
48 | | - /// Resets the CRC to the reset value. |
49 | | - void reset() { value_ = reset_value; } |
50 | | - |
51 | | - /// Updates the CRC to include @c byte. |
52 | | - void add(uint8_t byte) { |
53 | | - if constexpr (reflect_input) byte = reverse_byte(byte); |
54 | | - value_ = IntType((value_ << 8) ^ xor_table[(value_ >> multibyte_shift) ^ byte]); |
55 | | - } |
56 | | - |
57 | | - /// @returns The current value of the CRC. |
58 | | - inline IntType get_value() const { |
59 | | - IntType result = value_ ^ output_xor; |
60 | | - if constexpr (reflect_output) { |
61 | | - IntType reflected_output = 0; |
62 | | - for(std::size_t c = 0; c < sizeof(IntType); ++c) { |
63 | | - reflected_output = IntType(reflected_output << 8) | IntType(reverse_byte(result & 0xff)); |
64 | | - result >>= 8; |
65 | | - } |
66 | | - return reflected_output; |
67 | | - } |
| 17 | + uint16_t raw() const { return value_; } |
| 18 | + static ByteSwapped16 from_raw(const uint16_t raw) { |
| 19 | + ByteSwapped16 result{}; |
| 20 | + result.value_ = raw; |
68 | 21 | return result; |
69 | 22 | } |
70 | 23 |
|
71 | | - /// Sets the current value of the CRC. |
72 | | - inline void set_value(IntType value) { value_ = value; } |
73 | | - |
74 | | - /*! |
75 | | - A compound for: |
76 | | -
|
77 | | - reset() |
78 | | - [add all data from @c data] |
79 | | - get_value() |
80 | | - */ |
81 | | - template <typename Collection> IntType compute_crc(const Collection &data) { |
82 | | - return compute_crc(data.begin(), data.end()); |
83 | | - } |
84 | | - |
85 | | - /*! |
86 | | - A compound for: |
87 | | -
|
88 | | - reset() |
89 | | - [add all data from @c begin to @c end] |
90 | | - get_value() |
91 | | - */ |
92 | | - template <typename Iterator> IntType compute_crc(Iterator begin, Iterator end) { |
93 | | - reset(); |
94 | | - while(begin != end) { |
95 | | - add(*begin); |
96 | | - ++begin; |
97 | | - } |
98 | | - return get_value(); |
99 | | - } |
| 24 | + explicit operator uint16_t() const { return std::rotl(value_, 8); } |
| 25 | + uint8_t high() const { return uint8_t(value_ >> 0); } |
| 26 | + uint8_t low() const { return uint8_t(value_ >> 8); } |
100 | 27 |
|
101 | 28 | private: |
102 | | - static constexpr int multibyte_shift = (sizeof(IntType) * 8) - 8; |
103 | | - IntType xor_table[256]; |
104 | | - IntType value_; |
105 | | -}; |
106 | | - |
107 | | -/*! |
108 | | - Provides a generator of 16-bit CCITT CRCs, which amongst other uses are |
109 | | - those used by the FM and MFM disk encodings. |
110 | | -*/ |
111 | | -struct CCITT: public Generator<uint16_t, 0xffff, 0x0000, false, false> { |
112 | | - CCITT(): Generator(0x1021) {} |
113 | | -}; |
114 | | - |
115 | | -/*! |
116 | | - Provides a generator of "standard 32-bit" CRCs. |
117 | | -*/ |
118 | | -struct CRC32: public Generator<uint32_t, 0xffffffff, 0xffffffff, true, true> { |
119 | | - CRC32(): Generator(0x04c11db7) {} |
| 29 | + uint16_t value_ = 0; |
120 | 30 | }; |
121 | 31 |
|
| 32 | +template <uint16_t polynomial = 0x1021, typename IteratorT> |
| 33 | +ByteSwapped16 crc16(IteratorT begin, const IteratorT end, const ByteSwapped16 initial = ByteSwapped16{0x0000}) { |
| 34 | + // Generates an at-compile-time table mapping from the top byte of a 16-bit CRC in progress |
| 35 | + // to the net XOR mask that results from bit-by-bit rotates to the left. |
| 36 | + // |
| 37 | + // The final table is byte swapped to simplify the loop below; the compiler proved |
| 38 | + // trustworthy in spotting that these tables are independent of the type of IteratorT, |
| 39 | + // including only one per polynomial in the produced binary. |
| 40 | + static constexpr auto xor_table = [] { |
| 41 | + constexpr uint16_t xor_masks[] = {0, std::rotl(uint16_t(polynomial ^ 1), 8)}; |
| 42 | + std::array<uint16_t, 256> table; |
| 43 | + |
| 44 | + std::iota(table.begin(), table.end(), 0); |
| 45 | + for(auto &value: table) { |
| 46 | + for(int bit = 0; bit < 8; bit++) { |
| 47 | + value = std::rotl(value, 1); |
| 48 | + value ^= xor_masks[(value >> 8) & 1]; |
| 49 | + } |
| 50 | + } |
| 51 | + return table; |
| 52 | + } (); |
| 53 | + |
| 54 | + // Calculate the CRC in byte-swapped form so as slighltly to simplify the inner loop. |
| 55 | + uint16_t crc = initial.raw(); |
| 56 | + while(begin != end) { |
| 57 | + crc = xor_table[(*begin ^ crc) & 0xff] ^ (crc >> 8); |
| 58 | + ++begin; |
| 59 | + } |
| 60 | + return ByteSwapped16::from_raw(crc); |
| 61 | +} |
122 | 62 | } |
0 commit comments