blkar 7.2.7

Multithreaded archiver offering bit rot protection and sector level recoverability
Documentation
use crate::file_writer::{FileWriter, FileWriterParam};
use crate::general_error::Error;
use crate::misc_utils;
use crate::sbx_block::Block;
use crate::sbx_specs::{
    ver_to_block_size, Version, SBX_FILE_UID_LEN, SBX_FIRST_DATA_SEQ_NUM, SBX_LARGEST_BLOCK_SIZE,
};
use std::collections::HashMap;
use std::collections::LinkedList;

mod tests;

macro_rules! slice_slot_w_index {
    (
        full => $self:expr, $index:expr
    ) => {{
        let start = $index * SBX_LARGEST_BLOCK_SIZE;
        let end_exc = start + SBX_LARGEST_BLOCK_SIZE;

        &$self.data[start..end_exc]
    }};
    (
        full => mut => $self:expr, $index:expr
    ) => {{
        let start = $index * SBX_LARGEST_BLOCK_SIZE;
        let end_exc = start + SBX_LARGEST_BLOCK_SIZE;

        &mut $self.data[start..end_exc]
    }};
    (
        depend_on_block_ver => $self:expr, $index:expr
    ) => {{
        let version = $self.blocks[$index].get_version();
        let block_size = ver_to_block_size(version);

        let start = $index * SBX_LARGEST_BLOCK_SIZE;
        let end_exc = start + block_size;

        &$self.data[start..end_exc]
    }};
    (
        depend_on_block_ver => mut => $self:expr, $index:expr
    ) => {{
        let version = $self.blocks[$index].get_version();
        let block_size = ver_to_block_size(version);

        let start = $index * SBX_LARGEST_BLOCK_SIZE;
        let end_exc = start + block_size;

        &mut $self.data[start..end_exc]
    }};
}

pub struct Slot<'a> {
    pub block: &'a mut Block,
    pub slot: &'a mut [u8],
}

pub struct RescueBuffer {
    size: usize,
    slots_used: usize,
    blocks: Vec<Block>,
    data: Vec<u8>,
    uid_to_slot_indices: HashMap<[u8; SBX_FILE_UID_LEN], LinkedList<usize>>,
}

impl RescueBuffer {
    pub fn new(size: usize) -> Self {
        assert!(size > 0);

        let mut blocks = Vec::with_capacity(size);

        for _ in 0..size {
            blocks.push(Block::dummy());
        }

        RescueBuffer {
            size,
            slots_used: 0,
            blocks,
            data: vec![0; size * SBX_LARGEST_BLOCK_SIZE],
            uid_to_slot_indices: HashMap::with_capacity(size),
        }
    }

    pub fn get_slot(&mut self) -> Option<Slot> {
        if self.slots_used == self.size {
            None
        } else {
            let slot = slice_slot_w_index!(full => mut => self, self.slots_used);
            let block = &mut self.blocks[self.slots_used];

            self.slots_used += 1;

            Some(Slot { block, slot })
        }
    }

    pub fn group_by_uid(&mut self) {
        for i in 0..self.slots_used {
            let uid = self.blocks[i].get_uid();

            match self.uid_to_slot_indices.get_mut(&uid) {
                Some(l) => l.push_back(i),
                None => {
                    let mut l = LinkedList::new();
                    l.push_front(i);
                    self.uid_to_slot_indices.insert(uid, l);
                }
            }
        }
    }

    pub fn write(&mut self, out_dir: &str) -> Result<(), Error> {
        for (uid, l) in self.uid_to_slot_indices.iter() {
            let uid_str = misc_utils::bytes_to_upper_hex_string(uid);
            let path = misc_utils::make_path(&[out_dir, &uid_str]);

            let mut writer = FileWriter::new(
                &path,
                FileWriterParam {
                    read: false,
                    append: true,
                    truncate: false,
                    buffered: true,
                },
            )?;

            for &i in l.iter() {
                let slot = slice_slot_w_index!(depend_on_block_ver => self, i);

                writer.write(slot)?;
            }
        }

        Ok(())
    }

    pub fn reset(&mut self) {
        self.slots_used = 0;

        self.uid_to_slot_indices.clear();

        for block in self.blocks.iter_mut() {
            block.set_version(Version::V1);
            block.set_uid([0; SBX_FILE_UID_LEN]);
            block.set_seq_num(SBX_FIRST_DATA_SEQ_NUM);
        }
    }

    pub fn reset_slot(&mut self, slot_index: usize) {
        let block = &mut self.blocks[slot_index];
        block.set_version(Version::V1);
        block.set_uid([0; SBX_FILE_UID_LEN]);
        block.set_seq_num(SBX_FIRST_DATA_SEQ_NUM);
    }

    pub fn cancel_slot(&mut self) {
        assert!(self.slots_used > 0);

        self.slots_used -= 1;

        self.reset_slot(self.slots_used);
    }

    pub fn is_full(&self) -> bool {
        self.slots_used == self.size
    }
}