From 492879e4836b59f702fc7856af3c0c1729684895 Mon Sep 17 00:00:00 2001 From: yyc12345 Date: Fri, 6 Jun 2025 17:57:06 +0800 Subject: [PATCH] add seek trait for block reader --- loadsig/loadsig/src/hspice.rs | 97 +++++++++++++++++++++++++++++++++-- 1 file changed, 94 insertions(+), 3 deletions(-) diff --git a/loadsig/loadsig/src/hspice.rs b/loadsig/loadsig/src/hspice.rs index 8efd1ee..592a504 100644 --- a/loadsig/loadsig/src/hspice.rs +++ b/loadsig/loadsig/src/hspice.rs @@ -22,6 +22,8 @@ pub enum Error { WrongBlockHead(u32), #[error("wrong block tail value {0:#X}. it must be equal to head given value {1:#X}")] WrongBlockTail(u32, u32), + #[error("expected magic words \"$&%#\" is not found at block tail")] + NoMagicWords, } type Result = std::result::Result; @@ -59,6 +61,13 @@ impl HspiceBlock { block_len, } } + pub fn absolute_offset(&self, relative_offset: u32) -> u64 { + if relative_offset >= self.block_len { + panic!("relative offset is out of range") + } else { + self.block_offset + u64::from(relative_offset) + } + } } #[derive(Debug)] struct HspiceBlocks { @@ -101,6 +110,13 @@ where { reader: T, blocks: &'a [HspiceBlock], + + /// The full size of this reader + total_len: u64, + /// The table stored the offset relative to this reader. + /// It is convenient for Seek implement. + offset_table: Vec, + /// The cursor of block index. block_cursor: usize, /// The cursor inside block. @@ -111,9 +127,22 @@ where T: Read + Seek, { 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()); + for block in blocks { + offset_table.push(total_len); + total_len += u64::from(block.block_len); + } + // NOTE: This tail is essential for Seek impl. + // otherwise Seek impl will panic when SeekFrom::Current. + offset_table.push(total_len); + HspiceBlocksReader { reader, blocks, + total_len, + offset_table, block_cursor: 0, cursor: 0, } @@ -145,6 +174,7 @@ where // Check whether we need to switch to next block. if self.cursor >= self.blocks[self.block_cursor].block_len { self.block_cursor += 1; + self.cursor = 0; continue; } @@ -152,15 +182,16 @@ where 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); + let offset = self.blocks[self.block_cursor].absolute_offset(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. + // Shift buffer and cursor. buf = &mut buf[read_count..]; + self.cursor += u32::try_from(read_count).unwrap(); // Add count into total counter total_read_count += read_count; }; @@ -175,6 +206,61 @@ where } } } +impl<'a, T> Seek for HspiceBlocksReader<'a, T> +where + T: Read + Seek, +{ + fn seek(&mut self, pos: SeekFrom) -> std::io::Result { + use std::io::{Error, ErrorKind}; + + let err_instance = Error::new(ErrorKind::UnexpectedEof, "offset is out of range"); + let reader_cursor = match pos { + SeekFrom::Start(v) => { + if v > self.total_len { + return Err(err_instance) + } else { + v + } + }, + SeekFrom::End(v) => { + if v > 0 { + return Err(err_instance) + } else { + match self.total_len.checked_add_signed(v) { + Some(v) => v, + None => return Err(err_instance), + } + } + }, + SeekFrom::Current(v) => { + let current = self.offset_table[self.block_cursor] + u64::from(self.cursor); + match current.checked_add_signed(v) { + Some(v) => { + if v > self.total_len { + return Err(err_instance) + } else { + v + } + }, + None => return Err(err_instance), + } + }, + }; + + let block_cursor = match self.offset_table.binary_search(&reader_cursor) { + Ok(v) => v, + // This unwrap should be safe. + // Because there is no possible that every items are greater than given value, + // due to that the first item of table must be zero. + Err(v) => v.checked_sub(1).unwrap(), + }; + let cursor = reader_cursor - self.offset_table[block_cursor]; + + self.block_cursor = block_cursor; + self.cursor = u32::try_from(cursor).unwrap(); + Ok(reader_cursor) + } +} /// Guess whether given reader is a legal HSPICE signal file. /// @@ -268,9 +354,14 @@ where // We can continue analyze next blocks safely. } - // TODO: // Analyze header block count + // for block in &blocks { + // let relative_offset = block.block_len.sa + // let offset = block.absolute_offset(block.block_len - (SIGNAL_NAME_LEN as u32)); + // } // Build result and return. Ok(HspiceBlocks::new(sim_source, blocks, 0)) } + +const SIGNAL_NAME_LEN: usize = 8;