11use std:: {
22 borrow:: Cow ,
3+ ffi:: OsString ,
4+ iter:: Peekable ,
35 ops:: Deref ,
46 path:: { Component , Path , PathBuf } ,
57} ;
@@ -9,17 +11,14 @@ use smallvec::SmallVec;
911
1012use crate :: {
1113 SugarPath ,
12- utils:: { ComponentVec , IntoCowPath , get_current_dir, to_normalized_components } ,
14+ utils:: { IntoCowPath , get_current_dir} ,
1315} ;
1416
1517type StrVec < ' a > = SmallVec < [ & ' a str ; 8 ] > ;
1618
1719impl SugarPath for Path {
1820 fn normalize ( & self ) -> PathBuf {
19- let peekable = self . components ( ) . peekable ( ) ;
20- let mut components = to_normalized_components ( peekable) ;
21-
22- normalize_inner ( & mut components)
21+ normalize_inner ( self . components ( ) . peekable ( ) , self . as_os_str ( ) . len ( ) )
2322 }
2423
2524 fn absolutize ( & self ) -> PathBuf {
@@ -49,10 +48,9 @@ impl SugarPath for Path {
4948 // absolute path, get cwd for that drive, or the process cwd if
5049 // the drive cwd is not available. We're sure the device is not
5150 // a UNC path at this points, because UNC paths are always absolute.
52- let mut components: ComponentVec = components. collect ( ) ;
51+ let mut components: SmallVec < [ Component ; 8 ] > = components. collect ( ) ;
5352 components. insert ( 1 , Component :: RootDir ) ;
54- let mut components = to_normalized_components ( components. into_iter ( ) . peekable ( ) ) ;
55- normalize_inner ( & mut components)
53+ normalize_inner ( components. into_iter ( ) . peekable ( ) , self . as_os_str ( ) . len ( ) )
5654 } else {
5755 base. to_mut ( ) . push ( self ) ;
5856 base. normalize ( )
@@ -107,7 +105,7 @@ impl SugarPath for Path {
107105 matches ! ( com, Component :: Normal ( _) | Component :: Prefix ( _) | Component :: RootDir )
108106 } ;
109107
110- // Collect components using SmallVec to avoid heap allocation for typical paths
108+ // Iterate components without intermediate allocation
111109 let base_components = base. components ( ) . filter ( filter_fn) ;
112110 let target_components = target. components ( ) . filter ( filter_fn) ;
113111
@@ -167,19 +165,103 @@ impl SugarPath for Path {
167165}
168166
169167#[ inline]
170- fn normalize_inner ( components : & mut ComponentVec ) -> PathBuf {
171- if components. is_empty ( ) {
168+ fn normalize_inner < ' a > (
169+ mut components : Peekable < impl Iterator < Item = Component < ' a > > > ,
170+ hint_cap : usize ,
171+ ) -> PathBuf {
172+ let sep_byte = std:: path:: MAIN_SEPARATOR as u8 ;
173+ let mut buf: Vec < u8 > = Vec :: with_capacity ( hint_cap) ;
174+ let mut has_root = false ;
175+ let mut depth: usize = 0 ; // count of Normal segments currently in buf
176+ let mut need_sep = false ;
177+
178+ // --- Prefix (Windows only) ---
179+ #[ cfg( target_family = "windows" ) ]
180+ let prefix_len: usize ;
181+ #[ cfg( target_family = "windows" ) ]
182+ {
183+ if let Some ( Component :: Prefix ( p) ) = components. peek ( ) {
184+ if let std:: path:: Prefix :: UNC ( server, share) = p. kind ( ) {
185+ buf. extend_from_slice ( b"\\ \\ " ) ;
186+ buf. extend_from_slice ( server. as_encoded_bytes ( ) ) ;
187+ buf. push ( b'\\' ) ;
188+ buf. extend_from_slice ( share. as_encoded_bytes ( ) ) ;
189+ } else {
190+ buf. extend_from_slice ( p. as_os_str ( ) . as_encoded_bytes ( ) ) ;
191+ }
192+ components. next ( ) ;
193+ }
194+ prefix_len = buf. len ( ) ;
195+ }
196+
197+ // --- RootDir ---
198+ if matches ! ( components. peek( ) , Some ( Component :: RootDir ) ) {
199+ buf. push ( sep_byte) ;
200+ has_root = true ;
201+ components. next ( ) ;
202+ }
203+
204+ let root_end = buf. len ( ) ;
205+
206+ // --- Remaining components ---
207+ for component in components {
208+ match component {
209+ Component :: Prefix ( prefix) => unreachable ! ( "Unexpected prefix for {:?}" , prefix) ,
210+ Component :: RootDir => unreachable ! ( "Unexpected RootDir after initial position" ) ,
211+ Component :: CurDir => { }
212+ Component :: ParentDir => {
213+ if depth > 0 {
214+ // Roll back the last Normal segment using memrchr.
215+ let search_region = & buf[ root_end..] ;
216+ if let Some ( pos) = memrchr ( sep_byte, search_region) {
217+ buf. truncate ( root_end + pos) ;
218+ } else {
219+ buf. truncate ( root_end) ;
220+ }
221+ depth -= 1 ;
222+ need_sep = buf. len ( ) > root_end;
223+ } else if !has_root {
224+ // Relative path going above start: write ".." literally
225+ if need_sep {
226+ buf. push ( sep_byte) ;
227+ }
228+ buf. extend_from_slice ( b".." ) ;
229+ need_sep = true ;
230+ }
231+ // else: has_root && depth == 0 → ignore (can't go above root)
232+ }
233+ Component :: Normal ( s) => {
234+ if need_sep {
235+ buf. push ( sep_byte) ;
236+ }
237+ buf. extend_from_slice ( s. as_encoded_bytes ( ) ) ;
238+ depth += 1 ;
239+ need_sep = true ;
240+ }
241+ }
242+ }
243+
244+ // --- Empty result → "." ---
245+ if buf. is_empty ( ) {
172246 return PathBuf :: from ( "." ) ;
173247 }
174248
175- if cfg ! ( target_family = "windows" )
176- && components. len ( ) == 1
177- && matches ! ( components[ 0 ] , Component :: Prefix ( _) )
178- {
179- components. push ( Component :: CurDir )
249+ // --- Prefix-only: append trailing separator or CurDir ---
250+ #[ cfg( target_family = "windows" ) ]
251+ if buf. len ( ) == prefix_len && prefix_len > 0 {
252+ // Determine if the prefix is UNC by checking for leading "\\"
253+ if buf. len ( ) >= 2 && buf[ 0 ] == b'\\' && buf[ 1 ] == b'\\' {
254+ buf. push ( b'\\' ) ;
255+ } else {
256+ buf. push ( b'.' ) ;
257+ }
180258 }
181259
182- components. iter ( ) . collect ( )
260+ // SAFETY: `buf` was built entirely from:
261+ // - encoded bytes of OsStr components (valid platform encoding)
262+ // - ASCII separator bytes and ASCII '.' characters
263+ // This preserves the encoding invariants required by OsString.
264+ PathBuf :: from ( unsafe { OsString :: from_encoded_bytes_unchecked ( buf) } )
183265}
184266
185267impl < T : Deref < Target = str > > SugarPath for T {
0 commit comments