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-2026 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 != '\0') ? 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>
80requires (std::is_same_v<Others, const char*> && ...)
81inline bool areStringsEqual(const char* str1, const char* str2, Others... others)
82{
83 return (str1 && str2 && (... && others))
84 && ((std::strcmp(s1: str1, s2: str2) == 0) && ((std::strcmp(s1: str1, s2: others) == 0) && ...));
85}
86
87//! @brief Splice strings into constexpr type.
88//! @tparam Strings - target strings to be spliced
89template <const std::string_view&... Strings>
90class ConcatString
91{
92private:
93 //! @brief A sequence of characters.
94 static constexpr std::array characters{
95 []() constexpr noexcept
96 {
97 constexpr auto len = (Strings.length() + ... + 0);
98 std::array<char, len + 1> str{};
99 char* dst = str.data();
100 ((std::copy_n(first: Strings.cbegin(), n: Strings.length(), result: dst), dst += Strings.length()), ...);
101 str[len] = '\0';
102 return str;
103 }()};
104
105public:
106 //! @brief The splicing result. Converted from a sequence of characters.
107 static constexpr std::string_view value{characters.data(), characters.size() - 1};
108};
109//! @brief Get the result of splicing strings.
110//! @tparam Strings - target strings to be spliced
111template <const std::string_view&... Strings>
112inline constexpr auto concatString = ConcatString<Strings...>::value;
113
114//! @brief Check whether the target class is stateless.
115//! @tparam Cls - type of target class
116//! @return be stateless or not
117template <typename Cls>
118consteval bool isStatelessClass()
119{
120 return std::is_empty_v<Cls> || ((sizeof(Cls) == sizeof(void*)) && std::is_polymorphic_v<Cls>);
121}
122
123//! @brief Check whether the target value is part of the enumeration.
124//! @tparam Enum - type of enumeration
125//! @tparam Values - arguments of enumeration
126template <typename Enum, Enum... Values>
127requires std::is_enum_v<Enum>
128struct EnumCheck;
129//! @brief Check whether the target value is part of the enumeration.
130//! @tparam Enum - type of enumeration
131template <typename Enum>
132requires std::is_enum_v<Enum>
133struct EnumCheck<Enum>
134{
135 //! @brief Check whether it contains the value as an enumeration.
136 //! @return has or not
137 static constexpr bool has(const std::integral auto /*val*/) { return false; }
138};
139//! @brief Check whether the target value is part of the enumeration.
140//! @tparam Enum - type of enumeration
141//! @tparam Curr - current enumeration value
142//! @tparam Next - next enumeration value
143template <typename Enum, Enum Curr, Enum... Next>
144requires std::is_enum_v<Enum>
145struct EnumCheck<Enum, Curr, Next...> : private EnumCheck<Enum, Next...>
146{
147 //! @brief Check whether it contains the value as an enumeration.
148 //! @param val - target value
149 //! @return has or not
150 static constexpr bool has(const std::integral auto val)
151 {
152 return (static_cast<std::underlying_type_t<Enum>>(Curr) == val) || EnumCheck<Enum, Next...>::has(val);
153 }
154};
155
156//! @brief Pattern matching for the variant.
157//! @tparam Variant - type of target variant
158//! @tparam Cases - types of cases to match
159//! @param var - target variant
160//! @param cases - cases to match
161//! @return result of matching
162template <typename Variant, typename... Cases>
163constexpr decltype(auto) patternMatch(Variant&& var, Cases&&... cases)
164{
165 struct Overloaded : public Cases...
166 {
167 using Cases::operator()...;
168 };
169 return std::visit(Overloaded{std::forward<Cases>(cases)...}, std::forward<Variant>(var));
170}
171
172//! @brief Closure wrapper.
173//! @tparam Func - type of callable function
174//! @tparam Op - type of call operator
175//! @tparam Wrap - flag to indicate that further wrapping is required
176template <typename Func, typename Op = decltype(&Func::operator()), bool Wrap = (sizeof(Func) > (sizeof(void*) * 2U))>
177struct WrapClosure
178{
179 //! @brief Wrap operation.
180 //! @tparam Clos - type of closure
181 //! @param closure - target closure
182 //! @return original closure
183 template <typename Clos>
184 static constexpr auto&& wrap(Clos&& closure) noexcept
185 {
186 return std::forward<Clos>(closure);
187 }
188};
189//! @brief Closure wrapper. For the non-const member function.
190//! @tparam Func - type of callable function
191//! @tparam Ret - type of return value
192//! @tparam Obj - type of object to which the member belongs
193//! @tparam Args - type of function arguments
194template <typename Func, typename Ret, typename Obj, typename... Args>
195struct WrapClosure<Func, Ret (Obj::*)(Args...), true>
196{
197 //! @brief Wrap operation.
198 //! @tparam Clos - type of closure
199 //! @param closure - target closure
200 //! @return wrapped closure
201 template <typename Clos>
202 static auto wrap(Clos&& closure) // NOLINT(cppcoreguidelines-missing-std-forward)
203 {
204 return [sharedClosure = std::make_shared<Func>(std::forward<Clos>(closure))](Args&&... args) mutable
205 { return (*sharedClosure)(std::forward<Args>(args)...); };
206 }
207};
208//! @brief Closure wrapper. For the const member function.
209//! @tparam Func - type of callable function
210//! @tparam Ret - type of return value
211//! @tparam Obj - type of object to which the member belongs
212//! @tparam Args - type of function arguments
213template <typename Func, typename Ret, typename Obj, typename... Args>
214struct WrapClosure<Func, Ret (Obj::*)(Args...) const, true>
215{
216 //! @brief Wrap operation.
217 //! @tparam Clos - type of closure
218 //! @param closure - target closure
219 //! @return wrapped closure
220 template <typename Clos>
221 static auto wrap(Clos&& closure) // NOLINT(cppcoreguidelines-missing-std-forward)
222 {
223 return [sharedClosure = std::make_shared<Func>(std::forward<Clos>(closure))](Args&&... args)
224 { return (*sharedClosure)(std::forward<Args>(args)...); };
225 }
226};
227//! @brief Wrap closure further.
228//! @tparam Clos - type of closure
229//! @param closure - target closure
230//! @return wrapped closure
231template <typename Clos>
232inline auto wrapClosure(Clos&& closure)
233{
234 return WrapClosure<std::decay_t<Clos>>::wrap(std::forward<Clos>(closure));
235}
236
237//! @brief The instance manager that never calls the destructor. Use for the function-level static variables.
238//! @tparam Inst - type of instance
239template <typename Inst>
240class NoDestructor final
241{
242public:
243 //! @brief Construct a new NoDestructor object.
244 //! @tparam Args - type of constructor arguments
245 //! @param args - constructor arguments
246 template <typename... Args>
247 explicit NoDestructor(Args&&... args);
248 //! @brief Destroy the NoDestructor object.
249 ~NoDestructor() = default;
250 //! @brief Construct a new NoDestructor object.
251 NoDestructor(const NoDestructor&) = delete;
252 //! @brief Construct a new NoDestructor object.
253 NoDestructor(NoDestructor&&) noexcept = default;
254 //! @brief The operator (=) overloading of NoDestructor class.
255 //! @return reference of the NoDestructor object
256 NoDestructor& operator=(const NoDestructor&) = delete;
257 //! @brief The operator (=) overloading of NoDestructor class.
258 //! @return reference of the NoDestructor object
259 NoDestructor& operator=(NoDestructor&&) noexcept = default;
260
261 //! @brief Get the pointer of the instance.
262 //! @return pointer of the instance
263 Inst* get() noexcept;
264 //! @brief Get the const pointer of the instance.
265 //! @return const pointer of the instance
266 const Inst* get() const noexcept;
267
268private:
269 //! @brief Storage for the instance.
270 alignas(Inst) std::array<std::byte, sizeof(Inst)> storage{};
271
272 static_assert(
273 (sizeof(storage) >= sizeof(Inst)) // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
274 && (alignof(decltype(*reinterpret_cast<Inst*>(storage.data()))) >= alignof(Inst)));
275};
276// NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast)
277template <typename Inst>
278template <typename... Args>
279NoDestructor<Inst>::NoDestructor(Args&&... args)
280{
281 std::construct_at(reinterpret_cast<Inst*>(storage.data()), std::forward<Args>(args)...);
282}
283template <typename Inst>
284Inst* NoDestructor<Inst>::get() noexcept
285{
286 return reinterpret_cast<Inst*>(storage.data());
287}
288template <typename Inst>
289const Inst* NoDestructor<Inst>::get() const noexcept
290{
291 return reinterpret_cast<const Inst*>(storage.data());
292}
293// NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast)
294
295//! @brief Simple spin lock.
296class SpinLock
297{
298public:
299 //! @brief Acquire the lock.
300 void lock();
301 //! @brief Release the lock.
302 void unlock();
303 //! @brief Try to acquire the lock without blocking.
304 //! @return acquire or not
305 bool tryLock();
306
307private:
308 //! @brief The atomic flag used to implement the spin lock.
309 std::atomic_flag flag = ATOMIC_FLAG_INIT;
310};
311
312//! @brief Lock to control reading and writing.
313class ReadWriteLock
314{
315public:
316 //! @brief Acquire a read lock.
317 void readLock();
318 //! @brief Release a read lock.
319 void readUnlock();
320 //! @brief Acquire a write lock.
321 void writeLock();
322 //! @brief Release a write lock.
323 void writeUnlock();
324 //! @brief Enumerate specific lock modes.
325 enum class LockMode : std::uint8_t
326 {
327 //! @brief Read.
328 read,
329 //! @brief Write.
330 write
331 };
332
333private:
334 //! @brief Handling of shared and exclusive locks.
335 std::shared_mutex rwLock;
336 //! @brief Counter of readers that have acquired the shared lock.
337 std::atomic_uint_fast64_t reader{0};
338 //! @brief Counter of writers that have acquired the exclusive lock.
339 std::atomic_uint_fast64_t writer{0};
340 //! @brief Mutex for controlling counters.
341 mutable std::mutex mtx;
342 //! @brief The synchronization condition for counters. Use with mtx.
343 std::condition_variable cond;
344};
345
346//! @brief Manage the lifetime of a lock under control.
347class LockGuard
348{
349public:
350 //! @brief Alias for the lock mode.
351 using LockMode = ReadWriteLock::LockMode;
352 //! @brief Construct a new LockGuard object.
353 //! @param lock - object managed by the guard
354 //! @param mode - lock mode
355 LockGuard(ReadWriteLock& lock, const LockMode mode);
356 //! @brief Destroy the LockGuard object.
357 virtual ~LockGuard();
358 //! @brief Construct a new LockGuard object.
359 LockGuard(const LockGuard&) = delete;
360 //! @brief Construct a new LockGuard object.
361 LockGuard(LockGuard&&) = delete;
362 //! @brief The operator (=) overloading of LockGuard class.
363 //! @return reference of the LockGuard object
364 LockGuard& operator=(const LockGuard&) = delete;
365 //! @brief The operator (=) overloading of LockGuard class.
366 //! @return reference of the LockGuard object
367 LockGuard& operator=(LockGuard&&) = delete;
368
369private:
370 //! @brief Object managed by the guard.
371 ReadWriteLock& lock;
372 //! @brief Lock mode.
373 const LockMode mode{LockMode::read};
374};
375} // namespace common
376} // namespace utility
377