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}