diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index f3187d249..fcf242e6c 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -852,16 +852,19 @@ HistoryItem *ChannelHistory::addNewToBlocks(const MTPMessage &msg, NewMessageTyp clear(true); } - HistoryBlock *to = 0; + HistoryBlock *to = nullptr; bool newBlock = blocks.isEmpty(); if (newBlock) { to = new HistoryBlock(this); } else { to = blocks.back(); + t_assert(!to->items.isEmpty()); + t_assert(to->items.back() != nullptr); } - HistoryItem *item = createItem((type == NewMessageLast) ? 0 : to, msg, (type == NewMessageUnread)); - if (type == NewMessageLast) { + HistoryItem *item = createItem((type == NewMessageLast) ? nullptr : to, msg, (type == NewMessageUnread)); + if (type == NewMessageLast && item) { if (!item->detached()) { + t_assert(!newBlock); return item; } item->attach(to); @@ -896,10 +899,8 @@ void ChannelHistory::switchMode() { OtherList savedList; if (!blocks.isEmpty()) { savedList.reserve(((blocks.size() - 2) * MessagesPerPage + blocks.back()->items.size()) * (onlyImportant() ? 2 : 1)); - for (Blocks::const_iterator i = blocks.cbegin(), e = blocks.cend(); i != e; ++i) { - HistoryBlock *block = *i; - for (HistoryBlock::Items::const_iterator j = block->items.cbegin(), end = block->items.cend(); j != end; ++j) { - HistoryItem *item = *j; + for_const (const HistoryBlock *block, blocks) { + for_const (HistoryItem *item, block->items) { HistoryItemType itemType = item->type(); if (itemType == HistoryItemMsg || itemType == HistoryItemGroup) { savedList.push_back(item); @@ -994,7 +995,7 @@ HistoryBlock *ChannelHistory::findGroupBlock(MsgId msgId) const { // find block if (blocks.size() > 1) for (int32 minBlock = 0, maxBlock = blocks.size();;) { for (int32 startCheckBlock = (minBlock + maxBlock) / 2, checkBlock = startCheckBlock;;) { HistoryBlock *block = blocks.at(checkBlock); - HistoryBlock::Items::const_iterator i = block->items.cbegin(), e = block->items.cend(); + auto i = block->items.cbegin(), e = block->items.cend(); for (; i != e; ++i) { // out msgs could be a mess in monotonic ids if (((*i)->id > 0 && !(*i)->out()) || (*i)->type() == HistoryItemGroup) { MsgId threshold = ((*i)->id > 0) ? (*i)->id : static_cast(*i)->minId(); @@ -1106,6 +1107,12 @@ void ChannelHistory::messageWithIdDeleted(MsgId msgId) { } } +ChannelHistory::~ChannelHistory() { + // all items must be destroyed before ChannelHistory is destroyed + // or they will call history()->asChannelHistory() -> undefined behaviour + clearOnDestroy(); +} + bool DialogsList::del(const PeerId &peerId, DialogRow *replacedBy) { RowByPeer::iterator i = rowByPeer.find(peerId); if (i == rowByPeer.cend()) return false; @@ -1299,7 +1306,7 @@ HistoryItem *History::createItem(HistoryBlock *block, const MTPMessage &msg, boo case mtpc_message: msgId = msg.c_message().vid.v; break; case mtpc_messageService: msgId = msg.c_messageService().vid.v; break; } - if (!msgId) return 0; + if (!msgId) return nullptr; HistoryItem *result = App::histItemById(channelId(), msgId); if (result) { @@ -1574,6 +1581,8 @@ HistoryItem *History::addNewService(MsgId msgId, QDateTime date, const QString & to = new HistoryBlock(this); } else { to = blocks.back(); + t_assert(!to->items.isEmpty()); + t_assert(to->items.back() != nullptr); } HistoryItem *result = new HistoryServiceMsg(this, to, msgId, date, text, flags, media); @@ -1595,16 +1604,19 @@ HistoryItem *History::addNewMessage(const MTPMessage &msg, NewMessageType type) return item; } - HistoryBlock *to = 0; + HistoryBlock *to = nullptr; bool newBlock = blocks.isEmpty(); if (newBlock) { to = new HistoryBlock(this); } else { to = blocks.back(); + t_assert(!to->items.isEmpty()); + t_assert(to->items.back() != nullptr); } - HistoryItem *item = createItem((type == NewMessageLast) ? 0 : to, msg, (type == NewMessageUnread)); - if (type == NewMessageLast) { + HistoryItem *item = createItem((type == NewMessageLast) ? nullptr : to, msg, (type == NewMessageUnread)); + if (type == NewMessageLast && item) { if (!item->detached()) { + t_assert(!newBlock); return item; } item->attach(to); @@ -1613,16 +1625,18 @@ HistoryItem *History::addNewMessage(const MTPMessage &msg, NewMessageType type) } HistoryItem *History::addToHistory(const MTPMessage &msg) { - return createItem(0, msg, false); + return createItem(nullptr, msg, false); } HistoryItem *History::addNewForwarded(MsgId id, int32 flags, QDateTime date, int32 from, HistoryMessage *item) { - HistoryBlock *to = 0; + HistoryBlock *to = nullptr; bool newBlock = blocks.isEmpty(); if (newBlock) { to = new HistoryBlock(this); } else { to = blocks.back(); + t_assert(!to->items.isEmpty()); + t_assert(to->items.back() != nullptr); } return addNewItem(to, newBlock, createItemForwarded(to, id, flags, date, from, item), true); } @@ -1634,6 +1648,8 @@ HistoryItem *History::addNewDocument(MsgId id, int32 flags, int32 viaBotId, MsgI to = new HistoryBlock(this); } else { to = blocks.back(); + t_assert(!to->items.isEmpty()); + t_assert(to->items.back() != nullptr); } return addNewItem(to, newBlock, createItemDocument(to, id, flags, viaBotId, replyTo, date, from, doc, caption), true); } @@ -1645,6 +1661,8 @@ HistoryItem *History::addNewPhoto(MsgId id, int32 flags, int32 viaBotId, MsgId r to = new HistoryBlock(this); } else { to = blocks.back(); + t_assert(!to->items.isEmpty()); + t_assert(to->items.back() != nullptr); } return addNewItem(to, newBlock, createItemPhoto(to, id, flags, viaBotId, replyTo, date, from, photo, caption), true); } @@ -1913,10 +1931,10 @@ void History::addOlderSlice(const QVector &slice, const QVectorconstData() : 0, *groupsIt = groupsBegin, *groupsEnd = (isChannel() && collapsed) ? (groupsBegin + collapsed->size()) : 0; - HistoryItem *oldFirst = 0, *last = 0; + HistoryItem *oldFirst = nullptr, *last = nullptr; HistoryBlock *block = new HistoryBlock(this); block->items.reserve(slice.size() + (collapsed ? collapsed->size() : 0)); - for (QVector::const_iterator i = slice.cend(), e = slice.cbegin(); i != e;) { + for (auto i = slice.cend(), e = slice.cbegin(); i != e;) { --i; HistoryItem *adding = createItem(block, *i, false); if (!adding) continue; @@ -2079,11 +2097,11 @@ void History::addNewerSlice(const QVector &slice, const QVectorisEmpty())) { const MTPMessageGroup *groupsBegin = (isChannel() && collapsed) ? collapsed->constData() : 0, *groupsIt = groupsBegin, *groupsEnd = (isChannel() && collapsed) ? (groupsBegin + collapsed->size()) : 0; - HistoryItem *prev = blocks.isEmpty() ? 0 : blocks.back()->items.back(); + HistoryItem *prev = blocks.isEmpty() ? nullptr : blocks.back()->items.back(); HistoryBlock *block = new HistoryBlock(this); block->items.reserve(slice.size() + (collapsed ? collapsed->size() : 0)); - for (QVector::const_iterator i = slice.cend(), e = slice.cbegin(); i != e;) { + for (auto i = slice.cend(), e = slice.cbegin(); i != e;) { --i; HistoryItem *adding = createItem(block, *i, false); if (!adding) continue; @@ -2148,9 +2166,9 @@ void History::addNewerSlice(const QVector &slice, const QVectoritems.cend(), en = (*i)->items.cbegin(); j != en;) { + for (auto j = (*i)->items.cend(), en = (*i)->items.cbegin(); j != en;) { --j; if ((*j)->id > 0 && (*j)->id <= upTo) { break; @@ -2165,9 +2183,9 @@ int32 History::countUnread(MsgId upTo) { void History::updateShowFrom() { if (showFrom) return; - for (Blocks::const_iterator i = blocks.cend(); i != blocks.cbegin();) { + for (auto i = blocks.cend(); i != blocks.cbegin();) { --i; - for (HistoryBlock::Items::const_iterator j = (*i)->items.cend(); j != (*i)->items.cbegin();) { + for (auto j = (*i)->items.cend(); j != (*i)->items.cbegin();) { --j; if ((*j)->type() == HistoryItemMsg && (*j)->id > 0 && (!(*j)->out() || !showFrom)) { if ((*j)->id >= inboxReadBefore) { @@ -2463,10 +2481,10 @@ void History::fixLastMessage(bool wasAtBottom) { } MsgId History::minMsgId() const { - for (Blocks::const_iterator i = blocks.cbegin(), e = blocks.cend(); i != e; ++i) { - for (HistoryBlock::Items::const_iterator j = (*i)->items.cbegin(), en = (*i)->items.cend(); j != en; ++j) { - if ((*j)->id > 0) { - return (*j)->id; + for_const (const HistoryBlock *block, blocks) { + for_const (const HistoryItem *item, block->items) { + if (item->id > 0) { + return item->id; } } } @@ -2474,9 +2492,9 @@ MsgId History::minMsgId() const { } MsgId History::maxMsgId() const { - for (Blocks::const_iterator i = blocks.cend(), e = blocks.cbegin(); i != e;) { + for (auto i = blocks.cend(), e = blocks.cbegin(); i != e;) { --i; - for (HistoryBlock::Items::const_iterator j = (*i)->items.cend(), en = (*i)->items.cbegin(); j != en;) { + for (auto j = (*i)->items.cend(), en = (*i)->items.cbegin(); j != en;) { --j; if ((*j)->id > 0) { return (*j)->id; @@ -2547,14 +2565,7 @@ void History::clear(bool leaveItems) { if (App::wnd() && !App::quitting()) App::wnd()->mediaOverviewUpdated(peer, MediaOverviewType(i)); } } - Blocks lst = blocks; - blocks.clear(); - for (Blocks::const_iterator i = lst.cbegin(), e = lst.cend(); i != e; ++i) { - if (leaveItems) { - (*i)->clear(true); - } - delete *i; - } + clearBlocks(leaveItems); if (leaveItems) { lastKeyboardInited = false; } else { @@ -2574,6 +2585,21 @@ void History::clear(bool leaveItems) { if (leaveItems && App::main()) App::main()->historyCleared(this); } +void History::clearBlocks(bool leaveItems) { + Blocks lst; + std::swap(lst, blocks); + for_const (HistoryBlock *block, lst) { + if (leaveItems) { + block->clear(true); + } + delete block; + } +} + +void History::clearOnDestroy() { + clearBlocks(false); +} + QPair History::adjustByPosInChatsList(DialogsIndexed &indexed) { int32 movedFrom = _chatListLinks[0]->pos * st::dlgHeight; indexed.adjustByPos(_chatListLinks); @@ -2716,12 +2742,6 @@ void History::blockResized(HistoryBlock *block, int32 dh) { } } -void History::clearUpto(MsgId msgId) { - for (HistoryItem *item = isEmpty() ? 0 : blocks.back()->items.back(); item && (item->id < 0 || item->id >= msgId); item = isEmpty() ? 0 : blocks.back()->items.back()) { - item->destroy(); - } -} - void History::removeBlock(HistoryBlock *block) { int32 i = blocks.indexOf(block), h = block->height; if (i >= 0) { @@ -2741,15 +2761,14 @@ void History::removeBlock(HistoryBlock *block) { } History::~History() { - clear(); + clearOnDestroy(); deleteAndMark(msgDraft); deleteAndMark(editDraft); } int32 HistoryBlock::geomResize(int32 newWidth, int32 *ytransform, const HistoryItem *resizedItem) { int32 y = 0; - for (Items::iterator i = items.begin(), e = items.end(); i != e; ++i) { - HistoryItem *item = *i; + for_const (HistoryItem *item , items) { bool updTransform = ytransform && (*ytransform >= item->y) && (*ytransform < item->y + item->height()); if (updTransform) *ytransform -= item->y; item->y = y; @@ -2768,15 +2787,16 @@ int32 HistoryBlock::geomResize(int32 newWidth, int32 *ytransform, const HistoryI } void HistoryBlock::clear(bool leaveItems) { - Items lst = items; - items.clear(); + Items lst; + std::swap(lst, items); + if (leaveItems) { - for (Items::const_iterator i = lst.cbegin(), e = lst.cend(); i != e; ++i) { - (*i)->detachFast(); + for_const (HistoryItem *item, lst) { + item->detachFast(); } } else { - for (Items::const_iterator i = lst.cbegin(), e = lst.cend(); i != e; ++i) { - delete *i; + for_const (HistoryItem *item, lst) { + delete item; } } } diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index b76268e1d..0363da799 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -238,7 +238,6 @@ public: return blocks.isEmpty(); } void clear(bool leaveItems = false); - void clearUpto(MsgId msgId); void blockResized(HistoryBlock *block, int32 dh); void removeBlock(HistoryBlock *block); @@ -315,7 +314,7 @@ public: void removeNotification(HistoryItem *item) { if (!notifies.isEmpty()) { - for (NotifyQueue::iterator i = notifies.begin(), e = notifies.end(); i != e; ++i) { + for (auto i = notifies.begin(), e = notifies.end(); i != e; ++i) { if ((*i) == item) { notifies.erase(i); break; @@ -429,6 +428,10 @@ public: void changeMsgId(MsgId oldId, MsgId newId); +protected: + + void clearOnDestroy(); + private: ChatListLinksMap _chatListLinks; @@ -438,6 +441,8 @@ private: MediaOverviewIds overviewIds[OverviewCount]; int32 overviewCountData[OverviewCount]; // -1 - not loaded, 0 - all loaded, > 0 - count, but not all loaded + void clearBlocks(bool leaveItems); + friend class HistoryBlock; friend class ChannelHistory; @@ -448,6 +453,8 @@ private: HistoryItem *addMessageGroupAfterPrevToBlock(const MTPDmessageGroup &group, HistoryItem *prev, HistoryBlock *block); HistoryItem *addMessageGroupAfterPrev(HistoryItem *newItem, HistoryItem *prev); + History(const History &) = delete; + History &operator=(const History &) = delete; }; class HistoryGroup; @@ -490,6 +497,8 @@ public: void checkJoinedMessage(bool createUnread = false); const QDateTime &maxReadMessageDate(); + ~ChannelHistory(); + private: friend class History; @@ -815,6 +824,9 @@ public: int32 geomResize(int32 newWidth, int32 *ytransform, const HistoryItem *resizedItem); // return new size int32 y, height; History *history; + + HistoryBlock(const HistoryBlock &) = delete; + HistoryBlock &operator=(const HistoryBlock &) = delete; }; class HistoryElem { @@ -1198,9 +1210,6 @@ public: protected: - HistoryItem(const HistoryItem &); - HistoryItem &operator=(const HistoryItem &); - PeerData *_from; History *_history; HistoryBlock *_block; @@ -1208,6 +1217,8 @@ protected: mutable int32 _authorNameVersion; + HistoryItem(const HistoryItem &) = delete; + HistoryItem &operator=(const HistoryItem &) = delete; }; class MessageLink : public ITextLink { diff --git a/Telegram/SourceFiles/types.h b/Telegram/SourceFiles/types.h index 5cf498de2..ed7f9deff 100644 --- a/Telegram/SourceFiles/types.h +++ b/Telegram/SourceFiles/types.h @@ -36,28 +36,6 @@ T *exchange(T *&ptr) { struct NullType { }; -#if __cplusplus < 199711L -#define TDESKTOP_CUSTOM_NULLPTR -#endif - -#ifdef TDESKTOP_CUSTOM_NULLPTR -class NullPointerClass { -public: - template - operator T*() const { - return 0; - } - template - operator T C::*() const { - return 0; - } - -private: - void operator&() const; -}; -extern NullPointerClass nullptr; -#endif - template class OrderedSet : public QMap { public: @@ -68,6 +46,16 @@ public: }; +// using for_const instead of plain range-based for loop to ensure usage of const_iterator +// it is important for the copy-on-write Qt containers +// if you have "QVector v" then "for (T * const p : v)" will still call QVector::detach(), +// while "for_const(T *p, v)" won't and "for_const(T *&p, v)" won't compile +template +struct ForConstTraits { + typedef const T &ExpressionType; +}; +#define for_const(range_declaration, range_expression) for (range_declaration : static_cast::ExpressionType>(range_expression)) + //typedef unsigned char uchar; // Qt has uchar typedef qint16 int16; typedef quint16 uint16; diff --git a/Telegram/_qtbase_5_5_1_patch.diff b/Telegram/_qtbase_5_5_1_patch.diff index 2fd2db7b5..79677c798 100644 --- a/Telegram/_qtbase_5_5_1_patch.diff +++ b/Telegram/_qtbase_5_5_1_patch.diff @@ -11456,7 +11456,7 @@ index 1ec33df..45d436c 100644 } return [super performKeyEquivalent:nsevent]; diff --git a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp -index da0ba27..9fd21d4 100644 +index da0ba27..3f6388c 100644 --- a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp +++ b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp @@ -713,6 +713,9 @@ public: @@ -11546,7 +11546,7 @@ index da0ba27..9fd21d4 100644 private: inline IFileOpenDialog *openFileDialog() const -@@ -1546,6 +1580,55 @@ QList QWindowsNativeOpenFileDialog::dialogResult() const +@@ -1546,6 +1580,57 @@ QList QWindowsNativeOpenFileDialog::dialogResult() const return result; } @@ -11587,13 +11587,15 @@ index da0ba27..9fd21d4 100644 + continue; + + quint64 fullSize = stat.cbSize.QuadPart; -+ result.resize(fullSize); -+ ULONG read = 0; -+ HRESULT r = stream->Read(result.data(), fullSize, &read); -+ if (r == S_FALSE || r == S_OK) -+ return result; ++ if (fullSize <= 64 * 1024 * 1024) { ++ result.resize(fullSize); ++ ULONG read = 0; ++ HRESULT r = stream->Read(result.data(), fullSize, &read); ++ if (r == S_FALSE || r == S_OK) ++ return result; + -+ result.clear(); ++ result.clear(); ++ } + } + } + return result; @@ -11602,7 +11604,7 @@ index da0ba27..9fd21d4 100644 QList QWindowsNativeOpenFileDialog::selectedFiles() const { QList result; -@@ -1609,6 +1692,8 @@ public: +@@ -1609,6 +1694,8 @@ public: virtual QUrl directory() const Q_DECL_OVERRIDE; virtual void selectFile(const QUrl &filename) Q_DECL_OVERRIDE; virtual QList selectedFiles() const Q_DECL_OVERRIDE; @@ -11611,7 +11613,7 @@ index da0ba27..9fd21d4 100644 virtual void setFilter() Q_DECL_OVERRIDE; virtual void selectNameFilter(const QString &filter) Q_DECL_OVERRIDE; virtual QString selectedNameFilter() const Q_DECL_OVERRIDE; -@@ -1702,6 +1787,12 @@ QList QWindowsFileDialogHelper::selectedFiles() const +@@ -1702,6 +1789,12 @@ QList QWindowsFileDialogHelper::selectedFiles() const return m_data.selectedFiles(); } @@ -11624,7 +11626,7 @@ index da0ba27..9fd21d4 100644 void QWindowsFileDialogHelper::setFilter() { qCDebug(lcQpaDialogs) << __FUNCTION__; -@@ -1992,6 +2083,8 @@ public: +@@ -1992,6 +2085,8 @@ public: virtual QUrl directory() const Q_DECL_OVERRIDE; virtual void selectFile(const QUrl &url) Q_DECL_OVERRIDE; virtual QList selectedFiles() const Q_DECL_OVERRIDE; @@ -11633,7 +11635,7 @@ index da0ba27..9fd21d4 100644 virtual void setFilter() Q_DECL_OVERRIDE {} virtual void selectNameFilter(const QString &) Q_DECL_OVERRIDE; virtual QString selectedNameFilter() const Q_DECL_OVERRIDE; -@@ -2035,6 +2128,12 @@ QList QWindowsXpFileDialogHelper::selectedFiles() const +@@ -2035,6 +2130,12 @@ QList QWindowsXpFileDialogHelper::selectedFiles() const return m_data.selectedFiles(); }