beve 7.2.0

Rust implementation of the BEVE (Binary Efficient Versatile Encoding) specification with serde support
Documentation
//! `#[serde(with = ...)]` helpers that decode a numeric or complex array field
//! at bulk (memcpy) speed, the symmetric counterpart to the bulk *encode* that
//! beve already does.
//!
//! beve's serializer coalesces a homogeneous `Vec<f64>` / `Vec<Complex<f32>>`
//! into a bulk typed/complex array on the wire, but serde's `Vec<T>::deserialize`
//! decodes element by element, and there is no hook to bulk-fill a nested field.
//! These helpers close that gap: annotate the field and decoding routes through a
//! newtype marker that beve's deserializer recognizes and bulk-copies straight
//! into the result `Vec`.
//!
//! ```ignore
//! use serde::{Serialize, Deserialize};
//!
//! #[derive(Serialize, Deserialize)]
//! struct Frame {
//!     #[serde(with = "beve::typed::f64")]
//!     samples: Vec<f64>,
//!     #[serde(with = "beve::complex_array::f32")]
//!     iq: Vec<beve::Complex<f32>>,   // or any layout-compatible complex type
//! }
//! ```
//!
//! Both `from_slice` and `from_reader_streaming` hit the bulk path. The bulk path
//! needs the annotation because serde exposes no way to bulk-fill an
//! *un-annotated* `Vec<T>` field — the same reason `serde_bytes` exists for
//! `&[u8]`.
//!
//! **Format-agnostic.** The bulk byte path is used only for non-human-readable
//! serde formats (beve). Human-readable formats (e.g. JSON) get the portable
//! element-wise form — each element via its own `Serialize`/`Deserialize` — so a
//! field using these helpers still round-trips through JSON, as long as the
//! element type itself has a portable serde representation (every scalar does, as
//! does `num_complex::Complex`; note `beve::Complex`'s own representation is
//! beve-specific). Non-beve *binary* formats are not special-cased and will
//! mis-read the bulk form.
//!
//! `complex_array::*` decodes the bulk (beve) form by reinterpreting wire bytes as
//! the element type, so the element `T` must be [`bytemuck::AnyBitPattern`] (every
//! bit pattern is a valid value) and layout-compatible with `Complex<scalar>`.
//! `beve::Complex` qualifies, as does `num_complex::Complex` with its `bytemuck`
//! feature enabled.

use bytemuck::AnyBitPattern;
use serde::de::{self, Visitor};
use serde::{Deserialize, Deserializer, Serialize, Serializer};

use crate::ext::{Complex, ComplexSlice, serialize_typed_slice};
use crate::fast::BeveTypedSlice;

// ---------------------------------------------------------------------------
// The markers for both array kinds live in `crate::ext`, next to the wrapper
// types that write them: `typed_array_tag` / `NT_TYPED_ARRAY_*` and
// `complex_array_tag` / `NT_COMPLEX_ARRAY_*`. The `#[serde(with = ...)]`
// helpers below name those constants rather than repeating the string
// literals, so an encode marker and its decode marker cannot drift apart --
// a typo in a repeated literal would not fail to compile, it would silently
// drop the field to the element-wise path.
// ---------------------------------------------------------------------------

// ---------------------------------------------------------------------------
// Shared bulk-copy core
// ---------------------------------------------------------------------------

/// Copy a little-endian payload straight into a `Vec<E>`. The `E: AnyBitPattern`
/// bound guarantees any byte sequence is a valid `E`, which is what makes the
/// memcpy sound; `payload.len()` must be a whole multiple of `size_of::<E>()`
/// (the decoders guarantee this). `scalar_size` is the byte-swap granularity on
/// big-endian targets (the element's *scalar* width).
fn bytes_to_vec<E: AnyBitPattern>(payload: &[u8], scalar_size: usize) -> Vec<E> {
    let elem = core::mem::size_of::<E>();
    debug_assert!(elem != 0 && payload.len().is_multiple_of(elem));
    let n = payload.len() / elem.max(1);
    let mut out: Vec<E> = Vec::with_capacity(n);
    if n != 0 {
        let nbytes = n * elem;
        // SAFETY: `out` has capacity for `n` `E`s = `nbytes` bytes; `payload` is
        // exactly that long; `E: AnyBitPattern` accepts any bit pattern.
        unsafe {
            core::ptr::copy_nonoverlapping(payload.as_ptr(), out.as_mut_ptr() as *mut u8, nbytes);
            out.set_len(n);
        }
        #[cfg(not(target_endian = "little"))]
        {
            // Wire is little-endian; reverse each scalar in place on big-endian.
            let bytes =
                unsafe { core::slice::from_raw_parts_mut(out.as_mut_ptr() as *mut u8, nbytes) };
            for s in bytes.chunks_exact_mut(scalar_size) {
                s.reverse();
            }
        }
    }
    #[cfg(target_endian = "little")]
    let _ = scalar_size;
    out
}

