astro_rs/fits/header_value/
tform.rs1use crate::fits::hdu_macros::return_box;
4use crate::fits::FitsHeaderError;
5
6use super::FitsHeaderValue;
7
8#[allow(missing_docs)]
10#[derive(Debug, Clone, Copy)]
11pub enum TFormType {
12    Logical,
13    Bit,
14    UnsignedByte,
15    I16,
16    I32,
17    Character,
18    F32,
19    F64,
20    C64,
21    C128,
22    ArrayDescriptor,
23}
24
25impl TryFrom<char> for TFormType {
26    type Error = FitsHeaderError;
27
28    fn try_from(value: char) -> Result<Self, Self::Error> {
29        match value {
30            'L' => Ok(TFormType::Logical),
31            'X' => Ok(TFormType::Bit),
32            'B' => Ok(TFormType::UnsignedByte),
33            'I' => Ok(TFormType::I16),
34            'J' => Ok(TFormType::I32),
35            'A' => Ok(TFormType::Character),
36            'E' => Ok(TFormType::F32),
37            'D' => Ok(TFormType::F64),
38            'C' => Ok(TFormType::C64),
39            'M' => Ok(TFormType::C128),
40            'P' => Ok(TFormType::ArrayDescriptor),
41            _ => Err(FitsHeaderError::DeserializationError {
42                found: vec![value as u8],
43                intent: String::from("header card TFORM type value"),
44            }),
45        }
46    }
47}
48
49#[derive(Debug, Clone)]
51pub struct TForm {
52    pub r: usize,
54    pub t: TFormType,
56    pub a: String,
58}
59
60impl TForm {
61    pub fn value(&self) -> usize {
63        let type_bytes = match self.t {
64            TFormType::Logical => 1,
65            TFormType::Bit => todo!(),
66            TFormType::UnsignedByte => 1,
67            TFormType::I16 => 2,
68            TFormType::I32 => 4,
69            TFormType::Character => 1,
70            TFormType::F32 => 4,
71            TFormType::F64 => 8,
72            TFormType::C64 => 8,
73            TFormType::C128 => 16,
74            TFormType::ArrayDescriptor => 8,
75        };
76        self.r * type_bytes
77    }
78
79    pub fn create_column<T>(
81        &self,
82        data: &[u8],
83        column_start: usize,
84        row_len: usize,
85        num_rows: usize,
86    ) -> Box<Vec<T>> {
87        let column_len = self.value();
88        unsafe {
89            match self.t {
90                TFormType::Logical => {
91                    let mut result = Vec::with_capacity(num_rows * self.r);
92                    for i in 0..num_rows {
93                        let start = row_len * i + column_start;
94                        let column = data[start..start + column_len].to_vec();
95                        for value in column.iter().take(self.r) {
96                            result.push(*value != 0);
97                        }
98                    }
99
100                    return_box!(result)
101                }
102                TFormType::Bit => todo!(),
103                TFormType::UnsignedByte => {
104                    let mut result = Vec::with_capacity(num_rows * self.r);
105                    for i in 0..num_rows {
106                        let start = row_len * i + column_start;
107                        let mut column = data[start..start + column_len].to_vec();
108                        result.append(&mut column);
109                    }
110
111                    return_box!(result)
112                }
113                TFormType::I16 => {
114                    tform_macros::deserialize_column!(
115                        i16,
116                        num_rows,
117                        row_len,
118                        column_start,
119                        column_len,
120                        self.r,
121                        data,
122                    )
123                }
124                TFormType::I32 => {
125                    tform_macros::deserialize_column!(
126                        i32,
127                        num_rows,
128                        row_len,
129                        column_start,
130                        column_len,
131                        self.r,
132                        data,
133                    )
134                }
135                TFormType::Character => {
136                    unsafe fn deserialize_char(value: [u8; 4]) -> char {
137                        char::from_u32_unchecked(u32::from_be_bytes(value))
138                    }
139                    tform_macros::deserialize_column!(
140                        char,
141                        num_rows,
142                        row_len,
143                        column_start,
144                        column_len,
145                        self.r,
146                        data,
147                        deserialize_char,
148                    )
149                }
150                TFormType::F32 => {
151                    tform_macros::deserialize_column!(
152                        f32,
153                        num_rows,
154                        row_len,
155                        column_start,
156                        column_len,
157                        self.r,
158                        data,
159                    )
160                }
161                TFormType::F64 => {
162                    tform_macros::deserialize_column!(
163                        f64,
164                        num_rows,
165                        row_len,
166                        column_start,
167                        column_len,
168                        self.r,
169                        data,
170                    )
171                }
172                TFormType::C64 => todo!(),
173                TFormType::C128 => todo!(),
174                TFormType::ArrayDescriptor => todo!(),
175            }
176        }
177    }
178}
179
180#[macro_use]
181mod tform_macros {
182    macro_rules! deserialize_column {
184        (@dfn $value_type: ty) => {{
185            <$value_type>::from_be_bytes
186        }};
187        ($value_type: ty, $num_rows: expr, $row_len: expr, $column_start: expr, $column_len: expr, $repeats: expr, $data: expr,) => {{
188            let deserialize_fn = $crate::fits::header_value::tform::tform_macros::deserialize_column!(@dfn $value_type);
189            $crate::fits::header_value::tform::tform_macros::deserialize_column!(
190                $value_type,
191                $num_rows,
192                $row_len,
193                $column_start,
194                $column_len,
195                $repeats,
196                $data,
197                deserialize_fn,
198            )
199        }};
200        ($value_type: ty, $num_rows: expr, $row_len: expr, $column_start: expr, $column_len: expr, $repeats: expr, $data: expr, $deserialize_fn: tt,) => {{
201            let mut result = Vec::with_capacity($num_rows * $repeats);
202            for i in 0..$num_rows {
203                let start = $row_len * i + $column_start;
204                let column = $data[start..start + $column_len].to_vec();
205                let value_size = std::mem::size_of::<$value_type>();
206                for repeat in 0..$repeats {
207                    let value_start = repeat * value_size;
208                    let raw_value = column[value_start..value_start + value_size]
209                        .try_into()
210                        .unwrap();
211                    result.push($deserialize_fn(raw_value));
212                }
213            }
214
215            $crate::fits::hdu_macros::return_box!(result)
216        }};
217    }
218
219    pub(crate) use deserialize_column;
220}
221
222impl FitsHeaderValue for TForm {
249    fn from_bytes(raw: Vec<u8>) -> Result<Self, FitsHeaderError> {
250        let mut repeats = String::new();
251        let mut ttype = None;
252        let mut i = 1;
253        while i < raw.len() - 1 {
254            let ch = raw[i] as char;
255            if ch.is_ascii_digit() {
256                repeats.push(ch);
257                i += 1;
258            } else {
259                ttype = Some(TFormType::try_from(ch)?);
260                i += 1;
261                break;
262            }
263        }
264        let r = repeats.parse::<u32>().unwrap_or(1) as usize;
265        if let Some(t) = ttype {
266            if let Ok(a) = String::from_utf8(raw[i..raw.len() - 1].to_vec()) {
267                return Ok(TForm {
268                    r,
269                    t,
270                    a: a.trim_end().to_owned(),
271                });
272            }
273        }
274        Err(FitsHeaderError::DeserializationError {
275            found: raw,
276            intent: String::from("header card TFORM value"),
277        })
278    }
279
280    fn to_bytes(&self) -> [u8; 70] {
281        let mut result = [b' '; 70];
282        let mut i = 0;
283        result[i] = b'\'';
284        i += 1;
285        let repeats = self.r.to_string();
286        for b in repeats.bytes() {
287            result[i] = b;
288            i += 1;
289        }
290        match self.t {
291            TFormType::Logical => result[i] = b'L',
292            TFormType::Bit => result[i] = b'X',
293            TFormType::UnsignedByte => result[i] = b'B',
294            TFormType::I16 => result[i] = b'I',
295            TFormType::I32 => result[i] = b'J',
296            TFormType::Character => result[i] = b'A',
297            TFormType::F32 => result[i] = b'E',
298            TFormType::F64 => result[i] = b'D',
299            TFormType::C64 => result[i] = b'C',
300            TFormType::C128 => result[i] = b'M',
301            TFormType::ArrayDescriptor => result[i] = b'P',
302        }
303        i += 1;
304        for b in self.a.bytes() {
305            result[i] = b;
306            i += 1;
307        }
308        if i < 9 {
309            i = 9;
310        }
311        result[i] = b'\'';
312        result
313    }
314}