From 02c232f6cb6a31db759573e172b653254924b125 Mon Sep 17 00:00:00 2001 From: yyc12345 Date: Fri, 6 Jun 2025 11:12:29 +0800 Subject: [PATCH] add hspice block reader --- loadsig/loadsig/src/hspice.rs | 157 ++++++++++++++++++++++++++++------ 1 file changed, 132 insertions(+), 25 deletions(-) diff --git a/loadsig/loadsig/src/hspice.rs b/loadsig/loadsig/src/hspice.rs index 7581996..8efd1ee 100644 --- a/loadsig/loadsig/src/hspice.rs +++ b/loadsig/loadsig/src/hspice.rs @@ -1,5 +1,5 @@ use super::shared::{self, Endian}; -use byteorder::{BigEndian, LittleEndian, NativeEndian, ReadBytesExt}; +use byteorder::ReadBytesExt; use std::io::{Read, Seek, SeekFrom}; use thiserror::Error as TeError; @@ -14,7 +14,7 @@ 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")] + #[error("fail to cast value into integral value. this usually caused by some value out of range")] CastToInt(#[from] std::num::TryFromIntError), #[error("wrong endian integer {0:#X} in block head. it must be one of 0x4 or 0x4000000")] WrongEndianInt(u32), @@ -50,14 +50,130 @@ pub struct HSpiceData {} #[derive(Debug)] struct HspiceBlock { block_offset: u64, - block_len: usize, + block_len: u32, +} +impl HspiceBlock { + pub fn new(block_offset: u64, block_len: u32) -> Self { + Self { + block_offset, + block_len, + } + } } #[derive(Debug)] struct HspiceBlocks { sim_source: SimSource, blocks: Vec, header_blk_cnt: usize, - data_blk_cnt: usize, +} +impl HspiceBlocks { + pub fn new(sim_source: SimSource, blocks: Vec, header_blk_cnt: usize) -> Self { + Self { + sim_source, + blocks, + header_blk_cnt, + } + } +} +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..]) + } +} +struct HspiceBlocksReader<'a, T> +where + T: Read + Seek, +{ + reader: T, + blocks: &'a [HspiceBlock], + /// The cursor of block index. + block_cursor: usize, + /// The cursor inside block. + cursor: u32, +} +impl<'a, T> HspiceBlocksReader<'a, T> +where + T: Read + Seek, +{ + pub fn new(reader: T, blocks: &'a [HspiceBlock]) -> Self { + HspiceBlocksReader { + reader, + blocks, + block_cursor: 0, + cursor: 0, + } + } + pub fn into_inner(self) -> T { + self.reader + } +} +impl<'a, T> Read for HspiceBlocksReader<'a, T> +where + T: Read + Seek, +{ + fn read(&mut self, mut buf: &mut [u8]) -> std::io::Result { + use std::io::{Result, Error, ErrorKind}; + + let old_block_cursor = self.block_cursor; + let old_cursor = self.cursor; + let mut total_read_count: usize = 0; + + let rv: Result<()> = loop { + // Check whether there is space to be filled in buffer. + if buf.is_empty() { + break Ok(()); + } + // Check whether we reach the tail of block chain. + if self.block_cursor >= self.blocks.len() { + break Err(Error::new(ErrorKind::UnexpectedEof, "reach the tail of blocks")); + } + // Check whether we need to switch to next block. + if self.cursor >= self.blocks[self.block_cursor].block_len { + self.block_cursor += 1; + continue; + } + + // Decide the read count + let remain = self.blocks[self.block_cursor].block_len - self.cursor; + let read_count = std::cmp::min(usize::try_from(remain).unwrap(), buf.len()); + // Read data + let offset = self.blocks[self.block_cursor].block_offset + u64::from(self.cursor); + if let Err(e) = self.reader.seek(SeekFrom::Start(offset)) { + break Err(e); + } + if let Err(e) = self.read_exact(&mut buf[..read_count]) { + break Err(e); + } + // Shift buffer. + buf = &mut buf[read_count..]; + // Add count into total counter + total_read_count += read_count; + }; + + // If fail to read, we restore its original states. + if let Err(e) = rv { + self.block_cursor = old_block_cursor; + self.cursor = old_cursor; + Err(e) + } else { + Ok(total_read_count) + } + } } /// Guess whether given reader is a legal HSPICE signal file. @@ -70,12 +186,12 @@ where T: ReadBytesExt + Seek, { // Back to the head of file. - if let Err(_) = reader.seek(SeekFrom::Start(0)) { + if let Err(_) = reader.rewind() { return false; } // Check whether first u32 is 4 (in little or big endian) - match reader.read_u32::() { + match shared::read_int_value(Endian::NativeEndian, reader) { Ok(val) => match val { LITTLE_ENDIAN_INT | BIG_ENDIAN_INT => true, _ => false, @@ -101,10 +217,10 @@ where T: ReadBytesExt + Seek, { // Back to the file head - reader.seek(SeekFrom::Start(0))?; + reader.rewind()?; // Fetch the first u32 as little endian to decide the sim source - let endian_u32 = reader.read_u32::()?; + 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, @@ -114,7 +230,7 @@ where // Iterate each block let mut blocks: Vec = Vec::new(); - reader.seek(SeekFrom::Start(0))?; + reader.rewind()?; loop { // Read block header. // If there is no more data, break this loop. @@ -135,22 +251,18 @@ where } // Get the offset and length of block body - let raw_block_len = block_header[3]; - let block_offset = reader.seek(SeekFrom::Current(0))?; - let block_len = usize::try_from(raw_block_len).unwrap(); + 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(raw_block_len).unwrap()))?; + reader.seek(SeekFrom::Current(i64::try_from(block_len).unwrap()))?; let block_tail = shared::read_int_value(endian, reader)?; - if block_tail != raw_block_len { - return Err(Error::WrongBlockTail(block_tail, raw_block_len)); + if block_tail != block_len { + return Err(Error::WrongBlockTail(block_tail, block_len)); } // Push result - blocks.push(HspiceBlock { - block_offset, - block_len, - }); + blocks.push(HspiceBlock::new(block_offset, block_len)); // Cursor now at the beginning of next block. // We can continue analyze next blocks safely. @@ -160,10 +272,5 @@ where // Analyze header block count // Build result and return. - Ok(HspiceBlocks { - sim_source, - blocks, - header_blk_cnt: 0, - data_blk_cnt: 0, - }) + Ok(HspiceBlocks::new(sim_source, blocks, 0)) }