dope-runtime 0.28.1

Thread-per-core execution and process lifecycle for dope
Documentation
use std::{
    hash::{self, Hasher as _},
    mem,
};

use o3::base::marker::ThreadBound;

#[derive(Clone, Copy)]
pub struct Sip<const C: usize, const D: usize> {
    length: usize,
    state: [u64; 4],
    tail: u64,
    _thread: ThreadBound,
}

impl<const C: usize, const D: usize> Sip<C, D> {
    pub(super) fn new(k0: u64, k1: u64) -> Self {
        Self {
            length: 0,
            state: [
                k0 ^ 0x736f_6d65_7073_6575,
                k1 ^ 0x646f_7261_6e64_6f6d,
                k0 ^ 0x6c79_6765_6e65_7261,
                k1 ^ 0x7465_6462_7974_6573,
            ],
            tail: 0,
            _thread: ThreadBound::NEW,
        }
    }

    fn round(state: &mut [u64; 4]) {
        let [v0, v1, v2, v3] = state;
        *v0 = v0.wrapping_add(*v1);
        *v1 = v1.rotate_left(13) ^ *v0;
        *v0 = v0.rotate_left(32);
        *v2 = v2.wrapping_add(*v3);
        *v3 = v3.rotate_left(16) ^ *v2;
        *v0 = v0.wrapping_add(*v3);
        *v3 = v3.rotate_left(21) ^ *v0;
        *v2 = v2.wrapping_add(*v1);
        *v1 = v1.rotate_left(17) ^ *v2;
        *v2 = v2.rotate_left(32);
    }

    fn rounds(state: &mut [u64; 4], count: usize) {
        for _ in 0..count {
            Self::round(state);
        }
    }

    fn compress(state: &mut [u64; 4], word: u64) {
        state[3] ^= word;
        Self::rounds(state, C);
        state[0] ^= word;
    }

    fn pack(mut bytes: &[u8]) -> u64 {
        let mut word = 0;
        let mut shift = 0;
        if let Some((chunk, rest)) = bytes.split_first_chunk::<4>() {
            word = u64::from(u32::from_le_bytes(*chunk));
            bytes = rest;
            shift = 32;
        }
        if let Some((chunk, rest)) = bytes.split_first_chunk::<2>() {
            word |= u64::from(u16::from_le_bytes(*chunk)) << shift;
            bytes = rest;
            shift += 16;
        }
        if let Some(&byte) = bytes.first() {
            word |= u64::from(byte) << shift;
        }
        word
    }

    fn blocks<'a>(state: &mut [u64; 4], bytes: &'a [u8]) -> &'a [u8] {
        let (chunks, rest) = bytes.as_chunks::<8>();
        for chunk in chunks {
            Self::compress(state, u64::from_le_bytes(*chunk));
        }
        rest
    }

    fn short_write(&mut self, size: usize, value: u64) {
        let ntail = self.length & 7;
        self.length = self.length.wrapping_add(size);
        let needed = 8 - ntail;
        self.tail |= value << (8 * ntail);
        if size >= needed {
            Self::compress(&mut self.state, self.tail);
            self.tail = if needed < 8 { value >> (8 * needed) } else { 0 };
        }
    }

    pub(super) fn hash(mut self, bytes: &[u8]) -> u64 {
        let rest = Self::blocks(&mut self.state, bytes);
        self.length = bytes.len();
        self.tail = Self::pack(rest);
        self.finish()
    }
}

impl<const C: usize, const D: usize> hash::Hasher for Sip<C, D> {
    fn finish(&self) -> u64 {
        let mut state = self.state;
        let end = (((self.length as u64) & 0xff) << 56) | self.tail;
        Self::compress(&mut state, end);
        state[2] ^= 0xff;
        Self::rounds(&mut state, D);
        state[0] ^ state[1] ^ state[2] ^ state[3]
    }

    fn write(&mut self, mut bytes: &[u8]) {
        let ntail = self.length & 7;
        self.length = self.length.wrapping_add(bytes.len());
        if ntail != 0 {
            let take = bytes.len().min(8 - ntail);
            self.tail |= Self::pack(&bytes[..take]) << (8 * ntail);
            bytes = &bytes[take..];
            if ntail + take < 8 {
                return;
            }
            Self::compress(&mut self.state, self.tail);
        }

        let rest = Self::blocks(&mut self.state, bytes);
        self.tail = Self::pack(rest);
    }

    fn write_u8(&mut self, value: u8) {
        self.short_write(1, u64::from(value));
    }

    fn write_u16(&mut self, value: u16) {
        self.short_write(2, u64::from(value.to_le()));
    }

    fn write_u32(&mut self, value: u32) {
        self.short_write(4, u64::from(value.to_le()));
    }

    fn write_u64(&mut self, value: u64) {
        self.short_write(8, value.to_le());
    }

    fn write_usize(&mut self, value: usize) {
        self.short_write(mem::size_of::<usize>(), value.to_le() as u64);
    }
}