Branch data Line data Source code
1 : : /* 2 : : * Copyright (C) 2026 by Thun Lu. All rights reserved. 3 : : * Author: Thun Lu <thun.lu@zohomail.cn> 4 : : * Repo: https://github.com/thun-res/vlink 5 : : * _ __ __ _ __ 6 : : * | | / / / / (_) ____ / /__ 7 : : * | | / / / / / / / __ \ / //_/ 8 : : * | |/ / / /___ / / / / / / / ,< 9 : : * |___/ /_____/ /_/ /_/ /_/ /_/|_| 10 : : * 11 : : * Licensed under the Apache License, Version 2.0 (the "License"); 12 : : * you may not use this file except in compliance with the License. 13 : : * You may obtain a copy of the License at 14 : : * 15 : : * http://www.apache.org/licenses/LICENSE-2.0 16 : : * 17 : : * Unless required by applicable law or agreed to in writing, software 18 : : * distributed under the License is distributed on an "AS IS" BASIS, 19 : : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 : : * See the License for the specific language governing permissions and 21 : : * limitations under the License. 22 : : */ 23 : : 24 : : #include "./base/uuid.h" 25 : : 26 : : #include <algorithm> 27 : : #include <array> 28 : : #include <chrono> 29 : : #include <cstdint> 30 : : #include <functional> 31 : : #include <optional> 32 : : #include <ostream> 33 : : #include <random> 34 : : #include <string> 35 : : #include <string_view> 36 : : #include <vector> 37 : : 38 : : namespace vlink { 39 : : 40 : : static constexpr char kHexDigits[] = "0123456789abcdef"; 41 : : 42 : 16945 : static bool is_hex_char(char ch) noexcept { 43 [ + + + + : 16945 : return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F'); + + + + + + + + ] 44 : : } 45 : : 46 : 16941 : static uint8_t hex_to_nibble(char ch) noexcept { 47 [ + - + + ]: 16941 : if (ch >= '0' && ch <= '9') { 48 : 10604 : return static_cast<uint8_t>(ch - '0'); 49 : : } 50 : : 51 [ + + + - ]: 6337 : if (ch >= 'a' && ch <= 'f') { 52 : 6317 : return static_cast<uint8_t>(ch - 'a' + 10); 53 : : } 54 : : 55 [ + - + - ]: 20 : if (ch >= 'A' && ch <= 'F') { 56 : 20 : return static_cast<uint8_t>(ch - 'A' + 10); 57 : : } 58 : : 59 : : return 0U; // LCOV_EXCL_LINE GCOVR_EXCL_LINE 60 : : } 61 : : 62 : : // LCOV_EXCL_START GCOVR_EXCL_START 63 : : static std::mt19937::result_type fallback_seed_value() noexcept { 64 : : const auto count = std::chrono::high_resolution_clock::now().time_since_epoch().count(); 65 : : const auto unsigned_count = static_cast<uint64_t>(count); 66 : : const auto low = static_cast<std::mt19937::result_type>(unsigned_count & 0xFFFFFFFFULL); 67 : : const auto high = static_cast<std::mt19937::result_type>((unsigned_count >> 32U) & 0xFFFFFFFFULL); 68 : : 69 : : return static_cast<std::mt19937::result_type>(low ^ high); 70 : : } 71 : : // LCOV_EXCL_STOP GCOVR_EXCL_STOP 72 : : 73 : 2 : static std::mt19937 make_seeded_engine() noexcept { 74 : : try { 75 [ + - ]: 2 : std::random_device rd; 76 : 2 : std::array<uint32_t, 8> seed_data{}; 77 [ + - ]: 2 : std::generate(seed_data.begin(), seed_data.end(), std::ref(rd)); 78 [ + - ]: 2 : std::seed_seq seq(seed_data.begin(), seed_data.end()); 79 : : 80 [ + - ]: 2 : return std::mt19937(seq); 81 : 2 : } catch (...) { 82 : : return std::mt19937(fallback_seed_value()); // LCOV_EXCL_LINE GCOVR_EXCL_LINE 83 : : } // LCOV_EXCL_LINE GCOVR_EXCL_LINE 84 : : } 85 : : 86 : 2794 : static std::mt19937& thread_engine() noexcept { 87 [ + + ]: 2794 : thread_local std::mt19937 engine = make_seeded_engine(); 88 : : 89 : 2794 : return engine; 90 : : } 91 : : 92 : 2798 : static void fill_random_bytes(std::mt19937& engine, uint8_t* buffer, size_t count) noexcept { 93 [ + - - + ]: 2798 : if (count == 0U || buffer == nullptr) { 94 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE 95 : : } 96 : : 97 : 2798 : std::uniform_int_distribution<uint32_t> distribution; 98 : : 99 : 2798 : size_t i = 0U; 100 : : 101 [ + + ]: 14049 : while (count - i >= 4U) { 102 : 11251 : const uint32_t value = distribution(engine); 103 : 11251 : buffer[i] = static_cast<uint8_t>(value & 0xFFU); 104 : 11251 : buffer[i + 1U] = static_cast<uint8_t>((value >> 8U) & 0xFFU); 105 : 11251 : buffer[i + 2U] = static_cast<uint8_t>((value >> 16U) & 0xFFU); 106 : 11251 : buffer[i + 3U] = static_cast<uint8_t>((value >> 24U) & 0xFFU); 107 : 11251 : i += 4U; 108 : : } 109 : : 110 [ + + ]: 2798 : if (i < count) { 111 : 7 : uint32_t value = distribution(engine); 112 : : 113 [ + + ]: 22 : while (i < count) { 114 : 15 : buffer[i] = static_cast<uint8_t>(value & 0xFFU); 115 : 15 : value >>= 8U; 116 : 15 : ++i; 117 : : } 118 : : } 119 : : } 120 : : 121 : 270 : static void encode_hex(const uint8_t* bytes, size_t count, std::string& out) { 122 [ + - - + ]: 270 : if (count == 0U || bytes == nullptr) { 123 : : return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE 124 : : } 125 : : 126 [ + + ]: 4579 : for (size_t i = 0U; i < count; ++i) { 127 : 4309 : const uint8_t byte_value = bytes[i]; 128 : 4309 : out.push_back(kHexDigits[(byte_value >> 4U) & 0x0FU]); 129 : 4309 : out.push_back(kHexDigits[byte_value & 0x0FU]); 130 : : } 131 : : } 132 : : 133 : 538 : static std::string_view trim_braces(std::string_view str) noexcept { 134 [ + + + + : 538 : if (str.size() >= 2U && str.front() == '{' && str.back() == '}') { + - + + ] 135 : 5 : return str.substr(1U, str.size() - 2U); 136 : : } 137 : : 138 : 533 : return str; 139 : : } 140 : : 141 : 538 : static bool parse_into(std::string_view str, std::array<uint8_t, Uuid::kByteSize>* out) noexcept { 142 [ + + ]: 538 : if (str.size() < 2U) { 143 : 5 : return false; 144 : : } 145 : : 146 : 533 : bool first_digit = true; 147 : 533 : size_t index = 0U; 148 : 533 : std::array<uint8_t, Uuid::kByteSize> data{}; 149 : : 150 [ + + ]: 18579 : for (char ch : str) { 151 [ + + ]: 18051 : if (ch == '-') { 152 : 1105 : continue; 153 : : } 154 : : 155 [ + + + + : 16946 : if (index >= Uuid::kByteSize || !is_hex_char(ch)) { + + ] 156 : 5 : return false; 157 : : } 158 : : 159 [ + + ]: 16941 : if (first_digit) { 160 : 8472 : data[index] = static_cast<uint8_t>(hex_to_nibble(ch) << 4U); 161 : 8472 : first_digit = false; 162 : : } else { 163 : 8469 : data[index] = static_cast<uint8_t>(data[index] | hex_to_nibble(ch)); 164 : 8469 : ++index; 165 : 8469 : first_digit = true; 166 : : } 167 : : } 168 : : 169 [ + + - + ]: 528 : if (index != Uuid::kByteSize || !first_digit) { 170 : 3 : return false; 171 : : } 172 : : 173 [ + - ]: 525 : if (out != nullptr) { 174 : 525 : *out = data; 175 : : } 176 : : 177 : 525 : return true; 178 : : } 179 : : 180 : : // Uuid 181 : 266 : std::string Uuid::to_string() const noexcept { 182 : : try { 183 : 266 : std::string out; 184 [ + - ]: 266 : out.reserve(kStringSize); 185 : : 186 [ + + ]: 4522 : for (size_t i = 0U; i < kByteSize; ++i) { 187 [ + + + + : 4256 : if (i == 4U || i == 6U || i == 8U || i == 10U) { + + + + ] 188 [ + - ]: 1064 : out.push_back('-'); 189 : : } 190 : : 191 : 4256 : const uint8_t byte_value = data_[i]; 192 [ + - ]: 4256 : out.push_back(kHexDigits[(byte_value >> 4U) & 0x0FU]); 193 [ + - ]: 4256 : out.push_back(kHexDigits[byte_value & 0x0FU]); 194 : : } 195 : : 196 : 266 : return out; 197 : : } catch (...) { // LCOV_EXCL_LINE GCOVR_EXCL_LINE 198 : : return {}; // LCOV_EXCL_LINE GCOVR_EXCL_LINE 199 : : } // LCOV_EXCL_LINE GCOVR_EXCL_LINE 200 : : } 201 : : 202 : 263 : std::string Uuid::to_compact_string() const noexcept { 203 : : try { 204 : 263 : std::string out; 205 [ + - ]: 263 : out.reserve(kByteSize * 2U); 206 [ + - ]: 263 : encode_hex(data_.data(), kByteSize, out); 207 : : 208 : 263 : return out; 209 : : } catch (...) { // LCOV_EXCL_LINE GCOVR_EXCL_LINE 210 : : return {}; // LCOV_EXCL_LINE GCOVR_EXCL_LINE 211 : : } // LCOV_EXCL_LINE GCOVR_EXCL_LINE 212 : : } 213 : : 214 : 539 : std::optional<Uuid> Uuid::from_string(std::string_view str) noexcept { 215 [ + + + + : 539 : if (str.size() >= 2U && str.front() == '{' && str.back() != '}') { + + + + ] 216 : 1 : return std::nullopt; 217 : : } 218 : : 219 : 538 : std::array<uint8_t, kByteSize> data{}; 220 : : 221 [ + + ]: 538 : if (!parse_into(trim_braces(str), &data)) { 222 : 13 : return std::nullopt; 223 : : } 224 : : 225 : 525 : return Uuid{data}; 226 : : } 227 : : 228 : 0 : bool Uuid::is_valid(std::string_view str) noexcept { return from_string(str).has_value(); } 229 : : 230 : 27 : std::optional<Uuid> Uuid::from_string(const char* str) noexcept { 231 [ + + ]: 27 : if (str == nullptr) { 232 : 2 : return std::nullopt; 233 : : } 234 : : 235 : 25 : return from_string(std::string_view(str)); 236 : : } 237 : : 238 : 9 : bool Uuid::is_valid(const char* str) noexcept { return from_string(str).has_value(); } 239 : : 240 : 2773 : Uuid Uuid::generate_random() noexcept { return generate_random(thread_engine()); } 241 : : 242 : 2777 : Uuid Uuid::generate_random(std::mt19937& engine) noexcept { 243 : 2777 : std::array<uint8_t, kByteSize> data{}; 244 : 2777 : fill_random_bytes(engine, data.data(), kByteSize); 245 : : 246 : 2777 : data[6] = static_cast<uint8_t>((data[6] & 0x0FU) | 0x40U); 247 : 2777 : data[8] = static_cast<uint8_t>((data[8] & 0x3FU) | 0x80U); 248 : : 249 : 2777 : return Uuid{data}; 250 : : } 251 : : 252 : 15 : std::vector<Uuid::value_type> Uuid::random_bytes(size_t count) noexcept { 253 : : try { 254 [ + - ]: 15 : std::vector<value_type> buffer(count); 255 : : 256 [ + + ]: 15 : if (count > 0U) { 257 : 14 : fill_random_bytes(thread_engine(), buffer.data(), count); 258 : : } 259 : : 260 : 15 : return buffer; 261 : : } catch (...) { // LCOV_EXCL_LINE GCOVR_EXCL_LINE 262 : : return {}; // LCOV_EXCL_LINE GCOVR_EXCL_LINE 263 : : } // LCOV_EXCL_LINE GCOVR_EXCL_LINE 264 : : } 265 : : 266 : 8 : std::string Uuid::random_hex(size_t byte_count) noexcept { 267 : : try { 268 : 8 : std::string out; 269 : : 270 [ + + ]: 8 : if (byte_count == 0U) { 271 : 1 : return out; 272 : : } 273 : : 274 [ - + ]: 7 : if (byte_count > out.max_size() / 2U) { 275 : : return {}; // LCOV_EXCL_LINE GCOVR_EXCL_LINE 276 : : } 277 : : 278 [ + - ]: 7 : out.reserve(byte_count * 2U); 279 : : 280 [ + - ]: 7 : std::vector<value_type> buffer(byte_count); 281 : 7 : fill_random_bytes(thread_engine(), buffer.data(), byte_count); 282 [ + - ]: 7 : encode_hex(buffer.data(), byte_count, out); 283 : : 284 : 7 : return out; 285 : 8 : } catch (...) { 286 : : return {}; // LCOV_EXCL_LINE GCOVR_EXCL_LINE 287 : : } // LCOV_EXCL_LINE GCOVR_EXCL_LINE 288 : : } 289 : : 290 : 1 : std::ostream& operator<<(std::ostream& ostream, const Uuid& id) { 291 [ + - ]: 1 : ostream << id.to_string(); 292 : : 293 : 1 : return ostream; 294 : : } 295 : : 296 : : } // namespace vlink