Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  braid.rs   Sprache: unbekannt

 
// Several implementations of CRC-32:
// * A naive byte-granularity approach
// * A word-sized approach that processes a usize word at a time
// * A "braid" implementation that processes a block of N words
//   at a time, based on the algorithm in section 4.11 from
//   https://github.com/zlib-ng/zlib-ng/blob/develop/doc/crc-doc.1.0.pdf.

// The binary encoding of the CRC-32 polynomial.
// We are assuming little-endianness so we process the input
// LSB-first. We need to use the "reversed" value from e.g
// https://en.wikipedia.org/wiki/Cyclic_redundancy_check#Polynomial_representations.
pub(crate) const CRC32_LSB_POLY: usize = 0xedb8_8320usize;

const W: usize = core::mem::size_of::<usize>();

// The logic assumes that W >= sizeof(u32).
// In Rust, this is generally true.
const _: () = assert!(W >= core::mem::size_of::<u32>());

// Pre-computed tables for the CRC32 algorithm.
// CRC32_BYTE_TABLE corresponds to MulByXPowD from the paper.
static CRC32_BYTE_TABLE: [[u32; 256]; 1] = build_crc32_table::<256, 1, 1>();
// CRC32_WORD_TABLE is MulWordByXpowD.
static CRC32_WORD_TABLE: [[u32; 256]; W] = build_crc32_table::<256, W, 1>();

// Work-around for not being able to define generic consts or statics
// Crc32BraidTable::<N>::TABLE is the generic table for any braid size N.
struct Crc32BraidTable<const N: usize>;

impl<const N: usize> Crc32BraidTable<N> {
    const TABLE: [[u32; 256]; W] = build_crc32_table::<256, W, N>();
}

// Build the CRC32 tables using a more efficient and simpler approach
// than the combination of Multiply and XpowN (which implement polynomial
// multiplication and exponentiation, respectively) from the paper,
// but with identical results. This function is const, so it should be
// fully evaluated at compile time.
const fn build_crc32_table<const A: usize, const W: usize, const N: usize>() -> [[u32; A]; W] {
    let mut arr = [[0u32; A]; W];
    let mut i = 0;
    while i < W {
        let mut j = 0;
        while j < A {
            let mut c = j;
            let mut k = 0;
            while k < 8 * (W * N - i) {
                if c & 1 != 0 {
                    c = CRC32_LSB_POLY ^ (c >> 1);
                } else {
                    c >>= 1;
                }
                k += 1;
            }
            arr[i][j] = c as u32;
            j += 1;
        }
        i += 1;
    }
    arr
}

fn crc32_naive_inner(data: &[u8], start: u32) -> u32 {
    data.iter().fold(start, |crc, val| {
        let crc32_lsb = crc.to_le_bytes()[0];
        CRC32_BYTE_TABLE[0][usize::from(crc32_lsb ^ *val)] ^ (crc >> 8)
    })
}

fn crc32_words_inner(words: &[usize], start: u32, per_word_crcs: &[u32]) -> u32 {
    words.iter().enumerate().fold(start, |crc, (i, word)| {
        let value = word.to_le() ^ (crc ^ per_word_crcs.get(i).unwrap_or(&0)) as usize;
        value
            .to_le_bytes()
            .into_iter()
            .zip(CRC32_WORD_TABLE)
            .fold(0u32, |crc, (b, tab)| crc ^ tab[usize::from(b)])
    })
}

