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
+55 -26
View File
@@ -1343,39 +1343,68 @@ CString FixSlashes( CString sPath )
void CollapsePath( CString &sPath, bool bRemoveLeadingDot )
{
/* Don't ignore empty: we do want to keep trailing slashes. */
CStringArray as;
split( sPath, "/", as, false );
CString sOut;
sOut.reserve( sPath.size() );
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
* with multiple "../"--one .. can't eat another .., since that would
* cause "../../foo" to become "foo". */
if( as[i-1] != ".." )
if( !sOut.empty() )
continue;
}
/* If this is a dot, skip it. */
if( iNext - iPos == 2 && sPath[iPos] == '.' && sPath[iPos+1] == '/' )
{
if( bRemoveLeadingDot || !sOut.empty() )
continue;
}
/* If this is two dots, */
if( iNext - iPos == 3 && sPath[iPos] == '.' && sPath[iPos+1] == '.' && sPath[iPos+2] == '/' )
{
/* If this is the first path element (nothing to delete), or all we have is a slash,
* leave it. */
if( sOut.empty() || (sOut.size() == 1 && sOut[0] == '/') )
{
as.erase( as.begin()+i-1 );
as.erase( as.begin()+i-1 );
i -= 2;
sOut.append( sPath, iPos, iNext-iPos );
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;
}
sOut.erase( iPrev );
continue;
}
else if( as[i] == "" && i != 0 && i+1 < as.size() )
{
/* Remove empty parts that aren't at the beginning or end;
* "foo//bar/" -> "foo/bar/", but "/foo" -> "/foo" and "foo/"
* to "foo/". */
as.erase( as.begin()+i );
i -= 1;
}
else if( as[i] == "." && (bRemoveLeadingDot || i != 0) )
{
as.erase( as.begin()+i );
i -= 1;
}
sOut.append( sPath, iPos, iNext-iPos );
}
sPath = join( "/", as );
sOut.swap( sPath );
}