use crate::stdf_error::StdfError;
use crate::stdf_types::*;
#[cfg(feature = "bzip")]
use bzip2::bufread::BzDecoder;
#[cfg(feature = "gzip")]
use flate2::bufread::GzDecoder;
use std::io::{self, BufReader, SeekFrom}; use std::io::{BufRead, Read, Seek};
use std::{fs, path::Path}; #[cfg(feature = "zipfile")]
use zip::{read::ZipFile, ZipArchive};
#[cfg(feature = "zipfile")]
pub(crate) struct ZipBundle<R> {
file: Option<ZipFile<'static>>,
archive: Box<ZipArchive<R>>,
}
#[allow(clippy::large_enum_variant)]
pub(crate) enum StdfStream<R> {
Binary(R),
#[cfg(feature = "gzip")]
Gz(GzDecoder<R>),
#[cfg(feature = "bzip")]
Bz(BzDecoder<R>),
#[cfg(feature = "zipfile")]
Zip(ZipBundle<R>),
}
pub struct StdfReader<R> {
endianness: ByteOrder,
stream: StdfStream<R>,
}
pub struct RecordIter<'a, R> {
inner: &'a mut StdfReader<R>,
}
pub struct RawDataIter<'a, R> {
offset: u64,
inner: &'a mut StdfReader<R>,
}
impl StdfReader<BufReader<fs::File>> {
#[inline(always)]
pub fn new<P>(path: P) -> Result<Self, StdfError>
where
P: AsRef<Path>,
{
let path_string = path.as_ref().display().to_string();
let file_ext = path_string.rsplit('.').next();
let compress_type = match file_ext {
Some(ext) => match ext {
#[cfg(feature = "gzip")]
"gz" => CompressType::GzipCompressed,
#[cfg(feature = "bzip")]
"bz2" => CompressType::BzipCompressed,
#[cfg(feature = "zipfile")]
"zip" => CompressType::ZipCompressed,
_ => CompressType::Uncompressed,
},
None => CompressType::Uncompressed,
};
let fp = fs::OpenOptions::new().read(true).open(path)?;
let br = BufReader::with_capacity(2 << 20, fp);
StdfReader::from(br, &compress_type)
}
}
impl<R: BufRead + Seek> StdfReader<R> {
#[inline(always)]
pub fn from(in_stream: R, compress_type: &CompressType) -> Result<Self, StdfError> {
let mut stream = match compress_type {
#[cfg(feature = "gzip")]
CompressType::GzipCompressed => StdfStream::Gz(GzDecoder::new(in_stream)),
#[cfg(feature = "bzip")]
CompressType::BzipCompressed => StdfStream::Bz(BzDecoder::new(in_stream)),
#[cfg(feature = "zipfile")]
CompressType::ZipCompressed => StdfStream::Zip(ZipBundle::new(in_stream, 0)?),
_ => StdfStream::Binary(in_stream),
};
let mut buf = [0u8; 4];
stream.read_exact(&mut buf)?;
let far_header = RecordHeader::new().read_from_bytes(&buf, &ByteOrder::LittleEndian)?;
let endianness = match far_header.len {
2 => Ok(ByteOrder::LittleEndian),
512 => Ok(ByteOrder::BigEndian),
_ => Err(StdfError {
code: 1,
msg: String::from("Cannot determine endianness"),
}),
}?;
if (far_header.typ, far_header.sub) != (0, 10) {
return Err(StdfError {
code: 1,
msg: format!(
"FAR header (0, 10) expected, but {:?} is found",
(far_header.typ, far_header.sub)
),
});
}
stream = rewind_stream_position(stream)?;
Ok(StdfReader { endianness, stream })
}
#[inline(always)]
fn read_header(&mut self) -> Result<RecordHeader, StdfError> {
let mut buf = [0u8; 4];
self.stream.read_exact(&mut buf)?;
RecordHeader::new().read_from_bytes(&buf, &self.endianness)
}
#[inline(always)]
pub fn get_record_iter(&mut self) -> RecordIter<R> {
RecordIter { inner: self }
}
#[inline(always)]
pub fn get_rawdata_iter(&mut self) -> RawDataIter<R> {
RawDataIter {
offset: 0,
inner: self,
}
}
}
#[cfg(feature = "zipfile")]
impl<R: BufRead + Seek> ZipBundle<R> {
pub(crate) fn new(stream: R, file_index: usize) -> Result<ZipBundle<R>, StdfError> {
let archive = ZipArchive::new(stream)?;
let mut archive = Box::new(archive);
let file =
unsafe { std::mem::transmute::<_, ZipFile<'static>>(archive.by_index(file_index)?) };
Ok(ZipBundle {
archive,
file: Some(file),
})
}
pub(crate) fn reopen_file(&mut self, file_index: usize) -> Result<(), StdfError> {
self.file = None;
let file = unsafe {
std::mem::transmute::<_, ZipFile<'static>>(self.archive.by_index(file_index)?)
};
self.file = Some(file);
Ok(())
}
}
#[cfg(feature = "zipfile")]
impl<R: BufRead + Seek> Read for ZipBundle<R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.file.as_mut().unwrap().read(buf)
}
}
impl<R: BufRead + Seek> StdfStream<R> {
#[cfg(feature = "atdf")]
#[inline(always)]
pub(crate) fn read_until(&mut self, delim: u8, buf: &mut Vec<u8>) -> io::Result<usize> {
match self {
StdfStream::Binary(bstream) => bstream.read_until(delim, buf),
#[cfg(feature = "gzip")]
StdfStream::Gz(gzstream) => general_read_until(gzstream, delim, buf),
#[cfg(feature = "bzip")]
StdfStream::Bz(bzstream) => general_read_until(bzstream, delim, buf),
#[cfg(feature = "zipfile")]
StdfStream::Zip(zipstream) => general_read_until(zipstream, delim, buf),
}
}
}
impl<R: BufRead + Seek> Read for StdfStream<R> {
#[inline(always)]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match self {
StdfStream::Binary(bstream) => bstream.read(buf),
#[cfg(feature = "gzip")]
StdfStream::Gz(gzstream) => gzstream.read(buf),
#[cfg(feature = "bzip")]
StdfStream::Bz(bzstream) => bzstream.read(buf),
#[cfg(feature = "zipfile")]
StdfStream::Zip(zipstream) => zipstream.read(buf),
}
}
}
impl<R: BufRead + Seek> Iterator for RecordIter<'_, R> {
type Item = Result<StdfRecord, StdfError>;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
let header = match self.inner.read_header() {
Ok(h) => h,
Err(e) => {
return match e.code {
4 => None,
_ => Some(Err(e)),
};
}
};
let mut buffer = vec![0u8; header.len as usize];
if let Err(io_e) = self.inner.stream.read_exact(&mut buffer) {
return Some(Err(StdfError {
code: 3,
msg: io_e.to_string(),
}));
}
let mut rec = StdfRecord::new_from_header(header);
rec.read_from_bytes(&buffer, &self.inner.endianness);
Some(Ok(rec))
}
}
impl<R: BufRead + Seek> Iterator for RawDataIter<'_, R> {
type Item = Result<RawDataElement, StdfError>;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
let header = match self.inner.read_header() {
Ok(h) => h,
Err(e) => {
return match e.code {
4 => None,
_ => Some(Err(e)),
};
}
};
self.offset += 4;
let data_offset = self.offset;
let mut buffer = vec![0u8; header.len as usize];
if let Err(io_e) = self.inner.stream.read_exact(&mut buffer) {
return Some(Err(StdfError {
code: 3,
msg: io_e.to_string(),
}));
}
self.offset += header.len as u64;
Some(Ok(RawDataElement {
offset: data_offset,
header,
raw_data: buffer,
byte_order: self.inner.endianness,
}))
}
}
#[inline(always)]
pub(crate) fn rewind_stream_position<R: BufRead + Seek>(
old_stream: StdfStream<R>,
) -> Result<StdfStream<R>, StdfError> {
let new_stream = match old_stream {
StdfStream::Binary(mut br) => {
br.seek(SeekFrom::Start(0))?;
StdfStream::Binary(br)
}
#[cfg(feature = "gzip")]
StdfStream::Gz(gzr) => {
let mut fp = gzr.into_inner();
fp.seek(SeekFrom::Start(0))?;
StdfStream::Gz(GzDecoder::new(fp))
}
#[cfg(feature = "bzip")]
StdfStream::Bz(bzr) => {
let mut fp = bzr.into_inner();
fp.seek(SeekFrom::Start(0))?;
StdfStream::Bz(BzDecoder::new(fp))
}
#[cfg(feature = "zipfile")]
StdfStream::Zip(mut zipr) => {
zipr.reopen_file(0)?;
StdfStream::Zip(zipr)
}
};
Ok(new_stream)
}
#[cfg(all(feature = "atdf", any(feature = "gzip", feature = "bzip",)))]
#[inline(always)]
fn general_read_until<T: Read>(r: &mut T, delim: u8, buf: &mut Vec<u8>) -> io::Result<usize> {
let mut one_byte = [0u8; 1];
let mut n: usize = 0;
loop {
match r.read(&mut one_byte) {
Ok(num) => {
if num == 0 {
break;
}
}
Err(e) => return Err(e),
};
buf.extend_from_slice(&one_byte);
n += 1;
if delim == one_byte[0] {
break;
}
}
Ok(n)
}