prepare digest reading

This commit is contained in:
2025-06-23 21:32:34 +08:00
parent 71e2ed9dcf
commit d6317502a3
4 changed files with 127 additions and 143 deletions

View File

@ -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<T> = std::result::Result<T, Error>;
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<HspiceBlock>,
header_blk_cnt: usize,
blocks: Vec<HSpiceBlock>,
}
impl HspiceBlocks {
pub fn new(sim_source: SimSource, blocks: Vec<HspiceBlock>, header_blk_cnt: usize) -> Self {
Self {
sim_source,
blocks,
header_blk_cnt,
impl HSpiceBlocks {
pub fn new<T>(reader: &mut T) -> Result<Self>
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<HSpiceBlock> = 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<T>(&'a self, reader: T) -> HspiceBlocksReader<'a, T>
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..])
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<u64> = 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<Self, Self::Err> {
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<T>(reader: &mut T) -> Result<HSpiceData>
pub fn load_signal<T>(reader: T) -> Result<HSpiceData>
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<T>(reader: &mut T) -> Result<HspiceBlocks>
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<HspiceBlock> = 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<usize> = 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"$&%#";

View File

@ -71,13 +71,13 @@ pub enum SpiceData {
NgSpice(ngspice::NgSpiceData),
}
pub fn load_signal<T>(reader: &mut T) -> Result<SpiceData>
pub fn load_signal<T>(mut reader: T) -> Result<SpiceData>
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)

View File

@ -25,7 +25,7 @@ where
}
/// Load given reader as HSPICE signal file.
pub fn load_signal<T>(reader: &mut T) -> Result<NgSpiceData>
pub fn load_signal<T>(reader: T) -> Result<NgSpiceData>
where
T: ReadBytesExt + Seek,
{

View File

@ -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);
});