dfu_core/
memory_layout.rs

1#[cfg(any(feature = "std", test))]
2use displaydoc::Display;
3#[cfg(any(feature = "std", test))]
4use std::prelude::v1::*;
5#[cfg(any(feature = "std", test))]
6use thiserror::Error;
7
8/// Error while parsing a memory layout.
9#[cfg(any(feature = "std", test))]
10#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
11#[derive(Debug, Display, Error)]
12pub enum Error {
13    /// invalid page format: {0}
14    InvalidPageFormat(String),
15    /// could not parse page count: {0}
16    ParseErrorPageCount(String),
17    /// could not parse page size: {0}
18    ParseErrorPageSize(String),
19    /// invalid prefix: {0}
20    InvalidPrefix(String),
21}
22
23/// A memory page size.
24pub type MemoryPage = u32;
25
26/// A slice of memory pages.
27#[allow(non_camel_case_types)]
28pub type mem = [MemoryPage];
29
30/// Memory layout.
31#[cfg(any(feature = "std", test))]
32#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
33pub struct MemoryLayout(Vec<MemoryPage>);
34
35#[cfg(any(feature = "std", test))]
36impl AsRef<mem> for MemoryLayout {
37    fn as_ref(&self) -> &mem {
38        self.0.as_slice()
39    }
40}
41
42#[cfg(any(feature = "std", test))]
43impl MemoryLayout {
44    /// Create a new empty instance of [`MemoryLayout`].
45    pub fn new() -> Self {
46        Self(Vec::new())
47    }
48}
49
50#[cfg(any(feature = "std", test))]
51impl Default for MemoryLayout {
52    fn default() -> Self {
53        Self::new()
54    }
55}
56
57#[cfg(any(feature = "std", test))]
58impl From<Vec<MemoryPage>> for MemoryLayout {
59    fn from(vec: Vec<MemoryPage>) -> Self {
60        Self(vec)
61    }
62}
63
64#[cfg(any(feature = "std", test))]
65impl core::ops::Deref for MemoryLayout {
66    type Target = Vec<MemoryPage>;
67
68    fn deref(&self) -> &Self::Target {
69        &self.0
70    }
71}
72
73#[cfg(any(feature = "std", test))]
74impl core::ops::DerefMut for MemoryLayout {
75    fn deref_mut(&mut self) -> &mut Self::Target {
76        &mut self.0
77    }
78}
79
80#[cfg(any(feature = "std", test))]
81impl core::convert::TryFrom<&str> for MemoryLayout {
82    type Error = Error;
83
84    fn try_from(src: &str) -> Result<Self, Self::Error> {
85        use core::str::FromStr;
86
87        let mut pages = Vec::new();
88
89        for s in src.split(',') {
90            let (count, size) = s
91                .split_once('*')
92                .ok_or_else(|| Error::InvalidPageFormat(s.into()))?;
93            let (size, prefix) = size.split_at(
94                size.len()
95                    .checked_sub(2)
96                    .ok_or_else(|| Error::ParseErrorPageSize(size.into()))?,
97            );
98
99            let count =
100                u32::from_str(count).map_err(|_| Error::ParseErrorPageCount(count.into()))?;
101            let size = u32::from_str(size).map_err(|_| Error::ParseErrorPageSize(size.into()))?;
102            let prefix = match &prefix[..1] {
103                "K" => 1024,
104                "M" => 1024 * 1024,
105                " " => 1,
106                other => return Err(Error::InvalidPrefix(other.into())),
107            };
108
109            let size = size * prefix;
110            for _ in 0..count {
111                pages.push(size);
112            }
113        }
114
115        Ok(Self(pages))
116    }
117}
118
119#[cfg(test)]
120mod tests {
121    use super::*;
122    use core::convert::TryFrom;
123
124    #[test]
125    fn parsing() {
126        let s = "04*032Kg,01*128Kg,07*256Kg";
127        let m = MemoryLayout::try_from(s).unwrap();
128        assert_eq!(
129            m.as_slice(),
130            &[
131                32768, 32768, 32768, 32768, 131072, 262144, 262144, 262144, 262144, 262144, 262144,
132                262144
133            ]
134        );
135    }
136
137    #[test]
138    fn parsing_stm32_defuse_extensions() {
139        let s = "4*32Kg,1*128Kg";
140        let m = MemoryLayout::try_from(s).unwrap();
141        assert_eq!(m.as_slice(), &[32768, 32768, 32768, 32768, 131072]);
142    }
143}