1//! @file common.cpp
2//! @author ryftchen
3//! @brief The definitions (common) in the utility module.
4//! @version 0.1.0
5//! @copyright Copyright (c) 2022-2025 ryftchen. All rights reserved.
6
7#include "common.hpp"
8
9#include <cstdarg>
10#include <vector>
11
12namespace utility::common
13{
14//! @brief Function version number.
15//! @return version number (major.minor.patch)
16const char* version() noexcept
17{
18 static const char* const ver = "0.1.0";
19 return ver;
20}
21
22//! @brief Anonymous namespace.
23inline namespace
24{
25//! @brief The Base64 alphabet.
26constexpr std::string_view base64Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
27 "abcdefghijklmnopqrstuvwxyz"
28 "0123456789+/";
29} // namespace
30
31//! @brief The Brian-Kernighan Dennis-Ritchie hash function.
32//! @param str - input data
33//! @return hash value
34std::size_t bkdrHash(const char* str) noexcept
35{
36 if (!str)
37 {
38 return 0;
39 }
40
41 std::size_t hash = 0;
42 while (*str)
43 {
44 hash = hash * bkdrHashSeed + (*str++);
45 }
46 return hash & bkdrHashMask;
47}
48
49// NOLINTBEGIN(readability-magic-numbers)
50//! @brief Base64 encoding.
51//! @param data - decoded data
52//! @return encoded data
53std::string base64Encode(const std::string_view data)
54{
55 std::size_t counter = 0;
56 std::size_t offset = 0;
57 std::uint32_t bitStream = 0;
58 std::string encoded{};
59
60 for (const auto c : data)
61 {
62 const auto numVal = static_cast<unsigned int>(static_cast<unsigned char>(c));
63 offset = 16 - counter % 3 * 8;
64 bitStream += numVal << offset;
65 switch (offset)
66 {
67 case 16:
68 encoded += base64Alphabet.at(pos: (bitStream >> 18) & 0x3F);
69 break;
70 case 8:
71 encoded += base64Alphabet.at(pos: (bitStream >> 12) & 0x3F);
72 break;
73 case 0:
74 if (counter != 3)
75 {
76 encoded += base64Alphabet.at(pos: (bitStream >> 6) & 0x3F);
77 encoded += base64Alphabet.at(pos: bitStream & 0x3F);
78 bitStream = 0;
79 }
80 break;
81 default:
82 break;
83 }
84 ++counter;
85 }
86
87 switch (offset)
88 {
89 case 16:
90 encoded += base64Alphabet.at(pos: (bitStream >> 12) & 0x3F);
91 encoded += "==";
92 break;
93 case 8:
94 encoded += base64Alphabet.at(pos: (bitStream >> 6) & 0x3F);
95 encoded += '=';
96 break;
97 default:
98 break;
99 }
100 return encoded;
101}
102
103//! @brief Base64 decoding.
104//! @param data - encoded data
105//! @return decoded data
106std::string base64Decode(const std::string_view data)
107{
108 std::size_t counter = 0;
109 std::size_t offset = 0;
110 std::uint32_t bitStream = 0;
111 std::string decoded{};
112
113 for (const auto c : data)
114 {
115 const auto uc = static_cast<unsigned char>(c);
116 if (const auto numVal = base64Alphabet.find(c: uc); numVal != std::string::npos)
117 {
118 offset = 18 - counter % 4 * 6;
119 bitStream += numVal << offset;
120 switch (offset)
121 {
122 case 12:
123 decoded += static_cast<char>((bitStream >> 16) & 0xFF);
124 break;
125 case 6:
126 decoded += static_cast<char>((bitStream >> 8) & 0xFF);
127 break;
128 case 0:
129 if (counter != 4)
130 {
131 decoded += static_cast<char>(bitStream & 0xFF);
132 bitStream = 0;
133 }
134 break;
135 default:
136 break;
137 }
138 }
139 else if (uc != '=')
140 {
141 throw std::runtime_error{"Invalid base64 encoded data."};
142 }
143 ++counter;
144 }
145 return decoded;
146}
147// NOLINTEND(readability-magic-numbers)
148
149//! @brief Format as a string (printf style).
150//! @param fmt - null-terminated multibyte string specifying how to interpret the data
151//! @param ... - arguments
152//! @return string after formatting
153std::string printfString(const char* const fmt, ...) // NOLINT(cert-dcl50-cpp)
154{
155 if (!fmt)
156 {
157 return {};
158 }
159
160 std::va_list argList{};
161 ::va_start(argList, fmt);
162 const int reservedSize = std::vsnprintf(s: nullptr, maxlen: 0, format: fmt, arg: argList);
163 ::va_end(argList);
164 if (reservedSize < 0)
165 {
166 throw std::runtime_error{"Unable to reserve size for formatting string."};
167 }
168
169 ::va_start(argList, fmt);
170 std::vector<char> buffer(reservedSize + 1);
171 std::vsnprintf(s: buffer.data(), maxlen: reservedSize + 1, format: fmt, arg: argList);
172 ::va_end(argList);
173 return std::string{buffer.cbegin(), buffer.cbegin() + reservedSize};
174}
175
176void SpinLock::lock()
177{
178 while (flag.test_and_set(m: std::memory_order_acquire))
179 {
180 std::this_thread::yield();
181 }
182}
183
184void SpinLock::unlock()
185{
186 flag.clear(m: std::memory_order_release);
187}
188
189bool SpinLock::tryLock()
190{
191 return !flag.test_and_set(m: std::memory_order_acquire);
192}
193
194void ReadWriteLock::readLock()
195{
196 const std::shared_lock<std::shared_mutex> rLock(rwLock);
197 if (std::unique_lock<std::mutex> lock(mtx); true)
198 {
199 cond.wait(lock&: lock, p: [this]() { return writer.load() == 0; });
200 reader.fetch_add(i: 1);
201 }
202}
203
204void ReadWriteLock::readUnlock()
205{
206 std::unique_lock<std::mutex> lock(mtx);
207 reader.fetch_sub(i: 1);
208 lock.unlock();
209 cond.notify_all();
210}
211
212void ReadWriteLock::writeLock()
213{
214 const std::unique_lock<std::shared_mutex> wLock(rwLock);
215 if (std::unique_lock<std::mutex> lock(mtx); true)
216 {
217 cond.wait(lock&: lock, p: [this]() { return (reader.load() == 0) && (writer.load() == 0); });
218 writer.fetch_add(i: 1);
219 }
220}
221
222void ReadWriteLock::writeUnlock()
223{
224 std::unique_lock<std::mutex> lock(mtx);
225 writer.fetch_sub(i: 1);
226 lock.unlock();
227 cond.notify_all();
228}
229
230LockGuard::LockGuard(ReadWriteLock& lock, const LockMode mode) : lock{lock}, mode{mode}
231{
232 switch (mode)
233 {
234 case LockMode::read:
235 lock.readLock();
236 break;
237 case LockMode::write:
238 lock.writeLock();
239 break;
240 default:
241 break;
242 }
243}
244
245LockGuard::~LockGuard()
246{
247 switch (mode)
248 {
249 case LockMode::read:
250 lock.readUnlock();
251 break;
252 case LockMode::write:
253 lock.writeUnlock();
254 break;
255 default:
256 break;
257 }
258}
259} // namespace utility::common
260