// Copyright (C) 2017-2018 The Duniter Project Developers. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . //! Provide a trait and implementation to read and write `WebOfTrust` to disk. use data::NodeId; use std::fs; use std::fs::File; use std::io; use std::io::prelude::*; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use data::WebOfTrust; /// Results of `WebOfTrust` parsing from binary file. #[derive(Debug)] pub enum WotParseError { /// FailToOpenFile FailToOpenFile(io::Error), /// IOError IOError(io::Error), } impl From for WotParseError { fn from(e: io::Error) -> WotParseError { WotParseError::IOError(e) } } /// Results of `WebOfTrust` writing to binary file. #[derive(Debug)] pub enum WotWriteError { /// WrongWotSize WrongWotSize(), /// FailToCreateFile FailToCreateFile(io::Error), /// FailToWriteInFile FailToWriteInFile(io::Error), } impl From for WotWriteError { fn from(e: io::Error) -> WotWriteError { WotWriteError::FailToWriteInFile(e) } } /// Provide Read/Write functions for `WebOfTrust` objects. pub trait FileFormater { /// Try to read a `WebOfTrust` from a file. fn from_file( &self, path: &str, max_links: usize, ) -> Result<(T, Vec), WotParseError>; /// Tru to write a `WebOfTrust` in a file. fn to_file(&self, wot: &T, data: &[u8], path: &str) -> Result<(), WotWriteError>; } /// Read and write WebOfTrust in a binary format. #[derive(Debug, Clone, Copy)] pub struct BinaryFileFormater; impl FileFormater for BinaryFileFormater { /// Try to read a `WebOfTrust` from a file. fn from_file( &self, path: &str, max_links: usize, ) -> Result<(T, Vec), WotParseError> { let mut wot = T::new(max_links); let file_size = fs::metadata(path).expect("fail to read wotb file !").len(); let mut file_pointing_to_blockstamp_size: Vec = vec![0; file_size as usize]; match File::open(path) { Ok(mut file) => { file.read_exact(&mut file_pointing_to_blockstamp_size.as_mut_slice())?; } Err(e) => return Err(WotParseError::FailToOpenFile(e)), }; // Read up to 4 bytes (blockstamp_size) let mut file_pointing_to_blockstamp = file_pointing_to_blockstamp_size.split_off(4); // Get blockstamp size let mut buf = &file_pointing_to_blockstamp_size[..]; let blockstamp_size = buf.read_u32::().unwrap(); // Read up to blockstamp_size bytes (blockstamp) let mut file_pointing_to_nodes_count = file_pointing_to_blockstamp.split_off(blockstamp_size as usize); // Read up to 4 bytes (nodes_count) let mut file_pointing_to_nodes_states = file_pointing_to_nodes_count.split_off(4); // Read nodes_count let mut buf = &file_pointing_to_nodes_count[..]; let nodes_count = buf.read_u32::().unwrap(); // Calcule nodes_state size let nodes_states_size = match nodes_count % 8 { 0 => nodes_count / 8, _ => (nodes_count / 8) + 1, }; // Read up to nodes_states_size bytes (nodes_states) let file_pointing_to_links = file_pointing_to_nodes_states.split_off(nodes_states_size as usize); let count_total_bytes_read = file_pointing_to_links.len() + nodes_states_size as usize + 4 + blockstamp_size as usize + 4; if count_total_bytes_read != file_size as usize { panic!("not read all wot file !"); } // Apply nodes state let mut count_remaining_nodes = nodes_count; for byte in file_pointing_to_nodes_states { let mut byte_integer = u8::from_be(byte); let mut factor: u8 = 128; for _i in 0..8 { if count_remaining_nodes > 0 { wot.add_node(); if byte_integer >= factor { byte_integer -= factor; } else { let _test = wot.set_enabled( NodeId((nodes_count - count_remaining_nodes) as usize), false, ); } count_remaining_nodes -= 1; } factor /= 2; } } // Apply links let mut buffer_3b: Vec = Vec::with_capacity(3); let mut count_bytes = 0; let mut remaining_links: u8 = 0; let mut target: u32 = 0; for byte in file_pointing_to_links { if remaining_links == 0 { target += 1; remaining_links = u8::from_be(byte); count_bytes = 0; } else { buffer_3b.push(byte); if count_bytes % 3 == 2 { let mut buf = &buffer_3b.clone()[..]; let source = buf.read_u24::().expect("fail to parse source"); wot.add_link(NodeId(source as usize), NodeId((target - 1) as usize)); remaining_links -= 1; buffer_3b.clear(); } count_bytes += 1; } } if count_bytes % 3 != 0 { panic!("not read all wot file !"); } Ok((wot, file_pointing_to_blockstamp)) } /// Try to write a `WebOfTrust` in a file. fn to_file( &self, wot: &T, data: &[u8], path: &str, ) -> Result<(), WotWriteError> { let mut buffer: Vec = Vec::new(); // Write blockstamp size let blockstamp_size = data.len() as u32; let mut bytes: Vec = Vec::with_capacity(4); bytes.write_u32::(blockstamp_size).unwrap(); buffer.append(&mut bytes); // Write blockstamp buffer.append(&mut data.to_vec()); // Write nodes_count let nodes_count = wot.size() as u32; let mut bytes: Vec = Vec::with_capacity(4); bytes.write_u32::(nodes_count).unwrap(); buffer.append(&mut bytes); // Write enable state by groups of 8 let mut enable_states: u8 = 0; let mut factor: u8 = 128; for n in 0..nodes_count { match wot.is_enabled(NodeId(n as usize)) { Some(enable) => { if enable { enable_states += factor; } } None => { return Err(WotWriteError::WrongWotSize()); } } if n % 8 == 7 { factor = 128; let mut tmp_buf = Vec::with_capacity(1); tmp_buf.write_u8(enable_states).unwrap(); buffer.append(&mut tmp_buf); enable_states = 0; } else { factor /= 2; } } // nodes_states padding if nodes_count % 8 != 7 { let mut tmp_buf = Vec::with_capacity(1); tmp_buf.write_u8(enable_states).unwrap(); buffer.append(&mut tmp_buf); } // Write links for n in 0..nodes_count { if let Some(sources) = wot.get_links_source(NodeId(n as usize)) { // Write sources_counts let mut bytes = Vec::with_capacity(1); bytes.write_u8(sources.len() as u8).unwrap(); buffer.append(&mut bytes); for source in &sources { // Write source let mut bytes: Vec = Vec::with_capacity(3); bytes.write_u24::(source.0 as u32).unwrap(); buffer.append(&mut bytes); } }; } // Create or open file let mut file = match File::create(path) { Ok(file) => file, Err(e) => return Err(WotWriteError::FailToCreateFile(e)), }; // Write buffer in file file.write_all(&buffer)?; Ok(()) } }