1extern crate alloc;
6
7use alloc::string::String;
8use alloc::vec;
9use alloc::vec::Vec;
10
11use super::extent::{Extent, FileType, Timestamps};
12
13#[derive(Debug, Clone)]
19pub struct FileLayout {
20 pub name: String,
22 pub extent: Extent,
24 pub file_type: FileType,
26 pub timestamps: Timestamps,
28 pub attributes: u32,
30 pub symlink_target: Option<String>,
32}
33
34impl FileLayout {
35 pub fn new(name: impl Into<String>, extent: Extent) -> Self {
37 Self {
38 name: name.into(),
39 extent,
40 file_type: FileType::RegularFile,
41 timestamps: Timestamps::default(),
42 attributes: 0,
43 symlink_target: None,
44 }
45 }
46
47 pub fn with_type(mut self, file_type: FileType) -> Self {
49 self.file_type = file_type;
50 self
51 }
52
53 pub fn with_timestamps(mut self, timestamps: Timestamps) -> Self {
55 self.timestamps = timestamps;
56 self
57 }
58
59 pub fn with_attributes(mut self, attributes: u32) -> Self {
61 self.attributes = attributes;
62 self
63 }
64
65 pub fn with_symlink_target(mut self, target: impl Into<String>) -> Self {
67 self.symlink_target = Some(target.into());
68 self
69 }
70
71 #[inline]
73 pub fn size(&self) -> u64 {
74 self.extent.length
75 }
76}
77
78#[derive(Debug, Clone, Default)]
83pub struct DirectoryLayout {
84 pub name: String,
86 pub files: Vec<FileLayout>,
88 pub subdirs: Vec<DirectoryLayout>,
90 pub timestamps: Timestamps,
92 pub attributes: u32,
94 pub extent: Option<Extent>,
96}
97
98impl DirectoryLayout {
99 pub fn new(name: impl Into<String>) -> Self {
101 Self {
102 name: name.into(),
103 files: Vec::new(),
104 subdirs: Vec::new(),
105 timestamps: Timestamps::default(),
106 attributes: 0,
107 extent: None,
108 }
109 }
110
111 pub fn root() -> Self {
113 Self::new("")
114 }
115
116 pub fn add_file(&mut self, file: FileLayout) {
118 self.files.push(file);
119 }
120
121 pub fn add_subdir(&mut self, subdir: DirectoryLayout) {
123 self.subdirs.push(subdir);
124 }
125
126 pub fn with_timestamps(mut self, timestamps: Timestamps) -> Self {
128 self.timestamps = timestamps;
129 self
130 }
131
132 pub fn with_extent(mut self, extent: Extent) -> Self {
134 self.extent = Some(extent);
135 self
136 }
137
138 #[inline]
140 pub fn file_count(&self) -> usize {
141 self.files.len()
142 }
143
144 #[inline]
146 pub fn subdir_count(&self) -> usize {
147 self.subdirs.len()
148 }
149
150 #[inline]
152 pub fn entry_count(&self) -> usize {
153 self.files.len() + self.subdirs.len()
154 }
155
156 pub fn iter_files(&self) -> impl Iterator<Item = (&str, &FileLayout)> {
158 FileIterator::new(self)
159 }
160
161 pub fn find_file(&self, path: &str) -> Option<&FileLayout> {
163 let parts: Vec<&str> = path.split('/').filter(|s| !s.is_empty()).collect();
164 self.find_file_parts(&parts)
165 }
166
167 fn find_file_parts(&self, parts: &[&str]) -> Option<&FileLayout> {
169 if parts.is_empty() {
170 return None;
171 }
172
173 if parts.len() == 1 {
174 self.files.iter().find(|f| f.name == parts[0])
176 } else {
177 self.subdirs
179 .iter()
180 .find(|d| d.name == parts[0])
181 .and_then(|d| d.find_file_parts(&parts[1..]))
182 }
183 }
184
185 pub fn find_file_mut(&mut self, path: &str) -> Option<&mut FileLayout> {
187 let parts: Vec<&str> = path.split('/').filter(|s| !s.is_empty()).collect();
188 self.find_file_parts_mut(&parts)
189 }
190
191 fn find_file_parts_mut(&mut self, parts: &[&str]) -> Option<&mut FileLayout> {
193 if parts.is_empty() {
194 return None;
195 }
196
197 if parts.len() == 1 {
198 self.files.iter_mut().find(|f| f.name == parts[0])
199 } else {
200 self.subdirs
201 .iter_mut()
202 .find(|d| d.name == parts[0])
203 .and_then(|d| d.find_file_parts_mut(&parts[1..]))
204 }
205 }
206
207 pub fn get_or_create_dir(&mut self, path: &str) -> &mut DirectoryLayout {
209 let parts: Vec<&str> = path.split('/').filter(|s| !s.is_empty()).collect();
210 self.get_or_create_dir_parts(&parts)
211 }
212
213 fn get_or_create_dir_parts(&mut self, parts: &[&str]) -> &mut DirectoryLayout {
215 if parts.is_empty() {
216 return self;
217 }
218
219 let name = parts[0];
220
221 let idx = self.subdirs.iter().position(|d| d.name == name);
223 let idx = match idx {
224 Some(i) => i,
225 None => {
226 self.subdirs.push(DirectoryLayout::new(name));
227 self.subdirs.len() - 1
228 }
229 };
230
231 self.subdirs[idx].get_or_create_dir_parts(&parts[1..])
232 }
233
234 pub fn remove_file(&mut self, path: &str) -> Option<FileLayout> {
236 let parts: Vec<&str> = path.split('/').filter(|s| !s.is_empty()).collect();
237 self.remove_file_parts(&parts)
238 }
239
240 fn remove_file_parts(&mut self, parts: &[&str]) -> Option<FileLayout> {
242 if parts.is_empty() {
243 return None;
244 }
245
246 if parts.len() == 1 {
247 let idx = self.files.iter().position(|f| f.name == parts[0])?;
248 Some(self.files.remove(idx))
249 } else {
250 self.subdirs
251 .iter_mut()
252 .find(|d| d.name == parts[0])
253 .and_then(|d| d.remove_file_parts(&parts[1..]))
254 }
255 }
256}
257
258struct FileIterator<'a> {
260 stack: Vec<(&'a str, &'a DirectoryLayout, usize, usize)>,
261 path_prefix: String,
262}
263
264impl<'a> FileIterator<'a> {
265 fn new(root: &'a DirectoryLayout) -> Self {
266 Self {
267 stack: vec![("", root, 0, 0)],
268 path_prefix: String::new(),
269 }
270 }
271}
272
273impl<'a> Iterator for FileIterator<'a> {
274 type Item = (&'a str, &'a FileLayout);
275
276 fn next(&mut self) -> Option<Self::Item> {
277 while let Some((name, dir, file_idx, subdir_idx)) = self.stack.pop() {
278 if !name.is_empty() {
280 if !self.path_prefix.is_empty() {
281 self.path_prefix.push('/');
282 }
283 self.path_prefix.push_str(name);
284 }
285
286 if file_idx < dir.files.len() {
288 self.stack.push((name, dir, file_idx + 1, subdir_idx));
290 let file = &dir.files[file_idx];
291 return Some((&file.name, file));
294 }
295
296 if subdir_idx < dir.subdirs.len() {
298 self.stack.push((name, dir, file_idx, subdir_idx + 1));
300 let subdir = &dir.subdirs[subdir_idx];
301 self.stack.push((&subdir.name, subdir, 0, 0));
302 continue;
303 }
304
305 if !name.is_empty() {
307 if let Some(idx) = self.path_prefix.rfind('/') {
308 self.path_prefix.truncate(idx);
309 } else {
310 self.path_prefix.clear();
311 }
312 }
313 }
314 None
315 }
316}
317
318#[derive(Debug, Clone)]
323pub struct AllocationMap {
324 bitmap: Vec<u8>,
326 total_sectors: u32,
328 next_free: u32,
330}
331
332impl AllocationMap {
333 pub fn new(total_sectors: u32) -> Self {
335 let bitmap_size = (total_sectors as usize).div_ceil(8);
336 Self {
337 bitmap: alloc::vec![0u8; bitmap_size],
338 total_sectors,
339 next_free: 0,
340 }
341 }
342
343 pub fn from_existing(used_extents: &[Extent], total_sectors: u32, sector_size: u32) -> Self {
345 let mut map = Self::new(total_sectors);
346 for extent in used_extents {
347 map.mark_used(*extent, sector_size);
348 }
349 map
350 }
351
352 pub fn allocate(&mut self, size_bytes: u64, sector_size: u32) -> Option<Extent> {
356 if size_bytes == 0 {
357 return Some(Extent::new(self.next_free, 0));
358 }
359
360 let sectors_needed = size_bytes.div_ceil(sector_size as u64) as u32;
361
362 let mut start = self.next_free;
364 let mut consecutive = 0u32;
365 let mut found_start = start;
366
367 while start + consecutive < self.total_sectors {
368 let current = start + consecutive;
369 if self.is_free(current) {
370 if consecutive == 0 {
371 found_start = current;
372 }
373 consecutive += 1;
374 if consecutive >= sectors_needed {
375 let extent = Extent::new(found_start, size_bytes);
377 self.mark_used(extent, sector_size);
378 return Some(extent);
379 }
380 } else {
381 consecutive = 0;
383 start = current + 1;
384 found_start = start;
385 }
386 }
387
388 if self.next_free > 0 {
390 start = 0;
391 consecutive = 0;
392 found_start = 0;
393
394 while start + consecutive < self.next_free {
395 let current = start + consecutive;
396 if self.is_free(current) {
397 if consecutive == 0 {
398 found_start = current;
399 }
400 consecutive += 1;
401 if consecutive >= sectors_needed {
402 let extent = Extent::new(found_start, size_bytes);
403 self.mark_used(extent, sector_size);
404 return Some(extent);
405 }
406 } else {
407 consecutive = 0;
408 start = current + 1;
409 found_start = start;
410 }
411 }
412 }
413
414 None
415 }
416
417 pub fn mark_used(&mut self, extent: Extent, sector_size: u32) {
419 let end = extent.end_sector(sector_size);
420 for sector in extent.sector..end {
421 self.set_bit(sector, true);
422 }
423 if extent.sector == self.next_free {
425 self.next_free = end;
426 while self.next_free < self.total_sectors && !self.is_free(self.next_free) {
428 self.next_free += 1;
429 }
430 }
431 }
432
433 pub fn mark_free(&mut self, extent: Extent, sector_size: u32) {
435 let end = extent.end_sector(sector_size);
436 for sector in extent.sector..end {
437 self.set_bit(sector, false);
438 }
439 if extent.sector < self.next_free {
441 self.next_free = extent.sector;
442 }
443 }
444
445 #[inline]
447 pub fn is_free(&self, sector: u32) -> bool {
448 if sector >= self.total_sectors {
449 return false;
450 }
451 let byte_idx = sector as usize / 8;
452 let bit_idx = sector % 8;
453 (self.bitmap[byte_idx] & (1 << bit_idx)) == 0
454 }
455
456 #[inline]
458 pub fn is_used(&self, sector: u32) -> bool {
459 !self.is_free(sector)
460 }
461
462 #[inline]
464 pub fn total_sectors(&self) -> u32 {
465 self.total_sectors
466 }
467
468 pub fn free_sectors(&self) -> u32 {
470 let mut count = 0u32;
471 for sector in 0..self.total_sectors {
472 if self.is_free(sector) {
473 count += 1;
474 }
475 }
476 count
477 }
478
479 #[inline]
481 pub fn used_sectors(&self) -> u32 {
482 self.total_sectors - self.free_sectors()
483 }
484
485 #[inline]
487 fn set_bit(&mut self, sector: u32, used: bool) {
488 if sector >= self.total_sectors {
489 return;
490 }
491 let byte_idx = sector as usize / 8;
492 let bit_idx = sector % 8;
493 if used {
494 self.bitmap[byte_idx] |= 1 << bit_idx;
495 } else {
496 self.bitmap[byte_idx] &= !(1 << bit_idx);
497 }
498 }
499
500 pub fn reserve_initial(&mut self, sectors: u32, sector_size: u32) {
502 let extent = Extent::new(0, sectors as u64 * sector_size as u64);
503 self.mark_used(extent, sector_size);
504 }
505}
506
507#[cfg(test)]
508mod tests {
509 use super::*;
510
511 #[test]
512 fn test_file_layout() {
513 let file = FileLayout::new("test.txt", Extent::new(100, 1024))
514 .with_type(FileType::RegularFile)
515 .with_attributes(0x20);
516
517 assert_eq!(file.name, "test.txt");
518 assert_eq!(file.size(), 1024);
519 assert_eq!(file.extent.sector, 100);
520 }
521
522 #[test]
523 fn test_directory_layout() {
524 let mut root = DirectoryLayout::root();
525 root.add_file(FileLayout::new("file1.txt", Extent::new(100, 1024)));
526
527 let mut subdir = DirectoryLayout::new("docs");
528 subdir.add_file(FileLayout::new("readme.md", Extent::new(200, 512)));
529 root.add_subdir(subdir);
530
531 assert_eq!(root.file_count(), 1);
532 assert_eq!(root.subdir_count(), 1);
533
534 let file = root.find_file("file1.txt");
535 assert!(file.is_some());
536 assert_eq!(file.unwrap().name, "file1.txt");
537
538 let nested = root.find_file("docs/readme.md");
539 assert!(nested.is_some());
540 assert_eq!(nested.unwrap().name, "readme.md");
541 }
542
543 #[test]
544 fn test_get_or_create_dir() {
545 let mut root = DirectoryLayout::root();
546 let dir = root.get_or_create_dir("docs/api/v1");
547
548 assert_eq!(dir.name, "v1");
549 assert_eq!(root.subdirs[0].name, "docs");
550 assert_eq!(root.subdirs[0].subdirs[0].name, "api");
551 assert_eq!(root.subdirs[0].subdirs[0].subdirs[0].name, "v1");
552 }
553
554 #[test]
555 fn test_remove_file() {
556 let mut root = DirectoryLayout::root();
557 root.add_file(FileLayout::new("test.txt", Extent::new(100, 1024)));
558
559 let removed = root.remove_file("test.txt");
560 assert!(removed.is_some());
561 assert_eq!(removed.unwrap().name, "test.txt");
562 assert_eq!(root.file_count(), 0);
563 }
564
565 #[test]
566 fn test_allocation_map() {
567 let mut map = AllocationMap::new(100);
568 assert_eq!(map.total_sectors(), 100);
569 assert_eq!(map.free_sectors(), 100);
570
571 let extent = map.allocate(20480, 2048).unwrap();
573 assert_eq!(extent.sector, 0);
574 assert_eq!(extent.sector_count(2048), 10);
575 assert_eq!(map.free_sectors(), 90);
576
577 let extent2 = map.allocate(4096, 2048).unwrap();
579 assert_eq!(extent2.sector, 10);
580
581 map.mark_free(extent, 2048);
583 assert_eq!(map.free_sectors(), 98);
584
585 let extent3 = map.allocate(2048, 2048).unwrap();
587 assert_eq!(extent3.sector, 0);
588 }
589
590 #[test]
591 fn test_allocation_map_reserve() {
592 let mut map = AllocationMap::new(100);
593 map.reserve_initial(16, 2048); let extent = map.allocate(2048, 2048).unwrap();
596 assert_eq!(extent.sector, 16); }
598}