// ---------------------------------------------------------------------------
// Visitors
// ---------------------------------------------------------------------------

/// Visitor for a typed numeric array decoded into `Vec<S>`.
struct TypedArrayVisitor<S>(core::marker::PhantomData<S>);

impl<'de, S> Visitor<'de> for TypedArrayVisitor<S>
where
    S: BeveTypedSlice + AnyBitPattern + Deserialize<'de>,
{
    type Value = Vec<S>;

    fn expecting(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
        write!(f, "a beve typed numeric array")
    }

    fn visit_borrowed_bytes<E: de::Error>(self, v: &'de [u8]) -> Result<Self::Value, E> {
        Ok(bytes_to_vec::<S>(v, S::ELEM_SIZE))
    }
    fn visit_bytes<E: de::Error>(self, v: &[u8]) -> Result<Self::Value, E> {
        Ok(bytes_to_vec::<S>(v, S::ELEM_SIZE))
    }
    fn visit_byte_buf<E: de::Error>(self, v: Vec<u8>) -> Result<Self::Value, E> {
        Ok(bytes_to_vec::<S>(&v, S::ELEM_SIZE))
    }
    fn visit_seq<A: de::SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
        let mut out = Vec::with_capacity(seq.size_hint().unwrap_or(0));
        while let Some(x) = seq.next_element::<S>()? {
            out.push(x);
        }
        Ok(out)
    }
    fn visit_newtype_struct<D: Deserializer<'de>>(self, d: D) -> Result<Self::Value, D::Error> {
        Vec::<S>::deserialize(d)
    }
}

/// Visitor for a complex array decoded into `Vec<T>`, where `T` is layout-
/// compatible with `Complex<S>` (two contiguous `S` scalars: re then im).
struct ComplexArrayVisitor<S, T>(core::marker::PhantomData<(S, T)>);

impl<'de, S, T> Visitor<'de> for ComplexArrayVisitor<S, T>
where
    S: BeveTypedSlice,
    T: AnyBitPattern + Deserialize<'de>,
{
    type Value = Vec<T>;

    fn expecting(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
        write!(f, "a beve complex array")
    }

    fn visit_borrowed_bytes<E: de::Error>(self, v: &'de [u8]) -> Result<Self::Value, E> {
        Ok(bytes_to_vec::<T>(v, S::ELEM_SIZE))
    }
    fn visit_bytes<E: de::Error>(self, v: &[u8]) -> Result<Self::Value, E> {
        Ok(bytes_to_vec::<T>(v, S::ELEM_SIZE))
    }
    fn visit_byte_buf<E: de::Error>(self, v: Vec<u8>) -> Result<Self::Value, E> {
        Ok(bytes_to_vec::<T>(&v, S::ELEM_SIZE))
    }
    fn visit_seq<A: de::SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
        let mut out = Vec::with_capacity(seq.size_hint().unwrap_or(0));
        while let Some(x) = seq.next_element::<T>()? {
            out.push(x);
        }
        Ok(out)
    }
    fn visit_newtype_struct<D: Deserializer<'de>>(self, d: D) -> Result<Self::Value, D::Error> {
        Vec::<T>::deserialize(d)
    }
}

/// Assert `T` is layout-compatible with `Complex<S>` (same size and alignment),
/// the safety contract the bulk copy relies on. Field order is the caller's.
fn assert_complex_layout<S, T>() {
    assert_eq!(
        core::mem::size_of::<T>(),
        core::mem::size_of::<Complex<S>>(),
        "beve complex array: element type size mismatch"
    );
    assert_eq!(
        core::mem::align_of::<T>(),
        core::mem::align_of::<Complex<S>>(),
        "beve complex array: element type alignment mismatch"
    );
}

// ---------------------------------------------------------------------------
// Public `with` modules
// ---------------------------------------------------------------------------

/// `#[serde(with = "beve::typed::<scalar>")]` for a `Vec<scalar>` field: bulk
/// encode and bulk (memcpy) decode of a typed numeric array.
pub mod typed {
    use super::*;

    macro_rules! typed_with {
        ($module:ident, $scalar:ty, $nt:expr) => {
            #[doc = concat!("Bulk `Vec<", stringify!($scalar), ">` codec for `#[serde(with = ...)]`.")]
            pub mod $module {
                use super::*;

                pub fn serialize<S: Serializer>(
                    value: &[$scalar],
                    serializer: S,
                ) -> Result<S::Ok, S::Error> {
                    serialize_typed_slice(value, $nt, serializer)
                }

                pub fn deserialize<'de, D: Deserializer<'de>>(
                    deserializer: D,
                ) -> Result<Vec<$scalar>, D::Error> {
                    deserializer.deserialize_newtype_struct(
                        $nt,
                        TypedArrayVisitor::<$scalar>(core::marker::PhantomData),
                    )
                }
            }
        };
    }

