Skip to content
Snippets Groups Projects
index_table.rs 3 KiB
Newer Older
  • Learn to ignore specific revisions
  • Daniel Müller's avatar
    Daniel Müller committed
    use crate::read_write_extension::{ReadExtTypes, WriteExtTypes};
    use log::debug;
    use rustc_hash::FxHashMap;
    use std::{
        io::{Read, Write},
        path::PathBuf,
    };
    
    use super::Uid;
    
    pub type HashValue = [u8; 32];
    pub type BlockOffset = u64;
    
    #[derive(Debug, Clone, Default, PartialEq)]
    pub struct IndexTable {
        /// Unique ID of the BlockPool referenced in this FileTable
        pub(crate) uid: Uid,
        /// Map block hash to block byte offset
        pub(crate) blocks: FxHashMap<HashValue, BlockOffset>,
        /// Path to the file containing the IndexTable, if applicable
        pub(crate) file_path: Option<PathBuf>,
    }
    
    impl IndexTable {
        /// Serialize (write) the IndexTable to the given output stream. This will not apply compression
        pub fn serialize_to<T: Write>(&self, mut output: T) -> std::io::Result<()> {
            debug!(
                "Serializing IndexTable: {} Blocks, UID={:X?}",
                self.blocks.len(),
                self.uid
            );
    
            output.write_all(&self.uid)?;
            for (hash, byte_offset) in self.blocks.iter() {
                output.write_all(hash)?;
                output.write_all_u64(*byte_offset)?;
            }
            Ok(())
        }
    
        /// Try to deserialize (read) the IndexTable from the given input stream. This will not apply
        /// decompression
        pub fn try_deserialize_from<T: Read>(mut input: T) -> std::io::Result<Self> {
            use std::io::ErrorKind::UnexpectedEof;
    
            let mut index_table = Self::default();
    
            input.read_exact(&mut index_table.uid)?;
    
            loop {
                let mut hash = [0; 32];
    
                match input.read_exact(&mut hash) {
                    Err(e) if e.kind() == UnexpectedEof => break,
                    r => r?,
                }
    
                let start = input.read_exact_u64()?;
    
                index_table.blocks.insert(hash, start);
            }
    
            debug!(
                "Read IndexTable: {} Blocks, UID={:X?}",
                index_table.blocks.len(),
                index_table.uid
            );
    
            Ok(index_table)
        }
    }
    
    #[cfg(test)]
    mod test {
        use super::IndexTable;
        use std::io::Cursor;
    
        /// Test roundtrip for IndexTable (serialize <-> deserialize)
        #[test]
        pub fn test_index_table_io() {
            let mut index_table = IndexTable::default();
    
            let mut offset = 0;
    
            // Fill the IndexTable with entries
            for i in 0..50 as u8 {
                let mut hash = [0; 32];
                hash[3] = i + 57;
                hash[8] = i;
                hash[15] = i * 2;
    
                index_table.blocks.insert(hash, offset);
    
                offset += i as u64 * 2;
            }
    
            // Use a Vec buffer as the simulated IO for serialization and deserialization
            let mut io_buffer = Vec::new();
    
            index_table.serialize_to(&mut io_buffer).unwrap();
    
            let index_table2 = IndexTable::try_deserialize_from(Cursor::new(io_buffer)).unwrap();
    
            // Compare if the index table was deserialized successfully
            assert_eq!(
                index_table, index_table2,
                "IndexTable serialization <-> deserialization failed"
            );
        }
    }