Skip to main content

substrate_data/wire/frame/floats/
narrowing.rs

1//! The float lists' kernels, chosen once, and what a frame does with them.
2
3#[cfg(target_arch = "x86_64")]
4use super::avx2;
5use super::portable;
6use crate::sync::OnceLock;
7use crate::wire::frame::sizes::F32_BYTES;
8
9struct Kernels {
10    /// Whether every `f64` is an `f32` exactly (bit for bit back).
11    fits: fn(&[f64]) -> bool,
12    /// The `f64`s as `f32`s, little-endian, onto the bytes.
13    narrow: fn(&[f64], &mut [u8]),
14    /// `f32`s (little-endian bytes) as `f64`s.
15    widen: fn(&[u8], &mut [f64]),
16}
17
18fn kernels() -> &'static Kernels {
19    static KERNELS: OnceLock<Kernels> = OnceLock::new();
20    KERNELS.get_or_init(|| {
21        #[cfg(target_arch = "x86_64")]
22        if std::arch::is_x86_feature_detected!("avx2") {
23            return Kernels {
24                fits: avx2::fits,
25                narrow: avx2::narrow,
26                widen: avx2::widen,
27            };
28        }
29        Kernels {
30            fits: portable::fits,
31            narrow: portable::narrow,
32            widen: portable::widen,
33        }
34    })
35}
36
37/// Whether every one of `items` is an `f32` exactly.
38#[must_use]
39pub fn fits_f32(items: &[f64]) -> bool {
40    (kernels().fits)(items)
41}
42
43/// `items` as `f32`s onto `out`.
44pub fn put_narrow(out: &mut Vec<u8>, items: &[f64]) {
45    let at = out.len();
46    out.resize(at + items.len() * F32_BYTES, 0);
47    (kernels().narrow)(items, &mut out[at..]);
48}
49
50/// `items` as they are onto `out`: written once, with no zeroing first.
51pub fn put_wide(out: &mut Vec<u8>, items: &[f64]) {
52    out.extend(items.iter().flat_map(|f| f.to_le_bytes()));
53}
54
55/// `f32`s as they are onto `out`.
56pub fn put_f32s(out: &mut Vec<u8>, items: &[f32]) {
57    out.extend(items.iter().flat_map(|f| f.to_le_bytes()));
58}
59
60/// The `f32`s of `raw` as `f64`s.
61#[must_use]
62pub fn widen(raw: &[u8]) -> Vec<f64> {
63    let mut out = vec![0.0; raw.len() / F32_BYTES];
64    (kernels().widen)(raw, &mut out);
65    out
66}
67
68#[cfg(test)]
69mod tests {
70    use super::*;
71
72    #[test]
73    fn the_kernels_agree_with_the_portable_ones() {
74        let exact: Vec<f64> = (0..37).map(|i| f64::from(i) * 0.5 - 3.0).collect();
75        let mut odd = exact.clone();
76        odd[33] = 0.1;
77        let mut zero = exact.clone();
78        zero[2] = -0.0;
79        for items in [&exact, &odd, &zero] {
80            assert_eq!(fits_f32(items), portable::fits(items));
81        }
82        assert!(fits_f32(&exact) && !fits_f32(&odd) && fits_f32(&zero));
83        let (mut a, mut b) = (Vec::new(), vec![0; exact.len() * F32_BYTES]);
84        put_narrow(&mut a, &exact);
85        portable::narrow(&exact, &mut b);
86        assert_eq!(a, b);
87        assert_eq!(widen(&a), exact);
88    }
89}