1use comnum_core::{Error, Result};
4use comnum_math::C64;
5
6#[derive(Clone, Copy, Debug, PartialEq, Eq)]
8pub enum Scalar {
9 F32,
11 F64,
13 I32,
15 I16,
17 I8,
19 U32,
21 U16,
23 U8,
25}
26
27impl Scalar {
28 pub fn bytes(self) -> usize {
30 match self {
31 Scalar::F64 => 8,
32 Scalar::F32 | Scalar::I32 | Scalar::U32 => 4,
33 Scalar::I16 | Scalar::U16 => 2,
34 Scalar::I8 | Scalar::U8 => 1,
35 }
36 }
37
38 pub(super) fn name(self) -> &'static str {
39 match self {
40 Scalar::F32 => "f32",
41 Scalar::F64 => "f64",
42 Scalar::I32 => "i32",
43 Scalar::I16 => "i16",
44 Scalar::I8 => "i8",
45 Scalar::U32 => "u32",
46 Scalar::U16 => "u16",
47 Scalar::U8 => "u8",
48 }
49 }
50
51 pub(super) fn is_integer(self) -> bool {
52 !matches!(self, Scalar::F32 | Scalar::F64)
53 }
54
55 pub(super) fn half_range(self) -> f64 {
58 2f64.powi(8 * self.bytes() as i32 - 1)
59 }
60}
61
62#[derive(Clone, Copy, Debug, PartialEq, Eq)]
69pub struct Datatype {
70 pub complex: bool,
72 pub scalar: Scalar,
74 pub big_endian: bool,
76}
77
78impl Datatype {
79 pub const CF32: Datatype = Datatype { complex: true, scalar: Scalar::F32, big_endian: false };
81 pub const CF64: Datatype = Datatype { complex: true, scalar: Scalar::F64, big_endian: false };
83 pub const CI32: Datatype = Datatype { complex: true, scalar: Scalar::I32, big_endian: false };
85 pub const CI16: Datatype = Datatype { complex: true, scalar: Scalar::I16, big_endian: false };
87 pub const CI8: Datatype = Datatype { complex: true, scalar: Scalar::I8, big_endian: false };
89 pub const CU8: Datatype = Datatype { complex: true, scalar: Scalar::U8, big_endian: false };
91 pub const RF32: Datatype = Datatype { complex: false, scalar: Scalar::F32, big_endian: false };
93 pub const RF64: Datatype = Datatype { complex: false, scalar: Scalar::F64, big_endian: false };
95 pub const RI16: Datatype = Datatype { complex: false, scalar: Scalar::I16, big_endian: false };
97
98 pub fn parse(s: &str) -> Result<Datatype> {
104 let bad = || {
105 Error::Config(format!(
106 "unsupported data type {s:?}: [c|r][f32|f64|i32|i16|i8|u32|u16|u8][_le|_be] (cf32_le, ci16_le, cu8, …)"
107 ))
108 };
109 let (complex, rest) = match s.split_at_checked(1) {
110 Some(("c", r)) => (true, r),
111 Some(("r", r)) => (false, r),
112 _ => return Err(bad()),
113 };
114 let (body, big_endian) = match rest.rsplit_once('_') {
115 Some((b, "le")) => (b, false),
116 Some((b, "be")) => (b, true),
117 Some(_) => return Err(bad()),
118 None => (rest, false),
119 };
120 let scalar = match body {
121 "f32" => Scalar::F32,
122 "f64" => Scalar::F64,
123 "i32" => Scalar::I32,
124 "i16" => Scalar::I16,
125 "i8" => Scalar::I8,
126 "u32" => Scalar::U32,
127 "u16" => Scalar::U16,
128 "u8" => Scalar::U8,
129 _ => return Err(bad()),
130 };
131 Ok(Datatype { complex, scalar, big_endian: big_endian && scalar.bytes() > 1 })
132 }
133
134 pub fn name(self) -> String {
136 let end = match (self.scalar.bytes(), self.big_endian) {
137 (1, _) => "",
138 (_, false) => "_le",
139 (_, true) => "_be",
140 };
141 format!("{}{}{end}", if self.complex { "c" } else { "r" }, self.scalar.name())
142 }
143
144 pub fn sample_bytes(self) -> usize {
146 self.scalar.bytes() * if self.complex { 2 } else { 1 }
147 }
148
149 pub(super) fn component(self, b: &[u8]) -> f64 {
150 macro_rules! num {
151 ($t:ty, $n:expr) => {{
152 let a: [u8; $n] = b[..$n].try_into().unwrap_or([0; $n]);
153 if self.big_endian { <$t>::from_be_bytes(a) } else { <$t>::from_le_bytes(a) }
154 }};
155 }
156 let h = self.scalar.half_range();
157 match self.scalar {
158 Scalar::F32 => f64::from(num!(f32, 4)),
159 Scalar::F64 => num!(f64, 8),
160 Scalar::I32 => f64::from(num!(i32, 4)),
161 Scalar::I16 => f64::from(num!(i16, 2)),
162 Scalar::I8 => f64::from(b[0] as i8),
163 Scalar::U32 => f64::from(num!(u32, 4)) - h,
164 Scalar::U16 => f64::from(num!(u16, 2)) - h,
165 Scalar::U8 => f64::from(b[0]) - h,
166 }
167 }
168
169 pub(super) fn decode(self, b: &[u8]) -> C64 {
170 let k = self.scalar.bytes();
171 C64::new(self.component(b), if self.complex { self.component(&b[k..]) } else { 0.0 })
172 }
173
174 pub(super) fn put(self, x: f64, out: &mut Vec<u8>) {
175 macro_rules! put {
176 ($v:expr) => {{
177 let v = $v;
178 if self.big_endian { out.extend(v.to_be_bytes()) } else { out.extend(v.to_le_bytes()) }
179 }};
180 }
181 let h = self.scalar.half_range();
182 let sat = |lo: f64, hi: f64| x.round().clamp(lo, hi);
184 match self.scalar {
185 Scalar::F32 => put!(x as f32),
186 Scalar::F64 => put!(x),
187 Scalar::I32 => put!(sat(-h, h - 1.0) as i32),
188 Scalar::I16 => put!(sat(-h, h - 1.0) as i16),
189 Scalar::I8 => put!(sat(-h, h - 1.0) as i8),
190 Scalar::U32 => put!((sat(-h, h - 1.0) + h) as u32),
191 Scalar::U16 => put!((sat(-h, h - 1.0) + h) as u16),
192 Scalar::U8 => put!((sat(-h, h - 1.0) + h) as u8),
193 }
194 }
195
196 pub(super) fn encode(self, v: C64, out: &mut Vec<u8>) {
197 self.put(v.re, out);
198 if self.complex {
199 self.put(v.im, out);
200 }
201 }
202}