1 | // SPDX-FileCopyrightText: 2016 Kitsune Ral <kitsune-ral@users.sf.net> |
2 | // SPDX-FileCopyrightText: 2019 Alexey Andreyev <aa13q@ya.ru> |
3 | // SPDX-License-Identifier: LGPL-2.1-or-later |
4 | |
5 | #pragma once |
6 | |
7 | #include "quotient_export.h" |
8 | |
9 | #include <QtCore/QLatin1String> |
10 | #include <QtCore/QHashFunctions> |
11 | #include <QtCore/QDebug> |
12 | #include <QtCore/QElapsedTimer> |
13 | |
14 | #include <memory> |
15 | #include <unordered_map> |
16 | |
17 | #ifndef Q_DISABLE_MOVE |
18 | // Q_DISABLE_MOVE was introduced in Q_VERSION_CHECK(5,13,0) |
19 | # define Q_DISABLE_MOVE(_ClassName) \ |
20 | _ClassName(_ClassName&&) Q_DECL_EQ_DELETE; \ |
21 | _ClassName& operator=(_ClassName&&) Q_DECL_EQ_DELETE; |
22 | #endif |
23 | |
24 | #ifndef Q_DISABLE_COPY_MOVE |
25 | #define Q_DISABLE_COPY_MOVE(Class) \ |
26 | Q_DISABLE_COPY(Class) \ |
27 | Q_DISABLE_MOVE(Class) |
28 | #endif |
29 | |
30 | #define DISABLE_MOVE(_ClassName) \ |
31 | static_assert(false, "Use Q_DISABLE_MOVE instead; Quotient enables it across all used versions of Qt"); |
32 | |
33 | #ifndef QT_IGNORE_DEPRECATIONS |
34 | // QT_IGNORE_DEPRECATIONS was introduced in Q_VERSION_CHECK(5,15,0) |
35 | # define QT_IGNORE_DEPRECATIONS(statement) \ |
36 | QT_WARNING_PUSH \ |
37 | QT_WARNING_DISABLE_DEPRECATED \ |
38 | statement \ |
39 | QT_WARNING_POP |
40 | #endif |
41 | |
42 | #if __cpp_conditional_explicit >= 201806L |
43 | #define QUO_IMPLICIT explicit(false) |
44 | #else |
45 | #define QUO_IMPLICIT |
46 | #endif |
47 | |
48 | #define DECL_DEPRECATED_ENUMERATOR(Deprecated, Recommended) \ |
49 | Deprecated Q_DECL_ENUMERATOR_DEPRECATED_X("Use " #Recommended) = Recommended |
50 | |
51 | /// \brief Copy an object with slicing |
52 | /// |
53 | /// Unintended slicing is bad, which why there's a C++ Core Guideline that |
54 | /// basically says "don't slice, or if you do, make it explicit". Sonar and |
55 | /// clang-tidy have warnings matching this guideline; unfortunately, those |
56 | /// warnings trigger even when you have a dedicated method (as the guideline |
57 | /// recommends) that makes a slicing copy. |
58 | /// |
59 | /// This macro is meant for cases when slicing is intended: the static cast |
60 | /// silences the static analysis warning, and the macro appearance itself makes |
61 | /// it very clear that slicing is wanted here. It is made as a macro |
62 | /// (not as a function template) to support the case of private inheritance |
63 | /// in which a function template would not be able to cast to the private base |
64 | /// (see Uri::toUrl() for an example of just that situation). |
65 | #define SLICE(Object, ToType) ToType{static_cast<const ToType&>(Object)} |
66 | |
67 | namespace Quotient { |
68 | /// An equivalent of std::hash for QTypes to enable std::unordered_map<QType, ...> |
69 | template <typename T> |
70 | struct HashQ { |
71 | size_t operator()(const T& s) const Q_DECL_NOEXCEPT |
72 | { |
73 | return qHash(s, uint(qGlobalQHashSeed())); |
74 | } |
75 | }; |
76 | /// A wrapper around std::unordered_map compatible with types that have qHash |
77 | template <typename KeyT, typename ValT> |
78 | using UnorderedMap = std::unordered_map<KeyT, ValT, HashQ<KeyT>>; |
79 | |
80 | constexpr auto operator"" _ls(const char* s, std::size_t size) |
81 | { |
82 | return QLatin1String(s, int(size)); |
83 | } |
84 | |
85 | /** An abstraction over a pair of iterators |
86 | * This is a very basic range type over a container with iterators that |
87 | * are at least ForwardIterators. Inspired by Ranges TS. |
88 | */ |
89 | template <typename ArrayT> |
90 | class Range { |
91 | // Looking forward to C++20 ranges |
92 | using iterator = typename ArrayT::iterator; |
93 | using const_iterator = typename ArrayT::const_iterator; |
94 | using size_type = typename ArrayT::size_type; |
95 | |
96 | public: |
97 | constexpr Range(ArrayT& arr) : from(std::begin(arr)), to(std::end(arr)) {} |
98 | constexpr Range(iterator from, iterator to) : from(from), to(to) {} |
99 | |
100 | constexpr size_type size() const |
101 | { |
102 | Q_ASSERT(std::distance(from, to) >= 0); |
103 | return size_type(std::distance(from, to)); |
104 | } |
105 | constexpr bool empty() const { return from == to; } |
106 | constexpr const_iterator begin() const { return from; } |
107 | constexpr const_iterator end() const { return to; } |
108 | constexpr iterator begin() { return from; } |
109 | constexpr iterator end() { return to; } |
110 | |
111 | private: |
112 | iterator from; |
113 | iterator to; |
114 | }; |
115 | |
116 | namespace _impl { |
117 | template <typename T> |
118 | concept Holds_NonConst_LValue_Ref = requires { |
119 | std::is_lvalue_reference_v<T>; |
120 | !std::is_const_v<std::remove_reference<T>>; |
121 | }; |
122 | } |
123 | |
124 | //! \brief An adaptor for Qt (hash-)maps to make them iterable in STL style |
125 | //! |
126 | //! QMap/QHash container iterators returned by begin() and end() dereference |
127 | //! to values, unlike STL where similar iterators dereference to key-value |
128 | //! pairs. It is a problem in range-for if you want to also access map keys. |
129 | //! This adaptor allows to use range-for syntax with access to both keys and |
130 | //! values in QMap/QHash containers. Just use |
131 | //! `for (auto&& [key, value] : asKeyValueRange(myMap)` instead of |
132 | //! `for (auto&& value : myMap)`. |
133 | //! \note When an rvalue is passed as the constructor argument, asKeyValueRange |
134 | //! shallow-copies the map object to ensure its lifetime is maintained |
135 | //! throughout the loop; with lvalues, no copying occurs, assuming that |
136 | //! the map object outlives the range-for loop |
137 | template <typename T> |
138 | class asKeyValueRange |
139 | { |
140 | public: |
141 | explicit asKeyValueRange(T data) |
142 | : m_data { data } |
143 | {} |
144 | |
145 | auto begin() requires _impl::Holds_NonConst_LValue_Ref<T> |
146 | { |
147 | return m_data.keyValueBegin(); |
148 | } |
149 | auto end() requires _impl::Holds_NonConst_LValue_Ref<T> |
150 | { |
151 | return m_data.keyValueEnd(); |
152 | } |
153 | auto begin() const { return m_data.keyValueBegin(); } |
154 | auto end() const { return m_data.keyValueEnd(); } |
155 | |
156 | private: |
157 | T m_data; |
158 | }; |
159 | |
160 | template <typename U> |
161 | asKeyValueRange(U&) -> asKeyValueRange<U&>; |
162 | |
163 | template <typename U> |
164 | asKeyValueRange(U&&) -> asKeyValueRange<U>; |
165 | |
166 | /** A replica of std::find_first_of that returns a pair of iterators |
167 | * |
168 | * Convenient for cases when you need to know which particular "first of" |
169 | * [sFirst, sLast) has been found in [first, last). |
170 | */ |
171 | template <typename InputIt, typename ForwardIt, typename Pred> |
172 | inline std::pair<InputIt, ForwardIt> findFirstOf(InputIt first, InputIt last, |
173 | ForwardIt sFirst, |
174 | ForwardIt sLast, Pred pred) |
175 | { |
176 | for (; first != last; ++first) |
177 | for (auto it = sFirst; it != sLast; ++it) |
178 | if (pred(*first, *it)) |
179 | return { first, it }; |
180 | |
181 | return { last, sLast }; |
182 | } |
183 | |
184 | //! \brief An owning implementation pointer |
185 | //! |
186 | //! This is basically std::unique_ptr<> to hold your pimpl's but without having |
187 | //! to define default constructors/operator=() out of line. |
188 | //! Thanks to https://oliora.github.io/2015/12/29/pimpl-and-rule-of-zero.html |
189 | //! for inspiration |
190 | template <typename ImplType, typename TypeToDelete = ImplType> |
191 | using ImplPtr = std::unique_ptr<ImplType, void (*)(TypeToDelete*)>; |
192 | |
193 | // Why this works (see also the link above): because this defers the moment |
194 | // of requiring sizeof of ImplType to the place where makeImpl is invoked |
195 | // (which is located, necessarily, in the .cpp file after ImplType definition). |
196 | // The stock unique_ptr deleter (std::default_delete) normally needs sizeof |
197 | // at the same spot - as long as you defer definition of the owning type |
198 | // constructors and operator='s to the .cpp file as well. Which means you |
199 | // have to explicitly declare and define them (even if with = default), |
200 | // formally breaking the rule of zero; informally, just adding boilerplate code. |
201 | // The custom deleter itself is instantiated at makeImpl invocation - there's |
202 | // no way earlier to even know how ImplType will be deleted and whether that |
203 | // will need sizeof(ImplType) earlier. In theory it's a tad slower because |
204 | // the deleter is called by the pointer; however, the difference will not |
205 | // be noticeable (if exist at all) for any class with non-trivial contents. |
206 | |
207 | //! \brief make_unique for ImplPtr |
208 | //! |
209 | //! Since std::make_unique is not compatible with ImplPtr, this should be used |
210 | //! in constructors of frontend classes to create implementation instances. |
211 | template <typename ImplType, typename TypeToDelete = ImplType, typename... ArgTs> |
212 | inline ImplPtr<ImplType, TypeToDelete> makeImpl(ArgTs&&... args) |
213 | { |
214 | return ImplPtr<ImplType, TypeToDelete> { |
215 | new ImplType{std::forward<ArgTs>(args)...}, |
216 | [](TypeToDelete* impl) { delete impl; } |
217 | }; |
218 | } |
219 | |
220 | template <typename ImplType, typename TypeToDelete = ImplType> |
221 | inline ImplPtr<ImplType, TypeToDelete> acquireImpl(ImplType* from) |
222 | { |
223 | return ImplPtr<ImplType, TypeToDelete> { from, [](TypeToDelete* impl) { |
224 | delete impl; |
225 | } }; |
226 | } |
227 | |
228 | template <typename ImplType, typename TypeToDelete = ImplType> |
229 | constexpr ImplPtr<ImplType, TypeToDelete> ZeroImpl() |
230 | { |
231 | return { nullptr, [](TypeToDelete*) { /* nullptr doesn't need deletion */ } }; |
232 | } |
233 | |
234 | template <typename T> |
235 | struct CStructDeleter { |
236 | size_t (*destructor)(T*); |
237 | |
238 | void operator()(T* toDelete) |
239 | { |
240 | destructor(toDelete); |
241 | delete[] reinterpret_cast<std::byte*>(toDelete); |
242 | } |
243 | }; |
244 | |
245 | //! \brief An owning pointer to a C structure |
246 | //! |
247 | //! This is intented to ease lifecycle management of Olm structures. |
248 | //! \sa makeCStruct |
249 | template <typename T> |
250 | using CStructPtr = std::unique_ptr<T, CStructDeleter<T>>; |
251 | |
252 | //! \brief Create a C structure with pre-programmed deletion logic |
253 | //! |
254 | //! This facility function creates a CStructPtr that owns the pointer returned |
255 | //! by \p constructor. The memory passed to \p constructor is allocated |
256 | //! as an array of bytes; the size of that array is determined by calling |
257 | //! \p sizeFn. Finally, since the returned pointer is owning, it also stores |
258 | //! the corresponding CStructDeleter instance; when called at destruction of |
259 | //! the owning pointer, this deleter first calls \p destructor passing the |
260 | //! original C pointer returned by \p constructor; and then deletes the |
261 | //! allocated array of bytes. |
262 | template <typename T> |
263 | inline auto makeCStruct(T* (*constructor)(void*), size_t (*sizeFn)(), |
264 | auto destructor) |
265 | { |
266 | return CStructPtr<T>{ constructor(new std::byte[sizeFn()]), { destructor } }; |
267 | } |
268 | |
269 | //! \brief Multiplex several functors in one |
270 | //! |
271 | //! This is a well-known trick to wrap several lambdas into a single functor |
272 | //! class that can be passed to std::visit. |
273 | //! \sa https://en.cppreference.com/w/cpp/utility/variant/visit |
274 | template <typename... FunctorTs> |
275 | struct Overloads : FunctorTs... { |
276 | using FunctorTs::operator()...; |
277 | }; |
278 | |
279 | template <typename... FunctorTs> |
280 | Overloads(FunctorTs&&...) -> Overloads<FunctorTs...>; |
281 | |
282 | /** Convert what looks like a URL or a Matrix ID to an HTML hyperlink */ |
283 | QUOTIENT_API void linkifyUrls(QString& htmlEscapedText); |
284 | |
285 | /** Sanitize the text before showing in HTML |
286 | * |
287 | * This does toHtmlEscaped() and removes Unicode BiDi marks. |
288 | */ |
289 | QUOTIENT_API QString sanitized(const QString& plainText); |
290 | |
291 | /** Pretty-print plain text into HTML |
292 | * |
293 | * This includes HTML escaping of <,>,",& and calling linkifyUrls() |
294 | */ |
295 | QUOTIENT_API QString prettyPrint(const QString& plainText); |
296 | |
297 | /** Return a path to cache directory after making sure that it exists |
298 | * |
299 | * The returned path has a trailing slash, clients don't need to append it. |
300 | * \param dirName path to cache directory relative to the standard cache path |
301 | */ |
302 | QUOTIENT_API QString cacheLocation(const QString& dirName); |
303 | |
304 | /** Hue color component of based of the hash of the string. |
305 | * |
306 | * The implementation is based on XEP-0392: |
307 | * https://xmpp.org/extensions/xep-0392.html |
308 | * Naming and range are the same as QColor's hueF method: |
309 | * https://doc.qt.io/qt-5/qcolor.html#integer-vs-floating-point-precision |
310 | */ |
311 | QUOTIENT_API qreal stringToHueF(const QString& s); |
312 | |
313 | /** Extract the serverpart from MXID */ |
314 | QUOTIENT_API QString serverPart(const QString& mxId); |
315 | |
316 | QUOTIENT_API QString versionString(); |
317 | QUOTIENT_API int majorVersion(); |
318 | QUOTIENT_API int minorVersion(); |
319 | QUOTIENT_API int patchVersion(); |
320 | |
321 | //! Compile-time constant to determine whether the library is compiled with E2EE |
322 | constexpr auto E2EE_Enabled = |
323 | #ifdef Quotient_E2EE_ENABLED |
324 | true; |
325 | #else |
326 | false; |
327 | #endif |
328 | |
329 | QUOTIENT_API bool encryptionSupported(); |
330 | |
331 | // QDebug manipulators |
332 | |
333 | //! \brief QDebug manipulator to setup the stream for JSON output |
334 | //! |
335 | //! Originally made to encapsulate the change in QDebug behavior in Qt 5.4 |
336 | //! and the respective addition of QDebug::noquote(). |
337 | //! Together with the operator<<() helper, the proposed usage is |
338 | //! (similar to std:: I/O manipulators): |
339 | //! `qCDebug(MAIN) << formatJson << json_object; // (QJsonObject etc.)` |
340 | inline QDebug formatJson(QDebug dbg) { return dbg.noquote(); } |
341 | |
342 | //! Suppress full qualification of enums/QFlags when logging |
343 | inline QDebug terse(QDebug dbg) |
344 | { |
345 | return dbg.verbosity(verbosityLevel: QDebug::MinimumVerbosity); |
346 | } |
347 | |
348 | constexpr qint64 ProfilerMinNsecs = |
349 | #ifdef PROFILER_LOG_USECS |
350 | PROFILER_LOG_USECS |
351 | #else |
352 | 200 |
353 | #endif |
354 | * 1000; |
355 | } // namespace Quotient |
356 | |
357 | //! \brief A helper operator for QDebug manipulators, e.g. formatJson |
358 | //! |
359 | //! \param dbg to output the json to |
360 | //! \param manipFn a QDebug manipulator |
361 | //! \return a copy of dbg that has its mode altered by manipFn |
362 | template <typename FnT> |
363 | inline QDebug operator<<(QDebug dbg, FnT manipFn) |
364 | requires std::is_invocable_v<FnT, QDebug> |
365 | // TODO: move over to std::invocable once on Apple Clang 14 (lib 0.9, i.e.) |
366 | { |
367 | return std::invoke(manipFn, dbg); |
368 | } |
369 | |
370 | inline QDebug operator<<(QDebug dbg, QElapsedTimer et) |
371 | { |
372 | // NOLINTNEXTLINE(bugprone-integer-division) |
373 | dbg << static_cast<double>(et.nsecsElapsed() / 1000) / 1000 |
374 | << "ms" ; // Show in ms with 3 decimal digits precision |
375 | return dbg; |
376 | } |
377 | |