Browse Source

Don't create temporary containers just to iterate over them

Stops temporary containers being created needlessly due to API misuse.
For example, it’s common for developers to assume QHash::values() and
QHash::keys() are free and abuse them, failing to realize their
implementation internally actually iterates the whole container, allocates
memory, and fills a new QList.

Added a removeIf generic algorithm, similar to std ones. We can't use std
algorithms with Qt dictionaries because Qt iterators have different
behavior from the std ones.

Found using clazy.
adaptive-webui-19844
Luís Pereira 7 years ago committed by Vladimir Golovnev (Glassez)
parent
commit
ac42ccb5e4
No known key found for this signature in database
GPG Key ID: 52A2C7DEE2DFA6F7
  1. 5
      src/app/upgrade.h
  2. 1
      src/base/CMakeLists.txt
  3. 41
      src/base/algorithm.h
  4. 1
      src/base/base.pri
  5. 30
      src/base/bittorrent/session.cpp
  6. 27
      src/base/filesystemwatcher.cpp
  7. 6
      src/base/net/portforwarder.cpp
  8. 5
      src/base/search/searchpluginmanager.cpp
  9. 3
      src/gui/categoryfiltermodel.cpp
  10. 4
      src/gui/categoryfilterwidget.cpp
  11. 4
      src/gui/search/pluginselectdlg.cpp
  12. 3
      src/gui/transferlistfilterswidget.cpp
  13. 8
      src/webui/api/appcontroller.cpp
  14. 44
      src/webui/api/synccontroller.cpp

5
src/app/upgrade.h

