Skip to content

Commit 09d7ef4

Browse files
authored
Merge pull request #3 from TomHarte/SmallerCRC
Switch to specialised CRC generator.
2 parents a573b5e + 24a3459 commit 09d7ef4

File tree

2 files changed

+50
-115
lines changed

2 files changed

+50
-115
lines changed

src/CRC.hpp

Lines changed: 45 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1,122 +1,62 @@
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-
91
#pragma once
102

3+
#include <array>
4+
#include <bit>
115
#include <cstddef>
126
#include <cstdint>
7+
#include <numeric>
138

149
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 {
3111
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);
4614
}
15+
ByteSwapped16() = default;
4716

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;
6821
return result;
6922
}
7023

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); }
10027

10128
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;
12030
};
12131

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+
}
12262
}

src/main.cpp

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,6 @@
1111
namespace {
1212

1313
class UEFWriter {
14-
private:
15-
using CRCGenerator = CRC::Generator<uint16_t, 0x0000, 0x0000, false, false>;
16-
1714
public:
1815
UEFWriter(const std::string &file_name) {
1916
file_ = fopen(file_name.c_str(), "wb");
@@ -30,7 +27,7 @@ class UEFWriter {
3027

3128
struct Chunk {
3229
public:
33-
Chunk(uint16_t id, FILE *file, CRCGenerator &crc_generator) : id_(id), file_(file), crc_generator_(crc_generator) {}
30+
Chunk(uint16_t id, FILE *file) : id_(id), file_(file) {}
3431

3532
~Chunk() {
3633
// Write chunk ID.
@@ -58,9 +55,9 @@ class UEFWriter {
5855
chunk_contents_.push_back(*it);
5956
}
6057
if(append_crc) {
61-
const uint16_t crc = crc_generator_.compute_crc(begin, end);
62-
chunk_contents_.push_back(uint8_t(crc >> 8));
63-
chunk_contents_.push_back(uint8_t(crc >> 0));
58+
const auto crc = CRC::crc16(begin, end);
59+
chunk_contents_.push_back(crc.high());
60+
chunk_contents_.push_back(crc.low());
6461
}
6562
}
6663

@@ -72,12 +69,11 @@ class UEFWriter {
7269
private:
7370
uint16_t id_;
7471
FILE *file_;
75-
CRCGenerator &crc_generator_;
7672
std::vector<uint8_t> chunk_contents_;
7773
};
7874

7975
Chunk chunk(const uint16_t id) {
80-
return Chunk(id, file_, crc_generator_);
76+
return Chunk(id, file_);
8177
}
8278

8379
~UEFWriter() {
@@ -86,7 +82,6 @@ class UEFWriter {
8682

8783
private:
8884
FILE *file_ = nullptr;
89-
inline static CRCGenerator crc_generator_{0x1021};
9085
};
9186

9287
void print_help() {

0 commit comments

Comments
 (0)