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