From 1a913c502bf22ad7e7e7031948c3ce6f605e95be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Pereira?= <luis.artur.pereira@gmail.com> Date: Fri, 16 Feb 2018 18:31:48 +0000 Subject: [PATCH] Prevent possible c++11 range-loop container detach Explicit or implicit calls to begin() and end() cause a non-const container to detach from shared data, ie. to perform a deep-copy to gain a unique copy of the data. That can be a expensive although unneeded operation. In order to assist the developer a copyAsConst function is added. copyAsConst returns a const copy of the object. For lvalues just use qAsConst. It's only available on Qt 5.7.0. But we added also for earlier versions. The developer can always use qAsConst. Intended uses: QString s = ...; for (const auto &ch : qAsConst(s)) process(ch); for (const auto &ch : copyAsConst(funcReturningQString())) process(ch); --- src/base/global.h | 18 ++++++++++++++++++ src/base/rss/rss_autodownloader.cpp | 8 ++++---- src/base/rss/rss_folder.cpp | 5 +++-- src/base/utils/fs.cpp | 3 ++- src/gui/ipsubnetwhitelistoptionsdialog.cpp | 5 +++-- src/gui/properties/speedplotview.cpp | 5 +++-- src/webui/api/appcontroller.cpp | 3 ++- 7 files changed, 35 insertions(+), 12 deletions(-) diff --git a/src/base/global.h b/src/base/global.h index 2bfeda9cb..0284e3fe8 100644 --- a/src/base/global.h +++ b/src/base/global.h @@ -26,5 +26,23 @@ * exception statement from your version. */ +#pragma once + +#include <type_traits> +#include <QtGlobal> + const char C_TORRENT_FILE_EXTENSION[] = ".torrent"; + +#if QT_VERSION < QT_VERSION_CHECK(5, 7, 0) +template <typename T> +constexpr typename std::add_const<T>::type &qAsConst(T &t) noexcept { return t; } + +// prevent rvalue arguments: +template <typename T> +void qAsConst(const T &&) = delete; +#endif + +// returns a const object copy +template <typename T> +constexpr typename std::add_const<T>::type copyAsConst(T &&t) noexcept { return std::move(t); } diff --git a/src/base/rss/rss_autodownloader.cpp b/src/base/rss/rss_autodownloader.cpp index 8d0a43dc5..8d105f3db 100644 --- a/src/base/rss/rss_autodownloader.cpp +++ b/src/base/rss/rss_autodownloader.cpp @@ -43,6 +43,7 @@ #include "../bittorrent/magneturi.h" #include "../bittorrent/session.h" #include "../asyncfilestorage.h" +#include "../global.h" #include "../logger.h" #include "../profile.h" #include "../settingsstorage.h" @@ -239,7 +240,7 @@ void AutoDownloader::importRules(const QByteArray &data, AutoDownloader::RulesFi QByteArray AutoDownloader::exportRulesToJSONFormat() const { QJsonObject jsonObj; - for (const auto &rule : rules()) + for (const auto &rule : copyAsConst(rules())) jsonObj.insert(rule.name(), rule.toJsonObject()); return QJsonDocument(jsonObj).toJson(); @@ -247,15 +248,14 @@ QByteArray AutoDownloader::exportRulesToJSONFormat() const void AutoDownloader::importRulesFromJSONFormat(const QByteArray &data) { - const auto rules = rulesFromJSON(data); - for (const auto &rule : rules) + for (const auto &rule : copyAsConst(rulesFromJSON(data))) insertRule(rule); } QByteArray AutoDownloader::exportRulesToLegacyFormat() const { QVariantHash dict; - for (const auto &rule : rules()) + for (const auto &rule : copyAsConst(rules())) dict[rule.name()] = rule.toLegacyDict(); QByteArray data; diff --git a/src/base/rss/rss_folder.cpp b/src/base/rss/rss_folder.cpp index 2d05f3573..f9c934080 100644 --- a/src/base/rss/rss_folder.cpp +++ b/src/base/rss/rss_folder.cpp @@ -33,6 +33,7 @@ #include <QJsonObject> #include <QJsonValue> +#include "base/global.h" #include "rss_article.h" using namespace RSS; @@ -122,7 +123,7 @@ void Folder::addItem(Item *item) connect(item, &Item::articleAboutToBeRemoved, this, &Item::articleAboutToBeRemoved); connect(item, &Item::unreadCountChanged, this, &Folder::handleItemUnreadCountChanged); - for (auto article: item->articles()) + for (auto article: copyAsConst(item->articles())) emit newArticle(article); if (item->unreadCount() > 0) @@ -133,7 +134,7 @@ void Folder::removeItem(Item *item) { Q_ASSERT(m_items.contains(item)); - for (auto article: item->articles()) + for (auto article: copyAsConst(item->articles())) emit articleAboutToBeRemoved(article); item->disconnect(this); diff --git a/src/base/utils/fs.cpp b/src/base/utils/fs.cpp index 63a997b49..54349498f 100644 --- a/src/base/utils/fs.cpp +++ b/src/base/utils/fs.cpp @@ -55,6 +55,7 @@ #endif #include "base/bittorrent/torrenthandle.h" +#include "base/global.h" /** * Converts a path to a string suitable for display. @@ -128,7 +129,7 @@ bool Utils::Fs::smartRemoveEmptyFolderTree(const QString &path) std::sort(dirList.begin(), dirList.end() , [](const QString &l, const QString &r) { return l.count("/") > r.count("/"); }); - for (const QString &p : dirList) { + for (const QString &p : qAsConst(dirList)) { // remove unwanted files for (const QString &f : deleteFilesList) { forceRemove(p + f); diff --git a/src/gui/ipsubnetwhitelistoptionsdialog.cpp b/src/gui/ipsubnetwhitelistoptionsdialog.cpp index a4afb0981..5e60fa9e4 100644 --- a/src/gui/ipsubnetwhitelistoptionsdialog.cpp +++ b/src/gui/ipsubnetwhitelistoptionsdialog.cpp @@ -34,6 +34,7 @@ #include <QSortFilterProxyModel> #include <QStringListModel> +#include "base/global.h" #include "base/preferences.h" #include "base/utils/net.h" #include "ui_ipsubnetwhitelistoptionsdialog.h" @@ -47,7 +48,7 @@ IPSubnetWhitelistOptionsDialog::IPSubnetWhitelistOptionsDialog(QWidget *parent) m_ui->setupUi(this); QStringList authSubnetWhitelistStringList; - for (const Utils::Net::Subnet &subnet : Preferences::instance()->getWebUiAuthSubnetWhitelist()) + for (const Utils::Net::Subnet &subnet : copyAsConst(Preferences::instance()->getWebUiAuthSubnetWhitelist())) authSubnetWhitelistStringList << Utils::Net::subnetToString(subnet); m_model = new QStringListModel(authSubnetWhitelistStringList, this); @@ -100,7 +101,7 @@ void IPSubnetWhitelistOptionsDialog::on_buttonWhitelistIPSubnet_clicked() void IPSubnetWhitelistOptionsDialog::on_buttonDeleteIPSubnet_clicked() { - for (const auto &i : m_ui->whitelistedIPSubnetList->selectionModel()->selectedIndexes()) + for (const auto &i : copyAsConst(m_ui->whitelistedIPSubnetList->selectionModel()->selectedIndexes())) m_sortFilter->removeRow(i.row()); m_modified = true; diff --git a/src/gui/properties/speedplotview.cpp b/src/gui/properties/speedplotview.cpp index c7e4aceca..b1348fa51 100644 --- a/src/gui/properties/speedplotview.cpp +++ b/src/gui/properties/speedplotview.cpp @@ -30,6 +30,7 @@ #include <QPainter> #include <QPen> +#include "base/global.h" #include "base/utils/misc.h" SpeedPlotView::SpeedPlotView(QWidget *parent) @@ -257,7 +258,7 @@ void SpeedPlotView::paintEvent(QPaintEvent *) double legendHeight = 0; int legendWidth = 0; - for (const auto &property : m_properties) { + for (const auto &property : qAsConst(m_properties)) { if (!property.enable) continue; @@ -272,7 +273,7 @@ void SpeedPlotView::paintEvent(QPaintEvent *) painter.fillRect(legendBackgroundRect, legendBackgroundColor); i = 0; - for (const auto &property : m_properties) { + for (const auto &property : qAsConst(m_properties)) { if (!property.enable) continue; diff --git a/src/webui/api/appcontroller.cpp b/src/webui/api/appcontroller.cpp index 2b3035c61..ce283caf3 100644 --- a/src/webui/api/appcontroller.cpp +++ b/src/webui/api/appcontroller.cpp @@ -45,6 +45,7 @@ #endif #include "base/bittorrent/session.h" +#include "base/global.h" #include "base/net/portforwarder.h" #include "base/net/proxyconfigurationmanager.h" #include "base/preferences.h" @@ -200,7 +201,7 @@ void AppController::preferencesAction() data["bypass_local_auth"] = !pref->isWebUiLocalAuthEnabled(); data["bypass_auth_subnet_whitelist_enabled"] = pref->isWebUiAuthSubnetWhitelistEnabled(); QStringList authSubnetWhitelistStringList; - for (const Utils::Net::Subnet &subnet : pref->getWebUiAuthSubnetWhitelist()) + for (const Utils::Net::Subnet &subnet : copyAsConst(pref->getWebUiAuthSubnetWhitelist())) authSubnetWhitelistStringList << Utils::Net::subnetToString(subnet); data["bypass_auth_subnet_whitelist"] = authSubnetWhitelistStringList.join("\n"); // Update my dynamic domain name