starnix_core/vfs/
path.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5pub use starnix_types::string::{FsStr, FsString};
6
7pub const SEPARATOR: u8 = b'/';
8
9// Helper that can be used to build paths backwards, from the tail to head.
10pub struct PathBuilder {
11    // The path is kept in `data[pos..]`.
12    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    /// Build the absolute path string.
32    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    /// Build the relative path string.
41    pub fn build_relative(self) -> FsString {
42        let mut absolute = self.build_absolute();
43        // Remove the prefix slash.
44        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}