255 lines
		
	
	
	
		
			6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			255 lines
		
	
	
	
		
			6 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
 | 
						|
*/
 | 
						|
#pragma once
 | 
						|
 | 
						|
namespace Data {
 | 
						|
 | 
						|
enum class LoadDirection : char {
 | 
						|
	Around,
 | 
						|
	Before,
 | 
						|
	After,
 | 
						|
};
 | 
						|
 | 
						|
struct MessagePosition {
 | 
						|
	constexpr MessagePosition() = default;
 | 
						|
	constexpr MessagePosition(TimeId date, FullMsgId fullId)
 | 
						|
	: fullId(fullId)
 | 
						|
	, date(date) {
 | 
						|
	}
 | 
						|
 | 
						|
	explicit operator bool() const {
 | 
						|
		return (fullId.msg != 0);
 | 
						|
	}
 | 
						|
 | 
						|
	inline constexpr bool operator<(const MessagePosition &other) const {
 | 
						|
		if (date < other.date) {
 | 
						|
			return true;
 | 
						|
		} else if (other.date < date) {
 | 
						|
			return false;
 | 
						|
		}
 | 
						|
		return (fullId < other.fullId);
 | 
						|
	}
 | 
						|
	inline constexpr bool operator>(const MessagePosition &other) const {
 | 
						|
		return other < *this;
 | 
						|
	}
 | 
						|
	inline constexpr bool operator<=(const MessagePosition &other) const {
 | 
						|
		return !(other < *this);
 | 
						|
	}
 | 
						|
	inline constexpr bool operator>=(const MessagePosition &other) const {
 | 
						|
		return !(*this < other);
 | 
						|
	}
 | 
						|
	inline constexpr bool operator==(const MessagePosition &other) const {
 | 
						|
		return (date == other.date)
 | 
						|
			&& (fullId == other.fullId);
 | 
						|
	}
 | 
						|
	inline constexpr bool operator!=(const MessagePosition &other) const {
 | 
						|
		return !(*this == other);
 | 
						|
	}
 | 
						|
 | 
						|
	FullMsgId fullId;
 | 
						|
	TimeId date = 0;
 | 
						|
 | 
						|
};
 | 
						|
 | 
						|
struct MessagesRange {
 | 
						|
	constexpr MessagesRange() = default;
 | 
						|
	constexpr MessagesRange(MessagePosition from, MessagePosition till)
 | 
						|
	: from(from)
 | 
						|
	, till(till) {
 | 
						|
	}
 | 
						|
 | 
						|
	inline constexpr bool operator==(const MessagesRange &other) const {
 | 
						|
		return (from == other.from)
 | 
						|
			&& (till == other.till);
 | 
						|
	}
 | 
						|
	inline constexpr bool operator!=(const MessagesRange &other) const {
 | 
						|
		return !(*this == other);
 | 
						|
	}
 | 
						|
 | 
						|
	MessagePosition from;
 | 
						|
	MessagePosition till;
 | 
						|
 | 
						|
};
 | 
						|
 | 
						|
constexpr auto MinDate = TimeId(0);
 | 
						|
constexpr auto MaxDate = std::numeric_limits<TimeId>::max();
 | 
						|
constexpr auto MinMessagePosition = MessagePosition(
 | 
						|
	MinDate,
 | 
						|
	FullMsgId(NoChannel, 1));
 | 
						|
constexpr auto MaxMessagePosition = MessagePosition(
 | 
						|
	MaxDate,
 | 
						|
	FullMsgId(NoChannel, ServerMaxMsgId - 1));
 | 
						|
constexpr auto FullMessagesRange = MessagesRange(
 | 
						|
	MinMessagePosition,
 | 
						|
	MaxMessagePosition);
 | 
						|
constexpr auto UnreadMessagePosition = MessagePosition(
 | 
						|
	MinDate,
 | 
						|
	FullMsgId(NoChannel, ShowAtUnreadMsgId));
 | 
						|
 | 
						|
struct MessagesSlice {
 | 
						|
	std::vector<FullMsgId> ids;
 | 
						|
	base::optional<int> skippedBefore;
 | 
						|
	base::optional<int> skippedAfter;
 | 
						|
	base::optional<int> fullCount;
 | 
						|
 | 
						|
};
 | 
						|
 | 
						|
struct MessagesQuery {
 | 
						|
	MessagesQuery(
 | 
						|
		MessagePosition aroundId,
 | 
						|
		int limitBefore,
 | 
						|
		int limitAfter)
 | 
						|
	: aroundId(aroundId)
 | 
						|
	, limitBefore(limitBefore)
 | 
						|
	, limitAfter(limitAfter) {
 | 
						|
	}
 | 
						|
 | 
						|
	MessagePosition aroundId;
 | 
						|
	int limitBefore = 0;
 | 
						|
	int limitAfter = 0;
 | 
						|
 | 
						|
};
 | 
						|
 | 
						|
struct MessagesResult {
 | 
						|
	base::optional<int> count;
 | 
						|
	base::optional<int> skippedBefore;
 | 
						|
	base::optional<int> skippedAfter;
 | 
						|
	base::flat_set<MessagePosition> messageIds;
 | 
						|
};
 | 
						|
 | 
						|
struct MessagesSliceUpdate {
 | 
						|
	const base::flat_set<MessagePosition> *messages = nullptr;
 | 
						|
	MessagesRange range;
 | 
						|
	base::optional<int> count;
 | 
						|
};
 | 
						|
 | 
						|
class MessagesList {
 | 
						|
public:
 | 
						|
	void addNew(MessagePosition messageId);
 | 
						|
	void addSlice(
 | 
						|
		std::vector<MessagePosition> &&messageIds,
 | 
						|
		MessagesRange noSkipRange,
 | 
						|
		base::optional<int> count);
 | 
						|
	void removeOne(MessagePosition messageId);
 | 
						|
	void removeAll(ChannelId channelId);
 | 
						|
	void invalidate();
 | 
						|
	void invalidateBottom();
 | 
						|
	rpl::producer<MessagesResult> query(MessagesQuery &&query) const;
 | 
						|
	rpl::producer<MessagesSliceUpdate> sliceUpdated() const;
 | 
						|
 | 
						|
private:
 | 
						|
	struct Slice {
 | 
						|
		Slice(
 | 
						|
			base::flat_set<MessagePosition> &&messages,
 | 
						|
			MessagesRange range);
 | 
						|
 | 
						|
		template <typename Range>
 | 
						|
		void merge(
 | 
						|
			const Range &moreMessages,
 | 
						|
			MessagesRange moreNoSkipRange);
 | 
						|
 | 
						|
		base::flat_set<MessagePosition> messages;
 | 
						|
		MessagesRange range;
 | 
						|
 | 
						|
		inline bool operator<(const Slice &other) const {
 | 
						|
			return range.from < other.range.from;
 | 
						|
		}
 | 
						|
 | 
						|
	};
 | 
						|
 | 
						|
	template <typename Range>
 | 
						|
	int uniteAndAdd(
 | 
						|
		MessagesSliceUpdate &update,
 | 
						|
		base::flat_set<Slice>::iterator uniteFrom,
 | 
						|
		base::flat_set<Slice>::iterator uniteTill,
 | 
						|
		const Range &messages,
 | 
						|
		MessagesRange noSkipRange);
 | 
						|
	template <typename Range>
 | 
						|
	int addRangeItemsAndCountNew(
 | 
						|
		MessagesSliceUpdate &update,
 | 
						|
		const Range &messages,
 | 
						|
		MessagesRange noSkipRange);
 | 
						|
	template <typename Range>
 | 
						|
	void addRange(
 | 
						|
		const Range &messages,
 | 
						|
		MessagesRange noSkipRange,
 | 
						|
		base::optional<int> count,
 | 
						|
		bool incrementCount = false);
 | 
						|
 | 
						|
	MessagesResult queryFromSlice(
 | 
						|
		const MessagesQuery &query,
 | 
						|
		const Slice &slice) const;
 | 
						|
 | 
						|
	base::optional<int> _count;
 | 
						|
	base::flat_set<Slice> _slices;
 | 
						|
 | 
						|
	rpl::event_stream<MessagesSliceUpdate> _sliceUpdated;
 | 
						|
 | 
						|
};
 | 
						|
 | 
						|
class MessagesSliceBuilder {
 | 
						|
public:
 | 
						|
	using Key = MessagePosition;
 | 
						|
 | 
						|
	MessagesSliceBuilder(Key key, int limitBefore, int limitAfter);
 | 
						|
 | 
						|
	bool applyInitial(const MessagesResult &result);
 | 
						|
	bool applyUpdate(const MessagesSliceUpdate &update);
 | 
						|
	bool removeOne(MessagePosition messageId);
 | 
						|
	bool removeFromChannel(ChannelId channelId);
 | 
						|
	bool removeAll();
 | 
						|
	bool invalidated();
 | 
						|
	bool bottomInvalidated();
 | 
						|
 | 
						|
	void checkInsufficient();
 | 
						|
	struct AroundData {
 | 
						|
		MessagePosition aroundId;
 | 
						|
		LoadDirection direction = LoadDirection::Around;
 | 
						|
 | 
						|
		inline bool operator<(const AroundData &other) const {
 | 
						|
			return (aroundId < other.aroundId)
 | 
						|
				|| ((aroundId == other.aroundId)
 | 
						|
					&& (direction < other.direction));
 | 
						|
		}
 | 
						|
	};
 | 
						|
	auto insufficientAround() const {
 | 
						|
		return _insufficientAround.events();
 | 
						|
	}
 | 
						|
 | 
						|
	MessagesSlice 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<MessagePosition> &messageIds,
 | 
						|
		base::optional<int> skippedBefore = base::none,
 | 
						|
		base::optional<int> skippedAfter = base::none);
 | 
						|
 | 
						|
	MessagePosition _key;
 | 
						|
	base::flat_set<MessagePosition> _ids;
 | 
						|
	MessagesRange _range;
 | 
						|
	base::optional<int> _fullCount;
 | 
						|
	base::optional<int> _skippedBefore;
 | 
						|
	base::optional<int> _skippedAfter;
 | 
						|
	int _limitBefore = 0;
 | 
						|
	int _limitAfter = 0;
 | 
						|
 | 
						|
	rpl::event_stream<AroundData> _insufficientAround;
 | 
						|
 | 
						|
};
 | 
						|
 | 
						|
} // namespace Data
 |