pub fn crc32_braid<const N: usize>(start: u32, data: &[u8]) -> u32 {
    // Get a word-aligned sub-slice of the input data
    let (prefix, words, suffix) = unsafe { data.align_to::<usize>() };
    let crc = !start;
    let crc = crc32_naive_inner(prefix, crc);

    let mut crcs = [0u32; N];
    crcs[0] = crc;

    // TODO: this would normally use words.chunks_exact(N), but
    // we need to pass the last full block to crc32_words_inner
    // because we accumulate partial crcs in the array and we
    // need to roll those into the final value. The last call to
    // crc32_words_inner does that for us with its per_word_crcs
    // argument.
    let blocks = words.len() / N;
    let blocks = blocks.saturating_sub(1);
    for i in 0..blocks {
        // Load the next N words.
        let mut buffer: [usize; N] =
            core::array::from_fn(|j| usize::to_le(words[i * N + j]) ^ (crcs[j] as usize));

        crcs.fill(0);
        for j in 0..W {
            for k in 0..N {
                crcs[k] ^= Crc32BraidTable::<N>::TABLE[j][buffer[k] & 0xff];
                buffer[k] >>= 8;
            }
        }
    }

    let crc = core::mem::take(&mut crcs[0]);
    let crc = crc32_words_inner(&words[blocks * N..], crc, &crcs);
    let crc = crc32_naive_inner(suffix, crc);
    !crc
}

#[cfg(test)]
mod test {
    use super::*;

    fn crc32_naive(data: &[u8], start: u32) -> u32 {
        let crc = !start;
        let crc = crc32_naive_inner(data, crc);
        !crc
    }

    fn crc32_words(data: &[u8], start: u32) -> u32 {
        // Get a word-aligned sub-slice of the input data
        let (prefix, words, suffix) = unsafe { data.align_to::<usize>() };
        let crc = !start;
        let crc = crc32_naive_inner(prefix, crc);
        let crc = crc32_words_inner(words, crc, &[]);
        let crc = crc32_naive_inner(suffix, crc);
        !crc
    }

    #[test]
    fn empty_is_identity() {
        assert_eq!(crc32_naive(&[], 32), 32);
    }

    #[test]
    fn words_endianness() {
        let v = [0, 0, 0, 0, 0, 16, 0, 1];
        let start = 1534327806;

        let mut h = crc32fast::Hasher::new_with_initial(start);
        h.update(&v[..]);
        assert_eq!(crc32_words(&v[..], start), h.finalize());
    }

    #[test]
    fn crc32_naive_inner_endianness_and_alignment() {
        assert_eq!(crc32_naive_inner(&[0, 1], 0), 1996959894);

        let v: Vec<_> = (0..1024).map(|i| i as u8).collect();
        let start = 0;

        // test alignment
        for i in 0..8 {
            let mut h = crc32fast::Hasher::new_with_initial(start);
            h.update(&v[i..]);
            assert_eq!(crc32_braid::<5>(start, &v[i..]), h.finalize());
        }
    }

    quickcheck::quickcheck! {
        fn naive_is_crc32fast(v: Vec<u8>, start: u32) -> bool {
            let mut h = crc32fast::Hasher::new_with_initial(start);
            h.update(&v[..]);
            crc32_naive(&v[..], start) == h.finalize()
        }

        fn words_is_crc32fast(v: Vec<u8>, start: u32) -> bool {
            let mut h = crc32fast::Hasher::new_with_initial(start);
            h.update(&v[..]);
            crc32_words(&v[..], start) == h.finalize()
        }

        #[cfg_attr(miri, ignore)]
        fn braid_4_is_crc32fast(v: Vec<u8>, start: u32) -> bool {
            let mut h = crc32fast::Hasher::new_with_initial(start);
            h.update(&v[..]);
            crc32_braid::<4>(start, &v[..]) == h.finalize()
        }

        #[cfg_attr(miri, ignore)]
        fn braid_5_is_crc32fast(v: Vec<u8>, start: u32) -> bool {
            let mut h = crc32fast::Hasher::new_with_initial(start);
            h.update(&v[..]);
            crc32_braid::<5>(start, &v[..]) == h.finalize()
        }

        #[cfg_attr(miri, ignore)]
        fn braid_6_is_crc32fast(v: Vec<u8>, start: u32) -> bool {
            let mut h = crc32fast::Hasher::new_with_initial(start);
            h.update(&v[..]);
            crc32_braid::<6>(start, &v[..]) == h.finalize()
        }
    }
}

[ Dauer der Verarbeitung: 0.39 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge