Extract SparseIdsList module from SharedMedia.

This way it can be reused in search results management.
This commit is contained in:
John Preston 2017-10-29 19:32:01 +04:00
parent 15cc4502b4
commit a27edcad1c
19 changed files with 1254 additions and 1070 deletions

View File

@ -40,6 +40,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "storage/storage_facade.h" #include "storage/storage_facade.h"
#include "storage/storage_shared_media.h" #include "storage/storage_shared_media.h"
#include "storage/storage_user_photos.h" #include "storage/storage_user_photos.h"
#include "history/history_sparse_ids.h"
namespace { namespace {
@ -1922,6 +1923,12 @@ void ApiWrap::sendSaveChatAdminsRequests(not_null<ChatData*> chat) {
requestSendDelayed(); requestSendDelayed();
} }
void ApiWrap::requestSharedMediaCount(
not_null<PeerData*> peer,
Storage::SharedMediaType type) {
requestSharedMedia(peer, type, 0, SliceType::Before);
}
void ApiWrap::requestSharedMedia( void ApiWrap::requestSharedMedia(
not_null<PeerData*> peer, not_null<PeerData*> peer,
SharedMediaType type, SharedMediaType type,

View File

@ -33,6 +33,8 @@ namespace Storage {
enum class SharedMediaType : char; enum class SharedMediaType : char;
} // namespace Storage } // namespace Storage
enum class SparseIdsLoadDirection;
namespace Api { namespace Api {
inline const MTPVector<MTPChat> *getChatsFromMessagesChats(const MTPmessages_Chats &chats) { inline const MTPVector<MTPChat> *getChatsFromMessagesChats(const MTPmessages_Chats &chats) {
@ -113,11 +115,7 @@ public:
bool adminsEnabled, bool adminsEnabled,
base::flat_set<not_null<UserData*>> &&admins); base::flat_set<not_null<UserData*>> &&admins);
enum class SliceType { using SliceType = SparseIdsLoadDirection;
Around,
Before,
After,
};
void requestSharedMedia( void requestSharedMedia(
not_null<PeerData*> peer, not_null<PeerData*> peer,
Storage::SharedMediaType type, Storage::SharedMediaType type,
@ -125,9 +123,7 @@ public:
SliceType slice); SliceType slice);
void requestSharedMediaCount( void requestSharedMediaCount(
not_null<PeerData*> peer, not_null<PeerData*> peer,
Storage::SharedMediaType type) { Storage::SharedMediaType type);
requestSharedMedia(peer, type, 0, SliceType::Before);
}
void requestUserPhotos( void requestUserPhotos(
not_null<UserData*> user, not_null<UserData*> user,

View File

@ -386,6 +386,32 @@ public:
return compare()(value, *where) ? _impl.end() : where; return compare()(value, *where) ? _impl.end() : where;
} }
template <
typename OtherType,
typename = typename Compare::is_transparent>
iterator findFirst(const OtherType &value) {
if (empty()
|| compare()(value, front())
|| compare()(back(), value)) {
return end();
}
auto where = getLowerBound(value);
return compare()(value, *where) ? _impl.end() : where;
}
template <
typename OtherType,
typename = typename Compare::is_transparent>
const_iterator findFirst(const OtherType &value) const {
if (empty()
|| compare()(value, front())
|| compare()(back(), value)) {
return end();
}
auto where = getLowerBound(value);
return compare()(value, *where) ? _impl.end() : where;
}
bool contains(const Type &value) const { bool contains(const Type &value) const {
return findFirst(value) != end(); return findFirst(value) != end();
} }
@ -446,6 +472,18 @@ private:
typename impl::const_iterator getLowerBound(const Type &value) const { typename impl::const_iterator getLowerBound(const Type &value) const {
return base::lower_bound(_impl, value, compare()); return base::lower_bound(_impl, value, compare());
} }
template <
typename OtherType,
typename = typename Compare::is_transparent>
typename impl::iterator getLowerBound(const OtherType &value) {
return base::lower_bound(_impl, value, compare());
}
template <
typename OtherType,
typename = typename Compare::is_transparent>
typename impl::const_iterator getLowerBound(const OtherType &value) const {
return base::lower_bound(_impl, value, compare());
}
typename impl::iterator getUpperBound(const Type &value) { typename impl::iterator getUpperBound(const Type &value) {
return base::upper_bound(_impl, value, compare()); return base::upper_bound(_impl, value, compare());
} }
@ -557,6 +595,18 @@ public:
const_iterator find(const Type &value) const { const_iterator find(const Type &value) const {
return this->findFirst(value); return this->findFirst(value);
} }
template <
typename OtherType,
typename = typename Compare::is_transparent>
iterator find(const OtherType &value) {
return this->findFirst(value);
}
template <
typename OtherType,
typename = typename Compare::is_transparent>
const_iterator find(const OtherType &value) const {
return this->findFirst(value);
}
template <typename Action> template <typename Action>
void modify(iterator which, Action action) { void modify(iterator which, Action action) {

View File

@ -26,10 +26,11 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "storage/storage_facade.h" #include "storage/storage_facade.h"
#include "storage/storage_shared_media.h" #include "storage/storage_shared_media.h"
#include "history/history_media_types.h" #include "history/history_media_types.h"
#include "history/history_sparse_ids.h"
namespace { namespace {
using Type = SharedMediaSlice::Type; using Type = Storage::SharedMediaType;
inline MediaOverviewType SharedMediaTypeToOverview(Type type) { inline MediaOverviewType SharedMediaTypeToOverview(Type type) {
switch (type) { switch (type) {
@ -62,368 +63,8 @@ void SharedMediaShowOverview(
} }
} }
class SharedMediaSliceBuilder { rpl::producer<SparseIdsSlice> SharedMediaViewer(
public: Storage::SharedMediaKey key,
using Type = Storage::SharedMediaType;
using Key = Storage::SharedMediaKey;
SharedMediaSliceBuilder(Key key, int limitBefore, int limitAfter);
using Result = Storage::SharedMediaResult;
using SliceUpdate = Storage::SharedMediaSliceUpdate;
using RemoveOne = Storage::SharedMediaRemoveOne;
using RemoveAll = Storage::SharedMediaRemoveAll;
bool applyUpdate(const Result &result);
bool applyUpdate(const SliceUpdate &update);
bool applyUpdate(const RemoveOne &update);
bool applyUpdate(const RemoveAll &update);
void checkInsufficientMedia();
using AroundData = std::pair<MsgId, ApiWrap::SliceType>;
auto insufficientMediaAround() const {
return _insufficientMediaAround.events();
}
SharedMediaSlice snapshot() const;
private:
enum class RequestDirection {
Before,
After,
};
void requestMessages(RequestDirection direction);
void requestMessagesCount();
void fillSkippedAndSliceToLimits();
void sliceToLimits();
void mergeSliceData(
base::optional<int> count,
const base::flat_set<MsgId> &messageIds,
base::optional<int> skippedBefore = base::none,
base::optional<int> skippedAfter = base::none);
Key _key;
base::flat_set<MsgId> _ids;
MsgRange _range;
base::optional<int> _fullCount;
base::optional<int> _skippedBefore;
base::optional<int> _skippedAfter;
int _limitBefore = 0;
int _limitAfter = 0;
rpl::event_stream<AroundData> _insufficientMediaAround;
};
SharedMediaSlice::SharedMediaSlice(Key key) : SharedMediaSlice(
key,
{},
{},
base::none,
base::none,
base::none) {
}
SharedMediaSlice::SharedMediaSlice(
Key key,
const base::flat_set<MsgId> &ids,
MsgRange range,
base::optional<int> fullCount,
base::optional<int> skippedBefore,
base::optional<int> skippedAfter)
: _key(key)
, _ids(ids)
, _range(range)
, _fullCount(fullCount)
, _skippedBefore(skippedBefore)
, _skippedAfter(skippedAfter) {
}
base::optional<int> SharedMediaSlice::indexOf(MsgId msgId) const {
auto it = _ids.find(msgId);
if (it != _ids.end()) {
return (it - _ids.begin());
}
return base::none;
}
MsgId SharedMediaSlice::operator[](int index) const {
Expects(index >= 0 && index < size());
return *(_ids.begin() + index);
}
base::optional<int> SharedMediaSlice::distance(const Key &a, const Key &b) const {
if (a.type != _key.type
|| b.type != _key.type
|| a.peerId != _key.peerId
|| b.peerId != _key.peerId) {
return base::none;
}
if (auto i = indexOf(a.messageId)) {
if (auto j = indexOf(b.messageId)) {
return *j - *i;
}
}
return base::none;
}
base::optional<MsgId> SharedMediaSlice::nearest(MsgId msgId) const {
if (auto it = base::lower_bound(_ids, msgId); it != _ids.end()) {
return *it;
} else if (_ids.empty()) {
return base::none;
}
return _ids.back();
}
QString SharedMediaSlice::debug() const {
auto before = _skippedBefore
? (*_skippedBefore
? ('(' + QString::number(*_skippedBefore) + ").. ")
: QString())
: QString(".. ");
auto after = _skippedAfter
? (*_skippedAfter
? (" ..(" + QString::number(*_skippedAfter) + ')')
: QString())
: QString(" ..");
auto middle = (size() > 2)
? QString::number((*this)[0]) + " .. " + QString::number((*this)[size() - 1])
: (size() > 1)
? QString::number((*this)[0]) + ' ' + QString::number((*this)[1])
: ((size() > 0) ? QString::number((*this)[0]) : QString());
return before + middle + after;
}
SharedMediaSliceBuilder::SharedMediaSliceBuilder(
Key key,
int limitBefore,
int limitAfter)
: _key(key)
, _limitBefore(limitBefore)
, _limitAfter(limitAfter) {
}
bool SharedMediaSliceBuilder::applyUpdate(const Result &result) {
mergeSliceData(
result.count,
result.messageIds,
result.skippedBefore,
result.skippedAfter);
return true;
}
bool SharedMediaSliceBuilder::applyUpdate(const SliceUpdate &update) {
if (update.peerId != _key.peerId || update.type != _key.type) {
return false;
}
auto intersects = [](MsgRange range1, MsgRange range2) {
return (range1.from <= range2.till)
&& (range2.from <= range1.till);
};
auto needMergeMessages = (update.messages != nullptr)
&& intersects(update.range, {
_ids.empty() ? _key.messageId : _ids.front(),
_ids.empty() ? _key.messageId : _ids.back()
});
if (!needMergeMessages && !update.count) {
return false;
}
auto skippedBefore = (update.range.from == 0)
? 0
: base::optional<int> {};
auto skippedAfter = (update.range.till == ServerMaxMsgId)
? 0
: base::optional<int> {};
mergeSliceData(
update.count,
needMergeMessages
? *update.messages
: base::flat_set<MsgId> {},
skippedBefore,
skippedAfter);
return true;
}
bool SharedMediaSliceBuilder::applyUpdate(const RemoveOne &update) {
if (update.peerId != _key.peerId || !update.types.test(_key.type)) {
return false;
}
auto changed = false;
if (_fullCount && *_fullCount > 0) {
--*_fullCount;
changed = true;
}
if (_ids.contains(update.messageId)) {
_ids.remove(update.messageId);
changed = true;
} else if (!_ids.empty()) {
if (_ids.front() > update.messageId
&& _skippedBefore
&& *_skippedBefore > 0) {
--*_skippedBefore;
changed = true;
} else if (_ids.back() < update.messageId
&& _skippedAfter
&& *_skippedAfter > 0) {
--*_skippedAfter;
changed = true;
}
}
return changed;
}
bool SharedMediaSliceBuilder::applyUpdate(const RemoveAll &update) {
if (update.peerId != _key.peerId) {
return false;
}
_ids = {};
_range = { 0, ServerMaxMsgId };
_fullCount = 0;
_skippedBefore = 0;
_skippedAfter = 0;
return true;
}
void SharedMediaSliceBuilder::checkInsufficientMedia() {
sliceToLimits();
}
void SharedMediaSliceBuilder::mergeSliceData(
base::optional<int> count,
const base::flat_set<MsgId> &messageIds,
base::optional<int> skippedBefore,
base::optional<int> skippedAfter) {
if (messageIds.empty()) {
if (count && _fullCount != count) {
_fullCount = count;
if (*_fullCount <= _ids.size()) {
_fullCount = _ids.size();
_skippedBefore = _skippedAfter = 0;
}
}
fillSkippedAndSliceToLimits();
return;
}
if (count) {
_fullCount = count;
}
auto wasMinId = _ids.empty() ? -1 : _ids.front();
auto wasMaxId = _ids.empty() ? -1 : _ids.back();
_ids.merge(messageIds.begin(), messageIds.end());
auto adjustSkippedBefore = [&](MsgId oldId, int oldSkippedBefore) {
auto it = _ids.find(oldId);
Assert(it != _ids.end());
_skippedBefore = oldSkippedBefore - (it - _ids.begin());
accumulate_max(*_skippedBefore, 0);
};
if (skippedBefore) {
adjustSkippedBefore(messageIds.front(), *skippedBefore);
} else if (wasMinId >= 0 && _skippedBefore) {
adjustSkippedBefore(wasMinId, *_skippedBefore);
} else {
_skippedBefore = base::none;
}
auto adjustSkippedAfter = [&](MsgId oldId, int oldSkippedAfter) {
auto it = _ids.find(oldId);
Assert(it != _ids.end());
_skippedAfter = oldSkippedAfter - (_ids.end() - it - 1);
accumulate_max(*_skippedAfter, 0);
};
if (skippedAfter) {
adjustSkippedAfter(messageIds.back(), *skippedAfter);
} else if (wasMaxId >= 0 && _skippedAfter) {
adjustSkippedAfter(wasMaxId, *_skippedAfter);
} else {
_skippedAfter = base::none;
}
fillSkippedAndSliceToLimits();
}
void SharedMediaSliceBuilder::fillSkippedAndSliceToLimits() {
if (_fullCount) {
if (_skippedBefore && !_skippedAfter) {
_skippedAfter = *_fullCount
- *_skippedBefore
- int(_ids.size());
} else if (_skippedAfter && !_skippedBefore) {
_skippedBefore = *_fullCount
- *_skippedAfter
- int(_ids.size());
}
}
sliceToLimits();
}
void SharedMediaSliceBuilder::sliceToLimits() {
if (!_key.messageId) {
if (!_fullCount) {
requestMessagesCount();
}
return;
}
auto requestedSomething = false;
auto aroundIt = base::lower_bound(_ids, _key.messageId);
auto removeFromBegin = (aroundIt - _ids.begin() - _limitBefore);
auto removeFromEnd = (_ids.end() - aroundIt - _limitAfter - 1);
if (removeFromBegin > 0) {
_ids.erase(_ids.begin(), _ids.begin() + removeFromBegin);
if (_skippedBefore) {
*_skippedBefore += removeFromBegin;
}
} else if (removeFromBegin < 0
&& (!_skippedBefore || *_skippedBefore > 0)) {
requestedSomething = true;
requestMessages(RequestDirection::Before);
}
if (removeFromEnd > 0) {
_ids.erase(_ids.end() - removeFromEnd, _ids.end());
if (_skippedAfter) {
*_skippedAfter += removeFromEnd;
}
} else if (removeFromEnd < 0
&& (!_skippedAfter || *_skippedAfter > 0)) {
requestedSomething = true;
requestMessages(RequestDirection::After);
}
if (!_fullCount && !requestedSomething) {
requestMessagesCount();
}
}
void SharedMediaSliceBuilder::requestMessages(
RequestDirection direction) {
using SliceType = ApiWrap::SliceType;
auto requestAroundData = [&]() -> AroundData {
if (_ids.empty()) {
return { _key.messageId, SliceType::Around };
} else if (direction == RequestDirection::Before) {
return { _ids.front(), SliceType::Before };
}
return { _ids.back(), SliceType::After };
};
_insufficientMediaAround.fire(requestAroundData());
}
void SharedMediaSliceBuilder::requestMessagesCount() {
_insufficientMediaAround.fire({ 0, ApiWrap::SliceType::Around });
}
SharedMediaSlice SharedMediaSliceBuilder::snapshot() const {
return SharedMediaSlice(
_key,
_ids,
_range,
_fullCount,
_skippedBefore,
_skippedAfter);
}
rpl::producer<SharedMediaSlice> SharedMediaViewer(
SharedMediaSlice::Key key,
int limitBefore, int limitBefore,
int limitAfter) { int limitAfter) {
Expects(IsServerMsgId(key.messageId) || (key.messageId == 0)); Expects(IsServerMsgId(key.messageId) || (key.messageId == 0));
@ -431,199 +72,123 @@ rpl::producer<SharedMediaSlice> SharedMediaViewer(
return [=](auto consumer) { return [=](auto consumer) {
auto lifetime = rpl::lifetime(); auto lifetime = rpl::lifetime();
auto builder = lifetime.make_state<SharedMediaSliceBuilder>( auto builder = lifetime.make_state<SparseIdsSliceBuilder>(
key, key.messageId,
limitBefore, limitBefore,
limitAfter); limitAfter);
auto applyUpdate = [=](auto &&update) {
if (builder->applyUpdate(std::forward<decltype(update)>(update))) {
consumer.put_next(builder->snapshot());
}
};
auto requestMediaAround = [ auto requestMediaAround = [
peer = App::peer(key.peerId), peer = App::peer(key.peerId),
type = key.type type = key.type
](const SharedMediaSliceBuilder::AroundData &data) { ](const SparseIdsSliceBuilder::AroundData &data) {
Auth().api().requestSharedMedia( Auth().api().requestSharedMedia(
peer, peer,
type, type,
data.first, data.first,
data.second); data.second);
}; };
builder->insufficientMediaAround() builder->insufficientAround()
| rpl::start_with_next(requestMediaAround, lifetime); | rpl::start_with_next(requestMediaAround, lifetime);
Auth().storage().sharedMediaSliceUpdated() auto pushNextSnapshot = [=] {
| rpl::start_with_next(applyUpdate, lifetime); consumer.put_next(builder->snapshot());
Auth().storage().sharedMediaOneRemoved() };
| rpl::start_with_next(applyUpdate, lifetime);
Auth().storage().sharedMediaAllRemoved()
| rpl::start_with_next(applyUpdate, lifetime);
using SliceUpdate = Storage::SharedMediaSliceUpdate;
Auth().storage().sharedMediaSliceUpdated()
| rpl::filter([=](const SliceUpdate &update) {
return (update.peerId == key.peerId)
&& (update.type == key.type);
})
| rpl::filter([=](const SliceUpdate &update) {
return builder->applyUpdate(update.data);
})
| rpl::start_with_next(pushNextSnapshot, lifetime);
using OneRemoved = Storage::SharedMediaRemoveOne;
Auth().storage().sharedMediaOneRemoved()
| rpl::filter([=](const OneRemoved &update) {
return (update.peerId == key.peerId)
&& update.types.test(key.type);
})
| rpl::filter([=](const OneRemoved &update) {
return builder->removeOne(update.messageId);
})
| rpl::start_with_next(pushNextSnapshot, lifetime);
using AllRemoved = Storage::SharedMediaRemoveAll;
Auth().storage().sharedMediaAllRemoved()
| rpl::filter([=](const AllRemoved &update) {
return (update.peerId == key.peerId);
})
| rpl::filter([=](const AllRemoved &update) {
return builder->removeAll();
})
| rpl::start_with_next(pushNextSnapshot, lifetime);
using Result = Storage::SharedMediaResult;
Auth().storage().query( Auth().storage().query(
Storage::SharedMediaQuery( Storage::SharedMediaQuery(
key, key,
limitBefore, limitBefore,
limitAfter) limitAfter))
) | rpl::start_with_next_done( | rpl::filter([=](const Result &result) {
applyUpdate, return builder->applyInitial(result);
[=] { builder->checkInsufficientMedia(); }, })
lifetime); | rpl::start_with_next_done(
pushNextSnapshot,
[=] { builder->checkInsufficient(); },
lifetime);
return lifetime; return lifetime;
}; };
} }
SharedMediaMergedSlice::SharedMediaMergedSlice(Key key) : SharedMediaMergedSlice( rpl::producer<SparseIdsMergedSlice> SharedMediaMergedViewer(
key, SharedMediaMergedKey key,
SharedMediaSlice(PartKey(key)),
MigratedSlice(key)) {
}
SharedMediaMergedSlice::SharedMediaMergedSlice(
Key key,
SharedMediaSlice part,
base::optional<SharedMediaSlice> migrated)
: _key(key)
, _part(std::move(part))
, _migrated(std::move(migrated)) {
}
base::optional<int> SharedMediaMergedSlice::fullCount() const {
return Add(
_part.fullCount(),
_migrated ? _migrated->fullCount() : 0);
}
base::optional<int> SharedMediaMergedSlice::skippedBefore() const {
return Add(
isolatedInMigrated() ? 0 : _part.skippedBefore(),
_migrated
? (isolatedInPart()
? _migrated->fullCount()
: _migrated->skippedBefore())
: 0
);
}
base::optional<int> SharedMediaMergedSlice::skippedAfter() const {
return Add(
isolatedInMigrated() ? _part.fullCount() : _part.skippedAfter(),
isolatedInPart() ? 0 : _migrated->skippedAfter()
);
}
base::optional<int> SharedMediaMergedSlice::indexOf(FullMsgId fullId) const {
return isFromPart(fullId)
? (_part.indexOf(fullId.msg) | func::add(migratedSize()))
: isolatedInPart()
? base::none
: isFromMigrated(fullId)
? _migrated->indexOf(fullId.msg)
: base::none;
}
int SharedMediaMergedSlice::size() const {
return (isolatedInPart() ? 0 : migratedSize())
+ (isolatedInMigrated() ? 0 : _part.size());
}
FullMsgId SharedMediaMergedSlice::operator[](int index) const {
Expects(index >= 0 && index < size());
if (auto size = migratedSize()) {
if (index < size) {
return ComputeId(*_migrated, index);
}
index -= size;
}
return ComputeId(_part, index);
}
base::optional<int> SharedMediaMergedSlice::distance(
const Key &a,
const Key &b) const {
if (a.type != _key.type
|| b.type != _key.type
|| a.peerId != _key.peerId
|| b.peerId != _key.peerId
|| a.migratedPeerId != _key.migratedPeerId
|| b.migratedPeerId != _key.migratedPeerId) {
return base::none;
}
if (auto i = indexOf(ComputeId(a))) {
if (auto j = indexOf(ComputeId(b))) {
return *j - *i;
}
}
return base::none;
}
auto SharedMediaMergedSlice::nearest(
UniversalMsgId id) const -> base::optional<UniversalMsgId> {
auto convertFromMigratedNearest = [](MsgId result) {
return result - ServerMaxMsgId;
};
if (IsServerMsgId(id)) {
if (auto partNearestId = _part.nearest(id)) {
return partNearestId;
} else if (isolatedInPart()) {
return base::none;
}
return _migrated->nearest(ServerMaxMsgId - 1)
| convertFromMigratedNearest;
}
if (auto migratedNearestId = _migrated
? _migrated->nearest(id + ServerMaxMsgId)
: base::none) {
return migratedNearestId
| convertFromMigratedNearest;
} else if (isolatedInMigrated()) {
return base::none;
}
return _part.nearest(0);
}
QString SharedMediaMergedSlice::debug() const {
return (_migrated ? (_migrated->debug() + '|') : QString()) + _part.debug();
}
rpl::producer<SharedMediaMergedSlice> SharedMediaMergedViewer(
SharedMediaMergedSlice::Key key,
int limitBefore, int limitBefore,
int limitAfter) { int limitAfter) {
Expects(IsServerMsgId(key.universalId) Expects(IsServerMsgId(key.mergedKey.universalId)
|| (key.universalId == 0) || (key.mergedKey.universalId == 0)
|| (IsServerMsgId(ServerMaxMsgId + key.universalId) && key.migratedPeerId != 0)); || (IsServerMsgId(ServerMaxMsgId + key.mergedKey.universalId) && key.mergedKey.migratedPeerId != 0));
Expects((key.universalId != 0) || (limitBefore == 0 && limitAfter == 0)); Expects((key.mergedKey.universalId != 0)
|| (limitBefore == 0 && limitAfter == 0));
return [=](auto consumer) { return [=](auto consumer) {
if (!key.migratedPeerId) { if (!key.mergedKey.migratedPeerId) {
return SharedMediaViewer( return SharedMediaViewer(
SharedMediaMergedSlice::PartKey(key), Storage::SharedMediaKey(
key.mergedKey.peerId,
key.type,
SparseIdsMergedSlice::PartKey(key.mergedKey)),
limitBefore, limitBefore,
limitAfter limitAfter
) | rpl::start_with_next([=](SharedMediaSlice &&part) { ) | rpl::start_with_next([=](SparseIdsSlice &&part) {
consumer.put_next(SharedMediaMergedSlice( consumer.put_next(SparseIdsMergedSlice(
key, key.mergedKey,
std::move(part), std::move(part),
base::none)); base::none));
}); });
} }
return rpl::combine( return rpl::combine(
SharedMediaViewer( SharedMediaViewer(
SharedMediaMergedSlice::PartKey(key), Storage::SharedMediaKey(
key.mergedKey.peerId,
key.type,
SparseIdsMergedSlice::PartKey(key.mergedKey)),
limitBefore, limitBefore,
limitAfter), limitAfter),
SharedMediaViewer( SharedMediaViewer(
SharedMediaMergedSlice::MigratedKey(key), Storage::SharedMediaKey(
key.mergedKey.migratedPeerId,
key.type,
SparseIdsMergedSlice::MigratedKey(key.mergedKey)),
limitBefore, limitBefore,
limitAfter) limitAfter)
) | rpl::start_with_next([=]( ) | rpl::start_with_next([=](
SharedMediaSlice &&part, SparseIdsSlice &&part,
SharedMediaSlice &&migrated) { SparseIdsSlice &&migrated) {
consumer.put_next(SharedMediaMergedSlice( consumer.put_next(SparseIdsMergedSlice(
key, key.mergedKey,
std::move(part), std::move(part),
std::move(migrated))); std::move(migrated)));
}); });
@ -633,21 +198,21 @@ rpl::producer<SharedMediaMergedSlice> SharedMediaMergedViewer(
SharedMediaWithLastSlice::SharedMediaWithLastSlice(Key key) SharedMediaWithLastSlice::SharedMediaWithLastSlice(Key key)
: SharedMediaWithLastSlice( : SharedMediaWithLastSlice(
key, key,
SharedMediaMergedSlice(ViewerKey(key)), SparseIdsMergedSlice(ViewerKey(key)),
EndingSlice(key)) { EndingSlice(key)) {
} }
SharedMediaWithLastSlice::SharedMediaWithLastSlice( SharedMediaWithLastSlice::SharedMediaWithLastSlice(
Key key, Key key,
SharedMediaMergedSlice slice, SparseIdsMergedSlice slice,
base::optional<SharedMediaMergedSlice> ending) base::optional<SparseIdsMergedSlice> ending)
: _key(key) : _key(key)
, _slice(std::move(slice)) , _slice(std::move(slice))
, _ending(std::move(ending)) , _ending(std::move(ending))
, _lastPhotoId(LastPeerPhotoId(key.peerId)) , _lastPhotoId(LastPeerPhotoId(key.peerId))
, _isolatedLastPhoto(_key.type == Type::ChatPhoto , _isolatedLastPhoto(_key.type == Type::ChatPhoto
? IsLastIsolated(_slice, _ending, _lastPhotoId) ? IsLastIsolated(_slice, _ending, _lastPhotoId)
: false) { : false) {
} }
base::optional<int> SharedMediaWithLastSlice::fullCount() const { base::optional<int> SharedMediaWithLastSlice::fullCount() const {
@ -690,15 +255,9 @@ SharedMediaWithLastSlice::Value SharedMediaWithLastSlice::operator[](int index)
: Value(App::photo(_lastPhotoId)); : Value(App::photo(_lastPhotoId));
} }
base::optional<int> SharedMediaWithLastSlice::distance(const Key &a, const Key &b) const { base::optional<int> SharedMediaWithLastSlice::distance(
if (a.type != _key.type const Key &a,
|| b.type != _key.type const Key &b) const {
|| a.peerId != _key.peerId
|| b.peerId != _key.peerId
|| a.migratedPeerId != _key.migratedPeerId
|| b.migratedPeerId != _key.migratedPeerId) {
return base::none;
}
if (auto i = indexOf(ComputeId(a))) { if (auto i = indexOf(ComputeId(a))) {
if (auto j = indexOf(ComputeId(b))) { if (auto j = indexOf(ComputeId(b))) {
return *j - *i; return *j - *i;
@ -707,12 +266,6 @@ base::optional<int> SharedMediaWithLastSlice::distance(const Key &a, const Key &
return base::none; return base::none;
} }
QString SharedMediaWithLastSlice::debug() const {
return _slice.debug() + (_isolatedLastPhoto
? (*_isolatedLastPhoto ? "@" : "")
: "?");
}
PhotoId SharedMediaWithLastSlice::LastPeerPhotoId(PeerId peerId) { PhotoId SharedMediaWithLastSlice::LastPeerPhotoId(PeerId peerId) {
if (auto peer = App::peerLoaded(peerId)) { if (auto peer = App::peerLoaded(peerId)) {
return peer->photoId; return peer->photoId;
@ -721,8 +274,8 @@ PhotoId SharedMediaWithLastSlice::LastPeerPhotoId(PeerId peerId) {
} }
base::optional<bool> SharedMediaWithLastSlice::IsLastIsolated( base::optional<bool> SharedMediaWithLastSlice::IsLastIsolated(
const SharedMediaMergedSlice &slice, const SparseIdsMergedSlice &slice,
const base::optional<SharedMediaMergedSlice> &ending, const base::optional<SparseIdsMergedSlice> &ending,
PhotoId lastPeerPhotoId) { PhotoId lastPeerPhotoId) {
if (lastPeerPhotoId == UnknownPeerPhotoId) { if (lastPeerPhotoId == UnknownPeerPhotoId) {
return base::none; return base::none;
@ -742,7 +295,7 @@ base::optional<bool> SharedMediaWithLastSlice::IsLastIsolated(
} }
base::optional<FullMsgId> SharedMediaWithLastSlice::LastFullMsgId( base::optional<FullMsgId> SharedMediaWithLastSlice::LastFullMsgId(
const SharedMediaMergedSlice &slice) { const SparseIdsMergedSlice &slice) {
if (slice.fullCount() == 0) { if (slice.fullCount() == 0) {
return FullMsgId(); return FullMsgId();
} else if (slice.size() == 0 || slice.skippedAfter() != 0) { } else if (slice.size() == 0 || slice.skippedAfter() != 0) {
@ -758,10 +311,12 @@ rpl::producer<SharedMediaWithLastSlice> SharedMediaWithLastViewer(
return [=](auto consumer) { return [=](auto consumer) {
if (base::get_if<not_null<PhotoData*>>(&key.universalId)) { if (base::get_if<not_null<PhotoData*>>(&key.universalId)) {
return SharedMediaMergedViewer( return SharedMediaMergedViewer(
SharedMediaWithLastSlice::ViewerKey(key), SharedMediaMergedKey(
SharedMediaWithLastSlice::ViewerKey(key),
key.type),
limitBefore, limitBefore,
limitAfter limitAfter
) | rpl::start_with_next([=](SharedMediaMergedSlice &&update) { ) | rpl::start_with_next([=](SparseIdsMergedSlice &&update) {
consumer.put_next(SharedMediaWithLastSlice( consumer.put_next(SharedMediaWithLastSlice(
key, key,
std::move(update), std::move(update),
@ -770,16 +325,20 @@ rpl::producer<SharedMediaWithLastSlice> SharedMediaWithLastViewer(
} }
return rpl::combine( return rpl::combine(
SharedMediaMergedViewer( SharedMediaMergedViewer(
SharedMediaWithLastSlice::ViewerKey(key), SharedMediaMergedKey(
SharedMediaWithLastSlice::ViewerKey(key),
key.type),
limitBefore, limitBefore,
limitAfter), limitAfter),
SharedMediaMergedViewer( SharedMediaMergedViewer(
SharedMediaWithLastSlice::EndingKey(key), SharedMediaMergedKey(
SharedMediaWithLastSlice::EndingKey(key),
key.type),
1, 1,
1) 1)
) | rpl::start_with_next([=]( ) | rpl::start_with_next([=](
SharedMediaMergedSlice &&viewer, SparseIdsMergedSlice &&viewer,
SharedMediaMergedSlice &&ending) { SparseIdsMergedSlice &&ending) {
consumer.put_next(SharedMediaWithLastSlice( consumer.put_next(SharedMediaWithLastSlice(
key, key,
std::move(viewer), std::move(viewer),

View File

@ -22,6 +22,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "storage/storage_shared_media.h" #include "storage/storage_shared_media.h"
#include "base/weak_unique_ptr.h" #include "base/weak_unique_ptr.h"
#include "history/history_sparse_ids.h"
base::optional<Storage::SharedMediaType> SharedMediaOverviewType( base::optional<Storage::SharedMediaType> SharedMediaOverviewType(
Storage::SharedMediaType type); Storage::SharedMediaType type);
@ -29,176 +30,33 @@ void SharedMediaShowOverview(
Storage::SharedMediaType type, Storage::SharedMediaType type,
not_null<History*> history); not_null<History*> history);
class SharedMediaSlice { rpl::producer<SparseIdsSlice> SharedMediaViewer(
public: Storage::SharedMediaKey key,
using Type = Storage::SharedMediaType;
using Key = Storage::SharedMediaKey;
SharedMediaSlice(Key key);
SharedMediaSlice(
Key key,
const base::flat_set<MsgId> &ids,
MsgRange range,
base::optional<int> fullCount,
base::optional<int> skippedBefore,
base::optional<int> skippedAfter);
const Key &key() const { return _key; }
base::optional<int> fullCount() const { return _fullCount; }
base::optional<int> skippedBefore() const { return _skippedBefore; }
base::optional<int> skippedAfter() const { return _skippedAfter; }
base::optional<int> indexOf(MsgId msgId) const;
int size() const { return _ids.size(); }
MsgId operator[](int index) const;
base::optional<int> distance(const Key &a, const Key &b) const;
base::optional<MsgId> nearest(MsgId msgId) const;
QString debug() const;
private:
Key _key;
base::flat_set<MsgId> _ids;
MsgRange _range;
base::optional<int> _fullCount;
base::optional<int> _skippedBefore;
base::optional<int> _skippedAfter;
class SharedMediaSliceBuilder;
};
rpl::producer<SharedMediaSlice> SharedMediaViewer(
SharedMediaSlice::Key key,
int limitBefore, int limitBefore,
int limitAfter); int limitAfter);
class SharedMediaMergedSlice { struct SharedMediaMergedKey {
public:
using Type = Storage::SharedMediaType; using Type = Storage::SharedMediaType;
using UniversalMsgId = MsgId;
struct Key {
Key(
PeerId peerId,
PeerId migratedPeerId,
Type type,
UniversalMsgId universalId)
: peerId(peerId)
, migratedPeerId(migratedPeerId)
, type(type)
, universalId(universalId) {
}
bool operator==(const Key &other) const { SharedMediaMergedKey(
return (peerId == other.peerId) SparseIdsMergedSlice::Key mergedKey,
&& (migratedPeerId == other.migratedPeerId) Type type)
&& (type == other.type) : mergedKey(mergedKey)
&& (universalId == other.universalId); , type(type) {
}
PeerId peerId = 0;
PeerId migratedPeerId = 0;
Type type = Type::kCount;
UniversalMsgId universalId = 0;
};
SharedMediaMergedSlice(Key key);
SharedMediaMergedSlice(
Key key,
SharedMediaSlice part,
base::optional<SharedMediaSlice> migrated);
const Key &key() const { return _key; }
base::optional<int> fullCount() const;
base::optional<int> skippedBefore() const;
base::optional<int> skippedAfter() const;
base::optional<int> indexOf(FullMsgId fullId) const;
int size() const;
FullMsgId operator[](int index) const;
base::optional<int> distance(const Key &a, const Key &b) const;
base::optional<UniversalMsgId> nearest(UniversalMsgId id) const;
QString debug() const;
static SharedMediaSlice::Key PartKey(const Key &key) {
return {
key.peerId,
key.type,
(key.universalId < 0) ? 1 : key.universalId
};
}
static SharedMediaSlice::Key MigratedKey(const Key &key) {
return {
key.migratedPeerId,
key.type,
(key.universalId < 0)
? (ServerMaxMsgId + key.universalId)
: (key.universalId > 0) ? (ServerMaxMsgId - 1) : 0
};
} }
private: bool operator==(const SharedMediaMergedKey &other) const {
static base::optional<SharedMediaSlice> MigratedSlice(const Key &key) { return (mergedKey == other.mergedKey)
return key.migratedPeerId && (type == other.type);
? base::make_optional(SharedMediaSlice(MigratedKey(key)))
: base::none;
} }
static bool IsFromSlice(const SharedMediaSlice &slice, FullMsgId fullId) { SparseIdsMergedSlice::Key mergedKey;
auto peer = slice.key().peerId; Type type = Type::kCount;
return peerIsChannel(peer)
? (peer == peerFromChannel(fullId.channel))
: !fullId.channel;
}
static FullMsgId ComputeId(PeerId peerId, MsgId msgId) {
return FullMsgId(
peerIsChannel(peerId) ? peerToBareInt(peerId) : 0,
msgId);
}
static FullMsgId ComputeId(const SharedMediaSlice &slice, int index) {
return ComputeId(slice.key().peerId, slice[index]);
};
static FullMsgId ComputeId(const Key &key) {
return (key.universalId >= 0)
? ComputeId(key.peerId, key.universalId)
: ComputeId(key.migratedPeerId, ServerMaxMsgId + key.universalId);
}
static base::optional<int> Add(
const base::optional<int> &a,
const base::optional<int> &b) {
return (a && b) ? base::make_optional(*a + *b) : base::none;
}
bool isFromPart(FullMsgId fullId) const {
return IsFromSlice(_part, fullId);
}
bool isFromMigrated(FullMsgId fullId) const {
return _migrated ? IsFromSlice(*_migrated, fullId) : false;
}
int migratedSize() const {
return isolatedInPart() ? 0 : _migrated->size();
}
bool isolatedInPart() const {
return IsServerMsgId(_key.universalId)
&& (!_migrated || _part.skippedBefore() != 0);
}
bool isolatedInMigrated() const {
return IsServerMsgId(ServerMaxMsgId + _key.universalId)
&& (_migrated->skippedAfter() != 0);
}
Key _key;
SharedMediaSlice _part;
base::optional<SharedMediaSlice> _migrated;
friend class SharedMediaMergedSliceBuilder;
}; };
rpl::producer<SharedMediaMergedSlice> SharedMediaMergedViewer( rpl::producer<SparseIdsMergedSlice> SharedMediaMergedViewer(
SharedMediaMergedSlice::Key key, SharedMediaMergedKey key,
int limitBefore, int limitBefore,
int limitAfter); int limitAfter);
@ -206,9 +64,8 @@ class SharedMediaWithLastSlice {
public: public:
using Type = Storage::SharedMediaType; using Type = Storage::SharedMediaType;
// base::none in those mean CurrentPeerPhoto.
using Value = base::variant<FullMsgId, not_null<PhotoData*>>; using Value = base::variant<FullMsgId, not_null<PhotoData*>>;
using MessageId = SharedMediaMergedSlice::UniversalMsgId; using MessageId = SparseIdsMergedSlice::UniversalMsgId;
using UniversalMsgId = base::variant< using UniversalMsgId = base::variant<
MessageId, MessageId,
not_null<PhotoData*>>; not_null<PhotoData*>>;
@ -219,10 +76,10 @@ public:
PeerId migratedPeerId, PeerId migratedPeerId,
Type type, Type type,
UniversalMsgId universalId) UniversalMsgId universalId)
: peerId(peerId) : peerId(peerId)
, migratedPeerId(migratedPeerId) , migratedPeerId(migratedPeerId)
, type(type) , type(type)
, universalId(universalId) { , universalId(universalId) {
Expects(base::get_if<MessageId>(&universalId) != nullptr Expects(base::get_if<MessageId>(&universalId) != nullptr
|| type == Type::ChatPhoto); || type == Type::ChatPhoto);
} }
@ -233,6 +90,9 @@ public:
&& (type == other.type) && (type == other.type)
&& (universalId == other.universalId); && (universalId == other.universalId);
} }
bool operator!=(const Key &other) const {
return !(*this == other);
}
PeerId peerId = 0; PeerId peerId = 0;
PeerId migratedPeerId = 0; PeerId migratedPeerId = 0;
@ -244,8 +104,8 @@ public:
SharedMediaWithLastSlice(Key key); SharedMediaWithLastSlice(Key key);
SharedMediaWithLastSlice( SharedMediaWithLastSlice(
Key key, Key key,
SharedMediaMergedSlice slice, SparseIdsMergedSlice slice,
base::optional<SharedMediaMergedSlice> ending); base::optional<SparseIdsMergedSlice> ending);
base::optional<int> fullCount() const; base::optional<int> fullCount() const;
base::optional<int> skippedBefore() const; base::optional<int> skippedBefore() const;
@ -255,41 +115,37 @@ public:
Value operator[](int index) const; Value operator[](int index) const;
base::optional<int> distance(const Key &a, const Key &b) const; base::optional<int> distance(const Key &a, const Key &b) const;
QString debug() const; static SparseIdsMergedSlice::Key ViewerKey(const Key &key) {
static SharedMediaMergedSlice::Key ViewerKey(const Key &key) {
return { return {
key.peerId, key.peerId,
key.migratedPeerId, key.migratedPeerId,
key.type,
base::get_if<MessageId>(&key.universalId) base::get_if<MessageId>(&key.universalId)
? (*base::get_if<MessageId>(&key.universalId)) ? (*base::get_if<MessageId>(&key.universalId))
: ServerMaxMsgId - 1 : ServerMaxMsgId - 1
}; };
} }
static SharedMediaMergedSlice::Key EndingKey(const Key &key) { static SparseIdsMergedSlice::Key EndingKey(const Key &key) {
return { return {
key.peerId, key.peerId,
key.migratedPeerId, key.migratedPeerId,
key.type,
ServerMaxMsgId - 1 ServerMaxMsgId - 1
}; };
} }
private: private:
static base::optional<SharedMediaMergedSlice> EndingSlice(const Key &key) { static base::optional<SparseIdsMergedSlice> EndingSlice(const Key &key) {
return base::get_if<MessageId>(&key.universalId) return base::get_if<MessageId>(&key.universalId)
? base::make_optional(SharedMediaMergedSlice(EndingKey(key))) ? base::make_optional(SparseIdsMergedSlice(EndingKey(key)))
: base::none; : base::none;
} }
static PhotoId LastPeerPhotoId(PeerId peerId); static PhotoId LastPeerPhotoId(PeerId peerId);
static base::optional<bool> IsLastIsolated( static base::optional<bool> IsLastIsolated(
const SharedMediaMergedSlice &slice, const SparseIdsMergedSlice &slice,
const base::optional<SharedMediaMergedSlice> &ending, const base::optional<SparseIdsMergedSlice> &ending,
PhotoId lastPeerPhotoId); PhotoId lastPeerPhotoId);
static base::optional<FullMsgId> LastFullMsgId( static base::optional<FullMsgId> LastFullMsgId(
const SharedMediaMergedSlice &slice); const SparseIdsMergedSlice &slice);
static base::optional<int> Add( static base::optional<int> Add(
const base::optional<int> &a, const base::optional<int> &a,
const base::optional<int> &b) { const base::optional<int> &b) {
@ -318,13 +174,11 @@ private:
} }
Key _key; Key _key;
SharedMediaMergedSlice _slice; SparseIdsMergedSlice _slice;
base::optional<SharedMediaMergedSlice> _ending; base::optional<SparseIdsMergedSlice> _ending;
PhotoId _lastPhotoId = 0; PhotoId _lastPhotoId = 0;
base::optional<bool> _isolatedLastPhoto; base::optional<bool> _isolatedLastPhoto;
friend class SharedMediaWithLastSliceBuilder;
}; };
rpl::producer<SharedMediaWithLastSlice> SharedMediaWithLastViewer( rpl::producer<SharedMediaWithLastSlice> SharedMediaWithLastViewer(

View File

@ -0,0 +1,391 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop 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 3 of the License, or
(at your option) any later version.
It 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.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "history/history_sparse_ids.h"
#include "storage/storage_sparse_ids_list.h"
SparseIdsSlice::SparseIdsSlice(
const base::flat_set<MsgId> &ids,
MsgRange range,
base::optional<int> fullCount,
base::optional<int> skippedBefore,
base::optional<int> skippedAfter)
: _ids(ids)
, _range(range)
, _fullCount(fullCount)
, _skippedBefore(skippedBefore)
, _skippedAfter(skippedAfter) {
}
base::optional<int> SparseIdsSlice::indexOf(MsgId msgId) const {
auto it = _ids.find(msgId);
if (it != _ids.end()) {
return (it - _ids.begin());
}
return base::none;
}
MsgId SparseIdsSlice::operator[](int index) const {
Expects(index >= 0 && index < size());
return *(_ids.begin() + index);
}
base::optional<int> SparseIdsSlice::distance(
MsgId a,
MsgId b) const {
if (auto i = indexOf(a)) {
if (auto j = indexOf(b)) {
return *j - *i;
}
}
return base::none;
}
base::optional<MsgId> SparseIdsSlice::nearest(MsgId msgId) const {
if (auto it = base::lower_bound(_ids, msgId); it != _ids.end()) {
return *it;
} else if (_ids.empty()) {
return base::none;
}
return _ids.back();
}
SparseIdsMergedSlice::SparseIdsMergedSlice(Key key)
: SparseIdsMergedSlice(
key,
SparseIdsSlice(),
MigratedSlice(key)) {
}
SparseIdsMergedSlice::SparseIdsMergedSlice(
Key key,
SparseIdsSlice part,
base::optional<SparseIdsSlice> migrated)
: _key(key)
, _part(std::move(part))
, _migrated(std::move(migrated)) {
}
base::optional<int> SparseIdsMergedSlice::fullCount() const {
return Add(
_part.fullCount(),
_migrated ? _migrated->fullCount() : 0);
}
base::optional<int> SparseIdsMergedSlice::skippedBefore() const {
return Add(
isolatedInMigrated() ? 0 : _part.skippedBefore(),
_migrated
? (isolatedInPart()
? _migrated->fullCount()
: _migrated->skippedBefore())
: 0
);
}
base::optional<int> SparseIdsMergedSlice::skippedAfter() const {
return Add(
isolatedInMigrated() ? _part.fullCount() : _part.skippedAfter(),
isolatedInPart() ? 0 : _migrated->skippedAfter()
);
}
base::optional<int> SparseIdsMergedSlice::indexOf(
FullMsgId fullId) const {
return isFromPart(fullId)
? (_part.indexOf(fullId.msg) | func::add(migratedSize()))
: isolatedInPart()
? base::none
: isFromMigrated(fullId)
? _migrated->indexOf(fullId.msg)
: base::none;
}
int SparseIdsMergedSlice::size() const {
return (isolatedInPart() ? 0 : migratedSize())
+ (isolatedInMigrated() ? 0 : _part.size());
}
FullMsgId SparseIdsMergedSlice::operator[](int index) const {
Expects(index >= 0 && index < size());
if (auto size = migratedSize()) {
if (index < size) {
return ComputeId(_key.migratedPeerId, (*_migrated)[index]);
}
index -= size;
}
return ComputeId(_key.peerId, _part[index]);
}
base::optional<int> SparseIdsMergedSlice::distance(
const Key &a,
const Key &b) const {
if (auto i = indexOf(ComputeId(a))) {
if (auto j = indexOf(ComputeId(b))) {
return *j - *i;
}
}
return base::none;
}
auto SparseIdsMergedSlice::nearest(
UniversalMsgId id) const -> base::optional<UniversalMsgId> {
auto convertFromMigratedNearest = [](MsgId result) {
return result - ServerMaxMsgId;
};
if (IsServerMsgId(id)) {
if (auto partNearestId = _part.nearest(id)) {
return partNearestId;
} else if (isolatedInPart()) {
return base::none;
}
return _migrated->nearest(ServerMaxMsgId - 1)
| convertFromMigratedNearest;
}
if (auto migratedNearestId = _migrated
? _migrated->nearest(id + ServerMaxMsgId)
: base::none) {
return migratedNearestId
| convertFromMigratedNearest;
} else if (isolatedInMigrated()) {
return base::none;
}
return _part.nearest(0);
}
SparseIdsSliceBuilder::SparseIdsSliceBuilder(
Key key,
int limitBefore,
int limitAfter)
: _key(key)
, _limitBefore(limitBefore)
, _limitAfter(limitAfter) {
}
bool SparseIdsSliceBuilder::applyInitial(
const Storage::SparseIdsListResult &result) {
mergeSliceData(
result.count,
result.messageIds,
result.skippedBefore,
result.skippedAfter);
return true;
}
bool SparseIdsSliceBuilder::applyUpdate(
const Storage::SparseIdsSliceUpdate &update) {
auto intersects = [](MsgRange range1, MsgRange range2) {
return (range1.from <= range2.till)
&& (range2.from <= range1.till);
};
auto needMergeMessages = (update.messages != nullptr)
&& intersects(update.range, {
_ids.empty() ? _key : _ids.front(),
_ids.empty() ? _key : _ids.back()
});
if (!needMergeMessages && !update.count) {
return false;
}
auto skippedBefore = (update.range.from == 0)
? 0
: base::optional<int> {};
auto skippedAfter = (update.range.till == ServerMaxMsgId)
? 0
: base::optional<int> {};
mergeSliceData(
update.count,
needMergeMessages
? *update.messages
: base::flat_set<MsgId> {},
skippedBefore,
skippedAfter);
return true;
}
bool SparseIdsSliceBuilder::removeOne(MsgId messageId) {
auto changed = false;
if (_fullCount && *_fullCount > 0) {
--*_fullCount;
changed = true;
}
if (_ids.contains(messageId)) {
_ids.remove(messageId);
changed = true;
} else if (!_ids.empty()) {
if (_ids.front() > messageId
&& _skippedBefore
&& *_skippedBefore > 0) {
--*_skippedBefore;
changed = true;
} else if (_ids.back() < messageId
&& _skippedAfter
&& *_skippedAfter > 0) {
--*_skippedAfter;
changed = true;
}
}
return changed;
}
bool SparseIdsSliceBuilder::removeAll() {
_ids = {};
_range = { 0, ServerMaxMsgId };
_fullCount = 0;
_skippedBefore = 0;
_skippedAfter = 0;
return true;
}
void SparseIdsSliceBuilder::checkInsufficient() {
sliceToLimits();
}
void SparseIdsSliceBuilder::mergeSliceData(
base::optional<int> count,
const base::flat_set<MsgId> &messageIds,
base::optional<int> skippedBefore,
base::optional<int> skippedAfter) {
if (messageIds.empty()) {
if (count && _fullCount != count) {
_fullCount = count;
if (*_fullCount <= _ids.size()) {
_fullCount = _ids.size();
_skippedBefore = _skippedAfter = 0;
}
}
fillSkippedAndSliceToLimits();
return;
}
if (count) {
_fullCount = count;
}
auto wasMinId = _ids.empty() ? -1 : _ids.front();
auto wasMaxId = _ids.empty() ? -1 : _ids.back();
_ids.merge(messageIds.begin(), messageIds.end());
auto adjustSkippedBefore = [&](MsgId oldId, int oldSkippedBefore) {
auto it = _ids.find(oldId);
Assert(it != _ids.end());
_skippedBefore = oldSkippedBefore - (it - _ids.begin());
accumulate_max(*_skippedBefore, 0);
};
if (skippedBefore) {
adjustSkippedBefore(messageIds.front(), *skippedBefore);
} else if (wasMinId >= 0 && _skippedBefore) {
adjustSkippedBefore(wasMinId, *_skippedBefore);
} else {
_skippedBefore = base::none;
}
auto adjustSkippedAfter = [&](MsgId oldId, int oldSkippedAfter) {
auto it = _ids.find(oldId);
Assert(it != _ids.end());
_skippedAfter = oldSkippedAfter - (_ids.end() - it - 1);
accumulate_max(*_skippedAfter, 0);
};
if (skippedAfter) {
adjustSkippedAfter(messageIds.back(), *skippedAfter);
} else if (wasMaxId >= 0 && _skippedAfter) {
adjustSkippedAfter(wasMaxId, *_skippedAfter);
} else {
_skippedAfter = base::none;
}
fillSkippedAndSliceToLimits();
}
void SparseIdsSliceBuilder::fillSkippedAndSliceToLimits() {
if (_fullCount) {
if (_skippedBefore && !_skippedAfter) {
_skippedAfter = *_fullCount
- *_skippedBefore
- int(_ids.size());
} else if (_skippedAfter && !_skippedBefore) {
_skippedBefore = *_fullCount
- *_skippedAfter
- int(_ids.size());
}
}
sliceToLimits();
}
void SparseIdsSliceBuilder::sliceToLimits() {
if (!_key) {
if (!_fullCount) {
requestMessagesCount();
}
return;
}
auto requestedSomething = false;
auto aroundIt = base::lower_bound(_ids, _key);
auto removeFromBegin = (aroundIt - _ids.begin() - _limitBefore);
auto removeFromEnd = (_ids.end() - aroundIt - _limitAfter - 1);
if (removeFromBegin > 0) {
_ids.erase(_ids.begin(), _ids.begin() + removeFromBegin);
if (_skippedBefore) {
*_skippedBefore += removeFromBegin;
}
} else if (removeFromBegin < 0
&& (!_skippedBefore || *_skippedBefore > 0)) {
requestedSomething = true;
requestMessages(RequestDirection::Before);
}
if (removeFromEnd > 0) {
_ids.erase(_ids.end() - removeFromEnd, _ids.end());
if (_skippedAfter) {
*_skippedAfter += removeFromEnd;
}
} else if (removeFromEnd < 0
&& (!_skippedAfter || *_skippedAfter > 0)) {
requestedSomething = true;
requestMessages(RequestDirection::After);
}
if (!_fullCount && !requestedSomething) {
requestMessagesCount();
}
}
void SparseIdsSliceBuilder::requestMessages(
RequestDirection direction) {
auto requestAroundData = [&]() -> AroundData {
if (_ids.empty()) {
return { _key, SparseIdsLoadDirection::Around };
} else if (direction == RequestDirection::Before) {
return { _ids.front(), SparseIdsLoadDirection::Before };
}
return { _ids.back(), SparseIdsLoadDirection::After };
};
_insufficientAround.fire(requestAroundData());
}
void SparseIdsSliceBuilder::requestMessagesCount() {
_insufficientAround.fire({ 0, SparseIdsLoadDirection::Around });
}
SparseIdsSlice SparseIdsSliceBuilder::snapshot() const {
return SparseIdsSlice(
_ids,
_range,
_fullCount,
_skippedBefore,
_skippedAfter);
}

View File

@ -0,0 +1,213 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop 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 3 of the License, or
(at your option) any later version.
It 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.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#pragma once
namespace Storage {
struct SparseIdsListResult;
struct SparseIdsSliceUpdate;
} // namespace Storage
enum class SparseIdsLoadDirection {
Around,
Before,
After,
};
class SparseIdsSlice {
public:
using Key = MsgId;
SparseIdsSlice() = default;
SparseIdsSlice(
const base::flat_set<MsgId> &ids,
MsgRange range,
base::optional<int> fullCount,
base::optional<int> skippedBefore,
base::optional<int> skippedAfter);
base::optional<int> fullCount() const { return _fullCount; }
base::optional<int> skippedBefore() const { return _skippedBefore; }
base::optional<int> skippedAfter() const { return _skippedAfter; }
base::optional<int> indexOf(MsgId msgId) const;
int size() const { return _ids.size(); }
MsgId operator[](int index) const;
base::optional<int> distance(MsgId a, MsgId b) const;
base::optional<MsgId> nearest(MsgId msgId) const;
private:
base::flat_set<MsgId> _ids;
MsgRange _range;
base::optional<int> _fullCount;
base::optional<int> _skippedBefore;
base::optional<int> _skippedAfter;
};
class SparseIdsMergedSlice {
public:
using UniversalMsgId = MsgId;
struct Key {
Key(
PeerId peerId,
PeerId migratedPeerId,
UniversalMsgId universalId)
: peerId(peerId)
, migratedPeerId(migratedPeerId)
, universalId(universalId) {
}
bool operator==(const Key &other) const {
return (peerId == other.peerId)
&& (migratedPeerId == other.migratedPeerId)
&& (universalId == other.universalId);
}
PeerId peerId = 0;
PeerId migratedPeerId = 0;
UniversalMsgId universalId = 0;
};
SparseIdsMergedSlice(Key key);
SparseIdsMergedSlice(
Key key,
SparseIdsSlice part,
base::optional<SparseIdsSlice> migrated);
base::optional<int> fullCount() const;
base::optional<int> skippedBefore() const;
base::optional<int> skippedAfter() const;
base::optional<int> indexOf(FullMsgId fullId) const;
int size() const;
FullMsgId operator[](int index) const;
base::optional<int> distance(const Key &a, const Key &b) const;
base::optional<UniversalMsgId> nearest(UniversalMsgId id) const;
static SparseIdsSlice::Key PartKey(const Key &key) {
return (key.universalId < 0) ? 1 : key.universalId;
}
static SparseIdsSlice::Key MigratedKey(const Key &key) {
return (key.universalId < 0)
? (ServerMaxMsgId + key.universalId)
: (key.universalId > 0) ? (ServerMaxMsgId - 1) : 0;
}
private:
static base::optional<SparseIdsSlice> MigratedSlice(const Key &key) {
return key.migratedPeerId
? base::make_optional(SparseIdsSlice())
: base::none;
}
static bool IsFromSlice(PeerId peerId, FullMsgId fullId) {
return peerIsChannel(peerId)
? (peerId == peerFromChannel(fullId.channel))
: !fullId.channel;
}
static FullMsgId ComputeId(PeerId peerId, MsgId msgId) {
return FullMsgId(
peerIsChannel(peerId) ? peerToBareInt(peerId) : 0,
msgId);
}
static FullMsgId ComputeId(const Key &key) {
return (key.universalId >= 0)
? ComputeId(key.peerId, key.universalId)
: ComputeId(key.migratedPeerId, ServerMaxMsgId + key.universalId);
}
static base::optional<int> Add(
const base::optional<int> &a,
const base::optional<int> &b) {
return (a && b) ? base::make_optional(*a + *b) : base::none;
}
bool isFromPart(FullMsgId fullId) const {
return IsFromSlice(_key.peerId, fullId);
}
bool isFromMigrated(FullMsgId fullId) const {
return _migrated
? IsFromSlice(_key.migratedPeerId, fullId)
: false;
}
int migratedSize() const {
return isolatedInPart() ? 0 : _migrated->size();
}
bool isolatedInPart() const {
return IsServerMsgId(_key.universalId)
&& (!_migrated || _part.skippedBefore() != 0);
}
bool isolatedInMigrated() const {
return IsServerMsgId(ServerMaxMsgId + _key.universalId)
&& (_migrated->skippedAfter() != 0);
}
Key _key;
SparseIdsSlice _part;
base::optional<SparseIdsSlice> _migrated;
};
class SparseIdsSliceBuilder {
public:
using Key = SparseIdsSlice::Key;
SparseIdsSliceBuilder(Key key, int limitBefore, int limitAfter);
bool applyInitial(const Storage::SparseIdsListResult &result);
bool applyUpdate(const Storage::SparseIdsSliceUpdate &update);
bool removeOne(MsgId messageId);
bool removeAll();
void checkInsufficient();
using AroundData = std::pair<MsgId, SparseIdsLoadDirection>;
auto insufficientAround() const {
return _insufficientAround.events();
}
SparseIdsSlice snapshot() const;
private:
enum class RequestDirection {
Before,
After,
};
void requestMessages(RequestDirection direction);
void requestMessagesCount();
void fillSkippedAndSliceToLimits();
void sliceToLimits();
void mergeSliceData(
base::optional<int> count,
const base::flat_set<MsgId> &messageIds,
base::optional<int> skippedBefore = base::none,
base::optional<int> skippedAfter = base::none);
Key _key;
base::flat_set<MsgId> _ids;
MsgRange _range;
base::optional<int> _fullCount;
base::optional<int> _skippedBefore;
base::optional<int> _skippedAfter;
int _limitBefore = 0;
int _limitAfter = 0;
rpl::event_stream<AroundData> _insufficientAround;
};

View File

@ -738,27 +738,29 @@ void ListWidget::invalidatePaletteCache() {
} }
} }
SharedMediaMergedSlice::Key ListWidget::sliceKey( SparseIdsMergedSlice::Key ListWidget::sliceKey(
UniversalMsgId universalId) const { UniversalMsgId universalId) const {
using Key = SharedMediaMergedSlice::Key; using Key = SparseIdsMergedSlice::Key;
if (auto migrateFrom = _peer->migrateFrom()) { if (auto migrateFrom = _peer->migrateFrom()) {
return Key(_peer->id, migrateFrom->id, _type, universalId); return Key(_peer->id, migrateFrom->id, universalId);
} }
if (universalId < 0) { if (universalId < 0) {
// Convert back to plain id for non-migrated histories. // Convert back to plain id for non-migrated histories.
universalId += ServerMaxMsgId; universalId += ServerMaxMsgId;
} }
return Key(_peer->id, 0, _type, universalId); return Key(_peer->id, 0, universalId);
} }
void ListWidget::refreshViewer() { void ListWidget::refreshViewer() {
_viewerLifetime.destroy(); _viewerLifetime.destroy();
SharedMediaMergedViewer( SharedMediaMergedViewer(
sliceKey(_universalAroundId), SharedMediaMergedKey(
sliceKey(_universalAroundId),
_type),
_idsLimit, _idsLimit,
_idsLimit) _idsLimit)
| rpl::start_with_next([this]( | rpl::start_with_next([this](
SharedMediaMergedSlice &&slice) { SparseIdsMergedSlice &&slice) {
_slice = std::move(slice); _slice = std::move(slice);
if (auto nearest = _slice.nearest(_universalAroundId)) { if (auto nearest = _slice.nearest(_universalAroundId)) {
_universalAroundId = *nearest; _universalAroundId = *nearest;

View File

@ -178,7 +178,7 @@ private:
void refreshViewer(); void refreshViewer();
void invalidatePaletteCache(); void invalidatePaletteCache();
void refreshRows(); void refreshRows();
SharedMediaMergedSlice::Key sliceKey( SparseIdsMergedSlice::Key sliceKey(
UniversalMsgId universalId) const; UniversalMsgId universalId) const;
BaseLayout *getLayout(UniversalMsgId universalId); BaseLayout *getLayout(UniversalMsgId universalId);
BaseLayout *getExistingLayout(UniversalMsgId universalId) const; BaseLayout *getExistingLayout(UniversalMsgId universalId) const;
@ -280,7 +280,7 @@ private:
static constexpr auto kDefaultAroundId = (ServerMaxMsgId - 1); static constexpr auto kDefaultAroundId = (ServerMaxMsgId - 1);
UniversalMsgId _universalAroundId = kDefaultAroundId; UniversalMsgId _universalAroundId = kDefaultAroundId;
int _idsLimit = kMinimalIdsLimit; int _idsLimit = kMinimalIdsLimit;
SharedMediaMergedSlice _slice; SparseIdsMergedSlice _slice;
std::map<UniversalMsgId, CachedItem> _layouts; std::map<UniversalMsgId, CachedItem> _layouts;
std::vector<Section> _sections; std::vector<Section> _sections;

View File

@ -172,14 +172,15 @@ rpl::producer<int> SharedMediaCountValue(
auto aroundId = 0; auto aroundId = 0;
auto limit = 0; auto limit = 0;
auto updated = SharedMediaMergedViewer( auto updated = SharedMediaMergedViewer(
SharedMediaMergedSlice::Key( SharedMediaMergedKey(
real->id, SparseIdsMergedSlice::Key(
migrated ? migrated->id : 0, real->id,
type, migrated ? migrated->id : 0,
aroundId), aroundId),
type),
limit, limit,
limit) limit)
| rpl::map([](const SharedMediaMergedSlice &slice) { | rpl::map([](const SparseIdsMergedSlice &slice) {
return slice.fullCount(); return slice.fullCount();
}) })
| rpl::filter_optional(); | rpl::filter_optional();

View File

@ -1035,14 +1035,27 @@ bool MediaView::validSharedMedia() const {
if (!_sharedMedia) { if (!_sharedMedia) {
return false; return false;
} }
auto countDistanceInData = [](const auto &a, const auto &b) { using Key = SharedMediaWithLastSlice::Key;
auto inSameDomain = [](const Key &a, const Key &b) {
return (a.type == b.type)
&& (a.peerId == b.peerId)
&& (a.migratedPeerId == b.migratedPeerId);
};
auto countDistanceInData = [&](const Key &a, const Key &b) {
return [&](const SharedMediaWithLastSlice &data) { return [&](const SharedMediaWithLastSlice &data) {
return data.distance(a, b); return inSameDomain(a, b)
? data.distance(a, b)
: base::optional<int>();
}; };
}; };
auto distance = (key == _sharedMedia->key) ? 0 : if (key == _sharedMedia->key) {
_sharedMediaData return true;
} else if (!_sharedMediaDataKey
|| _sharedMedia->key != *_sharedMediaDataKey) {
return false;
}
auto distance = _sharedMediaData
| countDistanceInData(*key, _sharedMedia->key) | countDistanceInData(*key, _sharedMedia->key)
| func::abs; | func::abs;
if (distance) { if (distance) {
@ -1066,14 +1079,17 @@ void MediaView::validateSharedMedia() {
} else { } else {
_sharedMedia = nullptr; _sharedMedia = nullptr;
_sharedMediaData = base::none; _sharedMediaData = base::none;
_sharedMediaDataKey = base::none;
} }
} }
void MediaView::handleSharedMediaUpdate(SharedMediaWithLastSlice &&update) { void MediaView::handleSharedMediaUpdate(SharedMediaWithLastSlice &&update) {
if ((!_photo && !_doc) || !_sharedMedia) { if ((!_photo && !_doc) || !_sharedMedia) {
_sharedMediaData = base::none; _sharedMediaData = base::none;
_sharedMediaDataKey = base::none;
} else { } else {
_sharedMediaData = std::move(update); _sharedMediaData = std::move(update);
_sharedMediaDataKey = _sharedMedia->key;
} }
findCurrent(); findCurrent();
updateControls(); updateControls();
@ -2733,6 +2749,7 @@ void MediaView::setVisible(bool visible) {
if (!visible) { if (!visible) {
_sharedMedia = nullptr; _sharedMedia = nullptr;
_sharedMediaData = base::none; _sharedMediaData = base::none;
_sharedMediaDataKey = base::none;
_userPhotos = nullptr; _userPhotos = nullptr;
_userPhotosData = base::none; _userPhotosData = base::none;
if (_menu) _menu->hideMenu(true); if (_menu) _menu->hideMenu(true);

View File

@ -252,6 +252,7 @@ private:
DocumentData *_doc = nullptr; DocumentData *_doc = nullptr;
std::unique_ptr<SharedMedia> _sharedMedia; std::unique_ptr<SharedMedia> _sharedMedia;
base::optional<SharedMediaWithLastSlice> _sharedMediaData; base::optional<SharedMediaWithLastSlice> _sharedMediaData;
base::optional<SharedMediaWithLastSlice::Key> _sharedMediaDataKey;
std::unique_ptr<UserPhotos> _userPhotos; std::unique_ptr<UserPhotos> _userPhotos;
base::optional<UserPhotosSlice> _userPhotosData; base::optional<UserPhotosSlice> _userPhotosData;

View File

@ -279,12 +279,15 @@ public:
}; };
template <typename Request, typename = std::enable_if_t<std::is_rvalue_reference<Request&&>::value>, typename = typename Request::Unboxed> template <
typename Request,
typename = std::enable_if_t<!std::is_reference_v<Request>>,
typename = typename Request::Unboxed>
[[nodiscard]] SpecificRequestBuilder<Request> request(Request &&request) noexcept; [[nodiscard]] SpecificRequestBuilder<Request> request(Request &&request) noexcept;
[[nodiscard]] SentRequestWrap request(mtpRequestId requestId) noexcept; [[nodiscard]] SentRequestWrap request(mtpRequestId requestId) noexcept;
[[nodiscard]] decltype(auto) requestCanceller() noexcept { [[nodiscard]] auto requestCanceller() noexcept {
return [this](mtpRequestId requestId) { return [this](mtpRequestId requestId) {
request(requestId).cancel(); request(requestId).cancel();
}; };
@ -305,7 +308,20 @@ public:
private: private:
class RequestWrap { class RequestWrap {
public: public:
RequestWrap(Instance *instance, mtpRequestId requestId) noexcept : _id(requestId) { RequestWrap(
Instance *instance,
mtpRequestId requestId) noexcept
: _id(requestId) {
}
RequestWrap(const RequestWrap &other) = delete;
RequestWrap &operator=(const RequestWrap &other) = delete;
RequestWrap(RequestWrap &&other) : _id(base::take(other._id)) {
}
RequestWrap &operator=(RequestWrap &&other) {
cancelRequest();
_id = base::take(other._id);
return *this;
} }
mtpRequestId id() const noexcept { mtpRequestId id() const noexcept {
@ -315,12 +331,17 @@ private:
} }
~RequestWrap() { ~RequestWrap() {
if (auto instance = MainInstance()) { cancelRequest();
instance->cancel(_id);
}
} }
private: private:
void cancelRequest() {
if (_id) {
if (auto instance = MainInstance()) {
instance->cancel(_id);
}
}
}
mtpRequestId _id = 0; mtpRequestId _id = 0;
}; };
@ -370,7 +391,7 @@ private:
} }
} }
std::set<RequestWrap, RequestWrapComparator> _requests; // Better to use flatmap. base::flat_set<RequestWrap, RequestWrapComparator> _requests;
}; };

View File

@ -25,13 +25,15 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
namespace Storage { namespace Storage {
struct SparseIdsListResult;
struct SharedMediaAddNew; struct SharedMediaAddNew;
struct SharedMediaAddExisting; struct SharedMediaAddExisting;
struct SharedMediaAddSlice; struct SharedMediaAddSlice;
struct SharedMediaRemoveOne; struct SharedMediaRemoveOne;
struct SharedMediaRemoveAll; struct SharedMediaRemoveAll;
struct SharedMediaQuery; struct SharedMediaQuery;
struct SharedMediaResult; using SharedMediaResult = SparseIdsListResult;
struct SharedMediaSliceUpdate; struct SharedMediaSliceUpdate;
struct UserPhotosAddNew; struct UserPhotosAddNew;

View File

@ -20,213 +20,11 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/ */
#include "storage/storage_shared_media.h" #include "storage/storage_shared_media.h"
#include <rpl/map.h>
#include "base/task_queue.h" #include "base/task_queue.h"
namespace Storage { namespace Storage {
SharedMedia::List::Slice::Slice(
base::flat_set<MsgId> &&messages,
MsgRange range)
: messages(std::move(messages))
, range(range) {
}
template <typename Range>
void SharedMedia::List::Slice::merge(
const Range &moreMessages,
MsgRange moreNoSkipRange) {
Expects(moreNoSkipRange.from <= range.till);
Expects(range.from <= moreNoSkipRange.till);
messages.merge(std::begin(moreMessages), std::end(moreMessages));
range = {
qMin(range.from, moreNoSkipRange.from),
qMax(range.till, moreNoSkipRange.till)
};
}
template <typename Range>
int SharedMedia::List::uniteAndAdd(
SliceUpdate &update,
base::flat_set<Slice>::iterator uniteFrom,
base::flat_set<Slice>::iterator uniteTill,
const Range &messages,
MsgRange noSkipRange) {
auto uniteFromIndex = uniteFrom - _slices.begin();
auto was = uniteFrom->messages.size();
_slices.modify(uniteFrom, [&](Slice &slice) {
slice.merge(messages, noSkipRange);
});
auto firstToErase = uniteFrom + 1;
if (firstToErase != uniteTill) {
for (auto it = firstToErase; it != uniteTill; ++it) {
_slices.modify(uniteFrom, [&](Slice &slice) {
slice.merge(it->messages, it->range);
});
}
_slices.erase(firstToErase, uniteTill);
uniteFrom = _slices.begin() + uniteFromIndex;
}
update.messages = &uniteFrom->messages;
update.range = uniteFrom->range;
return uniteFrom->messages.size() - was;
}
template <typename Range>
int SharedMedia::List::addRangeItemsAndCountNew(
SliceUpdate &update,
const Range &messages,
MsgRange noSkipRange) {
Expects((noSkipRange.from < noSkipRange.till)
|| (noSkipRange.from == noSkipRange.till && messages.begin() == messages.end()));
if (noSkipRange.from == noSkipRange.till) {
return 0;
}
auto uniteFrom = base::lower_bound(
_slices,
noSkipRange.from,
[](const Slice &slice, MsgId from) { return slice.range.till < from; });
auto uniteTill = base::upper_bound(
_slices,
noSkipRange.till,
[](MsgId till, const Slice &slice) { return till < slice.range.from; });
if (uniteFrom < uniteTill) {
return uniteAndAdd(update, uniteFrom, uniteTill, messages, noSkipRange);
}
auto sliceMessages = base::flat_set<MsgId> {
std::begin(messages),
std::end(messages) };
auto slice = _slices.emplace(
std::move(sliceMessages),
noSkipRange);
update.messages = &slice->messages;
update.range = slice->range;
return slice->messages.size();
}
template <typename Range>
void SharedMedia::List::addRange(
const Range &messages,
MsgRange noSkipRange,
base::optional<int> count,
bool incrementCount) {
Expects(!count || !incrementCount);
auto wasCount = _count;
auto update = SliceUpdate();
auto result = addRangeItemsAndCountNew(update, messages, noSkipRange);
if (count) {
_count = count;
} else if (incrementCount && _count && result > 0) {
*_count += result;
}
if (_slices.size() == 1) {
if (_slices.front().range == MsgRange { 0, ServerMaxMsgId }) {
_count = _slices.front().messages.size();
}
}
update.count = _count;
_sliceUpdated.fire(std::move(update));
}
void SharedMedia::List::addNew(MsgId messageId) {
auto range = { messageId };
addRange(range, { messageId, ServerMaxMsgId }, base::none, true);
}
void SharedMedia::List::addExisting(
MsgId messageId,
MsgRange noSkipRange) {
auto range = { messageId };
addRange(range, noSkipRange, base::none);
}
void SharedMedia::List::addSlice(
std::vector<MsgId> &&messageIds,
MsgRange noSkipRange,
base::optional<int> count) {
addRange(messageIds, noSkipRange, count);
}
void SharedMedia::List::removeOne(MsgId messageId) {
auto slice = base::lower_bound(
_slices,
messageId,
[](const Slice &slice, MsgId from) { return slice.range.till < from; });
if (slice != _slices.end() && slice->range.from <= messageId) {
_slices.modify(slice, [messageId](Slice &slice) {
return slice.messages.remove(messageId);
});
}
if (_count) {
--*_count;
}
}
void SharedMedia::List::removeAll() {
_slices.clear();
_slices.emplace(base::flat_set<MsgId>{}, MsgRange { 0, ServerMaxMsgId });
_count = 0;
}
rpl::producer<SharedMediaResult> SharedMedia::List::query(
SharedMediaQuery &&query) const {
return [this, query = std::move(query)](auto consumer) {
auto slice = query.key.messageId
? base::lower_bound(
_slices,
query.key.messageId,
[](const Slice &slice, MsgId id) {
return slice.range.till < id;
})
: _slices.end();
if (slice != _slices.end()
&& slice->range.from <= query.key.messageId) {
consumer.put_next(queryFromSlice(query, *slice));
} else if (_count) {
auto result = SharedMediaResult {};
result.count = _count;
consumer.put_next(std::move(result));
}
consumer.put_done();
return rpl::lifetime();
};
}
SharedMediaResult SharedMedia::List::queryFromSlice(
const SharedMediaQuery &query,
const Slice &slice) const {
auto result = SharedMediaResult {};
auto position = base::lower_bound(slice.messages, query.key.messageId);
auto haveBefore = int(position - slice.messages.begin());
auto haveEqualOrAfter = int(slice.messages.end() - position);
auto before = qMin(haveBefore, query.limitBefore);
auto equalOrAfter = qMin(haveEqualOrAfter, query.limitAfter + 1);
auto ids = std::vector<MsgId>(position - before, position + equalOrAfter);
result.messageIds.merge(ids.begin(), ids.end());
if (slice.range.from == 0) {
result.skippedBefore = haveBefore - before;
}
if (slice.range.till == ServerMaxMsgId) {
result.skippedAfter = haveEqualOrAfter - equalOrAfter;
}
if (_count) {
result.count = _count;
if (!result.skippedBefore && result.skippedAfter) {
result.skippedBefore = *result.count
- *result.skippedAfter
- int(result.messageIds.size());
} else if (!result.skippedAfter && result.skippedBefore) {
result.skippedAfter = *result.count
- *result.skippedBefore
- int(result.messageIds.size());
}
}
return result;
}
std::map<PeerId, SharedMedia::Lists>::iterator std::map<PeerId, SharedMedia::Lists>::iterator
SharedMedia::enforceLists(PeerId peer) { SharedMedia::enforceLists(PeerId peer) {
auto result = _lists.find(peer); auto result = _lists.find(peer);
@ -239,15 +37,13 @@ std::map<PeerId, SharedMedia::Lists>::iterator
auto type = static_cast<SharedMediaType>(index); auto type = static_cast<SharedMediaType>(index);
list.sliceUpdated() list.sliceUpdated()
| rpl::start_with_next([this, peer, type]( | rpl::map([=](const SparseIdsSliceUpdate &update) {
const SliceUpdate &update) { return SharedMediaSliceUpdate(
_sliceUpdated.fire(SharedMediaSliceUpdate(
peer, peer,
type, type,
update.messages, update);
update.range, })
update.count)); | rpl::start_to_stream(_sliceUpdated, _lifetime);
}, _lifetime);
} }
return result; return result;
} }
@ -308,7 +104,10 @@ rpl::producer<SharedMediaResult> SharedMedia::query(SharedMediaQuery &&query) co
auto peerIt = _lists.find(query.key.peerId); auto peerIt = _lists.find(query.key.peerId);
if (peerIt != _lists.end()) { if (peerIt != _lists.end()) {
auto index = static_cast<int>(query.key.type); auto index = static_cast<int>(query.key.type);
return peerIt->second[index].query(std::move(query)); return peerIt->second[index].query(SparseIdsListQuery(
query.key.messageId,
query.limitBefore,
query.limitAfter));
} }
return [](auto consumer) { return [](auto consumer) {
consumer.put_done(); consumer.put_done();

View File

@ -22,6 +22,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include <rpl/event_stream.h> #include <rpl/event_stream.h>
#include "storage/storage_facade.h" #include "storage/storage_facade.h"
#include "storage/storage_sparse_ids_list.h"
namespace Storage { namespace Storage {
@ -105,9 +106,9 @@ struct SharedMediaRemoveOne {
PeerId peerId, PeerId peerId,
SharedMediaTypesMask types, SharedMediaTypesMask types,
MsgId messageId) MsgId messageId)
: peerId(peerId) : peerId(peerId)
, messageId(messageId) , messageId(messageId)
, types(types) { , types(types) {
} }
PeerId peerId = 0; PeerId peerId = 0;
@ -129,9 +130,9 @@ struct SharedMediaKey {
PeerId peerId, PeerId peerId,
SharedMediaType type, SharedMediaType type,
MsgId messageId) MsgId messageId)
: peerId(peerId) : peerId(peerId)
, type(type) , type(type)
, messageId(messageId) { , messageId(messageId) {
} }
bool operator==(const SharedMediaKey &other) const { bool operator==(const SharedMediaKey &other) const {
@ -154,9 +155,9 @@ struct SharedMediaQuery {
SharedMediaKey key, SharedMediaKey key,
int limitBefore, int limitBefore,
int limitAfter) int limitAfter)
: key(key) : key(key)
, limitBefore(limitBefore) , limitBefore(limitBefore)
, limitAfter(limitAfter) { , limitAfter(limitAfter) {
} }
SharedMediaKey key; SharedMediaKey key;
@ -165,32 +166,21 @@ struct SharedMediaQuery {
}; };
struct SharedMediaResult { using SharedMediaResult = SparseIdsListResult;
base::optional<int> count;
base::optional<int> skippedBefore;
base::optional<int> skippedAfter;
base::flat_set<MsgId> messageIds;
};
struct SharedMediaSliceUpdate { struct SharedMediaSliceUpdate {
SharedMediaSliceUpdate( SharedMediaSliceUpdate(
PeerId peerId, PeerId peerId,
SharedMediaType type, SharedMediaType type,
const base::flat_set<MsgId> *messages, const SparseIdsSliceUpdate &data)
MsgRange range, : peerId(peerId)
base::optional<int> count) , type(type)
: peerId(peerId) , data(data) {
, type(type)
, messages(messages)
, range(range)
, count(count) {
} }
PeerId peerId = 0; PeerId peerId = 0;
SharedMediaType type = SharedMediaType::kCount; SharedMediaType type = SharedMediaType::kCount;
const base::flat_set<MsgId> *messages = nullptr; SparseIdsSliceUpdate data;
MsgRange range;
base::optional<int> count;
}; };
class SharedMedia { class SharedMedia {
@ -216,74 +206,7 @@ public:
} }
private: private:
class List { using Lists = std::array<SparseIdsList, kSharedMediaTypeCount>;
public:
void addNew(MsgId messageId);
void addExisting(MsgId messageId, MsgRange noSkipRange);
void addSlice(
std::vector<MsgId> &&messageIds,
MsgRange noSkipRange,
base::optional<int> count);
void removeOne(MsgId messageId);
void removeAll();
rpl::producer<SharedMediaResult> query(SharedMediaQuery &&query) const;
struct SliceUpdate {
const base::flat_set<MsgId> *messages = nullptr;
MsgRange range;
base::optional<int> count;
};
rpl::producer<SliceUpdate> sliceUpdated() const {
return _sliceUpdated.events();
}
private:
struct Slice {
Slice(base::flat_set<MsgId> &&messages, MsgRange range);
template <typename Range>
void merge(const Range &moreMessages, MsgRange moreNoSkipRange);
base::flat_set<MsgId> messages;
MsgRange range;
inline bool operator<(const Slice &other) const {
return range.from < other.range.from;
}
};
template <typename Range>
int uniteAndAdd(
SliceUpdate &update,
base::flat_set<Slice>::iterator uniteFrom,
base::flat_set<Slice>::iterator uniteTill,
const Range &messages,
MsgRange noSkipRange);
template <typename Range>
int addRangeItemsAndCountNew(
SliceUpdate &update,
const Range &messages,
MsgRange noSkipRange);
template <typename Range>
void addRange(
const Range &messages,
MsgRange noSkipRange,
base::optional<int> count,
bool incrementCount = false);
SharedMediaResult queryFromSlice(
const SharedMediaQuery &query,
const Slice &slice) const;
base::optional<int> _count;
base::flat_set<Slice> _slices;
rpl::event_stream<SliceUpdate> _sliceUpdated;
};
using SliceUpdate = List::SliceUpdate;
using Lists = std::array<List, kSharedMediaTypeCount>;
std::map<PeerId, Lists>::iterator enforceLists(PeerId peer); std::map<PeerId, Lists>::iterator enforceLists(PeerId peer);

View File

@ -0,0 +1,228 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop 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 3 of the License, or
(at your option) any later version.
It 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.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "storage/storage_sparse_ids_list.h"
namespace Storage {
SparseIdsList::Slice::Slice(
base::flat_set<MsgId> &&messages,
MsgRange range)
: messages(std::move(messages))
, range(range) {
}
template <typename Range>
void SparseIdsList::Slice::merge(
const Range &moreMessages,
MsgRange moreNoSkipRange) {
Expects(moreNoSkipRange.from <= range.till);
Expects(range.from <= moreNoSkipRange.till);
messages.merge(std::begin(moreMessages), std::end(moreMessages));
range = {
qMin(range.from, moreNoSkipRange.from),
qMax(range.till, moreNoSkipRange.till)
};
}
template <typename Range>
int SparseIdsList::uniteAndAdd(
SparseIdsSliceUpdate &update,
base::flat_set<Slice>::iterator uniteFrom,
base::flat_set<Slice>::iterator uniteTill,
const Range &messages,
MsgRange noSkipRange) {
auto uniteFromIndex = uniteFrom - _slices.begin();
auto was = uniteFrom->messages.size();
_slices.modify(uniteFrom, [&](Slice &slice) {
slice.merge(messages, noSkipRange);
});
auto firstToErase = uniteFrom + 1;
if (firstToErase != uniteTill) {
for (auto it = firstToErase; it != uniteTill; ++it) {
_slices.modify(uniteFrom, [&](Slice &slice) {
slice.merge(it->messages, it->range);
});
}
_slices.erase(firstToErase, uniteTill);
uniteFrom = _slices.begin() + uniteFromIndex;
}
update.messages = &uniteFrom->messages;
update.range = uniteFrom->range;
return uniteFrom->messages.size() - was;
}
template <typename Range>
int SparseIdsList::addRangeItemsAndCountNew(
SparseIdsSliceUpdate &update,
const Range &messages,
MsgRange noSkipRange) {
Expects((noSkipRange.from < noSkipRange.till)
|| (noSkipRange.from == noSkipRange.till && messages.begin() == messages.end()));
if (noSkipRange.from == noSkipRange.till) {
return 0;
}
auto uniteFrom = base::lower_bound(
_slices,
noSkipRange.from,
[](const Slice &slice, MsgId from) { return slice.range.till < from; });
auto uniteTill = base::upper_bound(
_slices,
noSkipRange.till,
[](MsgId till, const Slice &slice) { return till < slice.range.from; });
if (uniteFrom < uniteTill) {
return uniteAndAdd(update, uniteFrom, uniteTill, messages, noSkipRange);
}
auto sliceMessages = base::flat_set<MsgId> {
std::begin(messages),
std::end(messages) };
auto slice = _slices.emplace(
std::move(sliceMessages),
noSkipRange);
update.messages = &slice->messages;
update.range = slice->range;
return slice->messages.size();
}
template <typename Range>
void SparseIdsList::addRange(
const Range &messages,
MsgRange noSkipRange,
base::optional<int> count,
bool incrementCount) {
Expects(!count || !incrementCount);
auto wasCount = _count;
auto update = SparseIdsSliceUpdate();
auto result = addRangeItemsAndCountNew(update, messages, noSkipRange);
if (count) {
_count = count;
} else if (incrementCount && _count && result > 0) {
*_count += result;
}
if (_slices.size() == 1) {
if (_slices.front().range == MsgRange { 0, ServerMaxMsgId }) {
_count = _slices.front().messages.size();
}
}
update.count = _count;
_sliceUpdated.fire(std::move(update));
}
void SparseIdsList::addNew(MsgId messageId) {
auto range = { messageId };
addRange(range, { messageId, ServerMaxMsgId }, base::none, true);
}
void SparseIdsList::addExisting(
MsgId messageId,
MsgRange noSkipRange) {
auto range = { messageId };
addRange(range, noSkipRange, base::none);
}
void SparseIdsList::addSlice(
std::vector<MsgId> &&messageIds,
MsgRange noSkipRange,
base::optional<int> count) {
addRange(messageIds, noSkipRange, count);
}
void SparseIdsList::removeOne(MsgId messageId) {
auto slice = base::lower_bound(
_slices,
messageId,
[](const Slice &slice, MsgId from) { return slice.range.till < from; });
if (slice != _slices.end() && slice->range.from <= messageId) {
_slices.modify(slice, [messageId](Slice &slice) {
return slice.messages.remove(messageId);
});
}
if (_count) {
--*_count;
}
}
void SparseIdsList::removeAll() {
_slices.clear();
_slices.emplace(base::flat_set<MsgId>{}, MsgRange { 0, ServerMaxMsgId });
_count = 0;
}
rpl::producer<SparseIdsListResult> SparseIdsList::query(
SparseIdsListQuery &&query) const {
return [this, query = std::move(query)](auto consumer) {
auto slice = query.aroundId
? base::lower_bound(
_slices,
query.aroundId,
[](const Slice &slice, MsgId id) {
return slice.range.till < id;
})
: _slices.end();
if (slice != _slices.end()
&& slice->range.from <= query.aroundId) {
consumer.put_next(queryFromSlice(query, *slice));
} else if (_count) {
auto result = SparseIdsListResult {};
result.count = _count;
consumer.put_next(std::move(result));
}
consumer.put_done();
return rpl::lifetime();
};
}
SparseIdsListResult SparseIdsList::queryFromSlice(
const SparseIdsListQuery &query,
const Slice &slice) const {
auto result = SparseIdsListResult {};
auto position = base::lower_bound(slice.messages, query.aroundId);
auto haveBefore = int(position - slice.messages.begin());
auto haveEqualOrAfter = int(slice.messages.end() - position);
auto before = qMin(haveBefore, query.limitBefore);
auto equalOrAfter = qMin(haveEqualOrAfter, query.limitAfter + 1);
auto ids = std::vector<MsgId>(position - before, position + equalOrAfter);
result.messageIds.merge(ids.begin(), ids.end());
if (slice.range.from == 0) {
result.skippedBefore = haveBefore - before;
}
if (slice.range.till == ServerMaxMsgId) {
result.skippedAfter = haveEqualOrAfter - equalOrAfter;
}
if (_count) {
result.count = _count;
if (!result.skippedBefore && result.skippedAfter) {
result.skippedBefore = *result.count
- *result.skippedAfter
- int(result.messageIds.size());
} else if (!result.skippedAfter && result.skippedBefore) {
result.skippedAfter = *result.count
- *result.skippedBefore
- int(result.messageIds.size());
}
}
return result;
}
} // namespace Storage

View File

@ -0,0 +1,116 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop 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 3 of the License, or
(at your option) any later version.
It 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.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#pragma once
namespace Storage {
struct SparseIdsListQuery {
SparseIdsListQuery(
MsgId aroundId,
int limitBefore,
int limitAfter)
: aroundId(aroundId)
, limitBefore(limitBefore)
, limitAfter(limitAfter) {
}
MsgId aroundId = 0;
int limitBefore = 0;
int limitAfter = 0;
};
struct SparseIdsListResult {
base::optional<int> count;
base::optional<int> skippedBefore;
base::optional<int> skippedAfter;
base::flat_set<MsgId> messageIds;
};
struct SparseIdsSliceUpdate {
const base::flat_set<MsgId> *messages = nullptr;
MsgRange range;
base::optional<int> count;
};
class SparseIdsList {
public:
void addNew(MsgId messageId);
void addExisting(MsgId messageId, MsgRange noSkipRange);
void addSlice(
std::vector<MsgId> &&messageIds,
MsgRange noSkipRange,
base::optional<int> count);
void removeOne(MsgId messageId);
void removeAll();
rpl::producer<SparseIdsListResult> query(SparseIdsListQuery &&query) const;
rpl::producer<SparseIdsSliceUpdate> sliceUpdated() const {
return _sliceUpdated.events();
}
private:
struct Slice {
Slice(base::flat_set<MsgId> &&messages, MsgRange range);
template <typename Range>
void merge(const Range &moreMessages, MsgRange moreNoSkipRange);
base::flat_set<MsgId> messages;
MsgRange range;
inline bool operator<(const Slice &other) const {
return range.from < other.range.from;
}
};
template <typename Range>
int uniteAndAdd(
SparseIdsSliceUpdate &update,
base::flat_set<Slice>::iterator uniteFrom,
base::flat_set<Slice>::iterator uniteTill,
const Range &messages,
MsgRange noSkipRange);
template <typename Range>
int addRangeItemsAndCountNew(
SparseIdsSliceUpdate &update,
const Range &messages,
MsgRange noSkipRange);
template <typename Range>
void addRange(
const Range &messages,
MsgRange noSkipRange,
base::optional<int> count,
bool incrementCount = false);
SparseIdsListResult queryFromSlice(
const SparseIdsListQuery &query,
const Slice &slice) const;
base::optional<int> _count;
base::flat_set<Slice> _slices;
rpl::event_stream<SparseIdsSliceUpdate> _sliceUpdated;
};
} // namespace Storage

View File

@ -203,6 +203,8 @@
<(src_loc)/history/history_service_layout.h <(src_loc)/history/history_service_layout.h
<(src_loc)/history/history_shared_media.cpp <(src_loc)/history/history_shared_media.cpp
<(src_loc)/history/history_shared_media.h <(src_loc)/history/history_shared_media.h
<(src_loc)/history/history_sparse_ids.cpp
<(src_loc)/history/history_sparse_ids.h
<(src_loc)/history/history_user_photos.cpp <(src_loc)/history/history_user_photos.cpp
<(src_loc)/history/history_user_photos.h <(src_loc)/history/history_user_photos.h
<(src_loc)/history/history_widget.cpp <(src_loc)/history/history_widget.cpp
@ -505,6 +507,8 @@
<(src_loc)/storage/storage_facade.h <(src_loc)/storage/storage_facade.h
<(src_loc)/storage/storage_shared_media.cpp <(src_loc)/storage/storage_shared_media.cpp
<(src_loc)/storage/storage_shared_media.h <(src_loc)/storage/storage_shared_media.h
<(src_loc)/storage/storage_sparse_ids_list.cpp
<(src_loc)/storage/storage_sparse_ids_list.h
<(src_loc)/storage/storage_user_photos.cpp <(src_loc)/storage/storage_user_photos.cpp
<(src_loc)/storage/storage_user_photos.h <(src_loc)/storage/storage_user_photos.h
<(src_loc)/ui/effects/cross_animation.cpp <(src_loc)/ui/effects/cross_animation.cpp