mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-08 06:13:14 +00:00
140 lines
4.5 KiB
Rust
140 lines
4.5 KiB
Rust
struct LazyBuf<'a> {
|
|
s: &'a str,
|
|
buf: Option<Vec<u8>>,
|
|
w: usize,
|
|
}
|
|
|
|
impl<'a> LazyBuf<'a> {
|
|
pub fn new(s: &'a str) -> Self {
|
|
Self { s, buf: None, w: 0 }
|
|
}
|
|
|
|
fn index(&self, i: usize) -> u8 {
|
|
self.buf.as_ref().map(|x| x[i]).unwrap_or_else(|| self.s.as_bytes()[i])
|
|
}
|
|
|
|
fn append(&mut self, c: u8) {
|
|
if self.buf.is_none() {
|
|
if self.w < self.s.len() && self.s.as_bytes()[self.w] == c {
|
|
self.w += 1;
|
|
return;
|
|
}
|
|
self.buf = Some({
|
|
let mut buf = vec![0u8; self.s.len()];
|
|
buf[..self.w].copy_from_slice(&self.s.as_bytes()[..self.w]);
|
|
buf
|
|
});
|
|
}
|
|
|
|
self.buf.as_mut().unwrap()[self.w] = c;
|
|
self.w += 1;
|
|
}
|
|
|
|
fn string(&self) -> String {
|
|
match self.buf {
|
|
Some(ref s) => String::from_utf8_lossy(&s[..self.w]).to_string(),
|
|
None => String::from_utf8_lossy(&self.s.as_bytes()[..self.w]).to_string(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// copy from golang(path.Clean)
|
|
pub fn clean(path: &str) -> String {
|
|
if path.is_empty() {
|
|
return ".".into();
|
|
}
|
|
|
|
let p = path.as_bytes();
|
|
let (rooted, n, mut out, mut r, mut dotdot) = (p[0] == b'/', path.len(), LazyBuf::new(path), 0, 0);
|
|
|
|
if rooted {
|
|
out.append(b'/');
|
|
r = 1;
|
|
dotdot = 1;
|
|
}
|
|
|
|
while r < n {
|
|
if p[r] == b'/' || (p[r] == b'.' && (r + 1 == n || p[r + 1] == b'/')) {
|
|
r += 1;
|
|
} else if p[r] == b'.' && p[r + 1] == b'.' && (r + 2 == n || p[r + 2] == b'/') {
|
|
r += 2;
|
|
if out.w > dotdot {
|
|
out.w -= 1;
|
|
|
|
while out.w > dotdot && out.index(out.w) != b'/' {
|
|
out.w -= 1;
|
|
}
|
|
} else if !rooted {
|
|
if out.w > 0 {
|
|
out.append(b'/');
|
|
}
|
|
|
|
out.append(b'.');
|
|
out.append(b'.');
|
|
dotdot = out.w;
|
|
}
|
|
} else {
|
|
if rooted && out.w != 1 || !rooted && out.w != 0 {
|
|
out.append(b'/');
|
|
}
|
|
|
|
while r < n && p[r] != b'/' {
|
|
out.append(p[r]);
|
|
r += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if out.w == 0 {
|
|
".".into()
|
|
} else {
|
|
out.string()
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::clean;
|
|
|
|
#[test_case::test_case("", "."; "1")]
|
|
#[test_case::test_case("abc", "abc"; "2")]
|
|
#[test_case::test_case("abc/def", "abc/def"; "3")]
|
|
#[test_case::test_case("a/b/c", "a/b/c"; "4")]
|
|
#[test_case::test_case(".", "."; "5")]
|
|
#[test_case::test_case("..", ".."; "6")]
|
|
#[test_case::test_case("../..", "../.."; "7")]
|
|
#[test_case::test_case("../../abc", "../../abc"; "8")]
|
|
#[test_case::test_case("/abc", "/abc"; "9")]
|
|
#[test_case::test_case("/", "/"; "10")]
|
|
#[test_case::test_case("abc/", "abc"; "11")]
|
|
#[test_case::test_case("abc/def/", "abc/def"; "12")]
|
|
#[test_case::test_case("a/b/c/", "a/b/c"; "13")]
|
|
#[test_case::test_case("./", "."; "14")]
|
|
#[test_case::test_case("../", ".."; "15")]
|
|
#[test_case::test_case("../../", "../.."; "16")]
|
|
#[test_case::test_case("/abc/", "/abc"; "17")]
|
|
#[test_case::test_case("abc//def//ghi", "abc/def/ghi"; "18")]
|
|
#[test_case::test_case("//abc", "/abc"; "19")]
|
|
#[test_case::test_case("///abc", "/abc"; "20")]
|
|
#[test_case::test_case("//abc//", "/abc"; "21")]
|
|
#[test_case::test_case("abc//", "abc"; "22")]
|
|
#[test_case::test_case("abc/./def", "abc/def"; "23")]
|
|
#[test_case::test_case("/./abc/def", "/abc/def"; "24")]
|
|
#[test_case::test_case("abc/.", "abc"; "25")]
|
|
#[test_case::test_case("abc/def/ghi/../jkl", "abc/def/jkl"; "26")]
|
|
#[test_case::test_case("abc/def/../ghi/../jkl", "abc/jkl"; "27")]
|
|
#[test_case::test_case("abc/def/..", "abc"; "28")]
|
|
#[test_case::test_case("abc/def/../..", "."; "29")]
|
|
#[test_case::test_case("/abc/def/../..", "/"; "30")]
|
|
#[test_case::test_case("abc/def/../../..", ".."; "31")]
|
|
#[test_case::test_case("/abc/def/../../..", "/"; "32")]
|
|
#[test_case::test_case("abc/def/../../../ghi/jkl/../../../mno", "../../mno"; "33")]
|
|
#[test_case::test_case("abc/./../def", "def"; "34")]
|
|
#[test_case::test_case("abc//./../def", "def"; "35")]
|
|
#[test_case::test_case("abc/../../././../def", "../../def"; "36")]
|
|
fn test_clean(path: &str, result: &str) {
|
|
assert_eq!(clean(path), result.to_owned());
|
|
assert_eq!(clean(result), result.to_owned());
|
|
}
|
|
}
|