add hspice block reader
This commit is contained in:
parent
b36902b047
commit
02c232f6cb
@ -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<HspiceBlock>,
|
||||
header_blk_cnt: usize,
|
||||
data_blk_cnt: usize,
|
||||
}
|
||||
impl HspiceBlocks {
|
||||
pub fn new(sim_source: SimSource, blocks: Vec<HspiceBlock>, header_blk_cnt: usize) -> Self {
|
||||
Self {
|
||||
sim_source,
|
||||
blocks,
|
||||
header_blk_cnt,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<'a> HspiceBlocks {
|
||||
pub fn to_reader<T>(&'a self, reader: T) -> HspiceBlocksReader<'a, T>
|
||||
where
|
||||
T: Read + Seek,
|
||||
{
|
||||
HspiceBlocksReader::new(reader, &self.blocks)
|
||||
}
|
||||
pub fn to_header_reader<T>(&'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<T>(&'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<usize> {
|
||||
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::<NativeEndian>() {
|
||||
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::<LittleEndian>()?;
|
||||
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<HspiceBlock> = 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))
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user