Skip to main content

rust_stdf/records/
mod.rs

1use crate::stdf_codec::*;
2use crate::stdf_error::{StdfError, StdfErrorKind};
3use rust_stdf_derive::{stdf_match_expr, stdf_records};
4use smart_default::SmartDefault;
5
6mod type_0_file_info;
7mod type_10_test_synopsis;
8mod type_15_test_info;
9mod type_180_reserved;
10mod type_1_lot_info;
11mod type_20_generic_data;
12mod type_2_wafer_info;
13mod type_50_program_segment;
14mod type_5_part_info;
15
16pub use type_0_file_info::*;
17pub use type_10_test_synopsis::*;
18pub use type_15_test_info::*;
19pub use type_180_reserved::*;
20pub use type_1_lot_info::*;
21pub use type_20_generic_data::*;
22pub use type_2_wafer_info::*;
23pub use type_50_program_segment::*;
24pub use type_5_part_info::*;
25
26#[derive(SmartDefault, Debug, Clone, Copy, PartialEq, Eq)]
27pub struct RecordHeader {
28    pub len: u16,
29    pub typ: u8,
30    pub sub: u8,
31}
32
33// Record Types
34
35/// This module contains constants
36/// for STDF Record type check and
37/// some help functions
38///
39/// ```
40/// use rust_stdf::{StdfRecord, stdf_record_type::*};
41///
42/// // use constant for record initializing
43/// let mut rec = StdfRecord::new(REC_MIR);
44///
45/// // for type check
46/// let t = REC_MIR | REC_MRR | REC_PTR;
47/// let is_t = rec.is_type(t);      // true
48/// ```
49pub mod stdf_record_type {
50    use crate::stdf_error::{StdfError, StdfErrorKind};
51    use rust_stdf_derive::{stdf_match_expr, stdf_records};
52
53    // Generates the `REC_*` record-type code constants.
54    stdf_records!(rec_codes);
55
56    /// This function gets record (typ, sub)
57    /// from record type constant.
58    ///
59    /// ```
60    /// use rust_stdf::stdf_record_type::*;
61    ///
62    /// let ptr_typ_sub = get_typ_sub_from_code(REC_PTR).unwrap();
63    /// assert_eq!((15, 10), ptr_typ_sub);
64    /// ```
65    #[inline(always)]
66    pub fn get_typ_sub_from_code(code: u64) -> Result<(u8, u8), StdfError> {
67        stdf_match_expr!(typ_sub_from_code)
68    }
69
70    /// This function gets record type constant
71    /// from record (typ, sub).
72    ///
73    /// ```
74    /// use rust_stdf::stdf_record_type::*;
75    ///
76    /// let type_code = get_code_from_typ_sub(15, 10);
77    /// assert_eq!(REC_PTR, type_code);
78    /// ```
79    #[inline(always)]
80    pub fn get_code_from_typ_sub(typ: u8, sub: u8) -> u64 {
81        stdf_match_expr!(code_from_typ_sub)
82    }
83
84    /// This function gets record name string
85    /// from record type constant.
86    ///
87    /// ```
88    /// use rust_stdf::stdf_record_type::*;
89    ///
90    /// let rec_name = get_rec_name_from_code(REC_PTR);
91    /// assert_eq!("PTR", rec_name);
92    /// ```
93    #[inline(always)]
94    pub fn get_rec_name_from_code(rec_type: u64) -> &'static str {
95        stdf_match_expr!(name_from_code)
96    }
97
98    /// This function gets the record type constant
99    /// from record name string.
100    ///
101    /// ```
102    /// use rust_stdf::stdf_record_type::*;
103    ///
104    /// let type_code = get_code_from_rec_name("PTR");
105    /// assert_eq!(REC_PTR, type_code);
106    /// ```
107    ///
108    #[inline(always)]
109    pub fn get_code_from_rec_name(rec_name: &str) -> u64 {
110        stdf_match_expr!(code_from_name)
111    }
112}
113
114// Generates the `StdfRecord` and `StdfRecordView` enums.
115stdf_records!(rec_enums);
116
117/// Element yielded by [`RawDataIter`](crate::stdf_file::RawDataIter). It owns unprocessed STDF record data,
118/// and can be converted to [`StdfRecord`], or borrowed as [`StdfRecordView`].
119///
120/// ```
121/// use rust_stdf::{RawDataElement, ByteOrder, StdfRecord, RecordHeader, stdf_record_type::REC_FAR};
122///
123/// let rde = RawDataElement {
124///     offset: 0,
125///     header: RecordHeader {typ: 0, sub: 10, len: 2},
126///     raw_data: vec![0u8; 0],
127///     byte_order: ByteOrder::LittleEndian
128/// };
129/// let rec: StdfRecord = (&rde).into();    // not consume
130/// let rec: StdfRecord = rde.into();       // consume
131/// println!("{:?}", rec);
132/// assert!(rec.is_type(REC_FAR));
133/// ```
134#[derive(Debug, Clone, PartialEq, Eq)]
135pub struct RawDataElement {
136    /// file offset of `raw_data` in file,
137    /// after header.len and before raw_data
138    ///
139    /// |-typ-|-sub-|--len--⬇️--raw..data--|
140    ///
141    /// note that the offset is relative to the
142    /// file position that runs `get_rawdata_iter`,
143    ///
144    /// it can be treated as file position **only if**
145    /// the iteration starts from beginning of the file.
146    pub offset: u64,
147
148    /// used for identifying StdfRecord types
149    pub header: RecordHeader,
150
151    /// field data of current STDF Record
152    pub raw_data: Vec<u8>,
153    pub byte_order: ByteOrder,
154}
155
156/// Element yielded by [`RawDataViewIter`](crate::stdf_file::RawDataViewIter). Unlike [`RawDataElement`],
157/// it borrows the unprocessed record bytes from the iterator's reused buffer, so it's only valid
158/// until the next call to [`RawDataViewIter::next`](crate::stdf_file::RawDataViewIter::next).
159///
160/// In return, it offers better performance and lower heap footprints.
161///
162/// It can be converted to [`StdfRecord`], borrowed as [`StdfRecordView`], or copied into an owned
163/// [`RawDataElement`] when it needs to outlive the current iteration scope.
164///
165/// ```
166/// use rust_stdf::{RawDataElementView, ByteOrder, StdfRecord, RecordHeader, stdf_record_type::REC_FAR};
167///
168/// let raw = [0u8; 0];
169/// let rdv = RawDataElementView {
170///     offset: 0,
171///     header: RecordHeader {typ: 0, sub: 10, len: 2},
172///     raw_data: &raw,
173///     byte_order: ByteOrder::LittleEndian
174/// };
175/// let rec: StdfRecord = (&rdv).into();
176/// println!("{:?}", rec);
177/// assert!(rec.is_type(REC_FAR));
178/// ```
179#[derive(Debug, Clone, Copy, PartialEq, Eq)]
180pub struct RawDataElementView<'a> {
181    /// file offset of `raw_data` in file,
182    /// after header.len and before raw_data
183    ///
184    /// |-typ-|-sub-|--len--⬇️--raw..data--|
185    ///
186    /// note that the offset is relative to the
187    /// file position that runs `get_rawdata_view_iter`,
188    ///
189    /// it can be treated as file position **only if**
190    /// the iteration starts from beginning of the file.
191    pub offset: u64,
192
193    /// used for identifying StdfRecord types
194    pub header: RecordHeader,
195
196    /// field data of current STDF Record, borrowed from the iterator's reused buffer
197    pub raw_data: &'a [u8],
198    pub byte_order: ByteOrder,
199}
200
201// implementation
202
203impl RecordHeader {
204    #[inline(always)]
205    pub fn new() -> Self {
206        RecordHeader::default()
207    }
208
209    /// Construct a STDF record header from first 4 elements of given byte array,
210    /// no error would occur even if the header is invalid.
211    ///
212    /// Unless the array size is less than 4, StdfError of
213    /// `EOF` or `Unexpected EOF` will be returned
214    #[inline(always)]
215    pub fn read_from_bytes(
216        mut self,
217        raw_data: &[u8],
218        order: &ByteOrder,
219    ) -> Result<Self, StdfError> {
220        match raw_data.len() {
221            0 => Err(StdfError::new(
222                StdfErrorKind::Eof,
223                String::from("No bytes to read"),
224            )),
225            1..=3 => Err(StdfError::new(
226                StdfErrorKind::UnexpectedEof,
227                String::from("Not enough data to construct record header"),
228            )),
229            _ => {
230                let len_bytes = [raw_data[0], raw_data[1]];
231                self.len = match order {
232                    ByteOrder::LittleEndian => u16::from_le_bytes(len_bytes),
233                    ByteOrder::BigEndian => u16::from_be_bytes(len_bytes),
234                };
235                self.typ = raw_data[2];
236                self.sub = raw_data[3];
237                // return even if we have a invalid record type, let other code to handle it
238                Ok(self)
239            }
240        }
241    }
242
243    /// return the type_code of current header
244    pub fn get_type(&self) -> u64 {
245        stdf_record_type::get_code_from_typ_sub(self.typ, self.sub)
246    }
247}
248
249impl StdfRecord {
250    /// Create a StdfRecord of a given type with default data
251    ///
252    /// ```
253    /// use rust_stdf::{StdfRecord, stdf_record_type::REC_PMR};
254    ///
255    /// // create StdfRecord with a nested PMR
256    /// let new_rec = StdfRecord::new(REC_PMR);
257    ///
258    /// if let StdfRecord::PMR(pmr_rec) = new_rec {
259    ///     assert_eq!(pmr_rec.head_num, 1);
260    ///     assert_eq!(pmr_rec.site_num, 1);
261    /// } else {
262    ///     // this case will not be hit
263    /// }
264    /// ```
265    #[inline(always)]
266    pub fn new(rec_type: u64) -> Self {
267        stdf_match_expr!(record_new)
268    }
269
270    /// Create a `StdfRecord` from a `RecordHeader` with default data
271    ///
272    /// The difference between `new()` is that this method can save
273    /// the info of an unknown/reserved record header, help the caller to
274    /// debug
275    ///
276    /// ```
277    /// use rust_stdf::{StdfRecord, RecordHeader, stdf_record_type::REC_PMR};
278    ///
279    /// // create a PMR StdfRecord from header
280    /// // (1, 60)
281    /// let pmr_header = RecordHeader {typ: 1, sub: 60, len: 0 };
282    /// let new_rec = StdfRecord::new_from_header(&pmr_header);
283    ///
284    /// if let StdfRecord::PMR(pmr_rec) = new_rec {
285    ///     assert_eq!(pmr_rec.head_num, 1);
286    ///     assert_eq!(pmr_rec.site_num, 1);
287    /// } else {
288    ///     // this case will not be hit
289    /// }
290    /// ```
291    #[inline(always)]
292    pub fn new_from_header(header: &RecordHeader) -> Self {
293        let code = stdf_record_type::get_code_from_typ_sub(header.typ, header.sub);
294        match code {
295            stdf_record_type::REC_RESERVE => {
296                let mut rec = ReservedRec::new();
297                rec.typ = header.typ;
298                rec.sub = header.sub;
299                StdfRecord::ReservedRec(rec)
300            }
301            stdf_record_type::REC_UNKNOWN => {
302                let mut rec = ReservedRec::new();
303                rec.typ = header.typ;
304                rec.sub = header.sub;
305                StdfRecord::UnknownRec(rec)
306            }
307            _ => StdfRecord::new(code),
308        }
309    }
310
311    /// Returns the record type cdoe of the given StdfRecord,
312    /// which is defined in `rust_stdf::stdf_record_type::*` module.
313    ///
314    /// ```
315    /// use rust_stdf::{StdfRecord, stdf_record_type::*};
316    ///
317    /// // `REC_PTR` type code can be used for creating a new StdfRecord
318    /// let new_rec = StdfRecord::new(REC_PTR);
319    /// let returned_code = new_rec.get_type();
320    ///
321    /// assert_eq!(REC_PTR, returned_code);
322    ///
323    /// // type code can be used in variety of functions
324    /// // get record (typ, sub)
325    /// assert_eq!((15, 10), get_typ_sub_from_code(returned_code).unwrap());
326    /// // get record name
327    /// assert_eq!("PTR", get_rec_name_from_code(returned_code));
328    /// ```
329    #[inline(always)]
330    pub fn get_type(&self) -> u64 {
331        stdf_match_expr!(record_type)
332    }
333
334    /// Check the StdfRecord belongs the given type code(s),
335    /// it is useful for filtering the records during the parsing iteration.
336    /// ```
337    /// use rust_stdf::{StdfRecord, stdf_record_type::*};
338    ///
339    /// let new_rec = StdfRecord::new(REC_PTR);
340    ///
341    /// assert!(new_rec.is_type(REC_PTR));
342    /// assert!(new_rec.is_type(REC_PTR | REC_FTR | REC_MPR));
343    /// assert!(!new_rec.is_type(REC_FTR | REC_MPR));
344    /// ```
345    #[inline(always)]
346    pub fn is_type(&self, rec_type: u64) -> bool {
347        (self.get_type() & rec_type) != 0
348    }
349
350    /// Parse StdfRecord from byte data which **DOES NOT**
351    /// contain the record header (len, typ, sub),
352    ///
353    /// requires a mutable StdfRecord to store the parsed data
354    ///
355    /// ```
356    /// use rust_stdf::{StdfRecord, ByteOrder, stdf_record_type::*};
357    ///
358    /// let raw_with_no_header: [u8; 2] = [1, 4];
359    /// let mut new_rec = StdfRecord::new(REC_FAR);
360    /// new_rec.read_from_bytes(&raw_with_no_header, &ByteOrder::LittleEndian);
361    ///
362    /// if let StdfRecord::FAR(ref far_rec) = new_rec {
363    ///     assert_eq!(4, far_rec.stdf_ver);
364    /// }
365    /// ```
366    #[inline(always)]
367    pub fn read_from_bytes(&mut self, raw_data: &[u8], order: &ByteOrder) {
368        stdf_match_expr!(record_read)
369    }
370
371    /// Parse StdfRecord from byte data which
372    /// **contains** the record header (len, typ, sub).
373    ///
374    /// ## Error
375    /// if the input data is not a valid (wrong typ, sub),
376    /// incomplete data or incorrect byte order, [`StdfError`] will be
377    /// returned instead.
378    ///
379    /// ```
380    /// use rust_stdf::{StdfRecord, ByteOrder, stdf_record_type::*};
381    ///
382    /// let raw_with_header: [u8; 6] = [0, 2, 0, 10, 1, 4];
383    /// let new_rec = StdfRecord::read_from_bytes_with_header(&raw_with_header, &ByteOrder::BigEndian).unwrap();
384    ///
385    /// if let StdfRecord::FAR(far_rec) = new_rec {
386    ///     assert_eq!(4, far_rec.stdf_ver);
387    /// }
388    /// ```
389    #[inline(always)]
390    pub fn read_from_bytes_with_header(
391        raw_data: &[u8],
392        order: &ByteOrder,
393    ) -> Result<StdfRecord, StdfError> {
394        let header = RecordHeader::new().read_from_bytes(raw_data, order)?;
395
396        let expected_end_pos = 4 + header.len as usize;
397        if raw_data.len() < expected_end_pos {
398            return Err(StdfError::new(
399                StdfErrorKind::UnexpectedEof,
400                format!(
401                    "Length of stdf field data ({} - 4 = {}) is less than what header specified ({})",
402                    raw_data.len(),
403                    raw_data.len() - 4,
404                    header.len
405                ),
406            ));
407        }
408
409        let data_slice = &raw_data[4..expected_end_pos];
410        let mut rec = StdfRecord::new_from_header(&header);
411        rec.read_from_bytes(data_slice, order);
412        Ok(rec)
413    }
414}
415
416impl<'a> StdfRecordView<'a> {
417    /// Build a view from `RecordHeader`, raw field data and byte order.
418    ///
419    /// ```
420    /// use rust_stdf::{StdfRecordView, ByteOrder, RecordHeader, stdf_record_type::REC_PTR};
421    ///
422    /// let header = RecordHeader { len: 0, typ: 15, sub: 10 }; // PTR
423    /// let raw: [u8; 0] = []; // empty field data; getters fall back to defaults
424    /// let view = StdfRecordView::read_from_bytes(header, &raw, &ByteOrder::LittleEndian);
425    ///
426    /// if let StdfRecordView::PTR(ptr_view) = view {
427    ///     assert_eq!(0, ptr_view.test_num());
428    /// }
429    /// assert!(view.is_type(REC_PTR));
430    /// ```
431    #[inline]
432    pub fn read_from_bytes(
433        header: RecordHeader,
434        raw_data: &'a [u8],
435        byte_order: &ByteOrder,
436    ) -> Self {
437        stdf_match_expr!(view_read)
438    }
439
440    /// Parse a `StdfRecordView` from byte data that **contains** the record
441    /// header (len, typ, sub).
442    ///
443    /// ## Error
444    /// Returns [`StdfError`] when the input is not a valid header or is
445    /// incomplete (the buffer is shorter than the header declares).
446    ///
447    /// ```
448    /// use rust_stdf::{StdfRecordView, ByteOrder};
449    ///
450    /// let raw_with_header: [u8; 6] = [0, 2, 0, 10, 1, 4];
451    /// let view = StdfRecordView::read_from_bytes_with_header(
452    ///     &raw_with_header,
453    ///     &ByteOrder::BigEndian,
454    /// ).unwrap();
455    ///
456    /// if let StdfRecordView::FAR(far_view) = view {
457    ///     assert_eq!(4, far_view.stdf_ver());
458    /// }
459    /// ```
460    #[inline(always)]
461    pub fn read_from_bytes_with_header(
462        raw_data: &'a [u8],
463        order: &ByteOrder,
464    ) -> Result<Self, StdfError> {
465        let header = RecordHeader::new().read_from_bytes(raw_data, order)?;
466
467        let expected_end_pos = 4 + header.len as usize;
468        if raw_data.len() < expected_end_pos {
469            return Err(StdfError::new(
470                StdfErrorKind::UnexpectedEof,
471                format!(
472                    "Length of stdf field data ({} - 4 = {}) is less than what header specified ({})",
473                    raw_data.len(),
474                    raw_data.len() - 4,
475                    header.len
476                ),
477            ));
478        }
479
480        let data_slice = &raw_data[4..expected_end_pos];
481        Ok(Self::read_from_bytes(header, data_slice, order))
482    }
483
484    /// Return the record type code (see `stdf_record_type::*`).
485    #[inline(always)]
486    pub fn get_type(&self) -> u64 {
487        stdf_match_expr!(view_type)
488    }
489
490    /// Check whether this view belongs to the given record type(s).
491    #[inline(always)]
492    pub fn is_type(&self, rec_type: u64) -> bool {
493        (self.get_type() & rec_type) != 0
494    }
495
496    /// Parse this borrowed view into an owned [`StdfRecord`], escaping the
497    /// borrow of the underlying buffer.
498    #[inline]
499    pub fn to_owned(&self) -> StdfRecord {
500        stdf_match_expr!(view_to_owned)
501    }
502}
503
504impl RawDataElement {
505    #[inline(always)]
506    pub fn is_type(&self, rec_type: u64) -> bool {
507        (self.header.get_type() & rec_type) != 0
508    }
509}
510
511impl From<&RawDataElement> for StdfRecord {
512    /// It will NOT consume the input RawDataElement
513    #[inline(always)]
514    fn from(raw_element: &RawDataElement) -> Self {
515        let mut rec = StdfRecord::new_from_header(&raw_element.header);
516        rec.read_from_bytes(&raw_element.raw_data, &raw_element.byte_order);
517        rec
518    }
519}
520
521impl<'a> From<&'a RawDataElement> for StdfRecordView<'a> {
522    /// Build a zero-copy view; does not consume the input.
523    #[inline]
524    fn from(raw_element: &'a RawDataElement) -> Self {
525        Self::read_from_bytes(
526            raw_element.header,
527            &raw_element.raw_data,
528            &raw_element.byte_order,
529        )
530    }
531}
532
533impl From<RawDataElement> for StdfRecord {
534    /// It will consume the input RawDataElement
535    #[inline(always)]
536    fn from(raw_element: RawDataElement) -> Self {
537        let mut rec = StdfRecord::new_from_header(&raw_element.header);
538        rec.read_from_bytes(&raw_element.raw_data, &raw_element.byte_order);
539        rec
540    }
541}
542
543impl RawDataElementView<'_> {
544    #[inline(always)]
545    pub fn is_type(&self, rec_type: u64) -> bool {
546        (self.header.get_type() & rec_type) != 0
547    }
548}
549
550impl From<&RawDataElementView<'_>> for StdfRecord {
551    /// Parse the borrowed bytes into an owned record; does not consume the input.
552    #[inline(always)]
553    fn from(raw_view: &RawDataElementView<'_>) -> Self {
554        let mut rec = StdfRecord::new_from_header(&raw_view.header);
555        rec.read_from_bytes(raw_view.raw_data, &raw_view.byte_order);
556        rec
557    }
558}
559
560impl<'a> From<&RawDataElementView<'a>> for StdfRecordView<'a> {
561    /// Build a zero-copy view; does not consume the input.
562    #[inline]
563    fn from(raw_view: &RawDataElementView<'a>) -> Self {
564        Self::read_from_bytes(raw_view.header, raw_view.raw_data, &raw_view.byte_order)
565    }
566}
567
568impl From<&RawDataElementView<'_>> for RawDataElement {
569    /// Copy the borrowed bytes into an owned [`RawDataElement`]; does not consume the input.
570    #[inline(always)]
571    fn from(raw_view: &RawDataElementView<'_>) -> Self {
572        RawDataElement {
573            offset: raw_view.offset,
574            header: raw_view.header,
575            raw_data: raw_view.raw_data.to_vec(),
576            byte_order: raw_view.byte_order,
577        }
578    }
579}
580
581impl<'a> From<&StdfRecordView<'a>> for StdfRecord {
582    /// Parse the borrowed view into an owned record; does not consume the view.
583    #[inline(always)]
584    fn from(view: &StdfRecordView<'a>) -> Self {
585        view.to_owned()
586    }
587}