240 lines
6.5 KiB
C++
240 lines
6.5 KiB
C++
/*
|
|
This file is part of Telegram Desktop,
|
|
the official desktop application for the Telegram messaging service.
|
|
|
|
For license and copyright information please follow this link:
|
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|
*/
|
|
#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>
|
|
SparseIdsList::AddResult SparseIdsList::uniteAndAdd(
|
|
SparseIdsSliceUpdate &update,
|
|
base::flat_set<Slice>::iterator uniteFrom,
|
|
base::flat_set<Slice>::iterator uniteTill,
|
|
const Range &messages,
|
|
MsgRange noSkipRange) {
|
|
const auto uniteFromIndex = uniteFrom - _slices.begin();
|
|
const auto was = int(uniteFrom->messages.size());
|
|
_slices.modify(uniteFrom, [&](Slice &slice) {
|
|
slice.merge(messages, noSkipRange);
|
|
});
|
|
const 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;
|
|
const auto count = int(uniteFrom->messages.size());
|
|
return { count, count - was };
|
|
}
|
|
|
|
template <typename Range>
|
|
SparseIdsList::AddResult SparseIdsList::addRangeItemsAndCountNew(
|
|
SparseIdsSliceUpdate &update,
|
|
const Range &messages,
|
|
MsgRange noSkipRange) {
|
|
Expects(noSkipRange.from <= noSkipRange.till);
|
|
|
|
auto uniteFrom = ranges::lower_bound(
|
|
_slices,
|
|
noSkipRange.from,
|
|
std::less<>(),
|
|
[](const Slice &slice) { return slice.range.till; });
|
|
auto uniteTill = ranges::upper_bound(
|
|
_slices,
|
|
noSkipRange.till,
|
|
std::less<>(),
|
|
[](const Slice &slice) { return 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
|
|
).first;
|
|
update.messages = &slice->messages;
|
|
update.range = slice->range;
|
|
const auto count = int(slice->messages.size());
|
|
return { count, count };
|
|
}
|
|
|
|
template <typename Range>
|
|
void SparseIdsList::addRange(
|
|
const Range &messages,
|
|
MsgRange noSkipRange,
|
|
std::optional<int> count,
|
|
bool incrementCount) {
|
|
Expects(!count || !incrementCount);
|
|
|
|
auto update = SparseIdsSliceUpdate();
|
|
const auto result = addRangeItemsAndCountNew(
|
|
update,
|
|
messages,
|
|
noSkipRange);
|
|
if (count) {
|
|
_count = count;
|
|
} else if (incrementCount && _count && result.added > 0) {
|
|
*_count += result.added;
|
|
}
|
|
if (_slices.size() == 1) {
|
|
if (_slices.front().range == MsgRange { 0, ServerMaxMsgId }) {
|
|
_count = _slices.front().messages.size();
|
|
}
|
|
}
|
|
if (_count) {
|
|
accumulate_max(*_count, result.inslice);
|
|
}
|
|
update.count = _count;
|
|
_sliceUpdated.fire(std::move(update));
|
|
}
|
|
|
|
void SparseIdsList::addNew(MsgId messageId) {
|
|
auto range = { messageId };
|
|
addRange(range, { messageId, ServerMaxMsgId }, std::nullopt, true);
|
|
}
|
|
|
|
void SparseIdsList::addExisting(
|
|
MsgId messageId,
|
|
MsgRange noSkipRange) {
|
|
auto range = { messageId };
|
|
addRange(range, noSkipRange, std::nullopt);
|
|
}
|
|
|
|
void SparseIdsList::addSlice(
|
|
std::vector<MsgId> &&messageIds,
|
|
MsgRange noSkipRange,
|
|
std::optional<int> count) {
|
|
addRange(messageIds, noSkipRange, count);
|
|
}
|
|
|
|
void SparseIdsList::removeOne(MsgId messageId) {
|
|
auto slice = ranges::lower_bound(
|
|
_slices,
|
|
messageId,
|
|
std::less<>(),
|
|
[](const Slice &slice) { return slice.range.till; });
|
|
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;
|
|
}
|
|
|
|
void SparseIdsList::invalidateBottom() {
|
|
if (!_slices.empty()) {
|
|
const auto &last = _slices.back();
|
|
if (last.range.till == ServerMaxMsgId) {
|
|
_slices.modify(_slices.end() - 1, [](Slice &slice) {
|
|
slice.range.till = slice.messages.empty()
|
|
? slice.range.from
|
|
: slice.messages.back();
|
|
});
|
|
}
|
|
}
|
|
_count = std::nullopt;
|
|
}
|
|
|
|
rpl::producer<SparseIdsListResult> SparseIdsList::query(
|
|
SparseIdsListQuery &&query) const {
|
|
return [this, query = std::move(query)](auto consumer) {
|
|
auto slice = query.aroundId
|
|
? ranges::lower_bound(
|
|
_slices,
|
|
query.aroundId,
|
|
std::less<>(),
|
|
[](const Slice &slice) { return slice.range.till; })
|
|
: _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();
|
|
};
|
|
}
|
|
|
|
rpl::producer<SparseIdsSliceUpdate> SparseIdsList::sliceUpdated() const {
|
|
return _sliceUpdated.events();
|
|
}
|
|
|
|
SparseIdsListResult SparseIdsList::queryFromSlice(
|
|
const SparseIdsListQuery &query,
|
|
const Slice &slice) const {
|
|
auto result = SparseIdsListResult {};
|
|
auto position = ranges::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
|