@ -186,9 +186,10 @@ bool upgrade(bool ask = true)
} }
} }
foreach (const QString &hash, oldResumeData.keys()) { for (auto i = oldResumeData.cbegin(); i != oldResumeData.cend(); ++i) {
QVariantHash oldTorrent = oldResumeData[hash].toHash(); const QVariantHash oldTorrent = i.value().toHash();
if (oldTorrent.value("is_magnet", false).toBool()) { if (oldTorrent.value("is_magnet", false).toBool()) {
const QString &hash = i.key();
libtorrent::entry resumeData; libtorrent::entry resumeData;
resumeData["qBt-magnetUri"] = oldTorrent.value("magnet_uri").toString().toStdString(); resumeData["qBt-magnetUri"] = oldTorrent.value("magnet_uri").toString().toStdString();
resumeData["qBt-paused"] = false; resumeData["qBt-paused"] = false;

1
src/base/CMakeLists.txt

@ -55,6 +55,7 @@ utils/net.h
utils/random.h utils/random.h
utils/string.h utils/string.h
utils/version.h utils/version.h
algorithm.h
asyncfilestorage.h asyncfilestorage.h
exceptions.h exceptions.h
filesystemwatcher.h filesystemwatcher.h

41
src/base/algorithm.h

@ -0,0 +1,41 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2018 Vladimir Golovnev <glassez@yandex.ru>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#pragma once
namespace Dict
{
// To be used with QMap, QHash and it's variants
template <typename Dictionary, typename BinaryPredicate>
void removeIf(Dictionary &&dict, BinaryPredicate p)
{
auto it = dict.begin();
while (it != dict.end())
it = (p(it.key(), it.value()) ? dict.erase(it) : it + 1);
}
}

1
src/base/base.pri

@ -1,4 +1,5 @@
HEADERS += \ HEADERS += \
$$PWD/algorithm.h \
$$PWD/asyncfilestorage.h \ $$PWD/asyncfilestorage.h \
$$PWD/bittorrent/addtorrentparams.h \ $$PWD/bittorrent/addtorrentparams.h \
$$PWD/bittorrent/cachestatus.h \ $$PWD/bittorrent/cachestatus.h \

30
src/base/bittorrent/session.cpp

@ -71,6 +71,7 @@
#include <libtorrent/session_status.hpp> #include <libtorrent/session_status.hpp>
#include <libtorrent/torrent_info.hpp> #include <libtorrent/torrent_info.hpp>
#include "base/algorithm.h"
#include "base/logger.h" #include "base/logger.h"
#include "base/net/downloadhandler.h" #include "base/net/downloadhandler.h"
#include "base/net/downloadmanager.h" #include "base/net/downloadmanager.h"
@ -128,16 +129,16 @@ namespace
QStringMap map_cast(const QVariantMap &map) QStringMap map_cast(const QVariantMap &map)
{ {
QStringMap result; QStringMap result;
foreach (const QString &key, map.keys()) for (auto i = map.cbegin(); i != map.cend(); ++i)
result[key] = map.value(key).toString(); result[i.key()] = i.value().toString();
return result; return result;
} }
QVariantMap map_cast(const QStringMap &map) QVariantMap map_cast(const QStringMap &map)
{ {
QVariantMap result; QVariantMap result;
foreach (const QString &key, map.keys()) for (auto i = map.cbegin(); i != map.cend(); ++i)
result[key] = map.value(key); result[i.key()] = i.value();
return result; return result;
} }
@ -199,7 +200,8 @@ namespace
{ {
QStringMap expanded = categories; QStringMap expanded = categories;
foreach (const QString &category, categories.keys()) { for (auto i = categories.cbegin(); i != categories.cend(); ++i) {
const QString &category = i.key();
foreach (const QString &subcat, Session::expandCategory(category)) { foreach (const QString &subcat, Session::expandCategory(category)) {
if (!expanded.contains(subcat)) if (!expanded.contains(subcat))
expanded[subcat] = ""; expanded[subcat] = "";
@ -785,14 +787,16 @@ bool Session::removeCategory(const QString &name)
bool result = false; bool result = false;
if (isSubcategoriesEnabled()) { if (isSubcategoriesEnabled()) {
// remove subcategories // remove subcategories
QString test = name + "/"; const QString test = name + "/";
foreach (const QString &category, m_categories.keys()) { Dict::removeIf(m_categories, [this, &test, &result](const QString &category, const QString &)
{
if (category.startsWith(test)) { if (category.startsWith(test)) {
m_categories.remove(category);
result = true; result = true;
emit categoryRemoved(category); emit categoryRemoved(category);
return true;
} }
} return false;
});
} }
result = (m_categories.remove(name) > 0) || result; result = (m_categories.remove(name) > 0) || result;
@ -2003,8 +2007,8 @@ void Session::decreaseTorrentsPriority(const QStringList &hashes)
torrentQueue.pop(); torrentQueue.pop();
} }
foreach (const InfoHash &hash, m_loadedMetadata.keys()) for (auto i = m_loadedMetadata.cbegin(); i != m_loadedMetadata.cend(); ++i)
torrentQueuePositionBottom(m_nativeSession->find_torrent(hash)); torrentQueuePositionBottom(m_nativeSession->find_torrent(i.key()));
} }
void Session::topTorrentsPriority(const QStringList &hashes) void Session::topTorrentsPriority(const QStringList &hashes)
@ -2048,8 +2052,8 @@ void Session::bottomTorrentsPriority(const QStringList &hashes)
torrentQueue.pop(); torrentQueue.pop();
} }
foreach (const InfoHash &hash, m_loadedMetadata.keys()) for (auto i = m_loadedMetadata.cbegin(); i != m_loadedMetadata.cend(); ++i)
torrentQueuePositionBottom(m_nativeSession->find_torrent(hash)); torrentQueuePositionBottom(m_nativeSession->find_torrent(i.key()));
} }
QHash<InfoHash, TorrentHandle *> Session::torrents() const QHash<InfoHash, TorrentHandle *> Session::torrents() const

27
src/base/filesystemwatcher.cpp

@ -12,6 +12,7 @@
#endif #endif
#endif #endif
#include "algorithm.h"
#include "base/bittorrent/magneturi.h" #include "base/bittorrent/magneturi.h"
#include "base/bittorrent/torrentinfo.h" #include "base/bittorrent/torrentinfo.h"
#include "base/preferences.h" #include "base/preferences.h"
@ -145,22 +146,24 @@ void FileSystemWatcher::processPartialTorrents()
QStringList noLongerPartial; QStringList noLongerPartial;
// Check which torrents are still partial // Check which torrents are still partial
foreach (const QString &torrentPath, m_partialTorrents.keys()) { Dict::removeIf(m_partialTorrents, [&noLongerPartial](const QString &torrentPath, int &value)
if (!QFile::exists(torrentPath)) { {
m_partialTorrents.remove(torrentPath); if (!QFile::exists(torrentPath))
} return true;
else if (BitTorrent::TorrentInfo::loadFromFile(torrentPath).isValid()) {
if (BitTorrent::TorrentInfo::loadFromFile(torrentPath).isValid()) {
noLongerPartial << torrentPath; noLongerPartial << torrentPath;
m_partialTorrents.remove(torrentPath); return true;
} }
else if (m_partialTorrents[torrentPath] >= MAX_PARTIAL_RETRIES) {
m_partialTorrents.remove(torrentPath); if (value >= MAX_PARTIAL_RETRIES) {
QFile::rename(torrentPath, torrentPath + ".invalid"); QFile::rename(torrentPath, torrentPath + ".invalid");
return true;
} }
else {
++m_partialTorrents[torrentPath]; ++value;
} return false;
} });
// Stop the partial timer if necessary // Stop the partial timer if necessary
if (m_partialTorrents.empty()) { if (m_partialTorrents.empty()) {

6
src/base/net/portforwarder.cpp

@ -121,8 +121,10 @@ void PortForwarder::start()
settingsPack.set_bool(libt::settings_pack::enable_natpmp, true); settingsPack.set_bool(libt::settings_pack::enable_natpmp, true);
m_provider->apply_settings(settingsPack); m_provider->apply_settings(settingsPack);
#endif #endif
foreach (quint16 port, m_mappedPorts.keys()) for (auto i = m_mappedPorts.begin(); i != m_mappedPorts.end(); ++i) {
m_mappedPorts[port] = m_provider->add_port_mapping(libt::session::tcp, port, port); // quint16 port = i.key();
i.value() = m_provider->add_port_mapping(libt::session::tcp, i.key(), i.key());
}
m_active = true; m_active = true;
Logger::instance()->addMessage(tr("UPnP / NAT-PMP support [ON]"), Log::INFO); Logger::instance()->addMessage(tr("UPnP / NAT-PMP support [ON]"), Log::INFO);
} }

5
src/base/search/searchpluginmanager.cpp

@ -38,6 +38,7 @@
#include <QPointer> #include <QPointer>
#include <QProcess> #include <QProcess>
#include "base/global.h"
#include "base/logger.h" #include "base/logger.h"
#include "base/net/downloadmanager.h" #include "base/net/downloadmanager.h"
#include "base/net/downloadhandler.h" #include "base/net/downloadhandler.h"
@ -99,7 +100,7 @@ QStringList SearchPluginManager::allPlugins() const
QStringList SearchPluginManager::enabledPlugins() const QStringList SearchPluginManager::enabledPlugins() const
{ {
QStringList plugins; QStringList plugins;
foreach (const PluginInfo *plugin, m_plugins.values()) { for (const PluginInfo *plugin : qAsConst(m_plugins)) {
if (plugin->enabled) if (plugin->enabled)
plugins << plugin->name; plugins << plugin->name;
} }
@ -110,7 +111,7 @@ QStringList SearchPluginManager::enabledPlugins() const
QStringList SearchPluginManager::supportedCategories() const QStringList SearchPluginManager::supportedCategories() const
{ {
QStringList result; QStringList result;
foreach (const PluginInfo *plugin, m_plugins.values()) { for (const PluginInfo *plugin : qAsConst(m_plugins)) {
if (plugin->enabled) { if (plugin->enabled) {
foreach (QString cat, plugin->supportedCategories) { foreach (QString cat, plugin->supportedCategories) {
if (!result.contains(cat)) if (!result.contains(cat))

3
src/gui/categoryfiltermodel.cpp

@ -405,7 +405,8 @@ void CategoryFilterModel::populate()
, [](Torrent *torrent) { return torrent->category().isEmpty(); }))); , [](Torrent *torrent) { return torrent->category().isEmpty(); })));
using Torrent = BitTorrent::TorrentHandle; using Torrent = BitTorrent::TorrentHandle;
foreach (const QString &category, session->categories().keys()) { for (auto i = session->categories().cbegin(); i != session->categories().cend(); ++i) {
const QString &category = i.key();
if (m_isSubcategoriesEnabled) { if (m_isSubcategoriesEnabled) {
CategoryModelItem *parent = m_rootItem; CategoryModelItem *parent = m_rootItem;
foreach (const QString &subcat, session->expandCategory(category)) { foreach (const QString &subcat, session->expandCategory(category)) {

4
src/gui/categoryfilterwidget.cpp

@ -232,8 +232,10 @@ void CategoryFilterWidget::removeCategory()
void CategoryFilterWidget::removeUnusedCategories() void CategoryFilterWidget::removeUnusedCategories()
{ {
auto session = BitTorrent::Session::instance(); auto session = BitTorrent::Session::instance();
foreach (const QString &category, session->categories().keys()) for (auto i = session->categories().cbegin(); i != session->categories().cend(); ++i) {
const QString &category = i.key();
if (model()->data(static_cast<CategoryFilterProxyModel *>(model())->index(category), Qt::UserRole) == 0) if (model()->data(static_cast<CategoryFilterProxyModel *>(model())->index(category), Qt::UserRole) == 0)
session->removeCategory(category); session->removeCategory(category);
}
updateGeometry(); updateGeometry();
} }

4
src/gui/search/pluginselectdlg.cpp

@ -424,10 +424,10 @@ void PluginSelectDlg::checkForUpdatesFinished(const QHash<QString, PluginVersion
return; return;
} }
foreach (const QString &pluginName, updateInfo.keys()) { for (auto i = updateInfo.cbegin(); i != updateInfo.cend(); ++i) {
startAsyncOp(); startAsyncOp();
m_pendingUpdates++; m_pendingUpdates++;
m_pluginManager->updatePlugin(pluginName); m_pluginManager->updatePlugin(i.key());
} }
} }

3
src/gui/transferlistfilterswidget.cpp

@ -333,7 +333,8 @@ void TrackerFiltersList::setDownloadTrackerFavicon(bool value)
m_downloadTrackerFavicon = value; m_downloadTrackerFavicon = value;
if (m_downloadTrackerFavicon) { if (m_downloadTrackerFavicon) {
foreach (const QString &tracker, m_trackers.keys()) { for (auto i = m_trackers.cbegin(); i != m_trackers.cend(); ++i) {
const QString &tracker = i.key();
if (!tracker.isEmpty()) if (!tracker.isEmpty())
downloadFavicon(QString("http://%1/favicon.ico").arg(tracker)); downloadFavicon(QString("http://%1/favicon.ico").arg(tracker));
} }

8
src/webui/api/appcontroller.cpp

@ -91,7 +91,7 @@ void AppController::preferencesAction()
data["incomplete_files_ext"] = session->isAppendExtensionEnabled(); data["incomplete_files_ext"] = session->isAppendExtensionEnabled();
const QVariantHash dirs = pref->getScanDirs(); const QVariantHash dirs = pref->getScanDirs();
QVariantMap nativeDirs; QVariantMap nativeDirs;
for (QVariantHash::const_iterator i = dirs.begin(), e = dirs.end(); i != e; ++i) { for (QVariantHash::const_iterator i = dirs.cbegin(), e = dirs.cend(); i != e; ++i) {
if (i.value().type() == QVariant::Int) if (i.value().type() == QVariant::Int)
nativeDirs.insert(Utils::Fs::toNativePath(i.key()), i.value().toInt()); nativeDirs.insert(Utils::Fs::toNativePath(i.key()), i.value().toInt());
else else
@ -246,7 +246,7 @@ void AppController::setPreferencesAction()
QVariantHash oldScanDirs = pref->getScanDirs(); QVariantHash oldScanDirs = pref->getScanDirs();
QVariantHash scanDirs; QVariantHash scanDirs;
ScanFoldersModel *model = ScanFoldersModel::instance(); ScanFoldersModel *model = ScanFoldersModel::instance();
for (QVariantMap::const_iterator i = nativeDirs.begin(), e = nativeDirs.end(); i != e; ++i) { for (QVariantMap::const_iterator i = nativeDirs.cbegin(), e = nativeDirs.cend(); i != e; ++i) {
QString folder = Utils::Fs::fromNativePath(i.key()); QString folder = Utils::Fs::fromNativePath(i.key());
int downloadType; int downloadType;
QString downloadPath; QString downloadPath;
@ -275,8 +275,8 @@ void AppController::setPreferencesAction()
} }
// Update deleted folders // Update deleted folders
foreach (QVariant folderVariant, oldScanDirs.keys()) { for (auto i = oldScanDirs.cbegin(); i != oldScanDirs.cend(); ++i) {
QString folder = folderVariant.toString(); QString folder = i.key();
if (!scanDirs.contains(folder)) { if (!scanDirs.contains(folder)) {
model->removePath(folder); model->removePath(folder);
qDebug("Removed watched folder %s", qUtf8Printable(folder)); qDebug("Removed watched folder %s", qUtf8Printable(folder));

44
src/webui/api/synccontroller.cpp

@ -152,20 +152,22 @@ namespace
syncData.clear(); syncData.clear();
QVariantList removedItems; QVariantList removedItems;
foreach (QString key, data.keys()) { for (auto i = data.cbegin(); i != data.cend(); ++i) {
const QString &key = i.key();
const QVariant &value = i.value();
removedItems.clear(); removedItems.clear();
switch (static_cast<QMetaType::Type>(data[key].type())) { switch (static_cast<QMetaType::Type>(value.type())) {
case QMetaType::QVariantMap: { case QMetaType::QVariantMap: {
QVariantMap map; QVariantMap map;
processMap(prevData[key].toMap(), data[key].toMap(), map); processMap(prevData[key].toMap(), value.toMap(), map);
if (!map.isEmpty()) if (!map.isEmpty())
syncData[key] = map; syncData[key] = map;
} }
break; break;
case QMetaType::QVariantHash: { case QMetaType::QVariantHash: {
QVariantMap map; QVariantMap map;
processHash(prevData[key].toHash(), data[key].toHash(), map, removedItems); processHash(prevData[key].toHash(), value.toHash(), map, removedItems);
if (!map.isEmpty()) if (!map.isEmpty())
syncData[key] = map; syncData[key] = map;
if (!removedItems.isEmpty()) if (!removedItems.isEmpty())
@ -174,7 +176,7 @@ namespace
break; break;
case QMetaType::QVariantList: { case QMetaType::QVariantList: {
QVariantList list; QVariantList list;
processList(prevData[key].toList(), data[key].toList(), list, removedItems); processList(prevData[key].toList(), value.toList(), list, removedItems);
if (!list.isEmpty()) if (!list.isEmpty())
syncData[key] = list; syncData[key] = list;
if (!removedItems.isEmpty()) if (!removedItems.isEmpty())
@ -190,13 +192,13 @@ namespace
case QMetaType::ULongLong: case QMetaType::ULongLong:
case QMetaType::UInt: case QMetaType::UInt:
case QMetaType::QDateTime: case QMetaType::QDateTime:
if (prevData[key] != data[key]) if (prevData[key] != value)
syncData[key] = data[key]; syncData[key] = value;
break; break;
default: default:
Q_ASSERT_X(false, "processMap" Q_ASSERT_X(false, "processMap"
, QString("Unexpected type: %1") , QString("Unexpected type: %1")
.arg(QMetaType::typeName(static_cast<QMetaType::Type>(data[key].type()))) .arg(QMetaType::typeName(static_cast<QMetaType::Type>(value.type())))
.toUtf8().constData()); .toUtf8().constData());
} }
} }
@ -213,25 +215,25 @@ namespace
if (prevData.isEmpty()) { if (prevData.isEmpty()) {
// If list was empty before, then difference is a whole new list. // If list was empty before, then difference is a whole new list.
foreach (QString key, data.keys()) for (auto i = data.cbegin(); i != data.cend(); ++i)
syncData[key] = data[key]; syncData[i.key()] = i.value();
} }
else { else {
foreach (QString key, data.keys()) { for (auto i = data.cbegin(); i != data.cend(); ++i) {
switch (data[key].type()) { switch (i.value().type()) {
case QVariant::Map: case QVariant::Map:
if (!prevData.contains(key)) { if (!prevData.contains(i.key())) {
// new list item found - append it to syncData // new list item found - append it to syncData
syncData[key] = data[key]; syncData[i.key()] = i.value();
} }
else { else {
QVariantMap map; QVariantMap map;
processMap(prevData[key].toMap(), data[key].toMap(), map); processMap(prevData[i.key()].toMap(), i.value().toMap(), map);
// existing list item found - remove it from prevData // existing list item found - remove it from prevData
prevData.remove(key); prevData.remove(i.key());
if (!map.isEmpty()) if (!map.isEmpty())
// changed list item found - append its changes to syncData // changed list item found - append its changes to syncData
syncData[key] = map; syncData[i.key()] = map;
} }
break; break;
default: default:
@ -242,8 +244,8 @@ namespace
if (!prevData.isEmpty()) { if (!prevData.isEmpty()) {
// prevData contains only items that are missing now - // prevData contains only items that are missing now -
// put them in removedItems // put them in removedItems
foreach (QString s, prevData.keys()) for (auto i = prevData.cbegin(); i != prevData.cend(); ++i)
removedItems << s; removedItems << i.key();
} }
} }
} }
@ -395,8 +397,8 @@ void SyncController::maindataAction()
data["torrents"] = torrents; data["torrents"] = torrents;
QVariantList categories; QVariantList categories;
foreach (const QString &category, session->categories().keys()) for (auto i = session->categories().cbegin(); i != session->categories().cend(); ++i)
categories << category; categories << i.key();
data["categories"] = categories; data["categories"] = categories;

Loading…
Cancel
Save