mrhooray / crc-rs

Rust implementation of CRC(16, 32, 64) with support of various standards
Apache License 2.0
191 stars 49 forks source link

impl Clone for Digest #67

Closed cbiffle closed 2 years ago

cbiffle commented 2 years ago

I needed a way to feed a Digest a stream of bytes while checking the CRC for possible matches after each byte. Using CRC::crc directly would make this O(n^2), so I looked to Digest. Because Digest::finalize consumes self, it must be thrown out after each finalize operation. This is probably what you want in the general case, but not in my slightly weird case.

This problem can be solved by allowing Digest to be duplicated, thereby letting the user "fork" the CRC state as desired. (This also enables certain niche CRC-search algorithms.)

I went with Clone rather than Copy because making Digest Copy seemed potentially error-prone; Clone has the advantage of requiring explicit intent.

Here's an example (not included in the commit message for brevity) of a method enabled by this change.

/// Recognizer for sequences of bytes ending in a valid 16-bit CRC.
struct TrailingCrcSpotter {
    digest: crc::Digest<'static, u16>,
    tail: Tail,
}

/// Recognizer state; buffers 0-2 bytes as data arrives.
#[derive(Copy, Clone)]
enum Tail {
    Empty,
    One(u8),
    Two(u8, u8),
}

impl TrailingCrcSpotter {
    /// Append `byte` to the sequence being processed. If this causes
    /// the sequence so far to end in a valid big-endian CRC16 over the
    /// earlier data, return `true`. Otherwise, return `false`.
    pub fn recognize(&mut self, byte: u8) -> bool {
        match self.tail {
            Tail::Empty => {
                self.tail = Tail::One(byte);
                false
            }
            Tail::One(x) => {
                self.tail = Tail::Two(x, byte);
                false
            }
            Tail::Two(x, y) => {
                // main stage
                self.tail = Tail::Two(y, byte);
                self.digest.update(&[x]);
                let crc = self.digest.clone().finalize();

                crc == u16::from_be_bytes([y, byte])
            }
        }
    }
}