oof 0.0.3

Naive proof scheme for accelerated testing
Documentation
#![cfg_attr(not(test), no_std)]

use core::mem::size_of;
use core::slice::{from_raw_parts, from_raw_parts_mut};

use arrayref::array_ref;
use bonsai::{expand, subtree_index_to_general};
use sha2::{Digest, Sha256};

type K = u128;
type V = [u8; 32];

pub struct Oof<'a> {
    pub keys: &'a mut [K],
    pub values: &'a mut [V],
    pub height: u32,
    is_dirty: bool,
}

#[derive(Debug, PartialEq)]
pub enum Error {
    EntryNotFound(K),
}

impl<'a> Oof<'a> {
    pub fn new(keys: &'a mut [K], values: &'a mut [V], height: u32) -> Self {
        Oof {
            keys,
            values,
            height,
            is_dirty: false,
        }
    }

    pub unsafe fn from_blob(data: *mut u8, height: u32) -> Self {
        let count = u32::from_le_bytes(*array_ref![from_raw_parts(data, 4), 0, 4]) as usize;
        let keys = data.offset(4) as *mut K;
        let values = data.offset(4 + (count * size_of::<K>()) as isize) as *mut V;

        Self::new(
            from_raw_parts_mut(keys, count),
            from_raw_parts_mut(values, count),
            height,
        )
    }

    pub fn get(&self, key: &K) -> Option<&V> {
        match self.keys.binary_search(&key) {
            Ok(index) => Some(&self.values[index]),
            Err(_) => None,
        }
    }

    pub fn set(&mut self, key: K, value: V) -> Result<V, Error> {
        match self.keys.binary_search(&key) {
            Ok(index) => {
                let old = self.values[index];
                self.values[index] = value;
                self.is_dirty = true;
                Ok(old)
            }
            Err(_) => Err(Error::EntryNotFound(key)),
        }
    }

    pub fn root(&mut self) -> Result<&V, Error> {
        if self.is_dirty {
            self.refresh()?;
        }

        Ok(self.get(&1).ok_or(Error::EntryNotFound(1))?)
    }

    fn refresh(&mut self) -> Result<(), Error> {
        let mut position = self.keys.len() - 1;

        while position > 0 {
            let (left, right, parent) = expand(self.keys[position]);

            let left = self.get(&left).ok_or(Error::EntryNotFound(left))?;
            let right = self.get(&right).ok_or(Error::EntryNotFound(right))?;

            let mut buf = [0u8; 64];
            hash_children(&mut buf, left, right);

            self.set(parent, *array_ref![buf, 0, 32])?;

            position -= 1;
        }

        self.is_dirty = false;

        Ok(())
    }

    pub fn to_subtree(&mut self, root: K) {
        for i in 0..self.keys.len() {
            self.keys[i] = subtree_index_to_general(root, self.keys[i]);
        }
    }
}

fn hash_children(buf: &mut [u8; 64], left: &V, right: &V) {
    buf[0..32].copy_from_slice(left);
    buf[32..64].copy_from_slice(right);
    let tmp = Sha256::digest(buf);
    buf[0..32].copy_from_slice(tmp.as_ref());
}

#[cfg(test)]
mod tests {
    use super::*;
    use core::mem::transmute;

    fn build_value(n: u8) -> [u8; 32] {
        let mut tmp = [0u8; 32];
        tmp[0] = n;
        tmp
    }

    #[test]
    fn get() {
        let oof = Oof {
            keys: &mut [1, 2, 3],
            values: &mut [build_value(1), build_value(2), build_value(3)],
            height: 1,
            is_dirty: false,
        };

        assert_eq!(oof.get(&1), Some(&build_value(1)));
        assert_eq!(oof.get(&2), Some(&build_value(2)));
        assert_eq!(oof.get(&3), Some(&build_value(3)));
        assert_eq!(oof.get(&4), None);
    }

    #[test]
    fn set() {
        let mut oof = Oof {
            keys: &mut [1, 2, 3],
            values: &mut [build_value(1), build_value(2), build_value(3)],
            height: 1,
            is_dirty: false,
        };

        assert_eq!(oof.set(1, build_value(2)), Ok(build_value(1)));
        assert_eq!(oof.set(2, build_value(3)), Ok(build_value(2)));
        assert_eq!(oof.set(3, build_value(4)), Ok(build_value(3)));
        assert_eq!(oof.set(4, build_value(5)), Err(Error::EntryNotFound(4)));
    }

    #[test]
    fn root() {
        let mut oof = Oof {
            keys: &mut [1, 2, 3],
            values: &mut [build_value(1), build_value(2), build_value(3)],
            height: 2,
            is_dirty: true,
        };

        let mut buf = [0u8; 64];
        hash_children(&mut buf, &oof.values[1], &oof.values[2]);

        assert_eq!(oof.root(), Ok(array_ref![buf, 0, 32]));
    }

    #[test]
    fn to_subtree() {
        let mut oof = Oof {
            keys: &mut [1, 2, 3],
            values: &mut [build_value(1), build_value(2), build_value(3)],
            height: 1,
            is_dirty: false,
        };

        oof.to_subtree(5);

        assert_eq!(oof.get(&5), Some(&build_value(1)));
        assert_eq!(oof.get(&10), Some(&build_value(2)));
        assert_eq!(oof.get(&11), Some(&build_value(3)));

        assert_eq!(oof.get(&1), None);
        assert_eq!(oof.get(&2), None);
        assert_eq!(oof.get(&3), None);
        assert_eq!(oof.get(&12), None);
    }

    #[test]
    fn from_blob() {
        let count: u32 = 3;

        let keys: [K; 3] = [1, 2, 3];
        let values: [V; 3] = [build_value(1), build_value(2), build_value(3)];

        let keys: [u8; 48] = unsafe { transmute(keys) };
        let values: [u8; 96] = unsafe { transmute(values) };

        let mut blob = [0u8; (4 + 48 + 96)];
        blob[0..4].copy_from_slice(&count.to_le_bytes());
        blob[4..52].copy_from_slice(&keys[..]);
        blob[52..148].copy_from_slice(&values[..]);

        let oof = unsafe { Oof::from_blob(blob[..].as_ptr() as *mut u8, 2) };

        assert_eq!(oof.get(&1), Some(&build_value(1)));
        assert_eq!(oof.get(&2), Some(&build_value(2)));
        assert_eq!(oof.get(&3), Some(&build_value(3)));
        assert_eq!(oof.get(&4), None);
    }
}