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_shared_media.h"
#include "storage/storage_user_photos.h"
#include "history/history_sparse_ids.h"
namespace {
@ -1922,6 +1923,12 @@ void ApiWrap::sendSaveChatAdminsRequests(not_null<ChatData*> chat) {
requestSendDelayed();
}
void ApiWrap::requestSharedMediaCount(
not_null<PeerData*> peer,
Storage::SharedMediaType type) {
requestSharedMedia(peer, type, 0, SliceType::Before);
}
void ApiWrap::requestSharedMedia(
not_null<PeerData*> peer,
SharedMediaType type,

View File

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

View File

@ -386,6 +386,32 @@ public:
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 {
return findFirst(value) != end();
}
@ -446,6 +472,18 @@ private:
typename impl::const_iterator getLowerBound(const Type &value) const {
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) {
return base::upper_bound(_impl, value, compare());
}
@ -557,6 +595,18 @@ public:
const_iterator find(const Type &value) const {
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>
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_shared_media.h"
#include "history/history_media_types.h"
#include "history/history_sparse_ids.h"
namespace {
using Type = SharedMediaSlice::Type;
using Type = Storage::SharedMediaType;
inline MediaOverviewType SharedMediaTypeToOverview(Type type) {
switch (type) {
@ -62,368 +63,8 @@ void SharedMediaShowOverview(
}
}
class SharedMediaSliceBuilder {
public:
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,
rpl::producer<SparseIdsSlice> SharedMediaViewer(
Storage::SharedMediaKey key,
int limitBefore,
int limitAfter) {
Expects(IsServerMsgId(key.messageId) || (key.messageId == 0));
@ -431,199 +72,123 @@ rpl::producer<SharedMediaSlice> SharedMediaViewer(
return [=](auto consumer) {
auto lifetime = rpl::lifetime();
auto builder = lifetime.make_state<SharedMediaSliceBuilder>(
key,
auto builder = lifetime.make_state<SparseIdsSliceBuilder>(
key.messageId,
limitBefore,
limitAfter);
auto applyUpdate = [=](auto &&update) {
if (builder->applyUpdate(std::forward<decltype(update)>(update))) {
consumer.put_next(builder->snapshot());
}
};
auto requestMediaAround = [
peer = App::peer(key.peerId),
type = key.type
](const SharedMediaSliceBuilder::AroundData &data) {
](const SparseIdsSliceBuilder::AroundData &data) {
Auth().api().requestSharedMedia(
peer,
type,
data.first,
data.second);
};
builder->insufficientMediaAround()
builder->insufficientAround()
| rpl::start_with_next(requestMediaAround, lifetime);
Auth().storage().sharedMediaSliceUpdated()
| rpl::start_with_next(applyUpdate, lifetime);
Auth().storage().sharedMediaOneRemoved()
| rpl::start_with_next(applyUpdate, lifetime);
Auth().storage().sharedMediaAllRemoved()
| rpl::start_with_next(applyUpdate, lifetime);
auto pushNextSnapshot = [=] {
consumer.put_next(builder->snapshot());
};
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(
Storage::SharedMediaQuery(
key,
limitBefore,
limitAfter)
) | rpl::start_with_next_done(
applyUpdate,
[=] { builder->checkInsufficientMedia(); },
lifetime);
limitAfter))
| rpl::filter([=](const Result &result) {
return builder->applyInitial(result);
})
| rpl::start_with_next_done(
pushNextSnapshot,
[=] { builder->checkInsufficient(); },
lifetime);
return lifetime;
};
}
SharedMediaMergedSlice::SharedMediaMergedSlice(Key key) : SharedMediaMergedSlice(
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,
rpl::producer<SparseIdsMergedSlice> SharedMediaMergedViewer(
SharedMediaMergedKey key,
int limitBefore,
int limitAfter) {
Expects(IsServerMsgId(key.universalId)
|| (key.universalId == 0)
|| (IsServerMsgId(ServerMaxMsgId + key.universalId) && key.migratedPeerId != 0));
Expects((key.universalId != 0) || (limitBefore == 0 && limitAfter == 0));
Expects(IsServerMsgId(key.mergedKey.universalId)
|| (key.mergedKey.universalId == 0)
|| (IsServerMsgId(ServerMaxMsgId + key.mergedKey.universalId) && key.mergedKey.migratedPeerId != 0));
Expects((key.mergedKey.universalId != 0)
|| (limitBefore == 0 && limitAfter == 0));
return [=](auto consumer) {
if (!key.migratedPeerId) {
if (!key.mergedKey.migratedPeerId) {
return SharedMediaViewer(
SharedMediaMergedSlice::PartKey(key),
Storage::SharedMediaKey(
key.mergedKey.peerId,
key.type,
SparseIdsMergedSlice::PartKey(key.mergedKey)),
limitBefore,
limitAfter
) | rpl::start_with_next([=](SharedMediaSlice &&part) {
consumer.put_next(SharedMediaMergedSlice(
key,
) | rpl::start_with_next([=](SparseIdsSlice &&part) {
consumer.put_next(SparseIdsMergedSlice(
key.mergedKey,
std::move(part),
base::none));
});
}
return rpl::combine(
SharedMediaViewer(
SharedMediaMergedSlice::PartKey(key),
Storage::SharedMediaKey(
key.mergedKey.peerId,
key.type,
SparseIdsMergedSlice::PartKey(key.mergedKey)),
limitBefore,
limitAfter),
SharedMediaViewer(
SharedMediaMergedSlice::MigratedKey(key),
Storage::SharedMediaKey(
key.mergedKey.migratedPeerId,
key.type,
SparseIdsMergedSlice::MigratedKey(key.mergedKey)),
limitBefore,
limitAfter)
) | rpl::start_with_next([=](
SharedMediaSlice &&part,
SharedMediaSlice &&migrated) {
consumer.put_next(SharedMediaMergedSlice(
key,
SparseIdsSlice &&part,
SparseIdsSlice &&migrated) {
consumer.put_next(SparseIdsMergedSlice(
key.mergedKey,
std::move(part),
std::move(migrated)));
});
@ -633,21 +198,21 @@ rpl::producer<SharedMediaMergedSlice> SharedMediaMergedViewer(
SharedMediaWithLastSlice::SharedMediaWithLastSlice(Key key)
: SharedMediaWithLastSlice(
key,
SharedMediaMergedSlice(ViewerKey(key)),
SparseIdsMergedSlice(ViewerKey(key)),
EndingSlice(key)) {
}
SharedMediaWithLastSlice::SharedMediaWithLastSlice(
Key key,
SharedMediaMergedSlice slice,
base::optional<SharedMediaMergedSlice> ending)
: _key(key)
, _slice(std::move(slice))
, _ending(std::move(ending))
, _lastPhotoId(LastPeerPhotoId(key.peerId))
, _isolatedLastPhoto(_key.type == Type::ChatPhoto
? IsLastIsolated(_slice, _ending, _lastPhotoId)
: false) {
SparseIdsMergedSlice slice,
base::optional<SparseIdsMergedSlice> ending)
: _key(key)
, _slice(std::move(slice))
, _ending(std::move(ending))
, _lastPhotoId(LastPeerPhotoId(key.peerId))
, _isolatedLastPhoto(_key.type == Type::ChatPhoto
? IsLastIsolated(_slice, _ending, _lastPhotoId)
: false) {
}
base::optional<int> SharedMediaWithLastSlice::fullCount() const {
@ -690,15 +255,9 @@ SharedMediaWithLastSlice::Value SharedMediaWithLastSlice::operator[](int index)
: Value(App::photo(_lastPhotoId));
}
base::optional<int> SharedMediaWithLastSlice::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;
}
base::optional<int> SharedMediaWithLastSlice::distance(
const Key &a,
const Key &b) const {
if (auto i = indexOf(ComputeId(a))) {
if (auto j = indexOf(ComputeId(b))) {
return *j - *i;
@ -707,12 +266,6 @@ base::optional<int> SharedMediaWithLastSlice::distance(const Key &a, const Key &
return base::none;
}
QString SharedMediaWithLastSlice::debug() const {
return _slice.debug() + (_isolatedLastPhoto
? (*_isolatedLastPhoto ? "@" : "")
: "?");
}
PhotoId SharedMediaWithLastSlice::LastPeerPhotoId(PeerId peerId) {
if (auto peer = App::peerLoaded(peerId)) {
return peer->photoId;
@ -721,8 +274,8 @@ PhotoId SharedMediaWithLastSlice::LastPeerPhotoId(PeerId peerId) {
}
base::optional<bool> SharedMediaWithLastSlice::IsLastIsolated(
const SharedMediaMergedSlice &slice,
const base::optional<SharedMediaMergedSlice> &ending,
const SparseIdsMergedSlice &slice,
const base::optional<SparseIdsMergedSlice> &ending,
PhotoId lastPeerPhotoId) {
if (lastPeerPhotoId == UnknownPeerPhotoId) {
return base::none;
@ -742,7 +295,7 @@ base::optional<bool> SharedMediaWithLastSlice::IsLastIsolated(
}
base::optional<FullMsgId> SharedMediaWithLastSlice::LastFullMsgId(
const SharedMediaMergedSlice &slice) {
const SparseIdsMergedSlice &slice) {
if (slice.fullCount() == 0) {
return FullMsgId();
} else if (slice.size() == 0 || slice.skippedAfter() != 0) {
@ -758,10 +311,12 @@ rpl::producer<SharedMediaWithLastSlice> SharedMediaWithLastViewer(
return [=](auto consumer) {
if (base::get_if<not_null<PhotoData*>>(&key.universalId)) {
return SharedMediaMergedViewer(
SharedMediaWithLastSlice::ViewerKey(key),
SharedMediaMergedKey(
SharedMediaWithLastSlice::ViewerKey(key),
key.type),
limitBefore,
limitAfter
) | rpl::start_with_next([=](SharedMediaMergedSlice &&update) {
) | rpl::start_with_next([=](SparseIdsMergedSlice &&update) {
consumer.put_next(SharedMediaWithLastSlice(
key,
std::move(update),
@ -770,16 +325,20 @@ rpl::producer<SharedMediaWithLastSlice> SharedMediaWithLastViewer(
}
return rpl::combine(
SharedMediaMergedViewer(
SharedMediaWithLastSlice::ViewerKey(key),
SharedMediaMergedKey(
SharedMediaWithLastSlice::ViewerKey(key),
key.type),
limitBefore,
limitAfter),
SharedMediaMergedViewer(
SharedMediaWithLastSlice::EndingKey(key),
SharedMediaMergedKey(
SharedMediaWithLastSlice::EndingKey(key),
key.type),
1,
1)
) | rpl::start_with_next([=](
SharedMediaMergedSlice &&viewer,
SharedMediaMergedSlice &&ending) {
SparseIdsMergedSlice &&viewer,
SparseIdsMergedSlice &&ending) {
consumer.put_next(SharedMediaWithLastSlice(
key,
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 "base/weak_unique_ptr.h"
#include "history/history_sparse_ids.h"
base::optional<Storage::SharedMediaType> SharedMediaOverviewType(
Storage::SharedMediaType type);
@ -29,176 +30,33 @@ void SharedMediaShowOverview(
Storage::SharedMediaType type,
not_null<History*> history);
class SharedMediaSlice {
public:
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,
rpl::producer<SparseIdsSlice> SharedMediaViewer(
Storage::SharedMediaKey key,
int limitBefore,
int limitAfter);
class SharedMediaMergedSlice {
public:
struct SharedMediaMergedKey {
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 {
return (peerId == other.peerId)
&& (migratedPeerId == other.migratedPeerId)
&& (type == other.type)
&& (universalId == other.universalId);
}
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
};
SharedMediaMergedKey(
SparseIdsMergedSlice::Key mergedKey,
Type type)
: mergedKey(mergedKey)
, type(type) {
}
private:
static base::optional<SharedMediaSlice> MigratedSlice(const Key &key) {
return key.migratedPeerId
? base::make_optional(SharedMediaSlice(MigratedKey(key)))
: base::none;
bool operator==(const SharedMediaMergedKey &other) const {
return (mergedKey == other.mergedKey)
&& (type == other.type);
}
static bool IsFromSlice(const SharedMediaSlice &slice, FullMsgId fullId) {
auto peer = slice.key().peerId;
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;
SparseIdsMergedSlice::Key mergedKey;
Type type = Type::kCount;
};
rpl::producer<SharedMediaMergedSlice> SharedMediaMergedViewer(
SharedMediaMergedSlice::Key key,
rpl::producer<SparseIdsMergedSlice> SharedMediaMergedViewer(
SharedMediaMergedKey key,
int limitBefore,
int limitAfter);
@ -206,9 +64,8 @@ class SharedMediaWithLastSlice {
public:
using Type = Storage::SharedMediaType;
// base::none in those mean CurrentPeerPhoto.
using Value = base::variant<FullMsgId, not_null<PhotoData*>>;
using MessageId = SharedMediaMergedSlice::UniversalMsgId;
using MessageId = SparseIdsMergedSlice::UniversalMsgId;
using UniversalMsgId = base::variant<
MessageId,
not_null<PhotoData*>>;
@ -219,10 +76,10 @@ public:
PeerId migratedPeerId,
Type type,
UniversalMsgId universalId)
: peerId(peerId)
, migratedPeerId(migratedPeerId)
, type(type)
, universalId(universalId) {
: peerId(peerId)
, migratedPeerId(migratedPeerId)
, type(type)
, universalId(universalId) {
Expects(base::get_if<MessageId>(&universalId) != nullptr
|| type == Type::ChatPhoto);
}
@ -233,6 +90,9 @@ public:
&& (type == other.type)
&& (universalId == other.universalId);
}
bool operator!=(const Key &other) const {
return !(*this == other);
}
PeerId peerId = 0;
PeerId migratedPeerId = 0;
@ -244,8 +104,8 @@ public:
SharedMediaWithLastSlice(Key key);
SharedMediaWithLastSlice(
Key key,
SharedMediaMergedSlice slice,
base::optional<SharedMediaMergedSlice> ending);
SparseIdsMergedSlice slice,
base::optional<SparseIdsMergedSlice> ending);
base::optional<int> fullCount() const;
base::optional<int> skippedBefore() const;
@ -255,41 +115,37 @@ public:
Value operator[](int index) const;
base::optional<int> distance(const Key &a, const Key &b) const;
QString debug() const;
static SharedMediaMergedSlice::Key ViewerKey(const Key &key) {
static SparseIdsMergedSlice::Key ViewerKey(const Key &key) {
return {
key.peerId,
key.migratedPeerId,
key.type,
base::get_if<MessageId>(&key.universalId)
? (*base::get_if<MessageId>(&key.universalId))
: ServerMaxMsgId - 1
? (*base::get_if<MessageId>(&key.universalId))
: ServerMaxMsgId - 1
};
}
static SharedMediaMergedSlice::Key EndingKey(const Key &key) {
static SparseIdsMergedSlice::Key EndingKey(const Key &key) {
return {
key.peerId,
key.migratedPeerId,
key.type,
ServerMaxMsgId - 1
};
}
private:
static base::optional<SharedMediaMergedSlice> EndingSlice(const Key &key) {
static base::optional<SparseIdsMergedSlice> EndingSlice(const Key &key) {
return base::get_if<MessageId>(&key.universalId)
? base::make_optional(SharedMediaMergedSlice(EndingKey(key)))
? base::make_optional(SparseIdsMergedSlice(EndingKey(key)))
: base::none;
}
static PhotoId LastPeerPhotoId(PeerId peerId);
static base::optional<bool> IsLastIsolated(
const SharedMediaMergedSlice &slice,
const base::optional<SharedMediaMergedSlice> &ending,
const SparseIdsMergedSlice &slice,
const base::optional<SparseIdsMergedSlice> &ending,
PhotoId lastPeerPhotoId);
static base::optional<FullMsgId> LastFullMsgId(
const SharedMediaMergedSlice &slice);
const SparseIdsMergedSlice &slice);
static base::optional<int> Add(
const base::optional<int> &a,
const base::optional<int> &b) {
@ -318,13 +174,11 @@ private:
}
Key _key;
SharedMediaMergedSlice _slice;
base::optional<SharedMediaMergedSlice> _ending;
SparseIdsMergedSlice _slice;
base::optional<SparseIdsMergedSlice> _ending;
PhotoId _lastPhotoId = 0;
base::optional<bool> _isolatedLastPhoto;
friend class SharedMediaWithLastSliceBuilder;
};
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 {
using Key = SharedMediaMergedSlice::Key;
using Key = SparseIdsMergedSlice::Key;
if (auto migrateFrom = _peer->migrateFrom()) {
return Key(_peer->id, migrateFrom->id, _type, universalId);
return Key(_peer->id, migrateFrom->id, universalId);
}
if (universalId < 0) {
// Convert back to plain id for non-migrated histories.
universalId += ServerMaxMsgId;
}
return Key(_peer->id, 0, _type, universalId);
return Key(_peer->id, 0, universalId);
}
void ListWidget::refreshViewer() {
_viewerLifetime.destroy();
SharedMediaMergedViewer(
sliceKey(_universalAroundId),
SharedMediaMergedKey(
sliceKey(_universalAroundId),
_type),
_idsLimit,
_idsLimit)
| rpl::start_with_next([this](
SharedMediaMergedSlice &&slice) {
SparseIdsMergedSlice &&slice) {
_slice = std::move(slice);
if (auto nearest = _slice.nearest(_universalAroundId)) {
_universalAroundId = *nearest;

View File

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

View File

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

View File

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

View File

@ -252,6 +252,7 @@ private:
DocumentData *_doc = nullptr;
std::unique_ptr<SharedMedia> _sharedMedia;
base::optional<SharedMediaWithLastSlice> _sharedMediaData;
base::optional<SharedMediaWithLastSlice::Key> _sharedMediaDataKey;
std::unique_ptr<UserPhotos> _userPhotos;
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]] SentRequestWrap request(mtpRequestId requestId) noexcept;
[[nodiscard]] decltype(auto) requestCanceller() noexcept {
[[nodiscard]] auto requestCanceller() noexcept {
return [this](mtpRequestId requestId) {
request(requestId).cancel();
};
@ -305,7 +308,20 @@ public:
private:
class RequestWrap {
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 {
@ -315,12 +331,17 @@ private:
}
~RequestWrap() {
if (auto instance = MainInstance()) {
instance->cancel(_id);
}
cancelRequest();
}
private:
void cancelRequest() {
if (_id) {
if (auto instance = MainInstance()) {
instance->cancel(_id);
}
}
}
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 {
struct SparseIdsListResult;
struct SharedMediaAddNew;
struct SharedMediaAddExisting;
struct SharedMediaAddSlice;
struct SharedMediaRemoveOne;
struct SharedMediaRemoveAll;
struct SharedMediaQuery;
struct SharedMediaResult;
using SharedMediaResult = SparseIdsListResult;
struct SharedMediaSliceUpdate;
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 <rpl/map.h>
#include "base/task_queue.h"
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
SharedMedia::enforceLists(PeerId peer) {
auto result = _lists.find(peer);
@ -239,15 +37,13 @@ std::map<PeerId, SharedMedia::Lists>::iterator
auto type = static_cast<SharedMediaType>(index);
list.sliceUpdated()
| rpl::start_with_next([this, peer, type](
const SliceUpdate &update) {
_sliceUpdated.fire(SharedMediaSliceUpdate(
| rpl::map([=](const SparseIdsSliceUpdate &update) {
return SharedMediaSliceUpdate(
peer,
type,
update.messages,
update.range,
update.count));
}, _lifetime);
update);
})
| rpl::start_to_stream(_sliceUpdated, _lifetime);
}
return result;
}
@ -308,7 +104,10 @@ rpl::producer<SharedMediaResult> SharedMedia::query(SharedMediaQuery &&query) co
auto peerIt = _lists.find(query.key.peerId);
if (peerIt != _lists.end()) {
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) {
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 "storage/storage_facade.h"
#include "storage/storage_sparse_ids_list.h"
namespace Storage {
@ -105,9 +106,9 @@ struct SharedMediaRemoveOne {
PeerId peerId,
SharedMediaTypesMask types,
MsgId messageId)
: peerId(peerId)
, messageId(messageId)
, types(types) {
: peerId(peerId)
, messageId(messageId)
, types(types) {
}
PeerId peerId = 0;
@ -129,9 +130,9 @@ struct SharedMediaKey {
PeerId peerId,
SharedMediaType type,
MsgId messageId)
: peerId(peerId)
, type(type)
, messageId(messageId) {
: peerId(peerId)
, type(type)
, messageId(messageId) {
}
bool operator==(const SharedMediaKey &other) const {
@ -154,9 +155,9 @@ struct SharedMediaQuery {
SharedMediaKey key,
int limitBefore,
int limitAfter)
: key(key)
, limitBefore(limitBefore)
, limitAfter(limitAfter) {
: key(key)
, limitBefore(limitBefore)
, limitAfter(limitAfter) {
}
SharedMediaKey key;
@ -165,32 +166,21 @@ struct SharedMediaQuery {
};
struct SharedMediaResult {
base::optional<int> count;
base::optional<int> skippedBefore;
base::optional<int> skippedAfter;
base::flat_set<MsgId> messageIds;
};
using SharedMediaResult = SparseIdsListResult;
struct SharedMediaSliceUpdate {
SharedMediaSliceUpdate(
PeerId peerId,
SharedMediaType type,
const base::flat_set<MsgId> *messages,
MsgRange range,
base::optional<int> count)
: peerId(peerId)
, type(type)
, messages(messages)
, range(range)
, count(count) {
const SparseIdsSliceUpdate &data)
: peerId(peerId)
, type(type)
, data(data) {
}
PeerId peerId = 0;
SharedMediaType type = SharedMediaType::kCount;
const base::flat_set<MsgId> *messages = nullptr;
MsgRange range;
base::optional<int> count;
SparseIdsSliceUpdate data;
};
class SharedMedia {
@ -216,74 +206,7 @@ public:
}
private:
class List {
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>;
using Lists = std::array<SparseIdsList, kSharedMediaTypeCount>;
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_shared_media.cpp
<(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.h
<(src_loc)/history/history_widget.cpp
@ -505,6 +507,8 @@
<(src_loc)/storage/storage_facade.h
<(src_loc)/storage/storage_shared_media.cpp
<(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.h
<(src_loc)/ui/effects/cross_animation.cpp