1use std::convert::TryInto;
14
15use crate::complex::{Complex32, Complex64};
16use crate::constants::ExpressionEnum;
17use crate::constants::NumericArrayEnum as DT;
18use crate::reader::Reader;
19use crate::wxf::reader::WxfReader;
20use crate::Error;
21
22pub trait NumericTarget: Sized + Copy + 'static {
26 const TARGET: DT;
28 fn widen_from(src: DT, bytes: &[u8]) -> Result<Vec<Self>, String>;
32}
33
34pub fn read_vec<'de, T: NumericTarget, R: Reader<'de>>(
42 r: &mut WxfReader<R>,
43 path: &str,
44) -> Result<Vec<T>, Error> {
45 let tok = r.read_expr_token()?;
46 read_vec_with_tag::<T, R>(r, tok, path)
47}
48
49pub fn read_vec_with_tag<'de, T: NumericTarget, R: Reader<'de>>(
51 r: &mut WxfReader<R>,
52 tok: ExpressionEnum,
53 path: &str,
54) -> Result<Vec<T>, Error> {
55 match tok {
56 ExpressionEnum::NumericArray | ExpressionEnum::PackedArray => {
57 let dt = r.read_numeric_type()?;
58 let (_dims, byte_count) = r.read_array_shape(dt.size_in_bytes())?;
59 let bytes = r.read_bytes(byte_count)?;
60 T::widen_from(dt, bytes)
61 .map_err(|m| err(path, "compatible numeric source", m))
62 },
63 ExpressionEnum::ByteArray => {
64 let len = r.read_varint()? as usize;
65 let bytes = r.read_bytes(len)?;
66 T::widen_from(DT::Integer8, bytes)
67 .map_err(|m| err(path, "compatible numeric source", m))
68 },
69 other => Err(err(
70 path,
71 "NumericArray, PackedArray, or ByteArray",
72 other.name().to_string(),
73 )),
74 }
75}
76
77pub fn read_fixed<'de, T: NumericTarget, R: Reader<'de>>(
79 r: &mut WxfReader<R>,
80 path: &str,
81 n: usize,
82) -> Result<Vec<T>, Error> {
83 let tok = r.read_expr_token()?;
84 read_fixed_with_tag::<T, R>(r, tok, path, n)
85}
86
87pub fn read_fixed_with_tag<'de, T: NumericTarget, R: Reader<'de>>(
89 r: &mut WxfReader<R>,
90 tok: ExpressionEnum,
91 path: &str,
92 n: usize,
93) -> Result<Vec<T>, Error> {
94 let v = read_vec_with_tag::<T, R>(r, tok, path)?;
95 if v.len() != n {
96 return Err(err(
97 path,
98 "numeric array with matching element count",
99 format!("expected {} elements, got {}", n, v.len()),
100 ));
101 }
102 Ok(v)
103}
104
105fn err(path: &str, expected: &'static str, got: String) -> Error {
106 Error::Deserialize {
107 path: path.to_string(),
108 expected: expected,
109 got: got,
110 }
111}
112
113macro_rules! make_reader {
119 ($name:ident, $t:ty, $n:expr) => {
120 #[inline]
121 fn $name(b: &[u8]) -> impl Iterator<Item = $t> + '_ {
122 b.chunks_exact($n).map(|c| {
123 let arr: [u8; $n] = c.try_into().unwrap();
124 <$t>::from_le_bytes(arr)
125 })
126 }
127 };
128}
129
130#[inline]
131fn read_i8(b: &[u8]) -> impl Iterator<Item = i8> + '_ {
132 b.iter().map(|&x| x as i8)
133}
134#[inline]
135fn read_u8(b: &[u8]) -> impl Iterator<Item = u8> + '_ {
136 b.iter().copied()
137}
138make_reader!(read_i16, i16, 2);
139make_reader!(read_i32, i32, 4);
140make_reader!(read_i64, i64, 8);
141make_reader!(read_u16, u16, 2);
142make_reader!(read_u32, u32, 4);
143make_reader!(read_u64, u64, 8);
144make_reader!(read_f32, f32, 4);
145make_reader!(read_f64, f64, 8);
146
147#[inline]
148fn read_complex32(b: &[u8]) -> impl Iterator<Item = Complex32> + '_ {
149 b.chunks_exact(8).map(|c| Complex32 {
150 re: f32::from_le_bytes(c[..4].try_into().unwrap()),
151 im: f32::from_le_bytes(c[4..].try_into().unwrap()),
152 })
153}
154#[inline]
155fn read_complex64(b: &[u8]) -> impl Iterator<Item = Complex64> + '_ {
156 b.chunks_exact(16).map(|c| Complex64 {
157 re: f64::from_le_bytes(c[..8].try_into().unwrap()),
158 im: f64::from_le_bytes(c[8..].try_into().unwrap()),
159 })
160}
161
162fn reject(src: DT, target: DT) -> String {
163 format!(
164 "cannot widen {} → {} without truncation or precision loss",
165 src.name(),
166 target.name()
167 )
168}
169
170macro_rules! impl_target {
173 ($t:ty, $target:ident, $target_reader:ident, { $($src:ident => $reader:ident),* $(,)? }) => {
174 impl NumericTarget for $t {
175 const TARGET: DT = DT::$target;
176 fn widen_from(src: DT, bytes: &[u8]) -> Result<Vec<Self>, String> {
177 match src {
178 DT::$target => Ok($target_reader(bytes).collect()),
179 $(DT::$src => Ok($reader(bytes).map(<$t>::from).collect()),)*
180 other => Err(reject(other, DT::$target)),
181 }
182 }
183 }
184 };
185}
186
187impl_target!(i8, Integer8, read_i8, {});
188impl_target!(i16, Integer16, read_i16, { Integer8 => read_i8, UnsignedInteger8 => read_u8 });
189impl_target!(i32, Integer32, read_i32, { Integer8 => read_i8, Integer16 => read_i16, UnsignedInteger8 => read_u8, UnsignedInteger16 => read_u16 });
190impl_target!(i64, Integer64, read_i64, { Integer8 => read_i8, Integer16 => read_i16, Integer32 => read_i32, UnsignedInteger8 => read_u8, UnsignedInteger16 => read_u16, UnsignedInteger32 => read_u32 });
191impl_target!(u8, UnsignedInteger8, read_u8, {});
192impl_target!(u16, UnsignedInteger16, read_u16, { UnsignedInteger8 => read_u8 });
193impl_target!(u32, UnsignedInteger32, read_u32, { UnsignedInteger8 => read_u8, UnsignedInteger16 => read_u16 });
194impl_target!(u64, UnsignedInteger64, read_u64, { UnsignedInteger8 => read_u8, UnsignedInteger16 => read_u16, UnsignedInteger32 => read_u32 });
195impl_target!(f32, Real32, read_f32, { Integer8 => read_i8, Integer16 => read_i16, UnsignedInteger8 => read_u8, UnsignedInteger16 => read_u16 });
196impl_target!(f64, Real64, read_f64, { Integer8 => read_i8, Integer16 => read_i16, Integer32 => read_i32, UnsignedInteger8 => read_u8, UnsignedInteger16 => read_u16, UnsignedInteger32 => read_u32, Real32 => read_f32 });
197impl_target!(Complex32, ComplexReal32, read_complex32, {});
198impl_target!(Complex64, ComplexReal64, read_complex64, {});