LCOV - code coverage report
Current view: top level - src/base - uuid.cc (source / functions) Hit Total Coverage
Test: vlink Lines: 121 122 99.2 %
Date: 2026-06-27 19:56:04 Functions: 18 19 94.7 %
Branches: 101 128 78.9 %

           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

Generated by: LCOV version 1.14