From fff087a76b9f1b472985cb54425b92dfc99d11d9 Mon Sep 17 00:00:00 2001 From: Steve Checkoway Date: Tue, 20 Jun 2006 13:34:28 +0000 Subject: [PATCH] Split out LoadFromSMNoteDataStringWithPlayer(). This fills out the NoteData without making any copies of sSMNoteData. Previously, this portion of the code copied the full string twice, once when splitting for measures and once when splitting for lines. Only make a single copy of the string in LoadFromSMNoteDataString() when stripping out comments. Previously, the string was copied as many times as there were comments. Now, the whole loading can happen with a single copy of the data. --- stepmania/src/NoteDataUtil.cpp | 153 +++++++++++++++++++++------------ stepmania/src/NoteDataUtil.h | 2 +- 2 files changed, 99 insertions(+), 56 deletions(-) diff --git a/stepmania/src/NoteDataUtil.cpp b/stepmania/src/NoteDataUtil.cpp index 3ea005627e..6bc9b11dca 100644 --- a/stepmania/src/NoteDataUtil.cpp +++ b/stepmania/src/NoteDataUtil.cpp @@ -7,6 +7,7 @@ #include "GameState.h" #include "RadarValues.h" #include "Foreach.h" +#include NoteType NoteDataUtil::GetSmallestNoteTypeForMeasure( const NoteData &n, int iMeasureIndex ) { @@ -46,54 +47,61 @@ NoteType NoteDataUtil::GetSmallestNoteTypeForMeasure( const NoteData &n, int iMe return nt; } -void NoteDataUtil::LoadFromSMNoteDataString( NoteData &out, RString sSMNoteData ) +static void LoadFromSMNoteDataStringWithPlayer( NoteData& out, const RString &sSMNoteData, int start, + int len, PlayerNumber pn, int iNumTracks ) { - // - // Load note data - // - - /* Clear notes, but keep the same number of tracks. */ - int iNumTracks = out.GetNumTracks(); - out.Init(); - out.SetNumTracks( iNumTracks ); - + /* Don't allocate memory for the entire string, nor per measure. Instead, use the in-place + * partial string split twice. By maintaining begin and end pointers to each measure line + * we can perform this without copying the string at all. */ + int size = -1; + const int end = start + len; + vector > aMeasureLines; + for( unsigned m = 0; true; ++m ) { - RString::size_type iIndexCommentStart = 0; + /* XXX Ignoring empty seems wrong for measures. It means that ",,," is treated as + * "," where I would expect most people would want 2 empty measures. ",\n,\n," + * would do as I would expect. */ + split( sSMNoteData, ",", start, size, end, true ); // Ignore empty is important. + if( start == end ) + break; - while( (iIndexCommentStart = sSMNoteData.find("//", iIndexCommentStart)) != RString::npos ) + // Partial string split. + int measureLineStart = start, measureLineSize = -1; + const int measureEnd = start + size; + + aMeasureLines.clear(); + while( true ) { - RString::size_type iIndexCommentEnd = sSMNoteData.find( "\n", iIndexCommentStart ); - sSMNoteData.erase( iIndexCommentStart, iIndexCommentEnd - iIndexCommentStart ); + // Ignore empty is clearly important here. + split( sSMNoteData, "\n", measureLineStart, measureLineSize, measureEnd, true ); + if( measureLineStart == measureEnd ) + break; + //RString &line = sSMNoteData.substr( measureLineStart, measureLineSize ); + const char *beginLine = sSMNoteData.data() + measureLineStart; + const char *endLine = beginLine + measureLineSize; + + while( beginLine < endLine && strchr("\r\n\t ", *beginLine) ) + ++beginLine; + while( endLine > beginLine && strchr("\r\n\t ", *(endLine - 1)) ) + --endLine; + if( beginLine < endLine ) // nonempty + aMeasureLines.push_back( pair(beginLine, endLine) ); } - } - - vector asMeasures; - split( sSMNoteData, ",", asMeasures, true ); // ignore empty is important - for( unsigned m=0; m asMeasureLines; - split( sMeasureString, "\n", asMeasureLines, true ); // ignore empty is important - - for( unsigned l=0; lWarn( "Unmatched 3 in \"%s\"", sMeasureLine.c_str() ); + int n = intptr_t(endLine) - intptr_t(beginLine); + LOG->Warn( "Unmatched 3 in \"%.*s\"", n, beginLine ); } else { @@ -129,11 +138,11 @@ void NoteDataUtil::LoadFromSMNoteDataString( NoteData &out, RString sSMNoteData head_tap.iDuration = iIndex - iHeadRow; out.SetTapNote( iTrack, iHeadRow, head_tap ); } - + /* This won't write tn, but keep parsing normally anyway. */ break; } -// case 'm': + // case 'm': // Don't be loose with the definition. Use only 'M' since // that's what we've been writing to disk. -Chris case 'M': tn = TAP_ORIGINAL_MINE; break; @@ -150,13 +159,13 @@ void NoteDataUtil::LoadFromSMNoteDataString( NoteData &out, RString sSMNoteData } p++; - + // We won't scan past the end of the line so these are safe to do. #if 0 // look for optional attack info (e.g. "{tipsy,50% drunk:15.2}") if( *p == '{' ) { p++; - + char szModifiers[256] = ""; float fDurationSeconds = 0; if( sscanf( p, "%255[^:]:%f}", szModifiers, &fDurationSeconds ) == 2 ) // not fatal if this fails due to malformed data @@ -165,16 +174,16 @@ void NoteDataUtil::LoadFromSMNoteDataString( NoteData &out, RString sSMNoteData tn.sAttackModifiers = szModifiers; tn.fAttackDurationSeconds = fDurationSeconds; } - + // skip past the '}' - while( *p ) + while( p < endLine ) { if( *(p++) == '}' ) break; } } #endif - + // look for optional keysound index (e.g. "[123]") if( *p == '[' ) { @@ -185,26 +194,29 @@ void NoteDataUtil::LoadFromSMNoteDataString( NoteData &out, RString sSMNoteData tn.bKeysound = true; tn.iKeysoundIndex = iKeysoundIndex; } - + // skip past the ']' - while( *p ) + while( p < endLine ) { if( *(p++) == ']' ) break; } } - + /* Optimization: if we pass TAP_EMPTY, NoteData will do a search * to remove anything in this position. We know that there's nothing * there, so avoid the search. */ if( tn.type != TapNote::empty && ch != '3' ) + { + tn.pn = pn; out.SetTapNote( iTrack, iIndex, tn ); - + } + iTrack++; } } } - + /* Make sure we don't have any hold notes that didn't find a tail. */ for( int t=0; tWarn( "Unmatched 2 at beat %f", NoteRowToBeat(iRow) ); out.RemoveTapNote( t, begin ); } - + begin = next; } } + +} + +void NoteDataUtil::LoadFromSMNoteDataString( NoteData &out, const RString &sSMNoteData_ ) +{ + // + // Load note data + // + RString sSMNoteData; + RString::size_type iIndexCommentStart = 0; + RString::size_type iIndexCommentEnd = 0; + RString::size_type origSize = sSMNoteData_.size(); + const char *p = sSMNoteData_.data(); + + sSMNoteData.reserve( origSize ); + while( (iIndexCommentStart = sSMNoteData.find("//", iIndexCommentEnd)) != RString::npos ) + { + sSMNoteData.append( p, iIndexCommentStart - iIndexCommentEnd ); + p += iIndexCommentStart - iIndexCommentEnd; + iIndexCommentEnd = sSMNoteData.find( "\n", iIndexCommentStart ); + iIndexCommentEnd = (iIndexCommentEnd == RString::npos ? origSize : iIndexCommentEnd+1); + p += iIndexCommentEnd - iIndexCommentStart; + } + sSMNoteData.append( p, origSize - iIndexCommentEnd ); + + /* Clear notes, but keep the same number of tracks. */ + int iNumTracks = out.GetNumTracks(); + out.Init(); + out.SetNumTracks( iNumTracks ); + + LoadFromSMNoteDataStringWithPlayer( out, sSMNoteData, 0, sSMNoteData.size(), PLAYER_INVALID, iNumTracks ); } void NoteDataUtil::InsertHoldTails( NoteData &inout ) diff --git a/stepmania/src/NoteDataUtil.h b/stepmania/src/NoteDataUtil.h index b22237fbb5..c75eb9ada0 100644 --- a/stepmania/src/NoteDataUtil.h +++ b/stepmania/src/NoteDataUtil.h @@ -17,7 +17,7 @@ struct RadarValues; namespace NoteDataUtil { NoteType GetSmallestNoteTypeForMeasure( const NoteData &n, int iMeasureIndex ); - void LoadFromSMNoteDataString( NoteData &out, RString sSMNoteData ); + void LoadFromSMNoteDataString( NoteData &out, const RString &sSMNoteData ); void GetSMNoteDataString( const NoteData &in, RString ¬es_out ); void LoadTransformedSlidingWindow( const NoteData &in, NoteData &out, int iNewNumTracks ); void LoadOverlapped( const NoteData &in, NoteData &out, int iNewNumTracks );