1pub use starnix_types::string::{FsStr, FsString};
6
7pub const SEPARATOR: u8 = b'/';
8
9pub struct PathBuilder {
11 data: FsString,
13 pos: usize,
14}
15
16impl PathBuilder {
17 const INITIAL_CAPACITY: usize = 32;
18
19 pub fn new() -> Self {
20 Self { data: Default::default(), pos: 0 }
21 }
22
23 pub fn prepend_element(&mut self, element: &FsStr) {
24 self.ensure_capacity(element.len() + 1);
25 let old_pos = self.pos;
26 self.pos -= element.len() + 1;
27 self.data[self.pos + 1..old_pos].copy_from_slice(element);
28 self.data[self.pos] = b'/';
29 }
30
31 pub fn build_absolute(mut self) -> FsString {
33 if self.pos == self.data.len() {
34 return "/".into();
35 }
36 self.data.drain(..self.pos);
37 self.data
38 }
39
40 pub fn build_relative(self) -> FsString {
42 let mut absolute = self.build_absolute();
43 absolute.remove(0);
45 absolute
46 }
47
48 fn ensure_capacity(&mut self, capacity_needed: usize) {
49 if capacity_needed > self.pos {
50 let current_size = self.data.len();
51 let len = current_size - self.pos;
52 let min_size = len + capacity_needed;
53 let mut new_size = std::cmp::max(current_size * 2, Self::INITIAL_CAPACITY);
54 while new_size < min_size {
55 new_size *= 2;
56 }
57 self.data.reserve(new_size - current_size);
58 self.data.resize(new_size - len, 0);
59 self.data.extend_from_within(self.pos..(self.pos + len));
60 self.pos = new_size - len;
61 }
62 }
63}
64
65#[cfg(test)]
66mod test {
67 use super::*;
68
69 #[::fuchsia::test]
70 fn test_path_builder() {
71 let p = PathBuilder::new();
72 assert_eq!(p.build_absolute(), "/");
73
74 let p = PathBuilder::new();
75 assert_eq!(p.build_relative(), "");
76
77 let mut p = PathBuilder::new();
78 p.prepend_element("foo".into());
79 assert_eq!(p.build_absolute(), "/foo");
80
81 let mut p = PathBuilder::new();
82 p.prepend_element("foo".into());
83 assert_eq!(p.build_relative(), "foo");
84
85 let mut p = PathBuilder::new();
86 p.prepend_element("foo".into());
87 p.prepend_element("bar".into());
88 assert_eq!(p.build_absolute(), "/bar/foo");
89
90 let mut p = PathBuilder::new();
91 p.prepend_element("foo".into());
92 p.prepend_element("1234567890123456789012345678901234567890".into());
93 p.prepend_element("bar".into());
94 assert_eq!(p.build_absolute(), "/bar/1234567890123456789012345678901234567890/foo");
95 }
96}