1use std::collections::HashMap;
4use std::fmt;
5
6pub use crate::file_writer::AttrValue;
7
8#[derive(Debug, Clone, PartialEq)]
13pub enum DType {
14 F32,
15 F64,
16 I8,
17 I16,
18 I32,
19 I64,
20 U8,
21 U16,
22 U32,
23 U64,
24 String,
25 Compound(Vec<(std::string::String, DType)>),
26 Enum(Vec<std::string::String>),
27 Array(Box<DType>, Vec<u32>),
28 VariableLengthString,
29 ObjectReference,
31 Other(std::string::String),
32}
33
34impl fmt::Display for DType {
35 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36 match self {
37 DType::F32 => write!(f, "f32"),
38 DType::F64 => write!(f, "f64"),
39 DType::I8 => write!(f, "i8"),
40 DType::I16 => write!(f, "i16"),
41 DType::I32 => write!(f, "i32"),
42 DType::I64 => write!(f, "i64"),
43 DType::U8 => write!(f, "u8"),
44 DType::U16 => write!(f, "u16"),
45 DType::U32 => write!(f, "u32"),
46 DType::U64 => write!(f, "u64"),
47 DType::String => write!(f, "string"),
48 DType::VariableLengthString => write!(f, "vlen_string"),
49 DType::ObjectReference => write!(f, "object_ref"),
50 DType::Compound(fields) => {
51 write!(f, "compound{{")?;
52 for (i, (name, dt)) in fields.iter().enumerate() {
53 if i > 0 {
54 write!(f, ", ")?;
55 }
56 write!(f, "{name}: {dt}")?;
57 }
58 write!(f, "}}")
59 }
60 DType::Enum(names) => write!(f, "enum[{}]", names.join(", ")),
61 DType::Array(base, dims) => write!(f, "array<{base}, {dims:?}>"),
62 DType::Other(desc) => write!(f, "other({desc})"),
63 }
64 }
65}
66
67pub(crate) fn classify_datatype(dt: &crate::datatype::Datatype) -> DType {
69 use crate::datatype::Datatype;
70
71 match dt {
72 Datatype::FloatingPoint { size: 4, .. } => DType::F32,
73 Datatype::FloatingPoint { size: 8, .. } => DType::F64,
74 Datatype::FloatingPoint { size, .. } => DType::Other(format!("float{}", size * 8)),
75 Datatype::FixedPoint {
76 size: 1,
77 signed: true,
78 ..
79 } => DType::I8,
80 Datatype::FixedPoint {
81 size: 2,
82 signed: true,
83 ..
84 } => DType::I16,
85 Datatype::FixedPoint {
86 size: 4,
87 signed: true,
88 ..
89 } => DType::I32,
90 Datatype::FixedPoint {
91 size: 8,
92 signed: true,
93 ..
94 } => DType::I64,
95 Datatype::FixedPoint {
96 size: 1,
97 signed: false,
98 ..
99 } => DType::U8,
100 Datatype::FixedPoint {
101 size: 2,
102 signed: false,
103 ..
104 } => DType::U16,
105 Datatype::FixedPoint {
106 size: 4,
107 signed: false,
108 ..
109 } => DType::U32,
110 Datatype::FixedPoint {
111 size: 8,
112 signed: false,
113 ..
114 } => DType::U64,
115 Datatype::FixedPoint { size, signed, .. } => {
116 let prefix = if *signed { "i" } else { "u" };
117 DType::Other(format!("{prefix}{}", size * 8))
118 }
119 Datatype::String { .. } => DType::String,
120 Datatype::VariableLength {
121 is_string: true, ..
122 } => DType::VariableLengthString,
123 Datatype::Compound { members, .. } => {
124 let fields = members
125 .iter()
126 .map(|m| (m.name.clone(), classify_datatype(&m.datatype)))
127 .collect();
128 DType::Compound(fields)
129 }
130 Datatype::Enumeration { members, .. } => {
131 let names = members.iter().map(|m| m.name.clone()).collect();
132 DType::Enum(names)
133 }
134 Datatype::Array {
135 base_type,
136 dimensions,
137 } => DType::Array(Box::new(classify_datatype(base_type)), dimensions.clone()),
138 Datatype::Reference {
139 ref_type: crate::datatype::ReferenceType::Object,
140 ..
141 } => DType::ObjectReference,
142 _ => DType::Other(format!("{dt:?}")),
143 }
144}
145
146pub(crate) fn attrs_to_map<S: crate::source::FileSource + ?Sized>(
153 attrs: &[crate::attribute::AttributeMessage],
154 source: &S,
155 offset_size: u8,
156 length_size: u8,
157 base_address: u64,
158) -> HashMap<std::string::String, AttrValue> {
159 let mut map = HashMap::new();
160 for attr in attrs {
161 if let Some(val) = decode_attr_value(attr, source, offset_size, length_size, base_address) {
162 map.insert(attr.name.clone(), val);
163 }
164 }
165 map
166}
167
168fn decode_attr_value<S: crate::source::FileSource + ?Sized>(
169 attr: &crate::attribute::AttributeMessage,
170 source: &S,
171 offset_size: u8,
172 length_size: u8,
173 base_address: u64,
174) -> Option<AttrValue> {
175 use crate::datatype::Datatype;
176
177 match &attr.datatype {
178 Datatype::FloatingPoint { .. } => {
179 let vals = attr.read_as_f64().ok()?;
180 if vals.len() == 1 {
181 Some(AttrValue::F64(vals[0]))
182 } else {
183 Some(AttrValue::F64Array(vals))
184 }
185 }
186 Datatype::FixedPoint { signed: true, .. } => {
187 let vals = attr.read_as_i64().ok()?;
188 if vals.len() == 1 {
189 Some(AttrValue::I64(vals[0]))
190 } else {
191 Some(AttrValue::I64Array(vals))
192 }
193 }
194 Datatype::FixedPoint { signed: false, .. } => {
195 let vals = attr.read_as_u64().ok()?;
196 if vals.len() == 1 {
197 Some(AttrValue::U64(vals[0]))
198 } else {
199 #[expect(
201 clippy::cast_possible_wrap,
202 reason = "no U64Array AttrValue variant; values above i64::MAX are \
203 reinterpreted as i64 by design (bit pattern preserved)"
204 )]
205 let i64_vals: Vec<i64> = vals.iter().map(|&v| v as i64).collect();
206 Some(AttrValue::I64Array(i64_vals))
207 }
208 }
209 Datatype::String { .. } => {
210 let strings = attr.read_as_strings().ok()?;
211 if strings.len() == 1 {
212 Some(AttrValue::String(strings[0].clone()))
213 } else {
214 Some(AttrValue::StringArray(strings))
215 }
216 }
217 Datatype::VariableLength {
218 is_string,
219 base_type,
220 ..
221 } if *is_string || is_ascii_char_vlen_base(base_type) => {
222 let strings = crate::vl_data::read_vl_strings_from_source(
231 source,
232 &attr.raw_data,
233 attr.dataspace.num_elements(),
234 offset_size,
235 length_size,
236 base_address,
237 crate::vl_data::VlenStringReadOptions::default(),
238 )
239 .ok()?;
240 if strings.len() == 1 {
241 Some(AttrValue::String(strings[0].clone()))
242 } else {
243 Some(AttrValue::StringArray(strings))
244 }
245 }
246 _ => None,
247 }
248}
249
250fn is_ascii_char_vlen_base(base: &crate::datatype::Datatype) -> bool {
255 use crate::datatype::{CharacterSet, Datatype};
256 matches!(
257 base,
258 Datatype::String {
259 size: 1,
260 charset: CharacterSet::Ascii,
261 ..
262 }
263 )
264}