crcany-core 0.0.2

Core implementation of crcany library.
Documentation
use byteorder::ByteOrder;

use crate::crc::Crc;
use crate::model::BytewiseModel;
use crate::spec::Spec;
use crate::util::{ones, reverse};

pub struct WordwiseModel<Endianness: byteorder::ByteOrder, const WORD_BITS: u16> {
    model: BytewiseModel,
    // tables for word-wise calculation
    pub table: [[u128; 256]; 8],
    _endianness: core::marker::PhantomData<Endianness>,
}

trait TableFiller {
    fn fill_table(&mut self);
    fn swap(&self) -> bool;
}

impl<const WORD_BITS: u16> TableFiller for WordwiseModel<byteorder::LittleEndian, WORD_BITS> {
    fn fill_table(&mut self) {
        self.fill_table_kernel(self.swap());
    }

    fn swap(&self) -> bool {
        !self.model.reflect()
    }
}

impl<const WORD_BITS: u16> TableFiller for WordwiseModel<byteorder::BigEndian, WORD_BITS> {
    fn fill_table(&mut self) {
        self.fill_table_kernel(self.swap());
    }

    fn swap(&self) -> bool {
        self.model.reflect()
    }
}

impl<const WORD_BITS: u16> WordwiseModel<byteorder::LittleEndian, WORD_BITS> {
    pub fn from_spec(spec: Spec) -> Self {
        let mut me = WordwiseModel::from_spec_kernel(spec);
        me.fill_table();
        me
    }
}

impl<const WORD_BITS: u16> WordwiseModel<byteorder::BigEndian, WORD_BITS> {
    pub fn from_spec(spec: Spec) -> Self {
        let mut me = WordwiseModel::from_spec_kernel(spec);
        me.fill_table();
        me
    }
}

impl Crc for WordwiseModel<byteorder::NativeEndian, { usize::BITS as _ }> {
    type Int = u128;

    fn init(&self) -> u128 {
        self.model.init()
    }

    fn add_bytes(&self, mut crc: u128, data: &[u8]) -> u128 {
        if data.is_empty() {
            return crc;
        }

        let top = self.top();
        let shift = if self.model.width() <= 8 {
            8 - self.model.width()
        } else {
            self.model.width() - 8
        };

        if self.model.reverse() {
            crc = reverse(crc, self.model.width());
        }

        let word_bytes = usize::BITS >> 3;

        let mut shifted = false;

        if self.model.reflect() {
            crc &= ones(self.model.width());
        } else if self.model.width() <= 8 {
            crc <<= shift;
        }

        // TODO: slice not iterate
        let mut iter = data.iter().copied();

        loop {
            let mut next = vec![];

            for _ in 0..word_bytes {
                if let Some(c) = iter.next() {
                    next.push(c);
                }
            }

            if next.len() == word_bytes as _ {
                if !shifted {
                    crc <<= top;
                    if self.swap() {
                        crc = (crc as usize).swap_bytes() as _;
                    }
                    shifted = true;
                }

                crc ^= byteorder::NativeEndian::read_uint128(&next[..], word_bytes as _);

                // TODO: FIX THIS!
                assert_eq!(word_bytes, 8);

                // TODO: THIS IS LITTLE-ENDIAN!
                crc = self.table[(word_bytes - 1) as usize][(crc as u8) as usize]
                    ^ self.table[(word_bytes - 2) as usize][((crc >> 8) as u8) as usize]
                    ^ self.table[(word_bytes - 3) as usize][((crc >> 16) as u8) as usize]
                    ^ self.table[(word_bytes - 4) as usize][((crc >> 24) as u8) as usize]
                    ^ self.table[(word_bytes - 5) as usize][((crc >> 32) as u8) as usize]
                    ^ self.table[(word_bytes - 6) as usize][((crc >> 40) as u8) as usize]
                    ^ self.table[(word_bytes - 7) as usize][((crc >> 48) as u8) as usize]
                    ^ self.table[(word_bytes - 8) as usize][((crc >> 56) as u8) as usize];
            } else if next.is_empty() {
                break;
            } else {
                if shifted {
                    if self.swap() {
                        crc = (crc as usize).swap_bytes() as _;
                    }
                    crc >>= top;
                    shifted = false;
                }

                if self.model.reflect() {
                    for byte in next {
                        let byte = byte as u128;
                        crc = (crc >> 8) ^ self.model.table[((crc ^ byte) as u8) as usize];
                    }
                } else if self.model.width() <= 8 {
                    for byte in next {
                        let byte = byte as u128;
                        crc = self.model.table[((crc ^ byte) as u8) as usize];
                    }
                    crc >>= shift;
                } else {
                    for byte in next {
                        let byte = byte as u128;
                        crc =
                            (crc << 8) ^ self.model.table[(((crc >> shift) ^ byte) as u8) as usize];
                    }
                    crc &= ones(self.model.width());
                }

                break;
            }
        }

        if shifted {
            if self.swap() {
                crc = crc.swap_bytes();
            }
            crc >>= top;
        }

        if !self.model.reflect() && self.model.width() > 8 {
            crc &= ones(self.model.width());
        }

        if self.model.reverse() {
            crc = reverse(crc, self.model.width());
        }

        crc as _
    }
}

impl<Endianness: byteorder::ByteOrder, const WORD_BITS: u16> WordwiseModel<Endianness, WORD_BITS> {
    fn from_spec_kernel(spec: Spec) -> WordwiseModel<Endianness, WORD_BITS> {
        WordwiseModel {
            model: BytewiseModel::from_spec(spec),
            table: [[0; 256]; 8],
            _endianness: core::marker::PhantomData,
        }
    }

    fn top(&self) -> u16 {
        if self.model.reflect() {
            0
        } else {
            WORD_BITS
                - (if self.model.width() > 8 {
                    self.model.width()
                } else {
                    8
                })
        }
    }

    fn fill_table_kernel(&mut self, swap: bool) {
        let top = self.top();

        let mut xor = self.model.xorout();
        if self.model.width() < 8 && !self.model.reflect() {
            xor <<= 8 - self.model.width();
        }

        for k in 0..256 {
            let mut crc = self.model.table[k];
            self.table[0][k] = if swap {
                ((crc << top) as usize).swap_bytes() as _ // TODO: this is inelegant
            } else {
                crc << top
            };
            for n in 1..(WORD_BITS as usize >> 3) {
                crc ^= xor;
                if self.model.reflect() {
                    crc = (crc >> 8) ^ self.model.table[(crc as u8) as usize];
                } else if self.model.width() <= 8 {
                    crc = self.model.table[(crc as u8) as usize];
                } else {
                    crc = (crc << 8)
                        ^ self.model.table[((crc >> (self.model.width() - 8)) as u8) as usize];
                }
                crc ^= xor;
                self.table[n][k] = if swap {
                    ((crc << top) as usize).swap_bytes() as _ // TODO: this is inelegant
                } else {
                    crc << top
                };
            }
        }
    }
}