From 5a9918285f78b1fb99a3b0781a185acec945b8b5 Mon Sep 17 00:00:00 2001 From: Janis Date: Sun, 2 Apr 2023 00:23:45 +0200 Subject: [PATCH] path helper fix - remove directory segment and instead just have "file" for directories and files and other kinds of files since we cant know just based on the path what it is - segmetns are stored as vecdeque in normalized path to allow for hopefully more efficient poping in FIFO order (but definitely more ergonomic) - test for trailing seperators, the last trailing seperator is ignored, multiple trailing seperators are turned into noops because i parse paths left to right (regex?) in the simplest way possible. in normalized paths noops are completely ignored/removed --- btrfs/src/path.rs | 90 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 69 insertions(+), 21 deletions(-) diff --git a/btrfs/src/path.rs b/btrfs/src/path.rs index 1702291..7515e5f 100644 --- a/btrfs/src/path.rs +++ b/btrfs/src/path.rs @@ -1,4 +1,4 @@ -use alloc::vec::Vec; +use alloc::{collections::VecDeque, vec::Vec}; const SEPERATOR: u8 = b'/'; @@ -31,15 +31,15 @@ pub trait Path: AsRef<[u8]> { let iter = self.segment_iter(); let mut segments = - Vec::with_capacity(self.as_ref().iter().filter(|&b| b == &SEPERATOR).count()); + VecDeque::with_capacity(self.as_ref().iter().filter(|&b| b == &SEPERATOR).count()); for segment in iter { match segment { Segment::NoOp | Segment::CurrentDir => {} - Segment::Root | Segment::Directory(_) | Segment::File(_) => segments.push(segment), + Segment::Root | Segment::File(_) => segments.push_back(segment), Segment::ParentDir => { - if segments.last() != Some(&Segment::Root) { - segments.pop(); + if segments.back() != Some(&Segment::Root) { + segments.pop_back(); } } } @@ -55,20 +55,26 @@ pub enum Segment<'a> { NoOp, CurrentDir, ParentDir, - Directory(&'a [u8]), File(&'a [u8]), } impl Path for T where T: AsRef<[u8]> {} pub struct NormalizedPath<'a> { - segments: Vec>, + segments: VecDeque>, } impl<'a> NormalizedPath<'a> { - pub fn iter(&self) -> core::slice::Iter { + pub fn into_iter(self) -> alloc::collections::vec_deque::IntoIter> { + self.segments.into_iter() + } + pub fn iter(&self) -> alloc::collections::vec_deque::Iter { self.segments.iter() } + + pub fn pop_segment(&mut self) -> Option> { + self.segments.pop_front() + } } pub struct PathSegmentIter<'a> { @@ -95,6 +101,9 @@ impl<'a> Iterator for PathSegmentIter<'a> { if self.offset == 0 { // handle root segment Some(Segment::Root) + } else if bytes.is_empty() { + // trailign seperator + None } else { // noop Some(Segment::NoOp) @@ -104,11 +113,7 @@ impl<'a> Iterator for PathSegmentIter<'a> { } else if segment == &b".."[..] { Some(Segment::ParentDir) } else { - if next == bytes.len() { - Some(Segment::File(segment)) - } else { - Some(Segment::Directory(segment)) - } + Some(Segment::File(segment)) }; self.offset += bytes.len().min(next + 1); @@ -174,11 +179,11 @@ mod segment_iter_tests { fn test_noop() { let mut iter = b"this//a//path//some.file".segment_iter(); - assert_eq!(iter.next(), Some(Segment::Directory(&b"this"[..]))); + assert_eq!(iter.next(), Some(Segment::File(&b"this"[..]))); assert_eq!(iter.next(), Some(Segment::NoOp)); - assert_eq!(iter.next(), Some(Segment::Directory(&b"a"[..]))); + assert_eq!(iter.next(), Some(Segment::File(&b"a"[..]))); assert_eq!(iter.next(), Some(Segment::NoOp)); - assert_eq!(iter.next(), Some(Segment::Directory(&b"path"[..]))); + assert_eq!(iter.next(), Some(Segment::File(&b"path"[..]))); assert_eq!(iter.next(), Some(Segment::NoOp)); assert_eq!(iter.next(), Some(Segment::File(&b"some.file"[..]))); assert_eq!(iter.next(), None); @@ -203,17 +208,60 @@ mod segment_iter_tests { } } + #[test] + fn test_trailing_seperator() { + let mut iter = b"/this//".segment_iter(); + + assert_eq!(iter.next(), Some(Segment::Root)); + assert_eq!(iter.next(), Some(Segment::File(&b"this"[..]))); + assert_eq!(iter.next(), Some(Segment::NoOp)); + assert_eq!(iter.next(), None); + + let mut iter = b"this//".segment_iter(); + + assert_eq!(iter.next(), Some(Segment::File(&b"this"[..]))); + assert_eq!(iter.next(), Some(Segment::NoOp)); + assert_eq!(iter.next(), None); + + let mut iter = b"/this////".segment_iter(); + + assert_eq!(iter.next(), Some(Segment::Root)); + assert_eq!(iter.next(), Some(Segment::File(&b"this"[..]))); + assert_eq!(iter.next(), Some(Segment::NoOp)); + assert_eq!(iter.next(), Some(Segment::NoOp)); + assert_eq!(iter.next(), Some(Segment::NoOp)); + assert_eq!(iter.next(), None); + + let mut iter = b"/this////".normalize().into_iter(); + + assert_eq!(iter.next(), Some(Segment::Root)); + assert_eq!(iter.next(), Some(Segment::File(&b"this"[..]))); + assert_eq!(iter.next(), None); + + let mut iter = b"/this/".normalize().into_iter(); + + assert_eq!(iter.next(), Some(Segment::Root)); + assert_eq!(iter.next(), Some(Segment::File(&b"this"[..]))); + assert_eq!(iter.next(), None); + + let mut iter = b"/this".normalize().into_iter(); + + assert_eq!(iter.next(), Some(Segment::Root)); + assert_eq!(iter.next(), Some(Segment::File(&b"this"[..]))); + assert_eq!(iter.next(), None); + } + #[test] fn test_iter() { let mut iter = b"/this/is/a//path/to/some.file".segment_iter(); assert_eq!(iter.next(), Some(Segment::Root)); - assert_eq!(iter.next(), Some(Segment::Directory(&b"this"[..]))); - assert_eq!(iter.next(), Some(Segment::Directory(&b"is"[..]))); - assert_eq!(iter.next(), Some(Segment::Directory(&b"a"[..]))); + assert_eq!(iter.next(), Some(Segment::File(&b"this"[..]))); + assert_eq!(iter.next(), Some(Segment::File(&b"is"[..]))); + assert_eq!(iter.next(), Some(Segment::File(&b"a"[..]))); assert_eq!(iter.next(), Some(Segment::NoOp)); - assert_eq!(iter.next(), Some(Segment::Directory(&b"path"[..]))); - assert_eq!(iter.next(), Some(Segment::Directory(&b"to"[..]))); + assert_eq!(iter.next(), Some(Segment::File(&b"path"[..]))); + assert_eq!(iter.next(), Some(Segment::File(&b"to"[..]))); assert_eq!(iter.next(), Some(Segment::File(&b"some.file"[..]))); assert_eq!(iter.next(), None); }