Skip to main content

rust_stdf/
stdf_file.rs

1//
2// stdf_file.rs
3// Author: noonchen - chennoon233@foxmail.com
4// Created Date: October 3rd 2022
5// -----
6// Last Modified: Sun Aug 23 2026
7// Modified By: noonchen
8// -----
9// Copyright (c) 2022 noonchen
10//
11
12use crate::records::*;
13use crate::stdf_codec::*;
14use crate::stdf_error::{StdfError, StdfErrorKind};
15#[cfg(feature = "bzip")]
16use bzip2::bufread::BzDecoder;
17#[cfg(feature = "gzip")]
18use flate2::bufread::GzDecoder;
19use rust_stdf_derive::stdf_match_expr;
20use std::io::{self, BufReader, SeekFrom}; // struct or enum
21use std::io::{BufRead, Read, Seek, Write};
22use std::{fs, path::Path}; // trait
23#[cfg(feature = "zipfile")]
24use zip::{read::ZipFile, ZipArchive};
25
26/// `Unsafe` struct for coupling
27/// file and `ZipArchive`
28/// in order to get access to
29/// the same `ZipFile`
30#[cfg(feature = "zipfile")]
31pub(crate) struct ZipBundle<R> {
32    // put `ZipFile` on top
33    // to ensure it is dropped
34    // before `ZipArchive`
35    file: Option<ZipFile<'static>>,
36    archive: Box<ZipArchive<R>>,
37}
38
39#[allow(clippy::large_enum_variant)]
40pub(crate) enum StdfStream<R> {
41    Binary(R),
42    #[cfg(feature = "gzip")]
43    Gz(GzDecoder<R>),
44    #[cfg(feature = "bzip")]
45    Bz(BzDecoder<R>),
46    #[cfg(feature = "zipfile")]
47    Zip(ZipBundle<R>),
48}
49
50/// STDF Reader
51///
52/// This reader can process STDF datalogs of Version V4 and V4-2007
53///
54/// Supported compression:
55///  - Uncompressed
56///  - Gzip (.gz)
57///  - Bzip (.bz2)
58///
59/// # Example
60///
61/// ```
62/// use rust_stdf::{stdf_file::*, stdf_record_type::*, StdfRecord};
63///
64/// let stdf_path = "demo_file.stdf";
65/// let mut reader = match StdfReader::new(&stdf_path) {
66///     Ok(r) => r,
67///     Err(e) => {
68///         println!("{}", e);
69///         return;
70///     }
71/// };
72///
73/// // we will count total DUT# in the file
74/// // and put test result of PTR named
75/// // "continuity test" in a vector.
76/// let mut dut_count: u64 = 0;
77/// let mut continuity_rlt = vec![];
78///
79/// // use type filter to work on certain types,
80/// // use `|` to combine multiple typs
81/// let rec_types = REC_PIR | REC_PTR;
82/// // iterator starts from current file position,
83/// // if file hits EOF, it will NOT redirect to 0.
84/// for rec in reader
85///     .get_record_iter()
86///     .map(|x| x.unwrap())
87///     .filter(|x| x.is_type(rec_types))
88/// {
89///     match rec {
90///         StdfRecord::PIR(_) => {dut_count += 1;}
91///         StdfRecord::PTR(ref ptr_rec) => {
92///             if ptr_rec.test_txt == "continuity test" {
93///                 continuity_rlt.push(ptr_rec.result);
94///             }
95///         }
96///         _ => {}
97///     }
98/// }
99/// println!("Total duts {} \n continuity result {:?}",
100///         dut_count,
101///         continuity_rlt);
102/// ```
103pub struct StdfReader<R> {
104    endianness: ByteOrder,
105    stream: StdfStream<R>,
106}
107
108/// STDF Writer
109///
110/// This writer accepts streams that implement [`Write`] trait,
111/// and writes STDF records in given byte order.
112///
113/// Multiple APIs are provided to write STDF records, including:
114/// - `write_record` for writing parsed STDF V4-2007 records, not including reserved/unknown records.
115/// - `write_stdf_record` for writing any parsed STDF records.
116/// - `write_stdf_record_view` for writing any STDF record views.
117/// - `write_raw` for writing any unprocessed STDF records.
118/// - `write_raw_view` for writing any unprocessed STDF record views.
119///
120/// ```
121/// use rust_stdf::{stdf_file::StdfWriter, ByteOrder, FAR};
122///
123/// let mut far = FAR::new();
124/// far.cpu_type = 2;
125/// far.stdf_ver = 4;
126///
127/// let mut out = Vec::new();
128/// let mut writer = StdfWriter::new(&mut out, ByteOrder::LittleEndian);
129/// writer.write_record(&far).unwrap();
130///
131/// assert_eq!(out, [2, 0, 0, 10, 2, 4]);
132/// ```
133pub struct StdfWriter<W> {
134    writer: W,
135    byte_order: ByteOrder,
136}
137
138impl<W: Write> StdfWriter<W> {
139    /// Create a new writer with the given output stream and byte order.
140    #[inline(always)]
141    pub fn new(writer: W, byte_order: ByteOrder) -> Self {
142        StdfWriter { writer, byte_order }
143    }
144
145    /// Write a STDF V4-2007 record to the output stream.
146    ///
147    /// Validation will be performed on the record before writing, such as
148    /// length check of Cn field, count check for Kx* fields, returns `StdfError`
149    /// if validation fails.
150    ///
151    /// Note: `ReservedRec` and `UnknownRec` cannot be written with this method,
152    /// use `write_stdf_record` instead.
153    #[inline]
154    #[allow(private_bounds)]
155    pub fn write_record<R: StdfRecordWrite>(&mut self, r: &R) -> Result<(), StdfError> {
156        r.validate()?;
157        let n = r.payload_len();
158        self.write_header(R::REC_TYP, R::REC_SUB, n)?;
159        r.write_payload(&mut self.writer, &self.byte_order)
160    }
161
162    /// Write a STDF V4-2007 record to the output stream, also supports
163    /// `ReservedRec` and `UnknownRec`.
164    ///
165    /// Validation will be performed on the record before writing.
166    ///
167    /// Note: reserved/unknown record must match the writer's byte order,
168    /// otherwise, error [`StdfErrorKind::ByteOrderMismatch`] is returned.
169    #[inline]
170    pub fn write_stdf_record(&mut self, r: &StdfRecord) -> Result<(), StdfError> {
171        stdf_match_expr!(record_write)
172    }
173
174    /// Similar to `write_stdf_record`, but takes a borrowed [`StdfRecordView`].
175    ///
176    /// If known record types have different byte order than the writer, they are re-encoded
177    /// via implicit `to_owned()`. Reserved/unknown views cannot be re-encoded,
178    /// so a byte order mismatch returns error
179    /// [`StdfErrorKind::ByteOrderMismatch`].
180    #[inline]
181    pub fn write_stdf_record_view(&mut self, v: &StdfRecordView) -> Result<(), StdfError> {
182        stdf_match_expr!(view_write)
183    }
184
185    /// Write an unprocessed record [`RawDataElement`] to the output stream.
186    ///
187    /// Byte order of the raw record must match the writer's.
188    pub fn write_raw(&mut self, raw: &RawDataElement) -> Result<(), StdfError> {
189        self.write_guarded_record(
190            raw.header.typ,
191            raw.header.sub,
192            &raw.raw_data,
193            raw.byte_order,
194        )
195    }
196
197    /// Write an unprocessed record view [`RawDataElementView`] to the output stream.
198    ///
199    /// Byte order of the raw record must match the writer's.
200    pub fn write_raw_view(&mut self, v: &RawDataElementView) -> Result<(), StdfError> {
201        self.write_guarded_record(v.header.typ, v.header.sub, v.raw_data, v.byte_order)
202    }
203
204    /// Manually flush any buffered data to the output stream.
205    ///
206    /// `StdfWriter` never buffers internally, but a wrapping `BufWriter` does;
207    /// use this method to force wrapped writers to flush.
208    #[inline]
209    pub fn flush(&mut self) -> Result<(), StdfError> {
210        self.writer.flush()?;
211        Ok(())
212    }
213
214    /// Check byte order before writing raw payload.
215    #[inline]
216    fn write_guarded_record(
217        &mut self,
218        typ: u8,
219        sub: u8,
220        raw_data: &[u8],
221        record_order: ByteOrder,
222    ) -> Result<(), StdfError> {
223        if record_order != self.byte_order {
224            return Err(StdfError::new(
225                StdfErrorKind::ByteOrderMismatch,
226                format!(
227                    "record byte order is {:?}, writer uses {:?}",
228                    record_order, self.byte_order
229                ),
230            ));
231        }
232        self.write_header_and_payload(typ, sub, raw_data)
233    }
234
235    #[inline]
236    fn write_header_and_payload(
237        &mut self,
238        typ: u8,
239        sub: u8,
240        raw_data: &[u8],
241    ) -> Result<(), StdfError> {
242        self.write_header(typ, sub, raw_data.len())?;
243        self.writer.write_all(raw_data)?;
244        Ok(())
245    }
246
247    #[inline]
248    fn write_header(&mut self, typ: u8, sub: u8, payload_len: usize) -> Result<(), StdfError> {
249        if payload_len > u16::MAX as usize {
250            return Err(StdfError::new(
251                StdfErrorKind::RecordTooLarge,
252                format!(
253                    "record ({typ}, {sub}) payload length {payload_len} exceeds {}",
254                    u16::MAX
255                ),
256            ));
257        }
258        let len = payload_len as u16;
259        match self.byte_order {
260            ByteOrder::LittleEndian => self.writer.write_all(&len.to_le_bytes())?,
261            ByteOrder::BigEndian => self.writer.write_all(&len.to_be_bytes())?,
262        }
263        self.writer.write_all(&[typ, sub])?;
264        Ok(())
265    }
266}
267
268/// Iterator over [`StdfRecord`]s, created by [`StdfReader::get_record_iter`].
269///
270/// Each item is a fully parsed [`StdfRecord`].
271pub struct RecordIter<'a, R> {
272    inner: &'a mut StdfReader<R>,
273    // reused across records, bytes are only borrowed during parsing
274    buffer: Vec<u8>,
275}
276
277/// Iterator over unprocessed STDF records, created by [`StdfReader::get_rawdata_iter`].
278///
279/// Each item is an unprocessed [`RawDataElement`] that holds an owned record data buffer.
280pub struct RawDataIter<'a, R> {
281    offset: u64,
282    inner: &'a mut StdfReader<R>,
283}
284
285/// Iterator over unprocessed STDF records that borrows the record bytes,
286/// created by [`StdfReader::get_rawdata_view_iter`].
287///
288/// Each item is an unprocessed [`RawDataElementView`] that borrows record data buffer.
289///
290/// This iterator outperforms [`RawDataIter`] because there is no allocation, but
291/// it cannot implement [`Iterator`], so it can only be driven by `next`.
292///
293/// The yielded item is only valid until the following `next` call.
294///
295/// # Example
296///
297/// ```no_run
298/// use rust_stdf::{stdf_file::*, stdf_record_type::*, StdfRecordView};
299///
300/// let mut reader = StdfReader::new("demo_file.stdf").unwrap();
301/// let mut iter = reader.get_rawdata_view_iter();
302/// while let Some(item) = iter.next() {
303///     let raw = item.unwrap();
304///     if !raw.is_type(REC_PTR) {
305///         continue;
306///     }
307///     let rec_view: StdfRecordView = (&raw).into();
308///     if let StdfRecordView::PTR(ptr) = rec_view {
309///         println!("{}", ptr.result());
310///     }
311/// }
312/// ```
313pub struct RawDataViewIter<'a, R> {
314    offset: u64,
315    inner: &'a mut StdfReader<R>,
316    // reused across records, bytes are borrowed by the yielded view
317    buffer: Vec<u8>,
318}
319
320// implementations
321
322impl StdfReader<BufReader<fs::File>> {
323    /// Open the given file and return a StdfReader, if successful
324    #[inline(always)]
325    pub fn new<P>(path: P) -> Result<Self, StdfError>
326    where
327        P: AsRef<Path>,
328    {
329        // determine the compress type by file extension
330        let path_string = path.as_ref().display().to_string();
331        let file_ext = path_string.rsplit('.').next();
332        let compress_type = match file_ext {
333            Some(ext) => match ext {
334                #[cfg(feature = "gzip")]
335                "gz" => CompressType::GzipCompressed,
336                #[cfg(feature = "bzip")]
337                "bz2" => CompressType::BzipCompressed,
338                #[cfg(feature = "zipfile")]
339                "zip" => CompressType::ZipCompressed,
340                _ => CompressType::Uncompressed,
341            },
342            None => CompressType::Uncompressed,
343        };
344        let fp = fs::OpenOptions::new().read(true).open(path)?;
345        let br = BufReader::with_capacity(2 << 20, fp);
346        StdfReader::from(br, &compress_type)
347    }
348}
349
350impl<R: BufRead + Seek> StdfReader<R> {
351    /// Consume a input stream and generate a StdfReader, if successful
352    #[inline(always)]
353    pub fn from(in_stream: R, compress_type: &CompressType) -> Result<Self, StdfError> {
354        let mut stream = match compress_type {
355            #[cfg(feature = "gzip")]
356            CompressType::GzipCompressed => StdfStream::Gz(GzDecoder::new(in_stream)),
357            #[cfg(feature = "bzip")]
358            CompressType::BzipCompressed => StdfStream::Bz(BzDecoder::new(in_stream)),
359            #[cfg(feature = "zipfile")]
360            CompressType::ZipCompressed => StdfStream::Zip(ZipBundle::new(in_stream, 0)?),
361            _ => StdfStream::Binary(in_stream),
362        };
363
364        // read FAR header from file
365        let mut buf = [0u8; 4];
366        stream.read_exact(&mut buf)?;
367        // parse header assuming little endian
368        let far_header = RecordHeader::new().read_from_bytes(&buf, &ByteOrder::LittleEndian)?;
369        let endianness = match far_header.len {
370            2 => Ok(ByteOrder::LittleEndian),
371            512 => Ok(ByteOrder::BigEndian),
372            _ => Err(StdfError::new(
373                StdfErrorKind::InvalidStdfFile,
374                String::from("Cannot determine endianness"),
375            )),
376        }?;
377        // check if it's FAR
378        if (far_header.typ, far_header.sub) != (0, 10) {
379            return Err(StdfError::new(
380                StdfErrorKind::InvalidStdfFile,
381                format!(
382                    "FAR header (0, 10) expected, but {:?} is found",
383                    (far_header.typ, far_header.sub)
384                ),
385            ));
386        }
387        // restore file position
388        // current flate2 does not support fseek, we need to consume
389        // old stream and create a new one.
390        // If seek is supported, this function can be replaced by:
391        //
392        // stream.seek(SeekFrom::Start(0))?;
393        //
394        stream = rewind_stream_position(stream)?;
395
396        Ok(StdfReader { endianness, stream })
397    }
398
399    /// Return the endianness of the STDF file.
400    #[inline(always)]
401    pub fn get_byte_order(&self) -> ByteOrder {
402        self.endianness
403    }
404
405    #[inline(always)]
406    fn read_header(&mut self) -> Result<RecordHeader, StdfError> {
407        let mut buf = [0u8; 4];
408        self.stream.read_exact(&mut buf)?;
409        RecordHeader::new().read_from_bytes(&buf, &self.endianness)
410    }
411
412    /// Return an iterator for [`StdfRecord`].
413    ///
414    /// The iterator yields records starting from the current
415    /// file position onward.
416    #[inline(always)]
417    pub fn get_record_iter(&mut self) -> RecordIter<'_, R> {
418        RecordIter {
419            inner: self,
420            buffer: Vec::with_capacity(1024),
421        }
422    }
423
424    /// Return an iterator for unprocessed STDF bytes [`RawDataElement`].
425    ///
426    /// The iterator yields records starting from the current file position
427    /// onward, and the `offset` field is counted relative to that position.
428    #[inline(always)]
429    pub fn get_rawdata_iter(&mut self) -> RawDataIter<'_, R> {
430        RawDataIter {
431            offset: 0,
432            inner: self,
433        }
434    }
435
436    /// Return a buffer-reusing iterator for unprocessed STDF bytes [`RawDataElementView`].
437    ///
438    /// Unlike [`get_rawdata_iter`](Self::get_rawdata_iter), the yielded
439    /// [`RawDataElementView`] borrows the iterator's reused buffer,
440    /// so it performs no per-record allocation. See [`RawDataViewIter`] for usage.
441    ///
442    /// The iterator yields records starting from the current file position
443    /// onward, and the `offset` field is counted relative to that position.
444    #[inline(always)]
445    pub fn get_rawdata_view_iter(&mut self) -> RawDataViewIter<'_, R> {
446        RawDataViewIter {
447            offset: 0,
448            inner: self,
449            buffer: Vec::with_capacity(1024),
450        }
451    }
452}
453
454#[cfg(feature = "zipfile")]
455impl<R: BufRead + Seek> ZipBundle<R> {
456    /// the following code is modified from this SO post:
457    /// https://stackoverflow.com/questions/67823680/open-a-single-file-from-a-zip-archive-and-pass-on-as-read-instance/
458    pub(crate) fn new(stream: R, file_index: usize) -> Result<ZipBundle<R>, StdfError> {
459        let archive = ZipArchive::new(stream)?;
460        let mut archive = Box::new(archive);
461
462        // SAFETY: fakes a 'static borrow of `archive`. Sound because `archive` is
463        // boxed (address stable across the move below), `file` drops before
464        // `archive` (whose Drop reads it), and `archive` is never aliased while
465        // `file` is alive (reopen_file clears `file` before re-borrowing).
466        let file = unsafe {
467            std::mem::transmute::<ZipFile<'_>, ZipFile<'static>>(archive.by_index(file_index)?)
468        };
469        Ok(ZipBundle {
470            archive,
471            file: Some(file),
472        })
473    }
474
475    pub(crate) fn reopen_file(&mut self, file_index: usize) -> Result<(), StdfError> {
476        // Drop the outstanding borrow before re-borrowing `archive`.
477        self.file = None;
478        // SAFETY: same invariants as `new`. `file` was just cleared above.
479        let file = unsafe {
480            std::mem::transmute::<ZipFile<'_>, ZipFile<'static>>(self.archive.by_index(file_index)?)
481        };
482        self.file = Some(file);
483        Ok(())
484    }
485}
486
487#[cfg(feature = "zipfile")]
488impl<R: BufRead + Seek> Read for ZipBundle<R> {
489    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
490        self.file.as_mut().unwrap().read(buf)
491    }
492}
493
494impl<R: BufRead + Seek> StdfStream<R> {
495    #[cfg(feature = "atdf")]
496    #[inline(always)]
497    pub(crate) fn read_until(&mut self, delim: u8, buf: &mut Vec<u8>) -> io::Result<usize> {
498        match self {
499            StdfStream::Binary(bstream) => bstream.read_until(delim, buf),
500            #[cfg(feature = "gzip")]
501            StdfStream::Gz(gzstream) => general_read_until(gzstream, delim, buf),
502            #[cfg(feature = "bzip")]
503            StdfStream::Bz(bzstream) => general_read_until(bzstream, delim, buf),
504            #[cfg(feature = "zipfile")]
505            StdfStream::Zip(zipstream) => general_read_until(zipstream, delim, buf),
506        }
507    }
508}
509
510impl<R: BufRead + Seek> Read for StdfStream<R> {
511    #[inline(always)]
512    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
513        match self {
514            StdfStream::Binary(bstream) => bstream.read(buf),
515            #[cfg(feature = "gzip")]
516            StdfStream::Gz(gzstream) => gzstream.read(buf),
517            #[cfg(feature = "bzip")]
518            StdfStream::Bz(bzstream) => bzstream.read(buf),
519            #[cfg(feature = "zipfile")]
520            StdfStream::Zip(zipstream) => zipstream.read(buf),
521        }
522    }
523}
524
525// impl<R: Seek> Seek for StdfStream<R> {
526//     fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
527//         match self {
528//             StdfStream::Binary(bstream) => bstream.seek(pos),
529//             // arm that does not support seek
530//             _ => Ok(0),
531//         }
532//     }
533// }
534
535impl<R: BufRead + Seek> Iterator for RecordIter<'_, R> {
536    type Item = Result<StdfRecord, StdfError>;
537
538    #[inline(always)]
539    fn next(&mut self) -> Option<Self::Item> {
540        let header = match self.inner.read_header() {
541            Ok(h) => h,
542            Err(e) => {
543                return match e.kind() {
544                    // only 2 error will be returned by `read_header`
545                    // Eof indicates normal EOF, UnexpectedEof indicates unexpected EOF
546                    StdfErrorKind::Eof => None,
547                    _ => Some(Err(e)),
548                };
549            }
550        };
551        // grow only when this record is larger than any seen so far, then
552        // fill and parse just the [..len] prefix
553        let len = header.len as usize;
554        if self.buffer.len() < len {
555            self.buffer.resize(len, 0);
556        }
557        if let Err(io_e) = self.inner.stream.read_exact(&mut self.buffer[..len]) {
558            return Some(Err(StdfError::new(StdfErrorKind::Io, io_e.to_string())));
559        }
560
561        let mut rec = StdfRecord::new_from_header(&header);
562        rec.read_from_bytes(&self.buffer[..len], &self.inner.endianness);
563        Some(Ok(rec))
564    }
565}
566
567impl<R: BufRead + Seek> Iterator for RawDataIter<'_, R> {
568    type Item = Result<RawDataElement, StdfError>;
569
570    #[inline(always)]
571    fn next(&mut self) -> Option<Self::Item> {
572        let header = match self.inner.read_header() {
573            Ok(h) => h,
574            Err(e) => {
575                return match e.kind() {
576                    // Eof indicates normal EOF, UnexpectedEof indicates unexpected EOF
577                    StdfErrorKind::Eof => None,
578                    _ => Some(Err(e)),
579                };
580            }
581        };
582        // advance position by 4 after reading a header successfully
583        self.offset += 4;
584        let data_offset = self.offset;
585        // create a buffer to store record raw data
586        let mut buffer = vec![0u8; header.len as usize];
587        if let Err(io_e) = self.inner.stream.read_exact(&mut buffer) {
588            return Some(Err(StdfError::new(StdfErrorKind::Io, io_e.to_string())));
589        }
590        self.offset += header.len as u64;
591
592        Some(Ok(RawDataElement {
593            offset: data_offset,
594            header,
595            raw_data: buffer,
596            byte_order: self.inner.endianness,
597        }))
598    }
599}
600
601impl<R: BufRead + Seek> RawDataViewIter<'_, R> {
602    /// Advance the iterator, returning the next [`RawDataElementView`], or `None` at EOF.
603    ///
604    /// The returned item will be invalidated by the following call.
605    ///
606    /// It mirrors [`Iterator::next`] but cannot be part of the [`Iterator`] trait
607    /// because the item borrows from `self`.
608    #[inline(always)]
609    #[allow(clippy::should_implement_trait)] // lending iterator: item borrows `self`
610    pub fn next(&mut self) -> Option<Result<RawDataElementView<'_>, StdfError>> {
611        let header = match self.inner.read_header() {
612            Ok(h) => h,
613            Err(e) => {
614                return match e.kind() {
615                    // Eof indicates normal EOF, UnexpectedEof indicates unexpected EOF
616                    StdfErrorKind::Eof => None,
617                    _ => Some(Err(e)),
618                };
619            }
620        };
621        // advance position by 4 after reading a header successfully
622        self.offset += 4;
623        let data_offset = self.offset;
624        // grow only when this record is larger than any seen so far, then
625        // fill just the [..len] prefix and borrow it in the view
626        let len = header.len as usize;
627        if self.buffer.len() < len {
628            self.buffer.resize(len, 0);
629        }
630        if let Err(io_e) = self.inner.stream.read_exact(&mut self.buffer[..len]) {
631            return Some(Err(StdfError::new(StdfErrorKind::Io, io_e.to_string())));
632        }
633        self.offset += header.len as u64;
634
635        Some(Ok(RawDataElementView {
636            offset: data_offset,
637            header,
638            raw_data: &self.buffer[..len],
639            byte_order: self.inner.endianness,
640        }))
641    }
642}
643
644// help functions
645
646#[inline(always)]
647pub(crate) fn rewind_stream_position<R: BufRead + Seek>(
648    old_stream: StdfStream<R>,
649) -> Result<StdfStream<R>, StdfError> {
650    let new_stream = match old_stream {
651        StdfStream::Binary(mut br) => {
652            br.seek(SeekFrom::Start(0))?;
653            StdfStream::Binary(br)
654        }
655        #[cfg(feature = "gzip")]
656        StdfStream::Gz(gzr) => {
657            // get the inner handle and create a new stream after seek
658            let mut fp = gzr.into_inner();
659            fp.seek(SeekFrom::Start(0))?;
660            StdfStream::Gz(GzDecoder::new(fp))
661        }
662        #[cfg(feature = "bzip")]
663        StdfStream::Bz(bzr) => {
664            // get the inner handle and create a new stream after seek
665            let mut fp = bzr.into_inner();
666            fp.seek(SeekFrom::Start(0))?;
667            StdfStream::Bz(BzDecoder::new(fp))
668        }
669        #[cfg(feature = "zipfile")]
670        StdfStream::Zip(mut zipr) => {
671            zipr.reopen_file(0)?;
672            StdfStream::Zip(zipr)
673        }
674    };
675    Ok(new_stream)
676}
677
678#[cfg(all(feature = "atdf", any(feature = "gzip", feature = "bzip",)))]
679#[inline(always)]
680fn general_read_until<T: Read>(r: &mut T, delim: u8, buf: &mut Vec<u8>) -> io::Result<usize> {
681    let mut one_byte = [0u8; 1];
682    let mut n: usize = 0;
683    loop {
684        // read one byte at a time
685        match r.read(&mut one_byte) {
686            Ok(num) => {
687                if num == 0 {
688                    // EOF reached
689                    break;
690                }
691            }
692            Err(e) => return Err(e),
693        };
694        buf.extend_from_slice(&one_byte);
695        n += 1;
696        // break at delimiter
697        if delim == one_byte[0] {
698            break;
699        }
700    }
701    Ok(n)
702}