1use crate::bin_table::Value;
2use crate::header::{Header, TableColumnFormat};
3use std::error::Error;
4
5fn parse_dimensions(value: &str) -> Vec<usize> {
11 let Some(inner) = value
12 .trim()
13 .strip_prefix('(')
14 .and_then(|value| value.strip_suffix(')'))
15 else {
16 return Vec::new();
17 };
18
19 inner
20 .split(',')
21 .map(|axis| axis.trim().parse::<usize>())
22 .collect::<Result<Vec<_>, _>>()
23 .unwrap_or_default()
24}
25
26#[derive(Debug, Clone, PartialEq)]
35pub struct FieldDefinition {
36 pub format: TableColumnFormat,
38 pub offset: usize,
40 pub name: String,
42 pub scale: Option<f64>,
44 pub zero: Option<f64>,
46 pub null: Option<i64>,
48 pub dimensions: Vec<usize>,
54}
55
56impl FieldDefinition {
57 pub fn all_from_header(header: &Header) -> Result<Vec<Self>, Box<dyn Error + Send + Sync>> {
60 let table_fields = header
61 .table_fields()
62 .ok_or("Table header is missing its TFIELDS card")?;
63 let table_fields = usize::try_from(table_fields)
64 .map_err(|_| format!("TFIELDS must not be negative, but was {}", table_fields))?;
65
66 let mut offset = 0;
67
68 (0..table_fields)
69 .map(|index| {
70 let format = header.table_column_format(index).ok_or_else(|| {
73 format!("Table header is missing its TFORM{} card", index + 1)
74 })?;
75
76 let name = header
79 .table_column_type(index)
80 .unwrap_or_default()
81 .to_string();
82
83 let field = Self {
84 format,
85 offset,
86 name,
87 scale: header.table_scaling_factor(index),
88 zero: header.table_scaling_zero_point(index),
89 null: header
90 .table_null_value(index)
91 .and_then(|null| null.as_integer()),
92 dimensions: header
93 .table_dimensions(index)
94 .map(parse_dimensions)
95 .unwrap_or_default(),
96 };
97 offset += format.bytes_len();
98
99 Ok::<_, Box<dyn Error + Send + Sync>>(field)
100 })
101 .collect()
102 }
103
104 pub fn decode(&self, row: &[u8], heap: &[u8]) -> crate::Result<Value> {
109 let data = row.get(self.offset..).ok_or_else(|| {
110 crate::Error::DeserializationError(format!(
111 "Column {} starts at byte {} of a {} byte row",
112 self.name,
113 self.offset,
114 row.len()
115 ))
116 })?;
117
118 let value = self.format.parse_into_value(data, heap)?;
119
120 if self.is_null(&value) {
121 return Ok(Value::Null);
122 }
123
124 Ok(self.scaled(value))
125 }
126
127 fn is_null(&self, value: &Value) -> bool {
136 let Some(null) = self.null else {
137 return false;
138 };
139
140 fn all_equal<T: Copy + Into<i64>>(values: &[T], null: i64) -> bool {
141 !values.is_empty() && values.iter().all(|value| (*value).into() == null)
142 }
143
144 match value {
145 Value::U8(values) => all_equal(values, null),
146 Value::I8(values) => all_equal(values, null),
147 Value::U16(values) => all_equal(values, null),
148 Value::I16(values) => all_equal(values, null),
149 Value::U32(values) => all_equal(values, null),
150 Value::I32(values) => all_equal(values, null),
151 Value::I64(values) => all_equal(values, null),
152 _ => false,
153 }
154 }
155
156 fn scaled(&self, value: Value) -> Value {
158 let scale = self.scale.unwrap_or(1.0);
159 let zero = self.zero.unwrap_or(0.0);
160
161 if scale == 1.0 && zero == 0.0 {
162 return value;
163 }
164
165 if scale == 1.0 {
176 match (&value, zero) {
177 (Value::U8(values), -128.0) => {
178 return Value::I8(
179 values
180 .iter()
181 .map(|v| v.wrapping_sub(1 << 7) as i8)
182 .collect(),
183 );
184 }
185 (Value::I16(values), 32768.0) => {
186 return Value::U16(
187 values
188 .iter()
189 .map(|v| (*v as u16).wrapping_add(1 << 15))
190 .collect(),
191 );
192 }
193 (Value::I32(values), 2147483648.0) => {
194 return Value::U32(
195 values
196 .iter()
197 .map(|v| (*v as u32).wrapping_add(1 << 31))
198 .collect(),
199 );
200 }
201 (Value::I64(values), 9223372036854775808.0) => {
202 return Value::U64(
203 values
204 .iter()
205 .map(|v| (*v as u64).wrapping_add(1 << 63))
206 .collect(),
207 );
208 }
209 _ => {}
210 }
211 }
212
213 let apply = |raw: f64| zero + scale * raw;
214
215 match value {
216 Value::U8(values) => Value::F64(values.into_iter().map(|v| apply(v as f64)).collect()),
217 Value::I8(values) => Value::F64(values.into_iter().map(|v| apply(v as f64)).collect()),
218 Value::U16(values) => Value::F64(values.into_iter().map(|v| apply(v as f64)).collect()),
219 Value::I16(values) => Value::F64(values.into_iter().map(|v| apply(v as f64)).collect()),
220 Value::U32(values) => Value::F64(values.into_iter().map(|v| apply(v as f64)).collect()),
221 Value::I32(values) => Value::F64(values.into_iter().map(|v| apply(v as f64)).collect()),
222 Value::I64(values) => Value::F64(values.into_iter().map(|v| apply(v as f64)).collect()),
223 Value::U64(values) => Value::F64(values.into_iter().map(|v| apply(v as f64)).collect()),
224 Value::F32(values) => Value::F64(values.into_iter().map(|v| apply(v as f64)).collect()),
225 Value::F64(values) => Value::F64(values.into_iter().map(apply).collect()),
226
227 Value::C32(values) => Value::C32(
229 values
230 .into_iter()
231 .map(|(re, im)| (apply(re as f64) as f32, apply(im as f64) as f32))
232 .collect(),
233 ),
234 Value::M64(values) => Value::M64(
235 values
236 .into_iter()
237 .map(|(re, im)| (apply(re), apply(im)))
238 .collect(),
239 ),
240
241 value @ (Value::String(_)
243 | Value::StringArray(_)
244 | Value::Boolean(_)
245 | Value::Bit { .. }
246 | Value::Null) => value,
247 }
248 }
249}
250
251#[cfg(test)]
252mod tests {
253 use super::parse_dimensions;
254
255 #[test]
256 fn a_tdim_card_gives_the_shape_of_a_column_entry() {
257 assert_eq!(parse_dimensions("(2,3)"), vec![2, 3]);
258 assert_eq!(parse_dimensions("(4, 5, 6)"), vec![4, 5, 6]);
259 assert_eq!(parse_dimensions(" (7) "), vec![7]);
260 }
261
262 #[test]
263 fn a_malformed_tdim_card_is_dropped_rather_than_raised() {
264 assert!(parse_dimensions("2,3").is_empty());
267 assert!(parse_dimensions("(2,x)").is_empty());
268 assert!(parse_dimensions("").is_empty());
269 }
270}