optimize CollapsePath: only make one copy, don't split and re-join (5:1 speedup)

This commit is contained in:
Glenn Maynard
2005-07-26 05:26:14 +00:00
parent 4bcf9fcbfd
commit b9818b6379
+53 -24
View File
@@ -1343,39 +1343,68 @@ CString FixSlashes( CString sPath )
void CollapsePath( CString &sPath, bool bRemoveLeadingDot ) void CollapsePath( CString &sPath, bool bRemoveLeadingDot )
{ {
/* Don't ignore empty: we do want to keep trailing slashes. */ CString sOut;
CStringArray as; sOut.reserve( sPath.size() );
split( sPath, "/", as, false );
for( unsigned i=0; i<as.size(); i++ ) size_t iPos = 0;
size_t iNext;
for( ; iPos < sPath.size(); iPos = iNext )
{ {
if( as[i] == ".." && i != 0 ) /* Find the next slash. */
iNext = sPath.find( '/', iPos );
if( iNext == CString::npos )
iNext = sPath.size();
else
++iNext;
/* Strip extra slashes, but don't remove slashes from the beginning of the string. */
if( iNext - iPos == 1 && sPath[iPos] == '/' )
{ {
/* If the previous element is also "..", then we have a path beginning if( !sOut.empty() )
* with multiple "../"--one .. can't eat another .., since that would continue;
* cause "../../foo" to become "foo". */ }
if( as[i-1] != ".." )
/* If this is a dot, skip it. */
if( iNext - iPos == 2 && sPath[iPos] == '.' && sPath[iPos+1] == '/' )
{ {
as.erase( as.begin()+i-1 ); if( bRemoveLeadingDot || !sOut.empty() )
as.erase( as.begin()+i-1 ); continue;
i -= 2;
} }
}
else if( as[i] == "" && i != 0 && i+1 < as.size() ) /* If this is two dots, */
if( iNext - iPos == 3 && sPath[iPos] == '.' && sPath[iPos+1] == '.' && sPath[iPos+2] == '/' )
{ {
/* Remove empty parts that aren't at the beginning or end; /* If this is the first path element (nothing to delete), or all we have is a slash,
* "foo//bar/" -> "foo/bar/", but "/foo" -> "/foo" and "foo/" * leave it. */
* to "foo/". */ if( sOut.empty() || (sOut.size() == 1 && sOut[0] == '/') )
as.erase( as.begin()+i );
i -= 1;
}
else if( as[i] == "." && (bRemoveLeadingDot || i != 0) )
{ {
as.erase( as.begin()+i ); sOut.append( sPath, iPos, iNext-iPos );
i -= 1; continue;
} }
/* Search backwards for the previous path element. */
size_t iPrev = sOut.rfind( '/', sOut.size()-2 );
if( iPrev == CString::npos )
iPrev = 0;
else
++iPrev;
/* If the previous element is also .., leave it. */
bool bLastIsTwoDots = (sOut.size() - iPrev == 3 && sOut[iPrev] == '.' && sOut[iPrev+1] == '.' );
if( bLastIsTwoDots )
{
sOut.append( sPath, iPos, iNext-iPos );
continue;
} }
sPath = join( "/", as );
sOut.erase( iPrev );
continue;
}
sOut.append( sPath, iPos, iNext-iPos );
}
sOut.swap( sPath );
} }