    typed_with!(i8, i8, crate::ext::NT_TYPED_ARRAY_I8);
    typed_with!(i16, i16, crate::ext::NT_TYPED_ARRAY_I16);
    typed_with!(i32, i32, crate::ext::NT_TYPED_ARRAY_I32);
    typed_with!(i64, i64, crate::ext::NT_TYPED_ARRAY_I64);
    typed_with!(i128, i128, crate::ext::NT_TYPED_ARRAY_I128);
    typed_with!(u8, u8, crate::ext::NT_TYPED_ARRAY_U8);
    typed_with!(u16, u16, crate::ext::NT_TYPED_ARRAY_U16);
    typed_with!(u32, u32, crate::ext::NT_TYPED_ARRAY_U32);
    typed_with!(u64, u64, crate::ext::NT_TYPED_ARRAY_U64);
    typed_with!(u128, u128, crate::ext::NT_TYPED_ARRAY_U128);
    typed_with!(f32, f32, crate::ext::NT_TYPED_ARRAY_F32);
    typed_with!(f64, f64, crate::ext::NT_TYPED_ARRAY_F64);
}

/// `#[serde(with = "beve::complex_array::<scalar>")]` for a `Vec<T>` field where
/// `T` is layout-compatible with `Complex<scalar>`: bulk encode and bulk (memcpy)
/// decode of a complex array.
pub mod complex_array {
    use super::*;

    macro_rules! complex_with {
        ($module:ident, $scalar:ty, $nt:expr) => {
            #[doc = concat!("Bulk `Vec<Complex<", stringify!($scalar), ">>` codec for `#[serde(with = ...)]`.")]
            pub mod $module {
                use super::*;

                pub fn serialize<S, T>(value: &[T], serializer: S) -> Result<S::Ok, S::Error>
                where
                    S: Serializer,
                    T: Serialize,
                {
                    // Human-readable formats get the portable element-wise form
                    // (each `T` via its own `Serialize`), so the field round-trips
                    // through e.g. JSON for a portable element type.
                    if serializer.is_human_readable() {
                        use serde::ser::SerializeSeq;
                        let mut seq = serializer.serialize_seq(Some(value.len()))?;
                        for v in value {
                            seq.serialize_element(v)?;
                        }
                        return seq.end();
                    }
                    assert_complex_layout::<$scalar, T>();
                    // SAFETY: `T` is layout-compatible with `Complex<$scalar>`
                    // (asserted above), so a `&[T]` is a valid `&[Complex<$scalar>]`.
                    let slice: &[Complex<$scalar>] = unsafe {
                        core::slice::from_raw_parts(
                            value.as_ptr() as *const Complex<$scalar>,
                            value.len(),
                        )
                    };
                    ComplexSlice(slice).serialize(serializer)
                }

                pub fn deserialize<'de, D, T>(deserializer: D) -> Result<Vec<T>, D::Error>
                where
                    D: Deserializer<'de>,
                    T: AnyBitPattern + Deserialize<'de>,
                {
                    assert_complex_layout::<$scalar, T>();
                    deserializer.deserialize_newtype_struct(
                        $nt,
                        ComplexArrayVisitor::<$scalar, T>(core::marker::PhantomData),
                    )
                }
            }
        };
    }

    complex_with!(f32, f32, crate::ext::NT_COMPLEX_ARRAY_F32);
    complex_with!(f64, f64, crate::ext::NT_COMPLEX_ARRAY_F64);
    complex_with!(i8, i8, crate::ext::NT_COMPLEX_ARRAY_I8);
    complex_with!(i16, i16, crate::ext::NT_COMPLEX_ARRAY_I16);
    complex_with!(i32, i32, crate::ext::NT_COMPLEX_ARRAY_I32);
    complex_with!(i64, i64, crate::ext::NT_COMPLEX_ARRAY_I64);
    complex_with!(i128, i128, crate::ext::NT_COMPLEX_ARRAY_I128);
    complex_with!(u8, u8, crate::ext::NT_COMPLEX_ARRAY_U8);
    complex_with!(u16, u16, crate::ext::NT_COMPLEX_ARRAY_U16);
    complex_with!(u32, u32, crate::ext::NT_COMPLEX_ARRAY_U32);
    complex_with!(u64, u64, crate::ext::NT_COMPLEX_ARRAY_U64);
    complex_with!(u128, u128, crate::ext::NT_COMPLEX_ARRAY_U128);
}