1use std::fs;
2use std::path::Path;
3
4#[derive(Debug, Clone)]
6pub struct MemoryRegion {
7 pub name: String,
8 pub origin: u64,
9 pub length: u64,
10}
11
12#[derive(Debug, Clone)]
14pub struct MemoryConfig {
15 pub regions: Vec<MemoryRegion>,
16}
17
18impl MemoryConfig {
19 pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self, String> {
30 let content = fs::read_to_string(path.as_ref())
31 .map_err(|e| format!("Failed to read {}: {}", path.as_ref().display(), e))?;
32 Self::parse(&content)
33 }
34
35 pub fn parse(content: &str) -> Result<Self, String> {
37 let mut regions = Vec::new();
38
39 for line in content.lines() {
40 let line = line.trim();
41
42 if line.is_empty()
44 || line.starts_with("/*")
45 || line.starts_with('*')
46 || line.starts_with("//")
47 || line.starts_with('#')
48 {
49 continue;
50 }
51
52 if line.starts_with("MEMORY") || line == "{" || line == "}" {
54 continue;
55 }
56
57 if let Some(region) = parse_region_line(line) {
61 regions.push(region);
62 }
63 }
64
65 if regions.is_empty() {
66 return Err("No memory regions found in memory.x".to_string());
67 }
68
69 Ok(MemoryConfig { regions })
70 }
71
72 pub fn find(&self, name: &str) -> Option<&MemoryRegion> {
74 let name_lower = name.to_lowercase();
75 self.regions.iter().find(|r| r.name.to_lowercase() == name_lower)
76 }
77
78 pub fn flash(&self) -> Option<&MemoryRegion> {
80 self.find("FLASH")
81 }
82
83 pub fn ram(&self) -> Option<&MemoryRegion> {
85 self.find("RAM")
86 }
87}
88
89fn parse_region_line(line: &str) -> Option<MemoryRegion> {
90 let colon_pos = line.find(':')?;
93 let name_part = line[..colon_pos].trim();
94
95 let name = if let Some(paren_pos) = name_part.find('(') {
97 name_part[..paren_pos].trim().to_string()
98 } else {
99 name_part.to_string()
100 };
101
102 let rest = &line[colon_pos + 1..];
103
104 let origin = parse_origin(rest)?;
106
107 let length = parse_length(rest)?;
109
110 Some(MemoryRegion { name, origin, length })
111}
112
113fn parse_origin(s: &str) -> Option<u64> {
114 let idx = s.find("ORIGIN")?;
115 let after = s[idx + "ORIGIN".len()..].trim();
116 let after = after.trim_start_matches('=').trim();
117 parse_number(after.split(|c: char| c == ',').next()?.trim())
118}
119
120fn parse_length(s: &str) -> Option<u64> {
121 let idx = s.find("LENGTH")?;
122 let after = s[idx + "LENGTH".len()..].trim();
123 let after = after.trim_start_matches('=').trim();
124 let val = after.split(|c: char| c == '}').next()?.trim();
125 let val = val.trim_end_matches(',').trim();
126 parse_size(val)
127}
128
129fn parse_number(s: &str) -> Option<u64> {
130 let s = s.trim();
131 if s.starts_with("0x") || s.starts_with("0X") {
132 u64::from_str_radix(&s[2..], 16).ok()
133 } else if s.starts_with("0o") || s.starts_with("0O") {
134 u64::from_str_radix(&s[2..], 8).ok()
135 } else if s.starts_with("0b") || s.starts_with("0B") {
136 u64::from_str_radix(&s[2..], 2).ok()
137 } else {
138 s.parse::<u64>().ok()
139 }
140}
141
142pub fn parse_size(s: &str) -> Option<u64> {
144 let s = s.trim().to_uppercase();
145 if s.ends_with('K') {
146 let num: f64 = s[..s.len() - 1].trim().parse().ok()?;
147 Some((num * 1024.0) as u64)
148 } else if s.ends_with('M') {
149 let num: f64 = s[..s.len() - 1].trim().parse().ok()?;
150 Some((num * 1024.0 * 1024.0) as u64)
151 } else {
152 s.parse::<u64>().ok()
153 }
154}
155
156#[cfg(test)]
157mod tests {
158 use super::*;
159
160 #[test]
161 fn test_parse_basic() {
162 let content = r#"
163/* Linker script for the STM32F103C8T6 */
164MEMORY
165{
166 FLASH : ORIGIN = 0x08000000, LENGTH = 64K
167 RAM : ORIGIN = 0x20000000, LENGTH = 20K
168}
169"#;
170 let config = MemoryConfig::parse(content).unwrap();
171 assert_eq!(config.regions.len(), 2);
172
173 let flash = config.flash().unwrap();
174 assert_eq!(flash.origin, 0x08000000);
175 assert_eq!(flash.length, 64 * 1024);
176
177 let ram = config.ram().unwrap();
178 assert_eq!(ram.origin, 0x20000000);
179 assert_eq!(ram.length, 20 * 1024);
180 }
181
182 #[test]
183 fn test_parse_with_flags() {
184 let content = r#"
185MEMORY
186{
187 FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 128K
188 RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K
189}
190"#;
191 let config = MemoryConfig::parse(content).unwrap();
192 let flash = config.flash().unwrap();
193 assert_eq!(flash.length, 128 * 1024);
194 }
195
196 #[test]
197 fn test_parse_size() {
198 assert_eq!(parse_size("64K"), Some(64 * 1024));
199 assert_eq!(parse_size("20K"), Some(20 * 1024));
200 assert_eq!(parse_size("1M"), Some(1024 * 1024));
201 assert_eq!(parse_size("512"), Some(512));
202 assert_eq!(parse_size("0.5K"), Some(512));
203 }
204}