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