1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
//! A minimal heapless no_std implementation of 8-bit [cyclic redundancy
//! checks](https://en.wikipedia.org/wiki/Cyclic_redundancy_check) in Rust. This
//! allows us to check for the integrity of data, and thus is mostly used when
//! transferring data over unstable or noisy connections. For example, this is connections with
//! embedded systems and network connections.
//!
//! Take a look at [the documentation](crate).
//!
//! # Features
//!
//! This crate provides the minimal functions needed to properly handle CRC's in an 8-bit
//! system. The provided functions are [`fetch_crc8`], [`has_valid_crc8`] and [`insert_crc8`]. This
//! should make handling most of the common CRC situations simple. Because of the minimalist
//! approach this crate takes, binary size should remain small. This especially fits well on
//! embedded hardware.
//!
//! # Usage
//!
//! Add this to your projects *Cargo.toml* with:
//!
//! ```toml
//! [dependencies]
//! crc8-rs = "1.1"
//! ```
//!
//! There are generally two ways to use this crate. We can use plain buffers or we wrap CRCs with
//! [`struct`](https://doc.rust-lang.org/std/keyword.struct.html) methods. Let us go over both
//! ways.
//!
//! ## Using plain buffers
//!
//! On the transferring end, we would similar code to the following.
//!
//! ```rust
//! use crc8_rs::{ has_valid_crc8, insert_crc8 };
//!
//! // We are given a data buffer we would like to transfer
//! // It is important to leave a unused byte at the end for the CRC byte
//! let data: [u8; 256] = [
//!     // ...snip
//! # 3; 256
//! ];
//!
//! // We can insert a CRC byte to the data buffer, this will be the last byte
//! // This time we use the generator polynomial of `0xD5`
//! let crc_data: [u8; 256] = insert_crc8(data, 0xD5);
//!
//! // Now we are able to verify that the CRC is valid
//! assert!(has_valid_crc8(crc_data, 0xD5));
//!
//! // Transfer the data...
//! ```
//!
//! Then on the receiving end, we would have code such as the following.
//!
//! ```rust
//! use crc8_rs::has_valid_crc8;
//!
//! // We receive the CRCed data from some source
//! // This buffer has the CRC byte as the last byte
//! let crc_data: [u8; 256] = // ...snip
//! # crc8_rs::insert_crc8([3; 256], 0xD5);
//!
//! // Now we can conditionally unpack it and use the data
//! if has_valid_crc8(crc_data, 0xD5) {
//!     // The data is contained in the crc_data
//!     let data = crc_data;
//!
//!     // ...snip
//! } else {
//!     panic!("CRC is invalid!")
//! }
//! ```
//!
//! ## Wrapping the CRC
//!
//! If we want to form packets from some given data, we may want to append a CRC byte when
//! transferring the data to verify the data's integrity.
//!
//! ```rust
//! use crc8_rs::insert_crc8;
//!
//! // Define a example packet structure
//! struct Packet {
//!     header:  [u8; 4],
//!     content: [u8; 247],
//!     footer:  [u8; 4],
//! }
//!
//! impl Packet {
//!     fn to_data_buffer(&self) -> [u8; 256] {
//!         let mut data = [0; 256];
//!         
//!         // First we insert the packet data into the buffer
//!         for i in 0..4   { data[i]       = self.header[i] }
//!         for i in 0..247 { data[i + 4]   = self.content[i] }
//!         for i in 0..4   { data[i + 251] = self.footer[i] }
//!
//!         // We use the generator polynomial `0xD5` here.
//!         insert_crc8(data, 0xD5)
//!     }
//! }
//! # // We add a little test here to make sure everything works.
//! # let pkt = Packet { header: [0xAB; 4], content: [0xCD; 247], footer: [0xEF; 4] };
//! # assert!(crc8_rs::has_valid_crc8(pkt.to_data_buffer(), 0xD5));
//! ```
//!
//! Receiving the given buffer is now quite simple.
//!
//! ```rust
//! use crc8_rs::has_valid_crc8;
//!
//! struct ReceivedPacket {
//!     header:  [u8; 4],
//!     content: [u8; 247],
//!     footer:  [u8; 4],
//! }
//!
//! impl ReceivedPacket {
//!     fn receive(data: [u8; 256]) -> Option<ReceivedPacket> {
//!         // Before we construct the instance, we first check the CRC
//!         if has_valid_crc8(data, 0xD6) {
//!             Some(ReceivedPacket {
//!                 // ...snip
//! #               header: {
//! #                   let mut header = [0; 4];
//! #                   for i in 0..4 {
//! #                       header[i] = data[i]
//! #                   }
//! #                   header
//! #               },
//! #               content: {
//! #                   let mut content = [0; 247];
//! #                   for i in 0..247 {
//! #                       content[i] = data[i + 4]
//! #                   }
//! #                   content
//! #               },
//! #               footer: {
//! #                   let mut footer = [0; 4];
//! #                   for i in 0..4 {
//! #                       footer[i] = data[i + 251]
//! #                   }
//! #                   footer
//! #               },
//!             })
//!         } else {
//!             None
//!         }
//!     }
//! }
//! # // We add a little test here to make sure everything works.
//! # assert!(ReceivedPacket::receive(crc8_rs::insert_crc8([0x42; 256], 0xD6)).is_some());
//! ```

