Skip to main content

rust_stdf/stdf_codec/
primitives.rs

1use super::primitive_read::*;
2use smart_default::SmartDefault;
3use std::borrow::Cow;
4
5#[cfg(feature = "serialize")]
6use serde::Serialize;
7
8// Common Type
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum ByteOrder {
11    LittleEndian,
12    BigEndian,
13}
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum CompressType {
17    Uncompressed,
18    #[cfg(feature = "gzip")]
19    GzipCompressed,
20    #[cfg(feature = "bzip")]
21    BzipCompressed,
22    #[cfg(feature = "zipfile")]
23    ZipCompressed,
24}
25
26// Data Types
27
28/// Altough B1 can be treated as u8, but its representation
29/// in ATDF is differ from U1, so I used a array of one u8 for B1
30pub type B1 = [u8; 1];
31/// Rust char is 4 bytes long, however STDF char is only 1 byte
32/// we will read u8 from file stream and convert to Rust char during parse step
33pub type C1 = char;
34pub type U1 = u8;
35pub type U2 = u16;
36pub type U4 = u32;
37pub type U8 = u64;
38pub type I1 = i8;
39pub type I2 = i16;
40pub type I4 = i32;
41pub type R4 = f32;
42pub type R8 = f64;
43
44// Cn;	//first byte = unsigned count of bytes to follow (maximum of 255 bytes)
45pub type Cn = String;
46
47// Variable length character string, string length is stored in another field
48pub type Cf = String;
49
50// first two bytes = unsigned count of bytes to follow (maximum of 65535 bytes)
51pub type Sn = String;
52
53// Bn;	//First byte = unsigned count of bytes to follow (maximum of 255 bytes)
54pub type Bn = Vec<u8>;
55
56// Dn;	//First two bytes = unsigned count of bits to follow (maximum of 65,535 bits)
57pub type Dn = Vec<u8>;
58
59pub type KxCn = Vec<Cn>;
60pub type KxSn = Vec<Sn>;
61pub type KxCf = Vec<Cf>;
62pub type KxU1 = Vec<U1>;
63pub type KxU2 = Vec<U2>;
64pub type KxU4 = Vec<U4>;
65pub type KxU8 = Vec<U8>;
66pub type KxR4 = Vec<R4>;
67pub type KxN1 = Vec<U1>;
68
69/// This enum is for STR that
70/// introduced in STDF V4-2007.
71///
72/// the nested data is a vector of Uf type,
73/// where f = 1, 2, 4 or 8
74#[cfg_attr(feature = "serialize", derive(Serialize))]
75#[derive(SmartDefault, Debug, Clone, PartialEq, Eq)]
76pub enum KxUf {
77    #[default]
78    F1(KxU1),
79    F2(KxU2),
80    F4(KxU4),
81    F8(KxU8),
82}
83
84/// This enum is for storing
85/// generic data V1, the data type
86/// is the field name.
87#[cfg_attr(feature = "serialize", derive(Serialize))]
88#[derive(Debug, Clone, PartialEq)]
89pub enum V1 {
90    B0,
91    U1(U1),
92    U2(U2),
93    U4(U4),
94    I1(I1),
95    I2(I2),
96    I4(I4),
97    R4(R4),
98    R8(R8),
99    Cn(Cn),
100    Bn(Bn),
101    Dn(Dn),
102    N1(U1),
103    Invalid,
104}
105
106pub type Vn = Vec<V1>;
107
108// ----------------------------------------------------------------------------
109// Zero-copy borrows of the variable-length payload types, used by the generated
110// `*View` getters. Each borrows into the raw record buffer; the `to_owned`
111// method reproduces the owned value with the same semantics as the eager
112// `read_*` helpers, so view and eager results match.
113// ----------------------------------------------------------------------------
114
115/// Sentinel stored in a view when a (trailing/optional) field is absent.
116pub(crate) const VIEW_ABSENT_OFT: u16 = u16::MAX;
117
118/// Validate a stored offset, turning it into `Some(pos)`, or `None` when the field is absent.
119#[inline]
120pub(crate) fn validate_offset(off: u16) -> Option<usize> {
121    if off == VIEW_ABSENT_OFT {
122        None
123    } else {
124        Some(off as usize)
125    }
126}
127
128/// Zero-copy borrow of a `Cn` payload (the bytes *after* the 1-byte length prefix).
129#[derive(SmartDefault, Clone, Copy, Debug)]
130pub struct CnRef<'a>(&'a [u8]);
131
132/// Zero-copy borrow of an `Sn` payload (the bytes *after* the 2-byte,
133/// byte-order-dependent length prefix).
134#[derive(SmartDefault, Clone, Copy, Debug)]
135pub struct SnRef<'a>(&'a [u8]);
136
137/// Zero-copy borrow of a `Bn` payload (the bytes *after* the 1-byte length prefix).
138#[derive(SmartDefault, Clone, Copy, Debug)]
139pub struct BnRef<'a>(&'a [u8]);
140
141/// Zero-copy borrow of a `Dn` payload (the bytes *after* the 2-byte,
142/// byte-order-dependent bit-count prefix).
143#[derive(SmartDefault, Clone, Copy, Debug)]
144pub struct DnRef<'a>(&'a [u8]);
145
146/// Zero-copy view over the `k` elements of a `KxCn` array (each element is a
147/// 1-byte length prefix followed by the payload).
148///
149/// Elements are decoded with the same rule as [`CnRef::as_str`] and can be
150/// iterated directly:
151///
152/// ```
153/// use rust_stdf::KxCnRef;
154///
155/// let raw = [2, b'A', b'B', 2, b'C', b'D']; // two elements: "AB", "CD"
156/// let kx = KxCnRef::new(&raw, 0, 2);
157/// for s in &kx {
158///     println!("{s}");
159/// }
160/// ```
161#[derive(Clone, Copy, Default)]
162pub struct KxCnRef<'a> {
163    raw: &'a [u8],
164    start: usize,
165    k: usize,
166}
167
168/// Iterator over the elements of a [`KxCnRef`], one pass.
169pub struct KxCnRefIter<'a> {
170    raw: &'a [u8],
171    pos: usize,
172    remaining: usize,
173}
174
175/// Zero-copy view over the `k` elements of a `KxSn` array (each element is a
176/// 2-byte, byte-order-dependent length prefix followed by the payload).
177///
178/// Elements are decoded with the same rule as [`SnRef::as_str`] and can be
179/// iterated directly:
180///
181/// ```
182/// use rust_stdf::{ByteOrder, KxSnRef};
183///
184/// let raw = [2, 0, b'A', b'B', 2, 0, b'C', b'D']; // two elements: "AB", "CD"
185/// let kx = KxSnRef::new(&raw, 0, 2, ByteOrder::LittleEndian);
186/// for s in &kx {
187///     println!("{s}");
188/// }
189/// ```
190#[derive(SmartDefault, Clone, Copy)]
191pub struct KxSnRef<'a> {
192    raw: &'a [u8],
193    start: usize,
194    k: usize,
195    #[default(ByteOrder::LittleEndian)]
196    order: ByteOrder,
197}
198
199/// Iterator over the elements of a [`KxSnRef`], one pass.
200pub struct KxSnRefIter<'a> {
201    raw: &'a [u8],
202    pos: usize,
203    remaining: usize,
204    order: ByteOrder,
205}
206
207/// Zero-copy view over the `k` elements of a `KxCf` array (each element is a
208/// fixed-width `f`-byte string, no length prefix).
209///
210/// Elements are decoded with the same rule as [`CnRef::as_str`] and can be
211/// iterated directly:
212///
213/// ```
214/// use rust_stdf::KxCfRef;
215///
216/// let raw = [b'A', b'B', b'C', b'D']; // two fixed 2-byte elements: "AB", "CD"
217/// let kx = KxCfRef::new(&raw, 0, 2, 2);
218/// for s in &kx {
219///     println!("{s}");
220/// }
221/// ```
222#[derive(Clone, Copy, Default)]
223pub struct KxCfRef<'a> {
224    raw: &'a [u8],
225    start: usize,
226    k: usize,
227    f: usize,
228}
229
230/// Iterator over the elements of a [`KxCfRef`], one pass.
231pub struct KxCfRefIter<'a> {
232    raw: &'a [u8],
233    pos: usize,
234    remaining: usize,
235    f: usize,
236}
237
238impl<'a> CnRef<'a> {
239    /// Read a `Cn` starting at the stored offset `off` (which points at the
240    /// length byte). Returns `None` if the field is absent.
241    #[inline]
242    pub(crate) fn read_at(raw: &'a [u8], off: u16) -> Option<Self> {
243        let p = validate_offset(off)?;
244        let cnt = *raw.get(p)? as usize;
245        let start = p + 1;
246        let end = std::cmp::min(start + cnt, raw.len()); // clamp truncated payloads
247        Some(CnRef(&raw[start..end]))
248    }
249
250    /// Raw bytes of the string, always zero-copy.
251    #[inline]
252    pub fn as_bytes(&self) -> &'a [u8] {
253        self.0
254    }
255
256    /// Zero-copy borrow when the payload is valid UTF-8; otherwise an owned
257    /// `String` decoded byte → char (Latin-1). Same decoding as
258    /// [`to_owned`](Self::to_owned).
259    #[inline]
260    pub fn as_str(&self) -> Cow<'a, str> {
261        bytes_to_cow_str(self.0)
262    }
263
264    /// Allocating; reproduces the owned `Cn` string (valid UTF-8 decoded as
265    /// UTF-8, otherwise byte → char Latin-1), matching the eager parser.
266    pub fn to_owned(&self) -> Cn {
267        bytes_to_string(self.0)
268    }
269}
270
271impl<'a> SnRef<'a> {
272    /// Read an `Sn` starting at the stored offset `off` (which points at the
273    /// 2-byte length prefix). Returns `None` if the field is absent.
274    #[inline]
275    pub(crate) fn read_at(raw: &'a [u8], off: u16, order: &ByteOrder) -> Option<Self> {
276        let p = validate_offset(off)?;
277        let mut cp = p;
278        let cnt = read_u2(raw, &mut cp, order) as usize;
279        let start = cp;
280        let end = std::cmp::min(start + cnt, raw.len());
281        Some(SnRef(raw.get(start..end).unwrap_or(&[])))
282    }
283
284    /// Raw bytes of the string, always zero-copy.
285    #[inline]
286    pub fn as_bytes(&self) -> &'a [u8] {
287        self.0
288    }
289
290    /// Zero-copy borrow when the payload is valid UTF-8; otherwise an owned
291    /// `String` decoded byte → char (Latin-1). Same decoding as
292    /// [`to_owned`](Self::to_owned).
293    #[inline]
294    pub fn as_str(&self) -> Cow<'a, str> {
295        bytes_to_cow_str(self.0)
296    }
297
298    /// Allocating; reproduces the owned `Sn` string (valid UTF-8 decoded as
299    /// UTF-8, otherwise byte → char Latin-1), matching the eager parser.
300    pub fn to_owned(&self) -> Sn {
301        bytes_to_string(self.0)
302    }
303}
304
305impl<'a> BnRef<'a> {
306    /// Read a `Bn` starting at the stored offset `off` (which points at the
307    /// length byte). Returns `None` if the field is absent.
308    #[inline]
309    pub(crate) fn read_at(raw: &'a [u8], off: u16) -> Option<Self> {
310        let p = validate_offset(off)?;
311        let cnt = *raw.get(p)? as usize;
312        let start = p + 1;
313        let end = std::cmp::min(start + cnt, raw.len());
314        Some(BnRef(&raw[start..end]))
315    }
316
317    /// Raw bytes, always zero-copy.
318    #[inline]
319    pub fn as_bytes(&self) -> &'a [u8] {
320        self.0
321    }
322
323    #[inline]
324    pub fn is_empty(&self) -> bool {
325        self.0.is_empty()
326    }
327
328    #[inline]
329    pub fn len(&self) -> usize {
330        self.0.len()
331    }
332
333    /// Allocating; reproduces the owned `Bn` (byte copy) semantics used by
334    /// `StdfRecord`, so results match the eager parser.
335    pub fn to_owned(&self) -> Bn {
336        self.0.to_vec()
337    }
338}
339
340impl<'a> DnRef<'a> {
341    /// Read a `Dn` starting at the stored offset `off` (which points at the
342    /// 2-byte bit-count prefix). Returns `None` if the field is absent.
343    #[inline]
344    pub(crate) fn read_at(raw: &'a [u8], off: u16, order: &ByteOrder) -> Option<Self> {
345        let p = validate_offset(off)?;
346        let mut cp = p;
347        let bitcount = read_u2(raw, &mut cp, order) as usize;
348        let bytecount = bitcount.div_ceil(8);
349        let start = cp;
350        let end = std::cmp::min(start + bytecount, raw.len());
351        Some(DnRef(raw.get(start..end).unwrap_or(&[])))
352    }
353
354    /// Raw bytes, always zero-copy.
355    #[inline]
356    pub fn as_bytes(&self) -> &'a [u8] {
357        self.0
358    }
359
360    #[inline]
361    pub fn is_empty(&self) -> bool {
362        self.0.is_empty()
363    }
364
365    #[inline]
366    pub fn len(&self) -> usize {
367        self.0.len()
368    }
369
370    /// Allocating; reproduces the owned `Dn` (byte copy) semantics used by
371    /// `StdfRecord`, so results match the eager parser.
372    pub fn to_owned(&self) -> Dn {
373        self.0.to_vec()
374    }
375}
376
377impl<'a> KxCnRef<'a> {
378    /// Construct a view over `k` elements starting at byte offset `start`.
379    ///
380    /// Normally obtained from a generated `*View` getter; only needed when
381    /// parsing a known raw layout directly.
382    #[inline]
383    pub fn new(raw: &'a [u8], start: usize, k: usize) -> Self {
384        KxCnRef { raw, start, k }
385    }
386
387    /// Number of string elements in the array.
388    #[inline]
389    pub fn len(&self) -> usize {
390        self.k
391    }
392
393    /// `true` when the array has no elements.
394    #[inline]
395    pub fn is_empty(&self) -> bool {
396        self.k == 0
397    }
398
399    /// Decoded `i`-th element; `None` when `i` is out of range.
400    ///
401    /// Valid UTF-8 payloads are borrowed zero-copy; other payloads are decoded
402    /// byte → char (Latin-1) into an owned `String`.
403    #[inline]
404    pub fn get_str(&self, i: usize) -> Option<Cow<'a, str>> {
405        self.get_bytes(i).map(bytes_to_cow_str)
406    }
407
408    /// Raw payload bytes of the `i`-th element (without the length prefix);
409    /// `None` when `i` is out of range.
410    #[inline]
411    pub fn get_bytes(&self, i: usize) -> Option<&'a [u8]> {
412        if i >= self.k {
413            return None;
414        }
415        // Walk to the start of element `i`: each previous element advances by
416        // 1 (length byte) + clamped payload.
417        let mut pos = self.start;
418        for _ in 0..i {
419            let cnt = *self.raw.get(pos)? as usize;
420            pos = std::cmp::min(pos + 1 + cnt, self.raw.len());
421        }
422        let cnt = *self.raw.get(pos)? as usize;
423        let start = pos + 1;
424        let end = std::cmp::min(start + cnt, self.raw.len());
425        Some(&self.raw[start..end])
426    }
427
428    /// Iterator over the decoded elements, one pass.
429    #[inline]
430    pub fn iter(&self) -> KxCnRefIter<'a> {
431        KxCnRefIter {
432            raw: self.raw,
433            pos: self.start,
434            remaining: self.k,
435        }
436    }
437
438    /// Allocating; collects all elements into `Vec<Cow<'a, str>>`.
439    #[inline]
440    pub fn as_vec(&self) -> Vec<Cow<'a, str>> {
441        self.iter().collect()
442    }
443
444    /// Allocating; reproduces the owned `KxCn` (`Vec<String>`), matching the
445    /// eager parser.
446    #[inline]
447    pub fn to_owned(&self) -> KxCn {
448        self.iter().map(Cow::into_owned).collect()
449    }
450}
451
452impl<'a> Iterator for KxCnRefIter<'a> {
453    type Item = Cow<'a, str>;
454
455    fn next(&mut self) -> Option<Self::Item> {
456        if self.remaining == 0 {
457            return None;
458        }
459        self.remaining -= 1;
460        let cnt = *self.raw.get(self.pos)? as usize;
461        self.pos += 1;
462        let end = std::cmp::min(self.pos + cnt, self.raw.len());
463        let slice = &self.raw[self.pos..end];
464        self.pos = end;
465        Some(bytes_to_cow_str(slice))
466    }
467}
468
469impl<'a> IntoIterator for &'a KxCnRef<'a> {
470    type Item = Cow<'a, str>;
471    type IntoIter = KxCnRefIter<'a>;
472
473    fn into_iter(self) -> Self::IntoIter {
474        self.iter()
475    }
476}
477
478impl<'a> KxSnRef<'a> {
479    /// Construct a view over `k` elements starting at byte offset `start`,
480    /// using `order` for the 2-byte length prefixes.
481    ///
482    /// Normally obtained from a generated `*View` getter; only needed when
483    /// parsing a known raw layout directly.
484    #[inline]
485    pub fn new(raw: &'a [u8], start: usize, k: usize, order: ByteOrder) -> Self {
486        KxSnRef {
487            raw,
488            start,
489            k,
490            order,
491        }
492    }
493
494    /// Number of string elements in the array.
495    #[inline]
496    pub fn len(&self) -> usize {
497        self.k
498    }
499
500    /// `true` when the array has no elements.
501    #[inline]
502    pub fn is_empty(&self) -> bool {
503        self.k == 0
504    }
505
506    /// Decoded `i`-th element; `None` when `i` is out of range.
507    ///
508    /// Valid UTF-8 payloads are borrowed zero-copy; other payloads are decoded
509    /// byte → char (Latin-1) into an owned `String`.
510    #[inline]
511    pub fn get_str(&self, i: usize) -> Option<Cow<'a, str>> {
512        self.get_bytes(i).map(bytes_to_cow_str)
513    }
514
515    /// Raw payload bytes of the `i`-th element (without the length prefix);
516    /// `None` when `i` is out of range.
517    #[inline]
518    pub fn get_bytes(&self, i: usize) -> Option<&'a [u8]> {
519        if i >= self.k {
520            return None;
521        }
522        // Walk to the start of element `i`: each previous element advances by
523        // 2 (length prefix) + clamped payload.
524        let mut pos = self.start;
525        for _ in 0..i {
526            let mut cp = pos;
527            let cnt = read_u2(self.raw, &mut cp, &self.order) as usize;
528            pos = std::cmp::min(cp + cnt, self.raw.len());
529        }
530        let mut cp = pos;
531        let cnt = read_u2(self.raw, &mut cp, &self.order) as usize;
532        let start = cp;
533        let end = std::cmp::min(start + cnt, self.raw.len());
534        Some(self.raw.get(start..end).unwrap_or(&[]))
535    }
536
537    /// Iterator over the decoded elements, one pass.
538    #[inline]
539    pub fn iter(&self) -> KxSnRefIter<'a> {
540        KxSnRefIter {
541            raw: self.raw,
542            pos: self.start,
543            remaining: self.k,
544            order: self.order,
545        }
546    }
547
548    /// Allocating; collects all elements into `Vec<Cow<'a, str>>`.
549    #[inline]
550    pub fn as_vec(&self) -> Vec<Cow<'a, str>> {
551        self.iter().collect()
552    }
553
554    /// Allocating; reproduces the owned `KxSn` (`Vec<String>`), matching the
555    /// eager parser.
556    #[inline]
557    pub fn to_owned(&self) -> KxSn {
558        self.iter().map(Cow::into_owned).collect()
559    }
560}
561
562impl<'a> Iterator for KxSnRefIter<'a> {
563    type Item = Cow<'a, str>;
564
565    fn next(&mut self) -> Option<Self::Item> {
566        if self.remaining == 0 {
567            return None;
568        }
569        self.remaining -= 1;
570        let mut cp = self.pos;
571        let cnt = read_u2(self.raw, &mut cp, &self.order) as usize;
572        self.pos = cp;
573        let end = std::cmp::min(self.pos + cnt, self.raw.len());
574        let slice = self.raw.get(self.pos..end).unwrap_or(&[]);
575        self.pos = end;
576        Some(bytes_to_cow_str(slice))
577    }
578}
579
580impl<'a> IntoIterator for &'a KxSnRef<'a> {
581    type Item = Cow<'a, str>;
582    type IntoIter = KxSnRefIter<'a>;
583
584    fn into_iter(self) -> Self::IntoIter {
585        self.iter()
586    }
587}
588
589impl<'a> KxCfRef<'a> {
590    /// Construct a view over `k` fixed `f`-byte elements starting at byte
591    /// offset `start`.
592    ///
593    /// Normally obtained from a generated `*View` getter; only needed when
594    /// parsing a known raw layout directly.
595    #[inline]
596    pub fn new(raw: &'a [u8], start: usize, k: usize, f: usize) -> Self {
597        KxCfRef { raw, start, k, f }
598    }
599
600    /// Number of string elements in the array.
601    #[inline]
602    pub fn len(&self) -> usize {
603        self.k
604    }
605
606    /// `true` when the array has no elements.
607    #[inline]
608    pub fn is_empty(&self) -> bool {
609        self.k == 0
610    }
611
612    /// Decoded `i`-th element; `None` when `i` is out of range.
613    ///
614    /// Valid UTF-8 payloads are borrowed zero-copy; other payloads are decoded
615    /// byte → char (Latin-1) into an owned `String`.
616    #[inline]
617    pub fn get_str(&self, i: usize) -> Option<Cow<'a, str>> {
618        self.get_bytes(i).map(bytes_to_cow_str)
619    }
620
621    /// Raw payload bytes of the `i`-th element; `None` when `i` is out of
622    /// range or lies past the end of the buffer.
623    #[inline]
624    pub fn get_bytes(&self, i: usize) -> Option<&'a [u8]> {
625        if i >= self.k {
626            return None;
627        }
628        let start = self.start + i * self.f;
629        let end = std::cmp::min(start + self.f, self.raw.len());
630        self.raw.get(start..end)
631    }
632
633    /// Iterator over the decoded elements, one pass.
634    #[inline]
635    pub fn iter(&self) -> KxCfRefIter<'a> {
636        KxCfRefIter {
637            raw: self.raw,
638            pos: self.start,
639            remaining: self.k,
640            f: self.f,
641        }
642    }
643
644    /// Allocating; collects all elements into `Vec<Cow<'a, str>>`.
645    #[inline]
646    pub fn as_vec(&self) -> Vec<Cow<'a, str>> {
647        self.iter().collect()
648    }
649
650    /// Allocating; reproduces the owned `KxCf` (`Vec<String>`), matching the
651    /// eager parser.
652    #[inline]
653    pub fn to_owned(&self) -> KxCf {
654        self.iter().map(Cow::into_owned).collect()
655    }
656}
657
658impl<'a> Iterator for KxCfRefIter<'a> {
659    type Item = Cow<'a, str>;
660
661    fn next(&mut self) -> Option<Self::Item> {
662        if self.remaining == 0 {
663            return None;
664        }
665        self.remaining -= 1;
666        let end = std::cmp::min(self.pos + self.f, self.raw.len());
667        let slice = self.raw.get(self.pos..end).unwrap_or(&[]);
668        self.pos = end;
669        Some(bytes_to_cow_str(slice))
670    }
671}
672
673impl<'a> IntoIterator for &'a KxCfRef<'a> {
674    type Item = Cow<'a, str>;
675    type IntoIter = KxCfRefIter<'a>;
676
677    fn into_iter(self) -> Self::IntoIter {
678        self.iter()
679    }
680}
681
682/// Decode STDF string bytes to `Cow<'a, str>`: valid UTF-8 is borrowed as-is,
683/// otherwise each byte is widened to a `char` (Latin-1) into an owned `String`.
684/// Shared by the eager `read_*` helpers and the `*Ref`/`Kx*Ref` views.
685#[inline(always)]
686pub(crate) fn bytes_to_cow_str(data: &[u8]) -> Cow<'_, str> {
687    match std::str::from_utf8(data) {
688        Ok(s) => Cow::Borrowed(s),
689        Err(_) => Cow::Owned(data.iter().map(|&x| x as char).collect()),
690    }
691}
692
693/// Decode STDF string bytes to `String`; same rule as [`bytes_to_cow_str`].
694#[inline(always)]
695pub(crate) fn bytes_to_string(data: &[u8]) -> String {
696    bytes_to_cow_str(data).into_owned()
697}