From 02fd1ab143bfa35d77ace5ee557ac344c1cd6b25 Mon Sep 17 00:00:00 2001 From: Ben Avison Date: Wed, 18 Sep 2013 19:31:59 +0100 Subject: [PATCH] Faster string comparison functions in StringUtils The methods EqualsNoCase(), StartsWith() and EndsWith() required quite a lot of unncessary creation and destruction of std::strings, whether it be the creation of a temporary copy that could be forced to lower-case, a temporary copy that was a substring of one of the inputs, or just marshalling a string literal argument into a std::string. These functions don't appear to be used all that much at the moment; when I profiled opening the songs library, I saw only a 1% improvement, which was within the sampling noise threshold. But with PR #3225 and PR #3290 coming along, that looks set to change. Once the functions are being called millions of times, those heap operations really start to get noticeable. Also, split StartsWith() and EndsWith() into multiple separately-named functions, according to case sensitivity. Formerly, there was an optional parameter (default off) to indicate that these operations were case-*sensitive*, which is actually computationally simpler to perform. Now the naming convention is consistent with EqualsNoCase: StartsWith - case-sensitive EndsWith - case-sensitive StartsWithNoCase - case-insensitive EndsWithNoCase - case-insensitive With the case-sensitive versions now easier to type, it will hopefully encourage future developers to use them in preference. --- xbmc/Application.cpp | 6 +- xbmc/CueDocument.cpp | 24 ++-- xbmc/addons/Addon.cpp | 2 +- .../AudioEngine/Sinks/AESinkDirectSound.cpp | 2 +- xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.cpp | 2 +- xbmc/filesystem/test/TestRarFile.cpp | 20 +-- .../generic/ScriptInvocationManager.cpp | 2 +- xbmc/music/windows/GUIWindowMusicBase.cpp | 2 +- xbmc/music/windows/GUIWindowMusicNav.cpp | 6 +- xbmc/network/WebServer.cpp | 2 +- xbmc/network/upnp/UPnPServer.cpp | 18 +-- xbmc/settings/SettingDependency.cpp | 4 +- xbmc/settings/SettingsManager.cpp | 2 +- xbmc/settings/SkinSettings.cpp | 4 +- xbmc/utils/FileUtils.cpp | 22 ++-- xbmc/utils/LegacyPathTranslation.cpp | 2 +- xbmc/utils/StringUtils.cpp | 121 +++++++++++++++--- xbmc/utils/StringUtils.h | 14 +- xbmc/utils/StringValidation.cpp | 2 +- xbmc/utils/test/TestStringUtils.cpp | 24 ++-- xbmc/video/windows/GUIWindowVideoBase.cpp | 2 +- xbmc/video/windows/GUIWindowVideoNav.cpp | 6 +- xbmc/view/ViewDatabase.cpp | 4 +- 23 files changed, 194 insertions(+), 99 deletions(-) diff --git a/xbmc/Application.cpp b/xbmc/Application.cpp index db04a0ec51039..4030ef68e1c63 100644 --- a/xbmc/Application.cpp +++ b/xbmc/Application.cpp @@ -1586,7 +1586,7 @@ void CApplication::OnSettingChanged(const CSetting *setting) } else if (settingId == "lookandfeel.skinzoom") g_windowManager.SendMessage(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_WINDOW_RESIZE); - else if (StringUtils::StartsWith(settingId, "audiooutput.")) + else if (StringUtils::StartsWithNoCase(settingId, "audiooutput.")) { if (settingId == "audiooutput.guisoundmode") { @@ -2225,13 +2225,13 @@ bool CApplication::OnKey(const CKey& key) m_idleTimer.StartZero(); bool processKey = AlwaysProcess(action); - if (StringUtils::StartsWith(action.GetName(),"CECToggleState") || StringUtils::StartsWith(action.GetName(),"CECStandby")) + if (StringUtils::StartsWithNoCase(action.GetName(),"CECToggleState") || StringUtils::StartsWithNoCase(action.GetName(),"CECStandby")) { bool ret = true; CLog::Log(LOGDEBUG, "%s: action %s [%d], toggling state of playing device", __FUNCTION__, action.GetName().c_str(), action.GetID()); // do not wake up the screensaver right after switching off the playing device - if (StringUtils::StartsWith(action.GetName(),"CECToggleState")) + if (StringUtils::StartsWithNoCase(action.GetName(),"CECToggleState")) ret = CApplicationMessenger::Get().CECToggleState(); else ret = CApplicationMessenger::Get().CECStandby(); diff --git a/xbmc/CueDocument.cpp b/xbmc/CueDocument.cpp index 53b1f14844738..e79cb020278d0 100644 --- a/xbmc/CueDocument.cpp +++ b/xbmc/CueDocument.cpp @@ -103,7 +103,7 @@ bool CCueDocument::Parse(const CStdString &strFile) { if (!ReadNextLine(strLine)) break; - if (StringUtils::StartsWith(strLine,"INDEX 01")) + if (StringUtils::StartsWithNoCase(strLine,"INDEX 01")) { if (bCurrentFileChanged) { @@ -124,7 +124,7 @@ bool CCueDocument::Parse(const CStdString &strFile) if (m_iTotalTracks >= 0) m_Track[m_iTotalTracks].iStartTime = time; // start time of the next track } - else if (StringUtils::StartsWith(strLine,"TITLE")) + else if (StringUtils::StartsWithNoCase(strLine,"TITLE")) { if (m_iTotalTracks == -1) // No tracks yet ExtractQuoteInfo(strLine, m_strAlbum); @@ -140,14 +140,14 @@ bool CCueDocument::Parse(const CStdString &strFile) } } } - else if (StringUtils::StartsWith(strLine,"PERFORMER")) + else if (StringUtils::StartsWithNoCase(strLine,"PERFORMER")) { if (m_iTotalTracks == -1) // No tracks yet ExtractQuoteInfo(strLine, m_strArtist); else // New Artist for this track ExtractQuoteInfo(strLine, m_Track[m_iTotalTracks].strArtist); } - else if (StringUtils::StartsWith(strLine,"TRACK")) + else if (StringUtils::StartsWithNoCase(strLine,"TRACK")) { int iTrackNumber = ExtractNumericInfo(strLine.Mid(5)); @@ -164,13 +164,13 @@ bool CCueDocument::Parse(const CStdString &strFile) bCurrentFileChanged = false; } - else if (StringUtils::StartsWith(strLine,"REM DISCNUMBER")) + else if (StringUtils::StartsWithNoCase(strLine,"REM DISCNUMBER")) { int iDiscNumber = ExtractNumericInfo(strLine.Mid(14)); if (iDiscNumber > 0) m_iDiscNumber = iDiscNumber; } - else if (StringUtils::StartsWith(strLine,"FILE")) + else if (StringUtils::StartsWithNoCase(strLine,"FILE")) { // already a file name? then the time computation will be changed if(strCurrentFile.size() > 0) @@ -182,13 +182,13 @@ bool CCueDocument::Parse(const CStdString &strFile) if (strCurrentFile.length() > 0) ResolvePath(strCurrentFile, strFile); } - else if (StringUtils::StartsWith(strLine,"REM DATE")) + else if (StringUtils::StartsWithNoCase(strLine,"REM DATE")) { int iYear = ExtractNumericInfo(strLine.Mid(8)); if (iYear > 0) m_iYear = iYear; } - else if (StringUtils::StartsWith(strLine,"REM GENRE")) + else if (StringUtils::StartsWithNoCase(strLine,"REM GENRE")) { if (!ExtractQuoteInfo(strLine, m_strGenre)) { @@ -201,13 +201,13 @@ bool CCueDocument::Parse(const CStdString &strFile) } } } - else if (StringUtils::StartsWith(strLine,"REM REPLAYGAIN_ALBUM_GAIN")) + else if (StringUtils::StartsWithNoCase(strLine,"REM REPLAYGAIN_ALBUM_GAIN")) m_replayGainAlbumGain = (float)atof(strLine.Mid(26)); - else if (StringUtils::StartsWith(strLine,"REM REPLAYGAIN_ALBUM_PEAK")) + else if (StringUtils::StartsWithNoCase(strLine,"REM REPLAYGAIN_ALBUM_PEAK")) m_replayGainAlbumPeak = (float)atof(strLine.Mid(26)); - else if (StringUtils::StartsWith(strLine,"REM REPLAYGAIN_TRACK_GAIN") && m_iTotalTracks >= 0) + else if (StringUtils::StartsWithNoCase(strLine,"REM REPLAYGAIN_TRACK_GAIN") && m_iTotalTracks >= 0) m_Track[m_iTotalTracks].replayGainTrackGain = (float)atof(strLine.Mid(26)); - else if (StringUtils::StartsWith(strLine,"REM REPLAYGAIN_TRACK_PEAK") && m_iTotalTracks >= 0) + else if (StringUtils::StartsWithNoCase(strLine,"REM REPLAYGAIN_TRACK_PEAK") && m_iTotalTracks >= 0) m_Track[m_iTotalTracks].replayGainTrackPeak = (float)atof(strLine.Mid(26)); } diff --git a/xbmc/addons/Addon.cpp b/xbmc/addons/Addon.cpp index 07a50cba399b0..4e5667135c7c5 100644 --- a/xbmc/addons/Addon.cpp +++ b/xbmc/addons/Addon.cpp @@ -314,7 +314,7 @@ bool CAddon::MeetsVersion(const AddonVersion &version) const // if the addon is one of xbmc's extension point definitions (addonid starts with "xbmc.") // and the minversion is "0.0.0" i.e. no tag has been specified // we need to assume that the current version is not backwards-compatible and therefore check against the actual version - if (StringUtils::StartsWith(m_props.id, "xbmc.") && + if (StringUtils::StartsWithNoCase(m_props.id, "xbmc.") && (strlen(m_props.minversion.c_str()) == 0 || StringUtils::EqualsNoCase(m_props.minversion.c_str(), "0.0.0"))) return m_props.version == version; diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkDirectSound.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkDirectSound.cpp index 58d8ca680910e..f4e7649aabe5e 100644 --- a/xbmc/cores/AudioEngine/Sinks/AESinkDirectSound.cpp +++ b/xbmc/cores/AudioEngine/Sinks/AESinkDirectSound.cpp @@ -149,7 +149,7 @@ bool CAESinkDirectSound::Initialize(AEAudioFormat &format, std::string &device) std::string deviceFriendlyName; DirectSoundEnumerate(DSEnumCallback, &DSDeviceList); - if(StringUtils::EndsWith(device, std::string("default"))) + if(StringUtils::EndsWithNoCase(device, std::string("default"))) strDeviceGUID = GetDefaultDevice(); for (std::list::iterator itt = DSDeviceList.begin(); itt != DSDeviceList.end(); ++itt) diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.cpp index f049bca9c0f0b..1e87712f4f767 100644 --- a/xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.cpp +++ b/xbmc/cores/AudioEngine/Sinks/AESinkWASAPI.cpp @@ -231,7 +231,7 @@ bool CAESinkWASAPI::Initialize(AEAudioFormat &format, std::string &device) hr = pEnumDevices->GetCount(&uiCount); EXIT_ON_FAILURE(hr, __FUNCTION__": Retrieval of audio endpoint count failed.") - if(StringUtils::EndsWith(device, std::string("default"))) + if(StringUtils::EndsWithNoCase(device, std::string("default"))) bdefault = true; if(!bdefault) diff --git a/xbmc/filesystem/test/TestRarFile.cpp b/xbmc/filesystem/test/TestRarFile.cpp index 9fcf914142b60..0536159dff32c 100644 --- a/xbmc/filesystem/test/TestRarFile.cpp +++ b/xbmc/filesystem/test/TestRarFile.cpp @@ -210,7 +210,7 @@ TEST(TestRarFile, StoredRAR) * an uncompressed RAR archive. See TestRarFile.Read test case. */ strpathinrar = itemlist[1]->GetPath(); - ASSERT_TRUE(StringUtils::EndsWith(strpathinrar, "/reffile.txt", true)); + ASSERT_TRUE(StringUtils::EndsWith(strpathinrar, "/reffile.txt")); EXPECT_EQ(0, XFILE::CFile::Stat(strpathinrar, &stat_buffer)); EXPECT_TRUE((stat_buffer.st_mode & S_IFMT) | S_IFREG); @@ -255,7 +255,7 @@ TEST(TestRarFile, StoredRAR) /* /testsymlink -> testdir/reffile.txt */ strpathinrar = itemlist[2]->GetPath(); - ASSERT_TRUE(StringUtils::EndsWith(strpathinrar, "/testsymlink", true)); + ASSERT_TRUE(StringUtils::EndsWith(strpathinrar, "/testsymlink")); EXPECT_EQ(0, XFILE::CFile::Stat(strpathinrar, &stat_buffer)); EXPECT_TRUE((stat_buffer.st_mode & S_IFMT) | S_IFLNK); @@ -270,7 +270,7 @@ TEST(TestRarFile, StoredRAR) /* /testsymlinksubdir -> testdir/testsubdir/reffile.txt */ strpathinrar = itemlist[3]->GetPath(); - ASSERT_TRUE(StringUtils::EndsWith(strpathinrar, "/testsymlinksubdir", true)); + ASSERT_TRUE(StringUtils::EndsWith(strpathinrar, "/testsymlinksubdir")); EXPECT_EQ(0, XFILE::CFile::Stat(strpathinrar, &stat_buffer)); EXPECT_TRUE((stat_buffer.st_mode & S_IFMT) | S_IFLNK); @@ -280,7 +280,7 @@ TEST(TestRarFile, StoredRAR) /* /testdir/ */ strpathinrar = itemlist[0]->GetPath(); - ASSERT_TRUE(StringUtils::EndsWith(strpathinrar, "/testdir/", true)); + ASSERT_TRUE(StringUtils::EndsWith(strpathinrar, "/testdir/")); EXPECT_EQ(0, XFILE::CFile::Stat(strpathinrar, &stat_buffer)); EXPECT_TRUE((stat_buffer.st_mode & S_IFMT) | S_IFDIR); @@ -345,7 +345,7 @@ TEST(TestRarFile, StoredRAR) /* FIXME: This directory appears a second time as a file */ strpathinrar = itemlist[3]->GetPath(); - ASSERT_TRUE(StringUtils::EndsWith(strpathinrar, "/testdir/testsubdir", true)); + ASSERT_TRUE(StringUtils::EndsWith(strpathinrar, "/testdir/testsubdir")); /* /testdir/testsymlink -> testsubdir/reffile.txt */ strpathinrar = itemlist[4]->GetPath(); @@ -432,7 +432,7 @@ TEST(TestRarFile, NormalRAR) /* /reffile.txt */ strpathinrar = itemlist[1]->GetPath(); - ASSERT_TRUE(StringUtils::EndsWith(strpathinrar, "/reffile.txt", true)); + ASSERT_TRUE(StringUtils::EndsWith(strpathinrar, "/reffile.txt")); EXPECT_EQ(0, XFILE::CFile::Stat(strpathinrar, &stat_buffer)); EXPECT_TRUE((stat_buffer.st_mode & S_IFMT) | S_IFREG); @@ -477,7 +477,7 @@ TEST(TestRarFile, NormalRAR) /* /testsymlink -> testdir/reffile.txt */ strpathinrar = itemlist[2]->GetPath(); - ASSERT_TRUE(StringUtils::EndsWith(strpathinrar, "/testsymlink", true)); + ASSERT_TRUE(StringUtils::EndsWith(strpathinrar, "/testsymlink")); EXPECT_EQ(0, XFILE::CFile::Stat(strpathinrar, &stat_buffer)); EXPECT_TRUE((stat_buffer.st_mode & S_IFMT) | S_IFLNK); @@ -492,7 +492,7 @@ TEST(TestRarFile, NormalRAR) /* /testsymlinksubdir -> testdir/testsubdir/reffile.txt */ strpathinrar = itemlist[3]->GetPath(); - ASSERT_TRUE(StringUtils::EndsWith(strpathinrar, "/testsymlinksubdir", true)); + ASSERT_TRUE(StringUtils::EndsWith(strpathinrar, "/testsymlinksubdir")); EXPECT_EQ(0, XFILE::CFile::Stat(strpathinrar, &stat_buffer)); EXPECT_TRUE((stat_buffer.st_mode & S_IFMT) | S_IFLNK); @@ -502,7 +502,7 @@ TEST(TestRarFile, NormalRAR) /* /testdir/ */ strpathinrar = itemlist[0]->GetPath(); - ASSERT_TRUE(StringUtils::EndsWith(strpathinrar, "/testdir/", true)); + ASSERT_TRUE(StringUtils::EndsWith(strpathinrar, "/testdir/")); EXPECT_EQ(0, XFILE::CFile::Stat(strpathinrar, &stat_buffer)); EXPECT_TRUE((stat_buffer.st_mode & S_IFMT) | S_IFDIR); @@ -567,7 +567,7 @@ TEST(TestRarFile, NormalRAR) /* FIXME: This directory appears a second time as a file */ strpathinrar = itemlist[3]->GetPath(); - ASSERT_TRUE(StringUtils::EndsWith(strpathinrar, "/testdir/testsubdir", true)); + ASSERT_TRUE(StringUtils::EndsWith(strpathinrar, "/testdir/testsubdir")); /* /testdir/testsymlink -> testsubdir/reffile.txt */ strpathinrar = itemlist[4]->GetPath(); diff --git a/xbmc/interfaces/generic/ScriptInvocationManager.cpp b/xbmc/interfaces/generic/ScriptInvocationManager.cpp index 3e47fa506dcde..b3c98f0dbb7b0 100644 --- a/xbmc/interfaces/generic/ScriptInvocationManager.cpp +++ b/xbmc/interfaces/generic/ScriptInvocationManager.cpp @@ -122,7 +122,7 @@ void CScriptInvocationManager::RegisterLanguageInvocationHandler(ILanguageInvoca string ext = extension; StringUtils::ToLower(ext); - if (!StringUtils::StartsWith(ext, ".")) + if (!StringUtils::StartsWithNoCase(ext, ".")) ext = "." + ext; CSingleLock lock(m_critSection); diff --git a/xbmc/music/windows/GUIWindowMusicBase.cpp b/xbmc/music/windows/GUIWindowMusicBase.cpp index 875a3f21c49ab..72566f763e53e 100644 --- a/xbmc/music/windows/GUIWindowMusicBase.cpp +++ b/xbmc/music/windows/GUIWindowMusicBase.cpp @@ -1154,7 +1154,7 @@ bool CGUIWindowMusicBase::CheckFilterAdvanced(CFileItemList &items) const bool CGUIWindowMusicBase::CanContainFilter(const CStdString &strDirectory) const { - return StringUtils::StartsWith(strDirectory, "musicdb://"); + return StringUtils::StartsWithNoCase(strDirectory, "musicdb://"); } void CGUIWindowMusicBase::OnInitWindow() diff --git a/xbmc/music/windows/GUIWindowMusicNav.cpp b/xbmc/music/windows/GUIWindowMusicNav.cpp index ef34b82587007..90f83272e08ab 100644 --- a/xbmc/music/windows/GUIWindowMusicNav.cpp +++ b/xbmc/music/windows/GUIWindowMusicNav.cpp @@ -447,7 +447,7 @@ void CGUIWindowMusicNav::GetContextButtons(int itemNumber, CContextButtons &butt { if (!item->m_bIsFolder) // music video buttons.Add(CONTEXT_BUTTON_INFO, 20393); - if (StringUtils::StartsWith(item->GetPath(), "videodb://musicvideos/artist/") && item->m_bIsFolder) + if (StringUtils::StartsWithNoCase(item->GetPath(), "videodb://musicvideos/artist/") && item->m_bIsFolder) { long idArtist = m_musicdatabase.GetArtistByName(m_vecItems->Get(itemNumber)->GetLabel()); if (idArtist > - 1) @@ -565,7 +565,7 @@ bool CGUIWindowMusicNav::OnContextButton(int itemNumber, CONTEXT_BUTTON button) return CGUIWindowMusicBase::OnContextButton(itemNumber,button); // music videos - artists - if (StringUtils::StartsWith(item->GetPath(), "videodb://musicvideos/artists/")) + if (StringUtils::StartsWithNoCase(item->GetPath(), "videodb://musicvideos/artists/")) { long idArtist = m_musicdatabase.GetArtistByName(item->GetLabel()); if (idArtist == -1) @@ -582,7 +582,7 @@ bool CGUIWindowMusicNav::OnContextButton(int itemNumber, CONTEXT_BUTTON button) } // music videos - albums - if (StringUtils::StartsWith(item->GetPath(), "videodb://musicvideos/albums/")) + if (StringUtils::StartsWithNoCase(item->GetPath(), "videodb://musicvideos/albums/")) { long idAlbum = m_musicdatabase.GetAlbumByName(item->GetLabel()); if (idAlbum == -1) diff --git a/xbmc/network/WebServer.cpp b/xbmc/network/WebServer.cpp index 68f9cbf83c464..82625e19b5666 100644 --- a/xbmc/network/WebServer.cpp +++ b/xbmc/network/WebServer.cpp @@ -965,7 +965,7 @@ int64_t CWebServer::ParseRangeHeader(const std::string &rangeHeaderValue, int64_ firstPosition = 0; lastPosition = totalLength - 1; - if (rangeHeaderValue.empty() || !StringUtils::StartsWith(rangeHeaderValue, "bytes=")) + if (rangeHeaderValue.empty() || !StringUtils::StartsWithNoCase(rangeHeaderValue, "bytes=")) return totalLength; int64_t rangesLength = 0; diff --git a/xbmc/network/upnp/UPnPServer.cpp b/xbmc/network/upnp/UPnPServer.cpp index dd92e6f3f8eeb..f737495abc46b 100644 --- a/xbmc/network/upnp/UPnPServer.cpp +++ b/xbmc/network/upnp/UPnPServer.cpp @@ -547,7 +547,7 @@ CUPnPServer::OnBrowseMetadata(PLT_ActionReference& action, // attempt to determine the parent of this item CStdString parent; - if (URIUtils::IsVideoDb((const char*)id) || URIUtils::IsMusicDb((const char*)id) || StringUtils::StartsWith((const char*)id, "library://video/")) { + if (URIUtils::IsVideoDb((const char*)id) || URIUtils::IsMusicDb((const char*)id) || StringUtils::StartsWithNoCase((const char*)id, "library://video/")) { if (!URIUtils::GetParentPath((const char*)id, parent)) { parent = "0"; } @@ -560,11 +560,11 @@ CUPnPServer::OnBrowseMetadata(PLT_ActionReference& action, // however this is quicker to implement and subsequently purge when a // better solution presents itself CStdString child_id((const char*)id); - if (StringUtils::StartsWith(child_id, "special://musicplaylists/")) parent = "musicdb://"; - else if (StringUtils::StartsWith(child_id, "special://videoplaylists/")) parent = "library://video/"; - else if (StringUtils::StartsWith(child_id, "sources://video/")) parent = "library://video/"; - else if (StringUtils::StartsWith(child_id, "special://profile/playlists/music/")) parent = "special://musicplaylists/"; - else if (StringUtils::StartsWith(child_id, "special://profile/playlists/video/")) parent = "special://videoplaylists/"; + if (StringUtils::StartsWithNoCase(child_id, "special://musicplaylists/")) parent = "musicdb://"; + else if (StringUtils::StartsWithNoCase(child_id, "special://videoplaylists/")) parent = "library://video/"; + else if (StringUtils::StartsWithNoCase(child_id, "sources://video/")) parent = "library://video/"; + else if (StringUtils::StartsWithNoCase(child_id, "special://profile/playlists/music/")) parent = "special://musicplaylists/"; + else if (StringUtils::StartsWithNoCase(child_id, "special://profile/playlists/video/")) parent = "special://videoplaylists/"; else parent = "sources://video/"; // this can only match video sources } @@ -729,13 +729,13 @@ CUPnPServer::BuildResponse(PLT_ActionReference& action, NPT_Reference thumb_loader; if (URIUtils::IsVideoDb(items.GetPath()) || - StringUtils::StartsWith(items.GetPath(), "library://video/") || - StringUtils::StartsWith(items.GetPath(), "special://profile/playlists/video/")) { + StringUtils::StartsWithNoCase(items.GetPath(), "library://video/") || + StringUtils::StartsWithNoCase(items.GetPath(), "special://profile/playlists/video/")) { thumb_loader = NPT_Reference(new CVideoThumbLoader()); } else if (URIUtils::IsMusicDb(items.GetPath()) || - StringUtils::StartsWith(items.GetPath(), "special://profile/playlists/music/")) { + StringUtils::StartsWithNoCase(items.GetPath(), "special://profile/playlists/music/")) { thumb_loader = NPT_Reference(new CMusicThumbLoader()); } diff --git a/xbmc/settings/SettingDependency.cpp b/xbmc/settings/SettingDependency.cpp index a4959e41f2e45..32ac5d1ce92b1 100644 --- a/xbmc/settings/SettingDependency.cpp +++ b/xbmc/settings/SettingDependency.cpp @@ -131,12 +131,12 @@ bool CSettingDependencyCondition::setTarget(const std::string &target) bool CSettingDependencyCondition::setOperator(const std::string &op) { size_t length = 0; - if (StringUtils::EndsWith(op, "is")) + if (StringUtils::EndsWithNoCase(op, "is")) { m_operator = SettingDependencyOperatorEquals; length = 2; } - else if (StringUtils::EndsWith(op, "contains")) + else if (StringUtils::EndsWithNoCase(op, "contains")) { m_operator = SettingDependencyOperatorContains; length = 8; diff --git a/xbmc/settings/SettingsManager.cpp b/xbmc/settings/SettingsManager.cpp index ae676c7920d14..9c65ab53caaf0 100644 --- a/xbmc/settings/SettingsManager.cpp +++ b/xbmc/settings/SettingsManager.cpp @@ -476,7 +476,7 @@ bool CSettingsManager::GetBool(const std::string &id) const if (setting == NULL || setting->GetType() != SettingTypeBool) { // Backward compatibility (skins use this setting) - if (setting == NULL && StringUtils::EqualsNoCase(id.c_str(), "lookandfeel.enablemouse")) + if (setting == NULL && StringUtils::EqualsNoCase(id, "lookandfeel.enablemouse")) return GetBool("input.enablemouse"); return false; diff --git a/xbmc/settings/SkinSettings.cpp b/xbmc/settings/SkinSettings.cpp index 31a885c720957..13aba27e2f671 100644 --- a/xbmc/settings/SkinSettings.cpp +++ b/xbmc/settings/SkinSettings.cpp @@ -175,13 +175,13 @@ void CSkinSettings::Reset() // clear all the settings and strings from this skin. for (map::iterator it = m_bools.begin(); it != m_bools.end(); ++it) { - if (StringUtils::StartsWith(it->second.name, currentSkin)) + if (StringUtils::StartsWithNoCase(it->second.name, currentSkin)) it->second.value = false; } for (map::iterator it = m_strings.begin(); it != m_strings.end(); ++it) { - if (StringUtils::StartsWith(it->second.name, currentSkin)) + if (StringUtils::StartsWithNoCase(it->second.name, currentSkin)) it->second.value.clear(); } diff --git a/xbmc/utils/FileUtils.cpp b/xbmc/utils/FileUtils.cpp index 220f8ac8bf3a2..18511ee1f8f4c 100644 --- a/xbmc/utils/FileUtils.cpp +++ b/xbmc/utils/FileUtils.cpp @@ -116,27 +116,27 @@ bool CFileUtils::RemoteAccessAllowed(const CStdString &strPath) while (URIUtils::IsInArchive(realPath)) realPath = CURL(realPath).GetHostName(); - if (StringUtils::StartsWith(realPath, "virtualpath://upnproot/")) + if (StringUtils::StartsWithNoCase(realPath, "virtualpath://upnproot/")) return true; - else if (StringUtils::StartsWith(realPath, "musicdb://")) + else if (StringUtils::StartsWithNoCase(realPath, "musicdb://")) return true; - else if (StringUtils::StartsWith(realPath, "videodb://")) + else if (StringUtils::StartsWithNoCase(realPath, "videodb://")) return true; - else if (StringUtils::StartsWith(realPath, "library://video")) + else if (StringUtils::StartsWithNoCase(realPath, "library://video")) return true; - else if (StringUtils::StartsWith(realPath, "sources://video")) + else if (StringUtils::StartsWithNoCase(realPath, "sources://video")) return true; - else if (StringUtils::StartsWith(realPath, "special://musicplaylists")) + else if (StringUtils::StartsWithNoCase(realPath, "special://musicplaylists")) return true; - else if (StringUtils::StartsWith(realPath, "special://profile/playlists")) + else if (StringUtils::StartsWithNoCase(realPath, "special://profile/playlists")) return true; - else if (StringUtils::StartsWith(realPath, "special://videoplaylists")) + else if (StringUtils::StartsWithNoCase(realPath, "special://videoplaylists")) return true; - else if (StringUtils::StartsWith(realPath, "addons://sources")) + else if (StringUtils::StartsWithNoCase(realPath, "addons://sources")) return true; - else if (StringUtils::StartsWith(realPath, "upnp://")) + else if (StringUtils::StartsWithNoCase(realPath, "upnp://")) return true; - else if (StringUtils::StartsWith(realPath, "plugin://")) + else if (StringUtils::StartsWithNoCase(realPath, "plugin://")) return true; bool isSource; for (unsigned int index = 0; index < SourcesSize; index++) diff --git a/xbmc/utils/LegacyPathTranslation.cpp b/xbmc/utils/LegacyPathTranslation.cpp index 035eadf8b4f64..7658b360661c7 100644 --- a/xbmc/utils/LegacyPathTranslation.cpp +++ b/xbmc/utils/LegacyPathTranslation.cpp @@ -95,7 +95,7 @@ std::string CLegacyPathTranslation::TranslatePath(const std::string &legacyPath, std::string newPath = legacyPath; for (size_t index = 0; index < translationMapSize; index++) { - if (StringUtils::StartsWith(newPath, translationMap[index].legacyPath)) + if (StringUtils::StartsWithNoCase(newPath, translationMap[index].legacyPath)) { StringUtils::Replace(newPath, translationMap[index].legacyPath, translationMap[index].newPath); break; diff --git a/xbmc/utils/StringUtils.cpp b/xbmc/utils/StringUtils.cpp index 0a41f0543da7a..49c4e88fc33fb 100644 --- a/xbmc/utils/StringUtils.cpp +++ b/xbmc/utils/StringUtils.cpp @@ -114,12 +114,26 @@ void StringUtils::ToLower(string &str) bool StringUtils::EqualsNoCase(const std::string &str1, const std::string &str2) { - string tmp1 = str1; - string tmp2 = str2; - ToLower(tmp1); - ToLower(tmp2); - - return tmp1.compare(tmp2) == 0; + return EqualsNoCase(str1.c_str(), str2.c_str()); +} + +bool StringUtils::EqualsNoCase(const std::string &str1, const char *s2) +{ + return EqualsNoCase(str1.c_str(), s2); +} + +bool StringUtils::EqualsNoCase(const char *s1, const char *s2) +{ + int c1, c2; // Yes, because the return type of tolower() is int. + // To make these chars would be to introduce an unnecesary extra bitmask/zero-extend (effectively caller-narowing) into the binary. + do + { + c1 = ::tolower(*s1++); + c2 = ::tolower(*s2++); + if (c1 != c2) // This includes the possibility that one of the characters is the null-terminator, which implies a string mismatch. + return false; + } while (c2 != '\0'); // At this point, we know c1 == c2, so there's no need to test them both. + return true; } string StringUtils::Left(const string &str, size_t count) @@ -225,24 +239,95 @@ int StringUtils::Replace(std::string &str, const std::string &oldStr, const std: return replacedChars; } -bool StringUtils::StartsWith(const std::string &str, const std::string &str2, bool useCase /* = false */) +bool StringUtils::StartsWith(const std::string &str1, const std::string &str2) { - std::string left = StringUtils::Left(str, str2.size()); - - if (useCase) - return left.compare(str2) == 0; + return str1.compare(0, str2.size(), str2) == 0; +} - return StringUtils::EqualsNoCase(left, str2); +bool StringUtils::StartsWith(const std::string &str1, const char *s2) +{ + return StartsWith(str1.c_str(), s2); } -bool StringUtils::EndsWith(const std::string &str, const std::string &str2, bool useCase /* = false */) +bool StringUtils::StartsWith(const char *s1, const char *s2) { - std::string right = StringUtils::Right(str, str2.size()); - - if (useCase) - return right.compare(str2) == 0; + while (*s2 != '\0') + { + if (*s1 != *s2) + return false; + s1++; + s2++; + } + return true; +} - return StringUtils::EqualsNoCase(right, str2); +bool StringUtils::StartsWithNoCase(const std::string &str1, const std::string &str2) +{ + return StartsWithNoCase(str1.c_str(), str2.c_str()); +} + +bool StringUtils::StartsWithNoCase(const std::string &str1, const char *s2) +{ + return StartsWithNoCase(str1.c_str(), s2); +} + +bool StringUtils::StartsWithNoCase(const char *s1, const char *s2) +{ + while (*s2 != '\0') + { + if (::tolower(*s1) != ::tolower(*s2)) + return false; + s1++; + s2++; + } + return true; +} + +bool StringUtils::EndsWith(const std::string &str1, const std::string &str2) +{ + if (str1.size() < str2.size()) + return false; + return str1.compare(str1.size() - str2.size(), str2.size(), str2) == 0; +} + +bool StringUtils::EndsWith(const std::string &str1, const char *s2) +{ + size_t len2 = strlen(s2); + if (str1.size() < len2) + return false; + return str1.compare(str1.size() - len2, len2, s2) == 0; +} + +bool StringUtils::EndsWithNoCase(const std::string &str1, const std::string &str2) +{ + if (str1.size() < str2.size()) + return false; + const char *s1 = str1.c_str() + str1.size() - str2.size(); + const char *s2 = str2.c_str(); + while (*s2 != '\0') + { + if (::tolower(*s1) != ::tolower(*s2)) + return false; + s1++; + s2++; + } + return true; +} + +bool StringUtils::EndsWithNoCase(const std::string &str1, const char *s2) +{ + size_t len2 = strlen(s2); + if (str1.size() < len2) + return false; + const char *s1 = str1.c_str() + str1.size() - len2; + while (*s2 != '\0') + { + if (::tolower(*s1) != ::tolower(*s2)) + return false; + s1++; + s2++; + } + return true; } void StringUtils::JoinString(const CStdStringArray &strings, const CStdString& delimiter, CStdString& result) diff --git a/xbmc/utils/StringUtils.h b/xbmc/utils/StringUtils.h index 343162525f14e..84fa9ae4d09ce 100644 --- a/xbmc/utils/StringUtils.h +++ b/xbmc/utils/StringUtils.h @@ -55,6 +55,8 @@ class StringUtils static void ToUpper(std::string &str); static void ToLower(std::string &str); static bool EqualsNoCase(const std::string &str1, const std::string &str2); + static bool EqualsNoCase(const std::string &str1, const char *s2); + static bool EqualsNoCase(const char *s1, const char *s2); static std::string Left(const std::string &str, size_t count); static std::string Mid(const std::string &str, size_t first, size_t count = std::string::npos); static std::string Right(const std::string &str, size_t count); @@ -64,8 +66,16 @@ class StringUtils static std::string& RemoveDuplicatedSpacesAndTabs(std::string& str); static int Replace(std::string &str, char oldChar, char newChar); static int Replace(std::string &str, const std::string &oldStr, const std::string &newStr); - static bool StartsWith(const std::string &str, const std::string &str2, bool useCase = false); - static bool EndsWith(const std::string &str, const std::string &str2, bool useCase = false); + static bool StartsWith(const std::string &str1, const std::string &str2); + static bool StartsWith(const std::string &str1, const char *s2); + static bool StartsWith(const char *s1, const char *s2); + static bool StartsWithNoCase(const std::string &str1, const std::string &str2); + static bool StartsWithNoCase(const std::string &str1, const char *s2); + static bool StartsWithNoCase(const char *s1, const char *s2); + static bool EndsWith(const std::string &str1, const std::string &str2); + static bool EndsWith(const std::string &str1, const char *s2); + static bool EndsWithNoCase(const std::string &str1, const std::string &str2); + static bool EndsWithNoCase(const std::string &str1, const char *s2); static void JoinString(const CStdStringArray &strings, const CStdString& delimiter, CStdString& result); static CStdString JoinString(const CStdStringArray &strings, const CStdString& delimiter); diff --git a/xbmc/utils/StringValidation.cpp b/xbmc/utils/StringValidation.cpp index 3a1df6decde83..79ac19fe2a043 100644 --- a/xbmc/utils/StringValidation.cpp +++ b/xbmc/utils/StringValidation.cpp @@ -37,7 +37,7 @@ bool StringValidation::IsTime(const std::string &input, void *data) std::string strTime = input; StringUtils::Trim(strTime); - if (StringUtils::EndsWith(strTime, " min")) + if (StringUtils::EndsWithNoCase(strTime, " min")) { strTime = StringUtils::Left(strTime, strTime.size() - 4); StringUtils::TrimRight(strTime); diff --git a/xbmc/utils/test/TestStringUtils.cpp b/xbmc/utils/test/TestStringUtils.cpp index 3bffc07f45ae1..c7d732d5427ed 100644 --- a/xbmc/utils/test/TestStringUtils.cpp +++ b/xbmc/utils/test/TestStringUtils.cpp @@ -175,28 +175,28 @@ TEST(TestStringUtils, StartsWith) { std::string refstr = "test"; - EXPECT_FALSE(StringUtils::StartsWith(refstr, "x")); + EXPECT_FALSE(StringUtils::StartsWithNoCase(refstr, "x")); - EXPECT_TRUE(StringUtils::StartsWith(refstr, "te", true)); - EXPECT_TRUE(StringUtils::StartsWith(refstr, "test", true)); - EXPECT_FALSE(StringUtils::StartsWith(refstr, "Te", true)); + EXPECT_TRUE(StringUtils::StartsWith(refstr, "te")); + EXPECT_TRUE(StringUtils::StartsWith(refstr, "test")); + EXPECT_FALSE(StringUtils::StartsWith(refstr, "Te")); - EXPECT_TRUE(StringUtils::StartsWith(refstr, "Te", false)); - EXPECT_TRUE(StringUtils::StartsWith(refstr, "TesT", false)); + EXPECT_TRUE(StringUtils::StartsWithNoCase(refstr, "Te")); + EXPECT_TRUE(StringUtils::StartsWithNoCase(refstr, "TesT")); } TEST(TestStringUtils, EndsWith) { std::string refstr = "test"; - EXPECT_FALSE(StringUtils::EndsWith(refstr, "x")); + EXPECT_FALSE(StringUtils::EndsWithNoCase(refstr, "x")); - EXPECT_TRUE(StringUtils::EndsWith(refstr, "st", true)); - EXPECT_TRUE(StringUtils::EndsWith(refstr, "test", true)); - EXPECT_FALSE(StringUtils::EndsWith(refstr, "sT", true)); + EXPECT_TRUE(StringUtils::EndsWith(refstr, "st")); + EXPECT_TRUE(StringUtils::EndsWith(refstr, "test")); + EXPECT_FALSE(StringUtils::EndsWith(refstr, "sT")); - EXPECT_TRUE(StringUtils::EndsWith(refstr, "sT", false)); - EXPECT_TRUE(StringUtils::EndsWith(refstr, "TesT", false)); + EXPECT_TRUE(StringUtils::EndsWithNoCase(refstr, "sT")); + EXPECT_TRUE(StringUtils::EndsWithNoCase(refstr, "TesT")); } TEST(TestStringUtils, JoinString) diff --git a/xbmc/video/windows/GUIWindowVideoBase.cpp b/xbmc/video/windows/GUIWindowVideoBase.cpp index 57c66415bbf99..b53144778f37e 100644 --- a/xbmc/video/windows/GUIWindowVideoBase.cpp +++ b/xbmc/video/windows/GUIWindowVideoBase.cpp @@ -1791,7 +1791,7 @@ bool CGUIWindowVideoBase::CheckFilterAdvanced(CFileItemList &items) const bool CGUIWindowVideoBase::CanContainFilter(const CStdString &strDirectory) const { - return StringUtils::StartsWith(strDirectory, "videodb://"); + return StringUtils::StartsWithNoCase(strDirectory, "videodb://"); } void CGUIWindowVideoBase::AddToDatabase(int iItem) diff --git a/xbmc/video/windows/GUIWindowVideoNav.cpp b/xbmc/video/windows/GUIWindowVideoNav.cpp index b888ea7f1cd6a..1dfbb521657b7 100644 --- a/xbmc/video/windows/GUIWindowVideoNav.cpp +++ b/xbmc/video/windows/GUIWindowVideoNav.cpp @@ -674,7 +674,7 @@ void CGUIWindowVideoNav::OnDeleteItem(CFileItemPtr pItem) !pItem->GetPath().Left(9).Equals("newtag://")) CGUIWindowVideoBase::OnDeleteItem(pItem); } - else if (StringUtils::StartsWith(pItem->GetPath(), "videodb://movies/sets/") && + else if (StringUtils::StartsWithNoCase(pItem->GetPath(), "videodb://movies/sets/") && pItem->GetPath().size() > 22 && pItem->m_bIsFolder) { CGUIDialogYesNo* pDialog = (CGUIDialogYesNo*)g_windowManager.GetWindow(WINDOW_DIALOG_YES_NO); @@ -967,7 +967,7 @@ void CGUIWindowVideoNav::GetContextButtons(int itemNumber, CContextButtons &butt if (node == NODE_TYPE_SEASONS && item->m_bIsFolder) buttons.Add(CONTEXT_BUTTON_SET_SEASON_ART, 13511); - if (StringUtils::StartsWith(item->GetPath(), "videodb://movies/sets/") && item->GetPath().size() > 22 && item->m_bIsFolder) // sets + if (StringUtils::StartsWithNoCase(item->GetPath(), "videodb://movies/sets/") && item->GetPath().size() > 22 && item->m_bIsFolder) // sets { buttons.Add(CONTEXT_BUTTON_SET_MOVIESET_ART, 13511); buttons.Add(CONTEXT_BUTTON_MOVIESET_ADD_REMOVE_ITEMS, 20465); @@ -991,7 +991,7 @@ void CGUIWindowVideoNav::GetContextButtons(int itemNumber, CContextButtons &butt if (node == NODE_TYPE_ACTOR && !dir.IsAllItem(item->GetPath()) && item->m_bIsFolder) { - if (StringUtils::StartsWith(m_vecItems->GetPath(), "videodb://musicvideos")) // mvids + if (StringUtils::StartsWithNoCase(m_vecItems->GetPath(), "videodb://musicvideos")) // mvids buttons.Add(CONTEXT_BUTTON_SET_ARTIST_THUMB, 13359); else buttons.Add(CONTEXT_BUTTON_SET_ACTOR_THUMB, 20403); diff --git a/xbmc/view/ViewDatabase.cpp b/xbmc/view/ViewDatabase.cpp index 25300f19a66ca..1425a4cb78cf6 100644 --- a/xbmc/view/ViewDatabase.cpp +++ b/xbmc/view/ViewDatabase.cpp @@ -93,9 +93,9 @@ bool CViewDatabase::UpdateOldVersion(int version) { std::string originalPath = m_pDS->fv(1).get_asString(); std::string path = originalPath; - if (StringUtils::StartsWith(path, "musicdb://")) + if (StringUtils::StartsWithNoCase(path, "musicdb://")) path = CLegacyPathTranslation::TranslateMusicDbPath(path); - else if (StringUtils::StartsWith(path, "videodb://")) + else if (StringUtils::StartsWithNoCase(path, "videodb://")) path = CLegacyPathTranslation::TranslateVideoDbPath(path); if (!StringUtils::EqualsNoCase(path, originalPath))