#![warn(missing_docs)]
#![no_std]

mod polynomial;

use polynomial::Polynomial;

/// Determine whether a `data` buffer for a given generator `polynomial` has a valid CRC.
///
/// Will fetch the CRC value for the `data` buffer under the generator `polynomial` and return
/// whether it equals zero, which indicates the integrity of the data. It is a short hand for
/// [`fetch_crc8(data, polynomial) == 0`](crate::fetch_crc8).
///
/// # Examples
///
/// ```
/// use crc8_rs::{ has_valid_crc8, insert_crc8 };
///
/// const GENERATOR_POLYNOMIAL: u8 = 0xD5;
///
/// // We add an empty byte at the end for the CRC
/// let msg = b"Hello World!\0";
/// let msg = insert_crc8(*msg, GENERATOR_POLYNOMIAL);
///
/// // Will verify just fine!
/// assert!(has_valid_crc8(msg, GENERATOR_POLYNOMIAL));
///
/// let corrupted_msg = {
///     let mut tmp_msg = msg;
///     tmp_msg[1] = b'a';
///     tmp_msg
/// };
///
/// // The message is now corrupted and thus it can't verify the integrity!
/// assert!(!has_valid_crc8(corrupted_msg, GENERATOR_POLYNOMIAL));
/// ```
///
/// # Panics
///
/// The function will panic if given a zero-sized buffer. As can be seen in the following example.
///
/// ```should_panic
/// use crc8_rs::has_valid_crc8;
///
/// has_valid_crc8([], 0x42);
/// ```
pub fn has_valid_crc8<const DATA_SIZE: usize>(data: [u8; DATA_SIZE], polynomial: u8) -> bool {
    fetch_crc8(data, polynomial) == 0
}

