VLink  2.0.0
A high-performance communication middleware
uuid.h
浏览该文件的文档.
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 /**
25  * @file uuid.h
26  * @brief Value-typed RFC 4122 UUID and project random-bytes primitive.
27  *
28  * @details
29  * @c vlink::Uuid stores 16 bytes in big-endian (network) order matching RFC 4122 section
30  * 4.1. The class is trivially comparable, parses and emits both the canonical 36-character
31  * hyphenated form and the 32-character compact form, and ships with a thread-local v4
32  * generator backed by @c std::mt19937.
33  *
34  * UUID variant and version reference:
35  *
36  * | Field | Enumerator | RFC 4122 meaning |
37  * | -------- | ------------------- | --------------------------------------------- |
38  * | Variant | @c kNcs | NCS backward compatibility (@c 0xxx) |
39  * | Variant | @c kRfc | RFC 4122 / DCE 1.1 (@c 10xx) |
40  * | Variant | @c kMicrosoft | Microsoft GUID (@c 110x) |
41  * | Variant | @c kReserved | Reserved (@c 111x) |
42  * | Version | @c kNone | Nil UUID or invalid version nibble |
43  * | Version | @c kTimeBased | v1 — gregorian time + node |
44  * | Version | @c kDceSecurity | v2 — DCE Security |
45  * | Version | @c kNameBasedMd5 | v3 — Name + MD5 |
46  * | Version | @c kRandomBased | v4 — Random / pseudo-random (this generator) |
47  * | Version | @c kNameBasedSha1 | v5 — Name + SHA-1 |
48  *
49  * Random v4 generation pipeline:
50  *
51  * @verbatim
52  * std::random_device x8 ---> std::seed_seq ---> std::mt19937 ---> uniform_int(uint32_t)
53  * |
54  * v
55  * byte extraction via shifts
56  * |
57  * v
58  * set variant (octet 8 = 10xxxxxx) + version (octet 6 = 0100xxxx)
59  * @endverbatim
60  *
61  * @note Bodies live in @c uuid.cc; only @c constexpr operations remain inline so
62  * @c Uuid stays a literal type for compile-time use.
63  */
64 
65 #pragma once
66 
67 #include <algorithm>
68 #include <array>
69 #include <cstddef>
70 #include <cstdint>
71 #include <functional>
72 #include <iterator>
73 #include <optional>
74 #include <ostream>
75 #include <random>
76 #include <string>
77 #include <string_view>
78 #include <type_traits>
79 #include <vector>
80 
81 #include "./macros.h"
82 
83 namespace vlink {
84 
85 /**
86  * @class Uuid
87  * @brief Value-typed RFC 4122 UUID.
88  *
89  * @details
90  * Stores 16 bytes in network byte order. Trivially copyable and comparable; provides
91  * canonical/compact text I/O and a v4 random generator built on top of a thread-local
92  * @c std::mt19937 engine.
93  */
94 class VLINK_EXPORT Uuid final {
95  public:
96  /**
97  * @brief Underlying byte type used by the 16-byte payload.
98  */
99  using value_type = uint8_t;
100 
101  /**
102  * @brief UUID payload length (16 bytes per RFC 4122).
103  */
104  static constexpr size_t kByteSize = 16U;
105 
106  /**
107  * @brief Canonical 36-character textual length: @c xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
108  */
109  static constexpr size_t kStringSize = 36U;
110 
111  /**
112  * @enum Variant
113  * @brief UUID variant inferred from the high bits of octet 8.
114  */
115  enum class Variant : uint8_t {
116  kNcs = 0, ///< NCS backward compatibility (@c 0xxx).
117  kRfc = 1, ///< RFC 4122 / DCE 1.1 (@c 10xx).
118  kMicrosoft = 2, ///< Microsoft GUID (@c 110x).
119  kReserved = 3, ///< Reserved for future use (@c 111x).
120  };
121 
122  /**
123  * @enum Version
124  * @brief UUID version inferred from the high nibble of octet 6.
125  */
126  enum class Version : uint8_t {
127  kNone = 0, ///< Nil UUID or invalid version nibble.
128  kTimeBased = 1, ///< v1 time-based.
129  kDceSecurity = 2, ///< v2 DCE Security.
130  kNameBasedMd5 = 3, ///< v3 Name-based MD5.
131  kRandomBased = 4, ///< v4 random (produced by @c generate_random).
132  kNameBasedSha1 = 5, ///< v5 Name-based SHA-1.
133  };
134 
135  /**
136  * @brief Default-constructs a nil UUID (all 16 bytes zero).
137  */
138  constexpr Uuid() noexcept;
139 
140  /**
141  * @brief Constructs from a 16-byte array.
142  *
143  * @param data Network-order UUID payload.
144  */
145  constexpr explicit Uuid(const std::array<value_type, kByteSize>& data) noexcept;
146 
147  /**
148  * @brief Constructs from a raw C array reference of exactly 16 bytes.
149  *
150  * @param arr Reference to a 16-byte array.
151  */
152  constexpr explicit Uuid(const value_type (&arr)[kByteSize]) noexcept;
153 
154  /**
155  * @brief Constructs from a forward-iterator range of exactly 16 bytes.
156  *
157  * @details
158  * When @c std::distance(first, last) is not @c 16 the resulting UUID is the nil value.
159  * A @c static_assert enforces forward-iterator capability so input-only iterators do
160  * not silently produce a misaligned UUID.
161  *
162  * @tparam ForwardIteratorT Forward iterator yielding @c uint8_t-convertible values.
163  * @param first Begin iterator.
164  * @param last End iterator.
165  */
166  template <typename ForwardIteratorT>
167  Uuid(ForwardIteratorT first, ForwardIteratorT last);
168 
169  /**
170  * @brief Returns the variant field inferred from octet 8.
171  *
172  * @return Variant enumerator.
173  */
174  [[nodiscard]] constexpr Variant variant() const noexcept;
175 
176  /**
177  * @brief Returns the version field inferred from the high nibble of octet 6.
178  *
179  * @return Version enumerator.
180  */
181  [[nodiscard]] constexpr Version version() const noexcept;
182 
183  /**
184  * @brief Reports whether every byte is zero (the nil UUID).
185  *
186  * @return @c true when the payload is all zeros.
187  */
188  [[nodiscard]] constexpr bool is_nil() const noexcept;
189 
190  /**
191  * @brief Provides read-only access to the underlying 16-byte payload.
192  *
193  * @return Const reference to the byte array.
194  */
195  [[nodiscard]] constexpr const std::array<value_type, kByteSize>& bytes() const noexcept;
196 
197  /**
198  * @brief Formats the UUID as the canonical 36-character lowercase hyphenated string.
199  *
200  * @return Canonical text representation.
201  */
202  [[nodiscard]] std::string to_string() const noexcept;
203 
204  /**
205  * @brief Formats the UUID as a 32-character lowercase hex string without hyphens.
206  *
207  * @return Compact text representation.
208  */
209  [[nodiscard]] std::string to_compact_string() const noexcept;
210 
211  /**
212  * @brief Swaps contents with @p other in @c noexcept fashion.
213  *
214  * @param other UUID to swap with.
215  */
216  void swap(Uuid& other) noexcept;
217 
218  /**
219  * @brief Validates whether @p str is a well-formed UUID literal.
220  *
221  * @details
222  * Accepts the canonical 36-character form, the 32-character compact form, and either
223  * form wrapped in an outer brace pair @c "{...}". Hyphen positions are not enforced:
224  * any 32 hex digits within the allowed shape pass.
225  *
226  * @param str Candidate string.
227  * @return @c true when @p str is valid.
228  */
229  [[nodiscard]] static bool is_valid(std::string_view str) noexcept;
230 
231  /**
232  * @brief Null-safe C-string overload of @c is_valid().
233  *
234  * @param str NUL-terminated candidate string, or @c nullptr.
235  * @return @c true when @p str is non-null and a valid UUID literal.
236  */
237  [[nodiscard]] static bool is_valid(const char* str) noexcept;
238 
239  /**
240  * @brief Parses a UUID literal and returns the resulting value.
241  *
242  * @details
243  * Accepts the same shapes as @c is_valid().
244  *
245  * @param str Candidate string.
246  * @return Parsed UUID, or @c std::nullopt on malformed input.
247  */
248  [[nodiscard]] static std::optional<Uuid> from_string(std::string_view str) noexcept;
249 
250  /**
251  * @brief Null-safe C-string overload of @c from_string().
252  *
253  * @param str NUL-terminated candidate string, or @c nullptr.
254  * @return Parsed UUID, or @c std::nullopt on malformed/null input.
255  */
256  [[nodiscard]] static std::optional<Uuid> from_string(const char* str) noexcept;
257 
258  /**
259  * @brief Generates a random v4 UUID using a thread-local seeded engine.
260  *
261  * @details
262  * The engine is lazily seeded on first use from eight @c std::random_device samples fed
263  * through a @c std::seed_seq, then reused for the rest of the thread's lifetime. Sets
264  * the RFC variant and v4 version bits before returning.
265  *
266  * @return Fresh v4 UUID.
267  */
268  [[nodiscard]] static Uuid generate_random() noexcept;
269 
270  /**
271  * @brief Generates a random v4 UUID from a caller-managed engine.
272  *
273  * @details
274  * Useful for deterministic test fixtures. Sets the RFC variant and v4 version bits
275  * identically to the no-argument overload.
276  *
277  * @param engine Caller-managed engine.
278  * @return Fresh v4 UUID.
279  */
280  [[nodiscard]] static Uuid generate_random(std::mt19937& engine) noexcept;
281 
282  /**
283  * @brief Produces @p count random-device-seeded pseudo-random bytes.
284  *
285  * @details
286  * Uses the same pipeline as @c generate_random(): @c std::seed_seq from eight
287  * @c std::random_device samples, then @c std::mt19937 plus
288  * @c std::uniform_int_distribution<uint32_t> emitting four bytes per draw. Byte
289  * extraction uses explicit shifts so the output is endian-deterministic. Returns
290  * an empty vector when @p count is @c 0 or allocation fails.
291  *
292  * @warning The underlying @c std::mt19937 engine is @b not a CSPRNG. Observing 624
293  * consecutive 32-bit outputs allows the engine state to be reconstructed. Do
294  * not use this for long-term secrets; prefer a dedicated CSPRNG (for example
295  * OpenSSL @c RAND_bytes).
296  *
297  * @param count Number of bytes to emit.
298  * @return Vector containing exactly @p count pseudo-random bytes.
299  */
300  [[nodiscard]] static std::vector<value_type> random_bytes(size_t count) noexcept;
301 
302  /**
303  * @brief Produces @p byte_count pseudo-random bytes encoded as a lowercase hex string.
304  *
305  * @details
306  * Length is always @c byte_count @c * @c 2. The default of 16 bytes matches the
307  * 128-bit auth-token width used by the proxy handshake. Returns an empty string when
308  * @p byte_count is @c 0 or allocation fails. Same non-CSPRNG caveat as
309  * @c random_bytes().
310  *
311  * @param byte_count Number of underlying bytes.
312  * @return Lowercase hex string.
313  */
314  [[nodiscard]] static std::string random_hex(size_t byte_count = 16U) noexcept;
315 
316  /**
317  * @brief Equality comparison over the 16-byte payload.
318  *
319  * @param lhs Left operand.
320  * @param rhs Right operand.
321  * @return @c true when both payloads compare byte-equal.
322  */
323  friend bool operator==(const Uuid& lhs, const Uuid& rhs) noexcept;
324 
325  /**
326  * @brief Inequality comparison over the 16-byte payload.
327  *
328  * @param lhs Left operand.
329  * @param rhs Right operand.
330  * @return @c true when the payloads differ in any byte.
331  */
332  friend bool operator!=(const Uuid& lhs, const Uuid& rhs) noexcept;
333 
334  /**
335  * @brief Lexicographic less-than comparison over the 16-byte payload.
336  *
337  * @param lhs Left operand.
338  * @param rhs Right operand.
339  * @return @c true when @p lhs sorts strictly before @p rhs.
340  */
341  friend bool operator<(const Uuid& lhs, const Uuid& rhs) noexcept;
342 
343  /**
344  * @brief Stream insertion delegating to @c to_string().
345  *
346  * @param ostream Output stream.
347  * @param id UUID to format.
348  * @return Reference to @p ostream.
349  */
350  VLINK_EXPORT friend std::ostream& operator<<(std::ostream& ostream, const Uuid& id);
351 
352  private:
353  std::array<value_type, kByteSize> data_{};
354 };
355 
356 ////////////////////////////////////////////////////////////////
357 /// Details
358 ////////////////////////////////////////////////////////////////
359 
360 template <typename ForwardIteratorT>
361 inline Uuid::Uuid(ForwardIteratorT first, ForwardIteratorT last) {
362  static_assert(
363  std::is_base_of_v<std::forward_iterator_tag, typename std::iterator_traits<ForwardIteratorT>::iterator_category>,
364  "Uuid range constructor requires at least a forward iterator");
365 
366  if (std::distance(first, last) == static_cast<std::ptrdiff_t>(kByteSize)) {
367  std::copy(first, last, data_.begin());
368  }
369 }
370 
371 inline constexpr Uuid::Uuid() noexcept = default;
372 
373 inline constexpr Uuid::Uuid(const std::array<value_type, kByteSize>& data) noexcept : data_{data} {}
374 
375 inline constexpr Uuid::Uuid(const value_type (&arr)[kByteSize]) noexcept {
376  for (size_t i = 0U; i < kByteSize; ++i) {
377  data_[i] = arr[i];
378  }
379 }
380 
381 inline constexpr Uuid::Variant Uuid::variant() const noexcept {
382  const uint8_t octet = data_[8];
383 
384  if ((octet & 0x80U) == 0x00U) {
385  return Variant::kNcs;
386  }
387 
388  if ((octet & 0xC0U) == 0x80U) {
389  return Variant::kRfc;
390  }
391 
392  if ((octet & 0xE0U) == 0xC0U) {
393  return Variant::kMicrosoft;
394  }
395 
396  return Variant::kReserved;
397 }
398 
399 inline constexpr Uuid::Version Uuid::version() const noexcept {
400  switch (data_[6] & 0xF0U) {
401  case 0x10U:
402  return Version::kTimeBased;
403  case 0x20U:
404  return Version::kDceSecurity;
405  case 0x30U:
406  return Version::kNameBasedMd5;
407  case 0x40U:
408  return Version::kRandomBased;
409  case 0x50U:
411  default:
412  return Version::kNone;
413  }
414 }
415 
416 inline constexpr bool Uuid::is_nil() const noexcept {
417  for (uint8_t byte_value : data_) {
418  if (byte_value != 0U) {
419  return false;
420  }
421  }
422 
423  return true;
424 }
425 
426 inline constexpr const std::array<Uuid::value_type, Uuid::kByteSize>& Uuid::bytes() const noexcept { return data_; }
427 
428 inline void Uuid::swap(Uuid& other) noexcept { data_.swap(other.data_); }
429 
430 inline bool operator==(const Uuid& lhs, const Uuid& rhs) noexcept { return lhs.data_ == rhs.data_; }
431 
432 inline bool operator!=(const Uuid& lhs, const Uuid& rhs) noexcept { return lhs.data_ != rhs.data_; }
433 
434 inline bool operator<(const Uuid& lhs, const Uuid& rhs) noexcept { return lhs.data_ < rhs.data_; }
435 
436 } // namespace vlink
437 
438 namespace std {
439 
440 /**
441  * @brief @c std::hash specialisation so @c vlink::Uuid can be used inside unordered containers.
442  */
443 template <>
444 struct hash<vlink::Uuid> {
445  size_t operator()(const vlink::Uuid& id) const noexcept {
446  const auto& data = id.bytes();
447  size_t result = 0U;
448 
449  for (uint8_t byte_value : data) {
450  result = (result * 131U) + static_cast<size_t>(byte_value);
451  }
452 
453  return result;
454  }
455 };
456 
457 } // namespace std
Cross-platform macros for visibility, branch hints, copy prevention, singletons and string helpers.
#define VLINK_EXPORT
Definition: macros.h:81