ieee1212_config_rom/
leaf.rs

1// SPDX-License-Identifier: MIT
2// Copyright (c) 2020 Takashi Sakamoto
3
4//! Leaf entry has structured data. The module includes structure, enumeration and trait
5//! implementation to parse it. The structure implements TryFrom trait to convert from the
6//! content of leaf entry.
7//!
8//! Descriptor structure expresss descriptor itself. DescriptorData enumeration expresss data
9//! of descriptor. At present, Textual descriptor is supported. TextualDescriptorData expresss
10//! the data of Texual descriptor.
11//!
12//! EUI-64 structure expresss 64-bit EUI.
13//!
14//! Unit_Location structure expresss a pair of base address and upper bound for data of specific
15//! unit.
16
17use 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/// The structure expresss data of textual descriptor.
28#[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/// The enumeration expresss data of descriptor according to its type.
71#[derive(Debug, Clone, PartialEq, Eq)]
72pub enum DescriptorData<'a> {
73    Textual(TextualDescriptorData<'a>),
74    // NOTE: it's possible to implement icon type but I have no need.
75    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/// The structure expresss descriptor in content of leaf entry.
100#[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/// The error to parse leaf entry for descriptor data.
133#[derive(Debug, Clone, Copy, PartialEq, Eq)]
134pub enum DescriptorLeafParseError {
135    /// Insufficient data to parse.
136    TooShort(usize),
137    /// Failed to parse the leaf as text string.
138    InvalidTextString,
139    /// Unsupported type of descriptor.
140    UnsupportedType(
141        /// The value of descriptor type.
142        u8,
143    ),
144    /// The entry is not for leaf.
145    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/// The structure to express data of EUI-64 leaf.
160#[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/// The error to parse leaf entry with EUI64 data.
191#[derive(Debug, Clone, Copy, PartialEq, Eq)]
192pub enum Eui64LeafParseError {
193    /// Insufficient data to parse, 8 bytes at least.
194    TooShort(usize),
195    /// The entry is not for leaf.
196    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/// The structure to express data of unit location leaf.
209#[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/// The error to parse leaf entry for unit location.
255#[derive(Debug, Clone, Copy, PartialEq, Eq)]
256pub enum UnitLocationParseError {
257    /// Insufficient data to parse, 8 bytes at least.
258    TooShort(usize),
259    /// The entry is not for leaf.
260    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}