/// Get the current CRC of a `data` buffer under a generator `polynomial`.
///
/// Calculates the polynomial modulo division of the `data` buffer with the `polynomial`. If we
/// give a valid CRC appended `data` buffer under `polynomial`, we will get `0` back. The
/// short-hand of this is the [`has_valid_crc8`] function. When given a null terminated `data`
/// buffer, the `fetch_crc8(data, polynomial) ^ polynomial` will equal the value needed to be set
/// as the last byte in order to get a valid CRC signed buffer. The short-hand of this is the
/// [`insert_crc8`] function.
///
/// # Examples
///
/// ```
/// use crc8_rs::{ insert_crc8, has_valid_crc8 };
///
/// // We can declare our packets ourselves
/// struct Packet {
///     header: u8,
///     content: [u8; 14],
/// }
///
/// impl Packet {
///     fn to_bytes(&self) -> [u8; 16] {
///         let mut data = [0; 16];
///
///         // Insert the packet data
///         data[0] = self.header;
///         for i in 0..14 { data[i + 1] = self.content[i] }
///
///         // Insert the CRC at the end of the buffer
///         // We use 0xD5 as the generator polynomial here
///         insert_crc8(data, 0xD5)
///     }
/// }
///
/// let pkt = Packet {
///     // ...
/// # header: b'H',
/// # content: *b"ello Everyone!",
/// };
/// assert!(has_valid_crc8(pkt.to_bytes(), 0xD5));
/// ```
///
/// # Panics
///
/// This function will panic when given a zero-sized buffer as can be seen in the following code
/// snippet.
///
/// ```should_panic
/// use crc8_rs::fetch_crc8;
///
/// fetch_crc8([], 0x42);
/// ```
pub fn fetch_crc8<const DATA_SIZE: usize>(data: [u8; DATA_SIZE], polynomial: u8) -> u8 {
    // Fetch the modulo division of the data with the generator polynomial
    let Polynomial(result_arr) = Polynomial(data) / Polynomial::new_from_byte(polynomial);

    // Then return the last byte
    result_arr[DATA_SIZE - 1]
}

/// Insert CRC byte in the last byte of `data` buffer under a generator `polynomial`.
///
/// This expects a last byte left for the CRC byte, any pre-existing last byte value will be
/// ignored and overwritten in the return value. This function is very similar to writing
/// [`data[data.len() - 1] = polynomial ^ fetch_crc8(data, polynomial)`](fetch_crc8).
///
/// # Examples
///
/// ```
/// use crc8_rs::{ has_valid_crc8, insert_crc8 };
///
/// const GENERATOR_POLYNOMIAL: u8 = 0xD5;
///
/// // We add an empty byte at the end for the CRC
/// let msg = b"Hello World!\0";
/// let msg = insert_crc8(*msg, GENERATOR_POLYNOMIAL);
///
/// // Will verify just fine!
/// assert!(has_valid_crc8(msg, GENERATOR_POLYNOMIAL));
///
/// let corrupted_msg = {
///     let mut tmp_msg = msg;
///     tmp_msg[1] = b'a';
///     tmp_msg
/// };
///
/// // The message is now corrupted and thus it can't verify the integrity!
/// assert!(!has_valid_crc8(corrupted_msg, GENERATOR_POLYNOMIAL));
/// ```
///
/// # Panics
///
/// This function will panic when given a zero-sized buffer as can be seen in the following code
/// snippet.
///
/// ```should_panic
/// use crc8_rs::insert_crc8;
///
/// insert_crc8([], 0x42);
/// ```
pub fn insert_crc8<const DATA_SIZE: usize>(
    mut data: [u8; DATA_SIZE],
    polynomial: u8,
) -> [u8; DATA_SIZE] {
    // Set the CRC byte to zero.
    data[DATA_SIZE - 1] = 0x00;

    // Fetch the crc and write to the last byte the byte which turns the crc into zero.
    data[DATA_SIZE - 1] = polynomial ^ fetch_crc8(data, polynomial);
    data
}

#[test]
fn crc_cycle() {
    let test_vectors = [
        [0x02, 0x30, 0xf0, 0x00],
        [0xff, 0x30, 0xf0, 0x00],
        [0x02, 0x56, 0xf0, 0x00],
        [0x02, 0x30, 0x49, 0x00],
        [0xab, 0xcd, 0xef, 0x00],
    ];

    for i in 0..test_vectors.len() {
        let test_vector = test_vectors[i];

        assert!(has_valid_crc8(insert_crc8(test_vector, 0xA6), 0xA6));
    }
}