1use super::*;
18
19fn detect_leaf_from_entry<'a>(entry: &'a Entry<'a>) -> Option<&'a [u8]> {
20 if let EntryData::Leaf(leaf) = entry.data {
21 Some(leaf)
22 } else {
23 None
24 }
25}
26
27#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
29pub struct TextualDescriptorData<'a> {
30 pub width: u8,
31 pub character_set: u16,
32 pub language: u16,
33 pub text: &'a str,
34}
35
36impl<'a> TryFrom<&'a [u8]> for TextualDescriptorData<'a> {
37 type Error = DescriptorLeafParseError;
38
39 fn try_from(raw: &'a [u8]) -> Result<Self, Self::Error> {
40 if raw.len() < 4 {
41 Err(Self::Error::TooShort(raw.len()))
42 } else {
43 let mut quadlet = [0; 4];
44 quadlet.copy_from_slice(&raw[..4]);
45 let meta = u32::from_be_bytes(quadlet);
46 let width = ((meta & 0xf0000000) >> 28) as u8;
47 let character_set = ((meta & 0x0fff0000) >> 16) as u16;
48 let language = (meta & 0x0000ffff) as u16;
49 let literal = &raw[4..];
50 literal
51 .iter()
52 .position(|&c| c == 0x00)
53 .ok_or(Self::Error::InvalidTextString)
54 .and_then(|pos| {
55 std::str::from_utf8(&literal[..pos]).map_err(|_| Self::Error::InvalidTextString)
56 })
57 .or_else(|_| {
58 std::str::from_utf8(literal).map_err(|_| Self::Error::InvalidTextString)
59 })
60 .map(|text| TextualDescriptorData {
61 width,
62 character_set,
63 language,
64 text,
65 })
66 }
67 }
68}
69
70#[derive(Debug, Clone, PartialEq, Eq)]
72pub enum DescriptorData<'a> {
73 Textual(TextualDescriptorData<'a>),
74 Reserved(&'a [u8]),
76}
77
78impl<'a> DescriptorData<'a> {
79 const TEXTUAL_DESCRIPTOR_TYPE: u8 = 0;
80}
81
82impl<'a> TryFrom<&'a [u8]> for DescriptorData<'a> {
83 type Error = DescriptorLeafParseError;
84
85 fn try_from(raw: &'a [u8]) -> Result<Self, Self::Error> {
86 match raw[0] {
87 Self::TEXTUAL_DESCRIPTOR_TYPE => {
88 if raw.len() < 4 {
89 Err(Self::Error::TooShort(raw.len()))
90 } else {
91 TextualDescriptorData::try_from(&raw[4..]).map(|d| Self::Textual(d))
92 }
93 }
94 _ => Err(Self::Error::UnsupportedType(raw[0])),
95 }
96 }
97}
98
99#[derive(Debug, Clone, PartialEq, Eq)]
101pub struct DescriptorLeaf<'a> {
102 pub spec_id: u32,
103 pub data: DescriptorData<'a>,
104}
105
106impl<'a> TryFrom<&'a [u8]> for DescriptorLeaf<'a> {
107 type Error = DescriptorLeafParseError;
108
109 fn try_from(raw: &'a [u8]) -> Result<Self, Self::Error> {
110 if raw.len() < 4 {
111 Err(Self::Error::TooShort(raw.len()))
112 } else {
113 let mut quadlet = [0; 4];
114 quadlet.copy_from_slice(&raw[..4]);
115 let spec_id = u32::from_be_bytes(quadlet) & 0x00ffffff;
116
117 DescriptorData::try_from(raw).map(|data| Self { spec_id, data })
118 }
119 }
120}
121
122impl<'a> TryFrom<&'a Entry<'a>> for DescriptorLeaf<'a> {
123 type Error = DescriptorLeafParseError;
124
125 fn try_from(entry: &'a Entry<'a>) -> Result<Self, Self::Error> {
126 detect_leaf_from_entry(entry)
127 .ok_or_else(|| Self::Error::WrongDirectoryEntry)
128 .and_then(|leaf| Self::try_from(leaf))
129 }
130}
131
132#[derive(Debug, Clone, Copy, PartialEq, Eq)]
134pub enum DescriptorLeafParseError {
135 TooShort(usize),
137 InvalidTextString,
139 UnsupportedType(
141 u8,
143 ),
144 WrongDirectoryEntry,
146}
147
148impl std::fmt::Display for DescriptorLeafParseError {
149 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
150 match self {
151 Self::TooShort(length) => write!(f, "The length of leaf {} is too short", length),
152 Self::InvalidTextString => write!(f, "invalid text string in leaf"),
153 Self::UnsupportedType(desc_type) => write!(f, "unsupported type {}", desc_type),
154 Self::WrongDirectoryEntry => write!(f, "wrong directory entry"),
155 }
156 }
157}
158
159#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
161pub struct Eui64Leaf(pub u64);
162
163impl TryFrom<&[u8]> for Eui64Leaf {
164 type Error = Eui64LeafParseError;
165
166 fn try_from(raw: &[u8]) -> Result<Self, Self::Error> {
167 if raw.len() < 8 {
168 Err(Self::Error::TooShort(raw.len()))
169 } else {
170 let mut quadlet = [0; 4];
171 quadlet.copy_from_slice(&raw[..4]);
172 let high = u32::from_be_bytes(quadlet) as u64;
173 quadlet.copy_from_slice(&raw[4..8]);
174 let low = u32::from_be_bytes(quadlet) as u64;
175 Ok(Eui64Leaf((high << 32) | low))
176 }
177 }
178}
179
180impl<'a> TryFrom<&Entry<'a>> for Eui64Leaf {
181 type Error = Eui64LeafParseError;
182
183 fn try_from(entry: &Entry<'a>) -> Result<Self, Self::Error> {
184 detect_leaf_from_entry(entry)
185 .ok_or_else(|| Self::Error::WrongDirectoryEntry)
186 .and_then(|leaf| Eui64Leaf::try_from(leaf))
187 }
188}
189
190#[derive(Debug, Clone, Copy, PartialEq, Eq)]
192pub enum Eui64LeafParseError {
193 TooShort(usize),
195 WrongDirectoryEntry,
197}
198
199impl std::fmt::Display for Eui64LeafParseError {
200 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
201 match self {
202 Self::TooShort(length) => write!(f, "The length of leaf {} too short", length),
203 Self::WrongDirectoryEntry => write!(f, "wrong directory entry"),
204 }
205 }
206}
207
208#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
210pub struct UnitLocationLeaf {
211 pub base_addr: u64,
212 pub upper_bound: u64,
213}
214
215impl TryFrom<&[u8]> for UnitLocationLeaf {
216 type Error = UnitLocationParseError;
217
218 fn try_from(raw: &[u8]) -> Result<Self, Self::Error> {
219 if raw.len() < 16 {
220 Err(Self::Error::TooShort(raw.len()))
221 } else {
222 let mut quadlet = [0; 4];
223
224 quadlet.copy_from_slice(&raw[..4]);
225 let high = u32::from_be_bytes(quadlet) as u64;
226 quadlet.copy_from_slice(&raw[4..8]);
227 let low = u32::from_be_bytes(quadlet) as u64;
228 let base_addr = (high << 32) | low;
229
230 quadlet.copy_from_slice(&raw[8..12]);
231 let high = u32::from_be_bytes(quadlet) as u64;
232 quadlet.copy_from_slice(&raw[12..16]);
233 let low = u32::from_be_bytes(quadlet) as u64;
234 let upper_bound = (high << 32) | low;
235
236 Ok(UnitLocationLeaf {
237 base_addr,
238 upper_bound,
239 })
240 }
241 }
242}
243
244impl<'a> TryFrom<&Entry<'a>> for UnitLocationLeaf {
245 type Error = UnitLocationParseError;
246
247 fn try_from(entry: &Entry<'a>) -> Result<Self, Self::Error> {
248 detect_leaf_from_entry(entry)
249 .ok_or_else(|| Self::Error::WrongDirectoryEntry)
250 .and_then(|leaf| UnitLocationLeaf::try_from(leaf))
251 }
252}
253
254#[derive(Debug, Clone, Copy, PartialEq, Eq)]
256pub enum UnitLocationParseError {
257 TooShort(usize),
259 WrongDirectoryEntry,
261}
262
263impl std::fmt::Display for UnitLocationParseError {
264 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
265 match self {
266 Self::TooShort(length) => write!(f, "The length of leaf {} is too short", length),
267 Self::WrongDirectoryEntry => write!(f, "wrong directory entry"),
268 }
269 }
270}
271
272#[cfg(test)]
273mod test {
274 use super::leaf::*;
275
276 #[test]
277 fn textual_desc_from_leaf_entry() {
278 let raw = [
279 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20,
280 0x46, 0x69, 0x72, 0x65, 0x77, 0x69, 0x72, 0x65, 0x00, 0x00,
281 ];
282 let entry = Entry {
283 key: KeyType::Descriptor,
284 data: EntryData::Leaf(&raw[..]),
285 };
286 let desc = DescriptorLeaf::try_from(&entry).unwrap();
287 assert_eq!(0, desc.spec_id);
288 if let DescriptorData::Textual(d) = desc.data {
289 assert_eq!(0, d.width);
290 assert_eq!(0, d.character_set);
291 assert_eq!(0, d.language);
292 assert_eq!(&"Linux Firewire", &d.text);
293 }
294 }
295
296 #[test]
297 fn eui64_from_leaf_entry() {
298 let raw = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07];
299 let entry = Entry {
300 key: KeyType::Eui64,
301 data: EntryData::Leaf(&raw[..]),
302 };
303 let eui64 = Eui64Leaf::try_from(&entry).unwrap();
304 assert_eq!(0x0001020304050607, eui64.0);
305 }
306
307 #[test]
308 fn unit_location_from_leaf_entry() {
309 let raw = [
310 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
311 0x0e, 0x0f,
312 ];
313 let entry = Entry {
314 key: KeyType::UnitLocation,
315 data: EntryData::Leaf(&raw[..]),
316 };
317 let unit_location = UnitLocationLeaf::try_from(&entry).unwrap();
318 assert_eq!(0x0001020304050607, unit_location.base_addr);
319 assert_eq!(0x08090a0b0c0d0e0f, unit_location.upper_bound);
320 }
321}