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) \
31static_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
67namespace Quotient {
68/// An equivalent of std::hash for QTypes to enable std::unordered_map<QType, ...>
69template <typename T>
70struct 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
77template <typename KeyT, typename ValT>
78using UnorderedMap = std::unordered_map<KeyT, ValT, HashQ<KeyT>>;
79
80constexpr 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 */
89template <typename ArrayT>
90class 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
96public:
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
111private:
112 iterator from;
113 iterator to;
114};
115
116namespace _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
137template <typename T>
138class asKeyValueRange
139{
140public:
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
156private:
157 T m_data;
158};
159
160template <typename U>
161asKeyValueRange(U&) -> asKeyValueRange<U&>;
162
163template <typename U>
164asKeyValueRange(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 */
171template <typename InputIt, typename ForwardIt, typename Pred>
172inline 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
190template <typename ImplType, typename TypeToDelete = ImplType>
191using 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.
211template <typename ImplType, typename TypeToDelete = ImplType, typename... ArgTs>
212inline 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
220template <typename ImplType, typename TypeToDelete = ImplType>
221inline ImplPtr<ImplType, TypeToDelete> acquireImpl(ImplType* from)
222{
223 return ImplPtr<ImplType, TypeToDelete> { from, [](TypeToDelete* impl) {
224 delete impl;
225 } };
226}
227
228template <typename ImplType, typename TypeToDelete = ImplType>
229constexpr ImplPtr<ImplType, TypeToDelete> ZeroImpl()
230{
231 return { nullptr, [](TypeToDelete*) { /* nullptr doesn't need deletion */ } };
232}
233
234template <typename T>
235struct 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
249template <typename T>
250using 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.
262template <typename T>
263inline 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
274template <typename... FunctorTs>
275struct Overloads : FunctorTs... {
276 using FunctorTs::operator()...;
277};
278
279template <typename... FunctorTs>
280Overloads(FunctorTs&&...) -> Overloads<FunctorTs...>;
281
282/** Convert what looks like a URL or a Matrix ID to an HTML hyperlink */
283QUOTIENT_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 */
289QUOTIENT_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 */
295QUOTIENT_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 */
302QUOTIENT_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 */
311QUOTIENT_API qreal stringToHueF(const QString& s);
312
313/** Extract the serverpart from MXID */
314QUOTIENT_API QString serverPart(const QString& mxId);
315
316QUOTIENT_API QString versionString();
317QUOTIENT_API int majorVersion();
318QUOTIENT_API int minorVersion();
319QUOTIENT_API int patchVersion();
320
321//! Compile-time constant to determine whether the library is compiled with E2EE
322constexpr auto E2EE_Enabled =
323#ifdef Quotient_E2EE_ENABLED
324 true;
325#else
326 false;
327#endif
328
329QUOTIENT_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.)`
340inline QDebug formatJson(QDebug dbg) { return dbg.noquote(); }
341
342//! Suppress full qualification of enums/QFlags when logging
343inline QDebug terse(QDebug dbg)
344{
345 return dbg.verbosity(verbosityLevel: QDebug::MinimumVerbosity);
346}
347
348constexpr 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
362template <typename FnT>
363inline 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
370inline 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