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