diff --git a/loadsig/loadsig/src/hspice.rs b/loadsig/loadsig/src/hspice.rs index f4b168d..728b683 100644 --- a/loadsig/loadsig/src/hspice.rs +++ b/loadsig/loadsig/src/hspice.rs @@ -1,6 +1,7 @@ use super::shared::{self, Endian}; use byteorder::ReadBytesExt; use std::io::{Read, Seek, SeekFrom}; +use std::str::FromStr; use thiserror::Error as TeError; // Reference: @@ -14,13 +15,11 @@ use thiserror::Error as TeError; pub enum Error { #[error("IO error {0}")] Io(#[from] std::io::Error), - #[error( - "fail to cast value into integral value. this usually caused by some value out of range" - )] + #[error("fail to cast value into integral value. maybe some values out of range")] CastToInt(#[from] std::num::TryFromIntError), #[error("something was overflow")] Overflow, - #[error("wrong endian integer {0:#X} in block head. it must be one of 0x4 or 0x4000000")] + #[error("wrong endian integer {0:#X} in block head. it must be one of 0x4 or 0x04000000")] WrongEndianInt(u32), #[error("wrong block head value {0:#X}. it must be 0x4")] WrongBlockHead(u32), @@ -31,10 +30,6 @@ pub enum Error { } type Result = std::result::Result; -const ENDIAN_INT: u32 = 0x4; -const LITTLE_ENDIAN_INT: u32 = 0x4; -const BIG_ENDIAN_INT: u32 = 0x4000000; - #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum SimSource { /// For HSPICE, read as big endian. @@ -51,14 +46,16 @@ impl SimSource { } } -pub struct HSpiceData {} +const ENDIAN_INT: u32 = 0x4; +const LITTLE_ENDIAN_INT: u32 = 0x4; +const BIG_ENDIAN_INT: u32 = 0x04000000; #[derive(Debug)] -struct HspiceBlock { +struct HSpiceBlock { block_offset: u64, block_len: u32, } -impl HspiceBlock { +impl HSpiceBlock { pub fn new(block_offset: u64, block_len: u32) -> Self { Self { block_offset, @@ -74,46 +71,86 @@ impl HspiceBlock { } } #[derive(Debug)] -struct HspiceBlocks { +struct HSpiceBlocks { sim_source: SimSource, - blocks: Vec, - header_blk_cnt: usize, + blocks: Vec, } -impl HspiceBlocks { - pub fn new(sim_source: SimSource, blocks: Vec, header_blk_cnt: usize) -> Self { - Self { - sim_source, - blocks, - header_blk_cnt, +impl HSpiceBlocks { + pub fn new(reader: &mut T) -> Result + where + T: ReadBytesExt + Seek, + { + // Back to the file head + reader.rewind()?; + + // Fetch the first u32 as little endian to decide the sim source + let endian_u32 = shared::read_int_value(Endian::LittleEndian, reader)?; + let sim_source = match endian_u32 { + LITTLE_ENDIAN_INT => SimSource::HspiceWin, + BIG_ENDIAN_INT => SimSource::Hspice, + other => return Err(Error::WrongEndianInt(other)), + }; + let endian = sim_source.to_endian(); + + // Iterate each block + let mut blocks: Vec = Vec::new(); + reader.rewind()?; + loop { + // Read block header. + // If there is no more data, break this loop. + let mut block_header = [0u32; 4]; + if let Err(e) = shared::read_int_values(endian, &mut block_header, reader) { + match e.kind() { + std::io::ErrorKind::UnexpectedEof => break, + _ => return Err(e.into()), + } + } + + // Check endian u32 + if block_header[0] != ENDIAN_INT { + return Err(Error::WrongBlockHead(block_header[0])); + } + if block_header[2] != ENDIAN_INT { + return Err(Error::WrongBlockHead(block_header[2])); + } + + // Get the offset and length of block body + let block_offset = reader.stream_position()?; + let block_len = block_header[3]; + + // Skip block body to check block tail + reader.seek(SeekFrom::Current(i64::try_from(block_len).unwrap()))?; + let block_tail = shared::read_int_value(endian, reader)?; + if block_tail != block_len { + return Err(Error::WrongBlockTail(block_tail, block_len)); + } + + // Push result + blocks.push(HSpiceBlock::new(block_offset, block_len)); + + // Cursor now at the beginning of next block. + // We can continue analyze next blocks safely. } + + // Build result and return. + Ok(Self { sim_source, blocks }) } } -impl<'a> HspiceBlocks { - pub fn to_reader(&'a self, reader: T) -> HspiceBlocksReader<'a, T> +impl<'a> HSpiceBlocks { + pub fn to_reader(&'a self, reader: T) -> HSpiceBlocksReader<'a, T> where T: Read + Seek, { - HspiceBlocksReader::new(reader, &self.blocks) - } - pub fn to_header_reader(&'a self, reader: T) -> HspiceBlocksReader<'a, T> - where - T: Read + Seek, - { - HspiceBlocksReader::new(reader, &self.blocks[..self.header_blk_cnt]) - } - pub fn to_data_reader(&'a self, reader: T) -> HspiceBlocksReader<'a, T> - where - T: Read + Seek, - { - HspiceBlocksReader::new(reader, &self.blocks[self.header_blk_cnt..]) + HSpiceBlocksReader::new(reader, &self.blocks) } } -struct HspiceBlocksReader<'a, T> + +struct HSpiceBlocksReader<'a, T> where T: Read + Seek, { reader: T, - blocks: &'a [HspiceBlock], + blocks: &'a [HSpiceBlock], /// The full size of this reader total_len: u64, @@ -126,11 +163,11 @@ where /// The cursor inside block. cursor: u32, } -impl<'a, T> HspiceBlocksReader<'a, T> +impl<'a, T> HSpiceBlocksReader<'a, T> where T: Read + Seek, { - pub fn new(reader: T, blocks: &'a [HspiceBlock]) -> Self { + pub fn new(reader: T, blocks: &'a [HSpiceBlock]) -> Self { // Build total length and offset table. let mut total_len: u64 = 0; let mut offset_table: Vec = Vec::with_capacity(blocks.len()); @@ -142,7 +179,7 @@ where // otherwise Seek impl will panic when SeekFrom::Current. offset_table.push(total_len); - HspiceBlocksReader { + HSpiceBlocksReader { reader, blocks, total_len, @@ -155,7 +192,7 @@ where self.reader } } -impl<'a, T> Read for HspiceBlocksReader<'a, T> +impl<'a, T> Read for HSpiceBlocksReader<'a, T> where T: Read + Seek, { @@ -213,7 +250,7 @@ where } } } -impl<'a, T> Seek for HspiceBlocksReader<'a, T> +impl<'a, T> Seek for HSpiceBlocksReader<'a, T> where T: Read + Seek, { @@ -269,6 +306,46 @@ where } } +const SIGNAL_NAME_LEN: usize = 8; +const SIGNAL_NAME_MAGIC_TAIL: &[u8; 4] = b"$&%#"; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum HSpiceVersion { + V9007, + V9601, + V2001, +} +#[derive(Debug, TeError)] +#[error("can not parse given string into valid HSpice version")] +pub struct ParseHSpiceVersionError {} +impl FromStr for HSpiceVersion { + type Err = ParseHSpiceVersionError; + + fn from_str(s: &str) -> std::result::Result { + match s { + "9007" => Ok(Self::V9007), + "9601" => Ok(Self::V9601), + "2001" => Ok(Self::V2001), + _ => Err(ParseHSpiceVersionError {}), + } + } +} + +#[derive(Debug)] +struct HSpiceDigest { + version: HSpiceVersion, + file_name: String, + timestamp: String, + copyright: String, +} + +#[derive(Debug)] +pub struct HSpiceContent { + digest: HSpiceDigest, +} + +pub type HSpiceData = HSpiceContent; + /// Guess whether given reader is a legal HSPICE signal file. /// /// Please note that this is a vauge check. @@ -294,102 +371,9 @@ where } /// Load given reader as HSPICE signal file. -pub fn load_signal(reader: &mut T) -> Result +pub fn load_signal(reader: T) -> Result where T: ReadBytesExt + Seek, { - // Fetch blocks list first - let blocks = analyze_hspice_block(reader)?; - println!("{:#?}", blocks); - - Ok(HSpiceData {}) + Err(Error::Overflow) } - -fn analyze_hspice_block(reader: &mut T) -> Result -where - T: ReadBytesExt + Seek, -{ - // Back to the file head - reader.rewind()?; - - // Fetch the first u32 as little endian to decide the sim source - let endian_u32 = shared::read_int_value(Endian::LittleEndian, reader)?; - let sim_source = match endian_u32 { - LITTLE_ENDIAN_INT => SimSource::HspiceWin, - BIG_ENDIAN_INT => SimSource::Hspice, - other => return Err(Error::WrongEndianInt(other)), - }; - let endian = sim_source.to_endian(); - - // Iterate each block - let mut blocks: Vec = Vec::new(); - reader.rewind()?; - loop { - // Read block header. - // If there is no more data, break this loop. - let mut block_header = [0u32; 4]; - if let Err(e) = shared::read_int_values(endian, &mut block_header, reader) { - match e.kind() { - std::io::ErrorKind::UnexpectedEof => break, - _ => return Err(e.into()), - } - } - - // Check endian u32 - if block_header[0] != ENDIAN_INT { - return Err(Error::WrongBlockHead(block_header[0])); - } - if block_header[2] != ENDIAN_INT { - return Err(Error::WrongBlockHead(block_header[2])); - } - - // Get the offset and length of block body - let block_offset = reader.stream_position()?; - let block_len = block_header[3]; - - // Skip block body to check block tail - reader.seek(SeekFrom::Current(i64::try_from(block_len).unwrap()))?; - let block_tail = shared::read_int_value(endian, reader)?; - if block_tail != block_len { - return Err(Error::WrongBlockTail(block_tail, block_len)); - } - - // Push result - blocks.push(HspiceBlock::new(block_offset, block_len)); - - // Cursor now at the beginning of next block. - // We can continue analyze next blocks safely. - } - - // Analyze header block count - let mut header_blk_cnt: Option = None; - for (i, block) in blocks.iter().enumerate() { - let signal_name_len = u32::try_from(SIGNAL_NAME_LEN).unwrap(); - let offset = if block.block_len < signal_name_len { - continue; - } else { - block.compute_offset(block.block_len - signal_name_len) - }; - - reader.seek(SeekFrom::Start(offset))?; - let mut magic_word = [0u8; MAGIC_WORDS.len()]; - reader.read_exact(&mut magic_word)?; - if &magic_word == MAGIC_WORDS { - match i.checked_add(1) { - Some(v) => header_blk_cnt = Some(v), - None => return Err(Error::Overflow), - } - break; - } - } - let header_blk_cnt = match header_blk_cnt { - Some(v) => v, - None => return Err(Error::NoMagicWords), - }; - - // Build result and return. - Ok(HspiceBlocks::new(sim_source, blocks, header_blk_cnt)) -} - -const SIGNAL_NAME_LEN: usize = 8; -const MAGIC_WORDS: &[u8; 4] = b"$&%#"; diff --git a/loadsig/loadsig/src/lib.rs b/loadsig/loadsig/src/lib.rs index 9fdfbe3..cfee6d0 100644 --- a/loadsig/loadsig/src/lib.rs +++ b/loadsig/loadsig/src/lib.rs @@ -71,13 +71,13 @@ pub enum SpiceData { NgSpice(ngspice::NgSpiceData), } -pub fn load_signal(reader: &mut T) -> Result +pub fn load_signal(mut reader: T) -> Result where T: ReadBytesExt + Seek, { - if ngspice::guess_signal(reader) { + if ngspice::guess_signal(&mut reader) { Ok(SpiceData::NgSpice(ngspice::load_signal(reader)?)) - } else if hspice::guess_signal(reader) { + } else if hspice::guess_signal(&mut reader) { Ok(SpiceData::HSpice(hspice::load_signal(reader)?)) } else { Err(Error::InvalidFmt) diff --git a/loadsig/loadsig/src/ngspice.rs b/loadsig/loadsig/src/ngspice.rs index 3851c2e..7031327 100644 --- a/loadsig/loadsig/src/ngspice.rs +++ b/loadsig/loadsig/src/ngspice.rs @@ -25,7 +25,7 @@ where } /// Load given reader as HSPICE signal file. -pub fn load_signal(reader: &mut T) -> Result +pub fn load_signal(reader: T) -> Result where T: ReadBytesExt + Seek, { diff --git a/loadsig/loadsig_exe/src/main.rs b/loadsig/loadsig_exe/src/main.rs index a8dd0a1..41325f3 100644 --- a/loadsig/loadsig_exe/src/main.rs +++ b/loadsig/loadsig_exe/src/main.rs @@ -14,11 +14,11 @@ struct LoadSigArgs { fn main() { let args = LoadSigArgs::parse(); - let mut reader = std::fs::File::open(&args.input).unwrap_or_else(|e| { + let reader = std::fs::File::open(&args.input).unwrap_or_else(|e| { eprintln!("{}", e); std::process::exit(1); }); - let _ = loadsig::load_signal(&mut reader).unwrap_or_else(|e| { + let _ = loadsig::load_signal(reader).unwrap_or_else(|e| { eprintln!("{}", e); std::process::exit(2); });