1//! @file common.hpp
2//! @author ryftchen
3//! @brief The declarations (common) in the utility module.
4//! @version 0.1.0
5//! @copyright Copyright (c) 2022-2025 ryftchen. All rights reserved.
6
7#pragma once
8
9#include <algorithm>
10#include <condition_variable>
11#include <cstring>
12#include <format>
13#include <shared_mutex>
14
15//! @brief The utility module.
16namespace utility // NOLINT(modernize-concat-nested-namespaces)
17{
18//! @brief Common-interface-related functions in the utility module.
19namespace common
20{
21//! @brief Brief function description.
22//! @return function description (module_function)
23inline static const char* description() noexcept
24{
25 return "UTIL_COMMON";
26}
27extern const char* version() noexcept;
28
29//! @brief Hash seed for BKDR hash function.
30constexpr std::size_t bkdrHashSeed = 131;
31//! @brief Hash mask for BKDR hash function.
32constexpr std::size_t bkdrHashMask = 0x7FFFFFFF;
33//! @brief The Brian-Kernighan Dennis-Ritchie hash function (recursive).
34//! @param str - input data
35//! @param hash - recursive hash value
36//! @return hash value
37constexpr std::size_t bkdrHashRecursive(const char* const str, const std::size_t hash = 0) noexcept
38{
39 return str ? (*str ? bkdrHashRecursive(str: str + 1, hash: (hash * bkdrHashSeed + *str) & bkdrHashMask) : hash) : 0;
40}
41//! @brief The operator ("") overloading with BKDR hash function.
42//! @param str - input data
43//! @return hash value
44constexpr std::size_t operator""_bkdrHash(const char* const str, const std::size_t /*len*/) noexcept
45{
46 return str ? bkdrHashRecursive(str) : 0;
47}
48//! @brief The operator ("") overloading with BKDR hash function.
49//! @param str - input data
50//! @return hash value
51constexpr std::size_t operator""_bkdrHash(const char* const str) noexcept
52{
53 return str ? bkdrHashRecursive(str) : 0;
54}
55extern std::size_t bkdrHash(const char* str) noexcept;
56
57extern std::string base64Encode(const std::string_view data);
58extern std::string base64Decode(const std::string_view data);
59
60extern std::string printfString(const char* const fmt, ...);
61//! @brief Format as a string (format style).
62//! @tparam Args - type of arguments
63//! @param fmt - null-terminated multibyte string specifying how to interpret the data
64//! @param args - arguments
65//! @return string after formatting
66template <typename... Args>
67inline std::string formatString(
68 const std::string_view fmt, Args&&... args) // NOLINT(cppcoreguidelines-missing-std-forward)
69{
70 return std::vformat(fmt, std::make_format_args(args...));
71}
72
73//! @brief Compare whether multiple strings are equal.
74//! @tparam Others - type of arguments of string
75//! @param str1 - string 1
76//! @param str2 - string 2
77//! @param others - arguments of string
78//! @return be equal or not
79template <typename... Others>
80inline bool areStringsEqual(const char* const str1, const char* const str2, const Others&... others)
81{
82 return (str1 && str2 && (... && others))
83 && ((std::strcmp(s1: str1, s2: str2) == 0) && ((std::strcmp(s1: str1, s2: others) == 0) && ...));
84}
85
86//! @brief Splice strings into constexpr type.
87//! @tparam Strings - target strings to be spliced
88template <const std::string_view&... Strings>
89class ConcatString
90{
91private:
92 //! @brief A sequence of characters.
93 static constexpr std::array characters{
94 []() constexpr noexcept
95 {
96 constexpr auto len = (Strings.length() + ... + 0);
97 std::array<char, len + 1> str{};
98 char* dst = str.data();
99 ((std::copy_n(first: Strings.cbegin(), n: Strings.length(), result: dst), dst += Strings.length()), ...);
100 str[len] = '\0';
101 return str;
102 }()};
103
104public:
105 //! @brief The splicing result. Converted from a sequence of characters.
106 static constexpr std::string_view value{characters.data(), characters.size() - 1};
107};
108//! @brief Get the result of splicing strings.
109//! @tparam Strings - target strings to be spliced
110template <const std::string_view&... Strings>
111static constexpr auto concatString = ConcatString<Strings...>::value;
112
113//! @brief Check whether the target class is stateless.
114//! @tparam Cls - type of target class
115//! @return be stateless or not
116template <typename Cls>
117consteval bool isStatelessClass()
118{
119 return std::is_empty_v<Cls> || ((sizeof(Cls) == sizeof(void*)) && std::is_polymorphic_v<Cls>);
120}
121
122//! @brief Check whether the target value is part of the enumeration.
123//! @tparam Enum - type of enumeration
124//! @tparam Values - arguments of enumeration
125template <typename Enum, Enum... Values>
126requires std::is_enum_v<Enum>
127struct EnumCheck;
128//! @brief Check whether the target value is part of the enumeration.
129//! @tparam Enum - type of enumeration
130template <typename Enum>
131requires std::is_enum_v<Enum>
132struct EnumCheck<Enum>
133{
134 //! @brief Check whether it contains the value as an enumeration.
135 //! @return has or not
136 static constexpr bool has(const std::integral auto /*val*/) { return false; }
137};
138//! @brief Check whether the target value is part of the enumeration.
139//! @tparam Enum - type of enumeration
140//! @tparam Curr - current enumeration value
141//! @tparam Next - next enumeration value
142template <typename Enum, Enum Curr, Enum... Next>
143requires std::is_enum_v<Enum>
144struct EnumCheck<Enum, Curr, Next...> : private EnumCheck<Enum, Next...>
145{
146 //! @brief Check whether it contains the value as an enumeration.
147 //! @param val - target value
148 //! @return has or not
149 static constexpr bool has(const std::integral auto val)
150 {
151 return (static_cast<std::underlying_type_t<Enum>>(Curr) == val) || EnumCheck<Enum, Next...>::has(val);
152 }
153};
154
155//! @brief Helper type for the visitor.
156//! @tparam Ts - type of visitors
157template <typename... Ts>
158struct VisitorOverload : public Ts...
159{
160 using Ts::operator()...;
161};
162
163//! @brief Closure wrapper.
164//! @tparam Func - type of callable function
165//! @tparam Op - type of call operator
166//! @tparam Wrap - flag to indicate that further wrapping is required
167template <typename Func, typename Op = decltype(&Func::operator()), bool Wrap = (sizeof(Func) > (sizeof(void*) * 2U))>
168struct WrapClosure
169{
170 //! @brief Wrap operation.
171 //! @tparam Clos - type of closure
172 //! @param closure - target closure
173 //! @return original closure
174 template <typename Clos>
175 static constexpr auto&& wrap(Clos&& closure) noexcept
176 {
177 return std::forward<Clos>(closure);
178 }
179};
180//! @brief Closure wrapper. For the non-const member function.
181//! @tparam Func - type of callable function
182//! @tparam Ret - type of return value
183//! @tparam Obj - type of object to which the member belongs
184//! @tparam Args - type of function arguments
185template <typename Func, typename Ret, typename Obj, typename... Args>
186struct WrapClosure<Func, Ret (Obj::*)(Args...), true>
187{
188 //! @brief Wrap operation.
189 //! @tparam Clos - type of closure
190 //! @param closure - target closure
191 //! @return wrapped closure
192 template <typename Clos>
193 static constexpr auto wrap(Clos&& closure) // NOLINT(cppcoreguidelines-missing-std-forward)
194 {
195 return [sharedClosure = std::make_shared<Func>(std::forward<Clos>(closure))](Args&&... args) mutable
196 { return (*sharedClosure)(std::forward<Args>(args)...); };
197 }
198};
199//! @brief Closure wrapper. For the const member function.
200//! @tparam Func - type of callable function
201//! @tparam Ret - type of return value
202//! @tparam Obj - type of object to which the member belongs
203//! @tparam Args - type of function arguments
204template <typename Func, typename Ret, typename Obj, typename... Args>
205struct WrapClosure<Func, Ret (Obj::*)(Args...) const, true>
206{
207 //! @brief Wrap operation.
208 //! @tparam Clos - type of closure
209 //! @param closure - target closure
210 //! @return wrapped closure
211 template <typename Clos>
212 static constexpr auto wrap(Clos&& closure) // NOLINT(cppcoreguidelines-missing-std-forward)
213 {
214 return [sharedClosure = std::make_shared<Func>(std::forward<Clos>(closure))](Args&&... args)
215 { return (*sharedClosure)(std::forward<Args>(args)...); };
216 }
217};
218//! @brief Wrap closure further.
219//! @tparam Clos - type of closure
220//! @param closure - target closure
221//! @return wrapped closure
222template <typename Clos>
223constexpr auto wrapClosure(Clos&& closure)
224{
225 return WrapClosure<std::decay_t<Clos>>::wrap(std::forward<Clos>(closure));
226}
227
228//! @brief The instance manager that never calls the destructor. Use for the function-level static variables.
229//! @tparam Inst - type of instance
230template <typename Inst>
231class NoDestructor final
232{
233public:
234 //! @brief Construct a new NoDestructor object.
235 //! @tparam Args - type of constructor arguments
236 //! @param args - constructor arguments
237 template <typename... Args>
238 explicit NoDestructor(Args&&... args);
239 //! @brief Destroy the NoDestructor object.
240 ~NoDestructor() = default;
241 //! @brief Construct a new NoDestructor object.
242 NoDestructor(const NoDestructor&) = delete;
243 //! @brief Construct a new NoDestructor object.
244 NoDestructor(NoDestructor&&) noexcept = default;
245 //! @brief The operator (=) overloading of NoDestructor class.
246 //! @return reference of the NoDestructor object
247 NoDestructor& operator=(const NoDestructor&) = delete;
248 //! @brief The operator (=) overloading of NoDestructor class.
249 //! @return reference of the NoDestructor object
250 NoDestructor& operator=(NoDestructor&&) noexcept = default;
251
252 //! @brief Get the pointer of the instance.
253 //! @return pointer of the instance
254 Inst* get() noexcept;
255 //! @brief Get the const pointer of the instance.
256 //! @return const pointer of the instance
257 const Inst* get() const noexcept;
258
259private:
260 //! @brief Storage for the instance.
261 alignas(Inst) std::array<std::byte, sizeof(Inst)> storage{};
262
263 static_assert(
264 (sizeof(storage) >= sizeof(Inst)) // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
265 && (alignof(decltype(*reinterpret_cast<Inst*>(storage.data()))) >= alignof(Inst)));
266};
267
268// NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast)
269template <typename Inst>
270template <typename... Args>
271NoDestructor<Inst>::NoDestructor(Args&&... args)
272{
273 std::construct_at(reinterpret_cast<Inst*>(storage.data()), std::forward<Args>(args)...);
274}
275
276template <typename Inst>
277Inst* NoDestructor<Inst>::get() noexcept
278{
279 return reinterpret_cast<Inst*>(storage.data());
280}
281
282template <typename Inst>
283const Inst* NoDestructor<Inst>::get() const noexcept
284{
285 return reinterpret_cast<const Inst*>(storage.data());
286}
287// NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast)
288
289//! @brief Simple spin lock.
290class SpinLock
291{
292public:
293 //! @brief Acquire the lock.
294 void lock();
295 //! @brief Release the lock.
296 void unlock();
297 //! @brief Try to acquire the lock without blocking.
298 //! @return acquire or not
299 bool tryLock();
300
301private:
302 //! @brief The atomic flag used to implement the spin lock.
303 std::atomic_flag flag = ATOMIC_FLAG_INIT;
304};
305
306//! @brief Lock to control reading and writing.
307class ReadWriteLock
308{
309public:
310 //! @brief Acquire a read lock.
311 void readLock();
312 //! @brief Release a read lock.
313 void readUnlock();
314 //! @brief Acquire a write lock.
315 void writeLock();
316 //! @brief Release a write lock.
317 void writeUnlock();
318 //! @brief Enumerate specific lock modes.
319 enum class LockMode : std::uint8_t
320 {
321 //! @brief Read.
322 read,
323 //! @brief Write.
324 write
325 };
326
327private:
328 //! @brief Handling of shared and exclusive locks.
329 std::shared_mutex rwLock;
330 //! @brief Counter of readers that have acquired the shared lock.
331 std::atomic_uint_fast64_t reader{0};
332 //! @brief Counter of writers that have acquired the exclusive lock.
333 std::atomic_uint_fast64_t writer{0};
334 //! @brief Mutex for controlling counters.
335 mutable std::mutex mtx;
336 //! @brief The synchronization condition for counters. Use with mtx.
337 std::condition_variable cond;
338};
339
340//! @brief Manage the lifetime of a lock under control.
341class LockGuard
342{
343public:
344 //! @brief Alias for the lock mode.
345 using LockMode = ReadWriteLock::LockMode;
346 //! @brief Construct a new LockGuard object.
347 //! @param lock - object managed by the guard
348 //! @param mode - lock mode
349 LockGuard(ReadWriteLock& lock, const LockMode mode);
350 //! @brief Destroy the LockGuard object.
351 virtual ~LockGuard();
352 //! @brief Construct a new LockGuard object.
353 LockGuard(const LockGuard&) = delete;
354 //! @brief Construct a new LockGuard object.
355 LockGuard(LockGuard&&) noexcept = delete;
356 //! @brief The operator (=) overloading of LockGuard class.
357 //! @return reference of the LockGuard object
358 LockGuard& operator=(const LockGuard&) = delete;
359 //! @brief The operator (=) overloading of LockGuard class.
360 //! @return reference of the LockGuard object
361 LockGuard& operator=(LockGuard&&) noexcept = delete;
362
363private:
364 //! @brief Object managed by the guard.
365 ReadWriteLock& lock;
366 //! @brief Lock mode.
367 const LockMode mode{LockMode::read};
368};
369} // namespace common
370} // namespace utility
371