1 /*
2 This file is part of Telegram Desktop,
3 the official desktop application for the Telegram messaging service.
4 
5 For license and copyright information please follow this link:
6 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
7 */
8 #pragma once
9 
10 namespace Data {
11 
12 enum class LoadDirection : char {
13 	Around,
14 	Before,
15 	After,
16 };
17 
18 struct MessagePosition {
19 	FullMsgId fullId;
20 	TimeId date = 0;
21 
22 	explicit operator bool() const {
23 		return (fullId.msg != 0);
24 	}
25 
26 	inline constexpr bool operator<(const MessagePosition &other) const {
27 		if (date < other.date) {
28 			return true;
29 		} else if (other.date < date) {
30 			return false;
31 		}
32 		return (fullId < other.fullId);
33 	}
34 	inline constexpr bool operator>(const MessagePosition &other) const {
35 		return other < *this;
36 	}
37 	inline constexpr bool operator<=(const MessagePosition &other) const {
38 		return !(other < *this);
39 	}
40 	inline constexpr bool operator>=(const MessagePosition &other) const {
41 		return !(*this < other);
42 	}
43 	inline constexpr bool operator==(const MessagePosition &other) const {
44 		return (date == other.date)
45 			&& (fullId == other.fullId);
46 	}
47 	inline constexpr bool operator!=(const MessagePosition &other) const {
48 		return !(*this == other);
49 	}
50 };
51 
52 struct MessagesRange {
53 	MessagePosition from;
54 	MessagePosition till;
55 
56 	inline constexpr bool operator==(const MessagesRange &other) const {
57 		return (from == other.from)
58 			&& (till == other.till);
59 	}
60 	inline constexpr bool operator!=(const MessagesRange &other) const {
61 		return !(*this == other);
62 	}
63 };
64 
65 constexpr auto MinDate = TimeId(0);
66 constexpr auto MaxDate = std::numeric_limits<TimeId>::max();
67 constexpr auto MinMessagePosition = MessagePosition{
68 	.fullId = FullMsgId(NoChannel, 1),
69 	.date = MinDate,
70 };
71 constexpr auto MaxMessagePosition = MessagePosition{
72 	.fullId = FullMsgId(NoChannel, ServerMaxMsgId - 1),
73 	.date = MaxDate,
74 };
75 constexpr auto FullMessagesRange = MessagesRange{
76 	.from = MinMessagePosition,
77 	.till = MaxMessagePosition,
78 };
79 constexpr auto UnreadMessagePosition = MessagePosition{
80 	.fullId = FullMsgId(NoChannel, ShowAtUnreadMsgId),
81 	.date = MinDate,
82 };
83 
84 struct MessagesSlice {
85 	std::vector<FullMsgId> ids;
86 	FullMsgId nearestToAround;
87 	std::optional<int> skippedBefore;
88 	std::optional<int> skippedAfter;
89 	std::optional<int> fullCount;
90 };
91 
92 struct MessagesQuery {
93 	MessagePosition aroundId;
94 	int limitBefore = 0;
95 	int limitAfter = 0;
96 };
97 
98 struct MessagesResult {
99 	std::optional<int> count;
100 	std::optional<int> skippedBefore;
101 	std::optional<int> skippedAfter;
102 	base::flat_set<MessagePosition> messageIds;
103 };
104 
105 struct MessagesSliceUpdate {
106 	const base::flat_set<MessagePosition> *messages = nullptr;
107 	MessagesRange range;
108 	std::optional<int> count;
109 };
110 
111 class MessagesList {
112 public:
113 	void addOne(MessagePosition messageId);
114 	void addNew(MessagePosition messageId);
115 	void addSlice(
116 		std::vector<MessagePosition> &&messageIds,
117 		MessagesRange noSkipRange,
118 		std::optional<int> count);
119 	void removeOne(MessagePosition messageId);
120 	void removeAll(ChannelId channelId);
121 	void removeLessThan(MessagePosition messageId);
122 	void invalidate();
123 	void invalidateBottom();
124 	[[nodiscard]] rpl::producer<MessagesResult> query(
125 		MessagesQuery &&query) const;
126 	[[nodiscard]] rpl::producer<MessagesSliceUpdate> sliceUpdated() const;
127 
128 	[[nodiscard]] MessagesResult snapshot(MessagesQuery &&query) const;
129 	[[nodiscard]] rpl::producer<MessagesResult> viewer(
130 		MessagesQuery &&query) const;
131 
132 	[[nodiscard]] bool empty() const;
133 
134 private:
135 	struct Slice {
136 		Slice(
137 			base::flat_set<MessagePosition> &&messages,
138 			MessagesRange range);
139 
140 		template <typename Range>
141 		void merge(
142 			const Range &moreMessages,
143 			MessagesRange moreNoSkipRange);
144 
145 		base::flat_set<MessagePosition> messages;
146 		MessagesRange range;
147 
148 		inline bool operator<(const Slice &other) const {
149 			return range.from < other.range.from;
150 		}
151 
152 	};
153 
154 	template <typename Range>
155 	int uniteAndAdd(
156 		MessagesSliceUpdate &update,
157 		base::flat_set<Slice>::iterator uniteFrom,
158 		base::flat_set<Slice>::iterator uniteTill,
159 		const Range &messages,
160 		MessagesRange noSkipRange);
161 	template <typename Range>
162 	int addRangeItemsAndCountNew(
163 		MessagesSliceUpdate &update,
164 		const Range &messages,
165 		MessagesRange noSkipRange);
166 	template <typename Range>
167 	void addRange(
168 		const Range &messages,
169 		MessagesRange noSkipRange,
170 		std::optional<int> count,
171 		bool incrementCount = false);
172 
173 	MessagesResult queryFromSlice(
174 		const MessagesQuery &query,
175 		const Slice &slice) const;
176 	MessagesResult queryCurrent(const MessagesQuery &query) const;
177 
178 	std::optional<int> _count;
179 	base::flat_set<Slice> _slices;
180 
181 	rpl::event_stream<MessagesSliceUpdate> _sliceUpdated;
182 
183 };
184 
185 class MessagesSliceBuilder {
186 public:
187 	using Key = MessagePosition;
188 
189 	MessagesSliceBuilder(Key key, int limitBefore, int limitAfter);
190 
191 	bool applyInitial(const MessagesResult &result);
192 	bool applyUpdate(const MessagesSliceUpdate &update);
193 	bool removeOne(MessagePosition messageId);
194 	bool removeFromChannel(ChannelId channelId);
195 	bool removeAll();
196 	bool invalidated();
197 	bool bottomInvalidated();
198 
199 	void checkInsufficient();
200 	struct AroundData {
201 		MessagePosition aroundId;
202 		LoadDirection direction = LoadDirection::Around;
203 
204 		inline bool operator<(const AroundData &other) const {
205 			return (aroundId < other.aroundId)
206 				|| ((aroundId == other.aroundId)
207 					&& (direction < other.direction));
208 		}
209 	};
insufficientAround()210 	auto insufficientAround() const {
211 		return _insufficientAround.events();
212 	}
213 
214 	MessagesSlice snapshot() const;
215 
216 private:
217 	enum class RequestDirection {
218 		Before,
219 		After,
220 	};
221 	void requestMessages(RequestDirection direction);
222 	void requestMessagesCount();
223 	void fillSkippedAndSliceToLimits();
224 	void sliceToLimits();
225 
226 	void mergeSliceData(
227 		std::optional<int> count,
228 		const base::flat_set<MessagePosition> &messageIds,
229 		std::optional<int> skippedBefore = std::nullopt,
230 		std::optional<int> skippedAfter = std::nullopt);
231 
232 	MessagePosition _key;
233 	base::flat_set<MessagePosition> _ids;
234 	MessagesRange _range;
235 	std::optional<int> _fullCount;
236 	std::optional<int> _skippedBefore;
237 	std::optional<int> _skippedAfter;
238 	int _limitBefore = 0;
239 	int _limitAfter = 0;
240 
241 	rpl::event_stream<AroundData> _insufficientAround;
242 
243 };
244 
245 } // namespace Data
246