| // Copyright 2026 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| |
| // A Uuid is an identifier, that is unique over all space and time. The default |
| // implementation is based on UUID (DCE version), which uses the Ethernet MAC |
| // address of the machine, and a timestamp (which we replace with a sequence |
| // number). |
| // |
| // http://en.wikipedia.org/wiki/Universally_unique_identifier |
| // http://tools.ietf.org/html/rfc4122.html |
| // |
| // The reason to use DCE is that it is based on the physical |
| // address, which may aid debugging. |
| |
| #ifndef THIRD_PARTY_CREDENTIO_UUID_UUID_H_ |
| #define THIRD_PARTY_CREDENTIO_UUID_UUID_H_ |
| |
| #include <cstdint> |
| #include <ostream> |
| #include <string> |
| |
| #include "absl/log/check.h" |
| #include "absl/numeric/int128.h" |
| #include "absl/status/statusor.h" |
| #include "absl/strings/string_view.h" |
| |
| namespace credentio { |
| |
| // Uuid is the type of unique identifiers. |
| class Uuid { |
| public: |
| // In addition to the following constructors, the default copy constructor and |
| // assignment operator are also allowed. |
| // Uuids are not POD, because the default constructor initializes them to |
| // kInvalid. However, the Google Style Guide allows static Uuid constants |
| // because Uuid constructors are all constexpr, and the type has no |
| // destructors. |
| constexpr Uuid() : high_(0), low_(InvalidMinLo()) {} // equal to kInvalid |
| explicit constexpr Uuid(uint64_t low) : high_(0), low_(low) {} |
| constexpr Uuid(uint64_t high, uint64_t low) : high_(high), low_(low) {} |
| constexpr explicit Uuid(absl::uint128 raw) |
| : Uuid(absl::Uint128High64(raw), absl::Uint128Low64(raw)) {} |
| |
| bool IsValid() const; |
| |
| // Conversion to/from the ASCII form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. The |
| // format uses the following ABNF, where each numerical component is |
| // represented in hex, with leading zeros (although the FromString variants |
| // will accept input without leading zeros). The 'x's above show the maximum |
| // length of each of those components. |
| // |
| // UUID = time-low "-" time-mid "-" time-high-and-version "-" |
| // clock-seq-and-reserved clock-seq-low "-" node |
| // |
| // FromString returns INVALID_ARGUMENT, and FromStringOrDie CHECK-fails if |
| // there is a syntax error. |
| std::string ToString() const; |
| static absl::StatusOr<Uuid> FromString(absl::string_view s); |
| static constexpr Uuid FromStringOrDie(absl::string_view s) { |
| Uuid uuid; |
| CHECK(Uuid::ParseFromString(s, &uuid)) |
| << "Syntax error: string '" << s << "' is not a Uuid"; |
| return uuid; |
| } |
| |
| absl::uint128 ToRawNumber() const { return absl::MakeUint128(high_, low_); } |
| uint64_t high64() const { return high_; } |
| uint64_t low64() const { return low_; } |
| |
| // Conversion to/from 16-byte binary strings. Layout and byte order |
| // are specified by RFC 4122. |
| std::string ToProtoBytes() const; |
| void ToProtoBytes(std::string* s) const; |
| |
| // Convert proto string to Uuid. Returns INVALID_ARGUMENT if the Uuid could |
| // not be parsed correctly. |
| static absl::StatusOr<Uuid> FromProtoBytes(absl::string_view bytes); |
| |
| // Canonical Uuids |
| static const Uuid kInvalid; // Represents errors. |
| static constexpr absl::string_view kInvalidRepr = |
| "Uid::kInvalid"; // String representation of kInvalid. |
| |
| // We reserve 999 in invalid Uuids (in addition to kInvalid) that can be |
| // used as special sentinel values that will be different from any id returned |
| // by a Uuid generator. Magic values can be declared as static compile-time |
| // constants. |
| template <uint64_t k> |
| static constexpr Uuid Magic() { |
| static_assert(k < ValidMinLo(), "Argument too large"); |
| static_assert(k > InvalidMinLo(), "Argument too small"); |
| return Uuid(k); |
| } |
| bool IsMagic() const; |
| |
| // Range of valid Uuids |
| static const Uuid kValidMin; |
| static const Uuid kValidMax; |
| |
| template <typename H> |
| friend H AbslHashValue(H h, const Uuid& uuid) { |
| return H::combine(std::move(h), uuid.high_, uuid.low_); |
| } |
| |
| private: |
| // Underlying parsing function for ASCII form conversions. |
| static constexpr bool ParseFromString(absl::string_view s, Uuid* uuid) { |
| auto consume_hex = [](int max_chars, absl::string_view* input, |
| uint64_t* res) { |
| *res = 0; |
| int count = 0; |
| while (count < max_chars && !input->empty()) { |
| char c = input->front(); |
| uint32_t v = 0; |
| if (c >= '0' && c <= '9') { |
| v = c - '0'; |
| } else if (c >= 'a' && c <= 'f') { |
| v = c - 'a' + 10; |
| } else if (c >= 'A' && c <= 'F') { |
| v = c - 'A' + 10; |
| } else { |
| break; |
| } |
| *res = (*res << 4) + v; |
| input->remove_prefix(1); |
| ++count; |
| } |
| return count > 0; |
| }; |
| auto consume_char = [](char check_char, absl::string_view* input) { |
| if (input->empty() || input->front() != check_char) return false; |
| input->remove_prefix(1); |
| return true; |
| }; |
| |
| if (s == kInvalidRepr) { |
| // Equal to kInvalid (canonical value cannot be used here due to constexpr |
| // restrictions). |
| *uuid = Uuid(); |
| return true; |
| } |
| |
| absl::string_view input(s); |
| uint64_t time_low = 0, time_mid = 0, time_high = 0, sequence = 0, node = 0; |
| bool valid = true; |
| valid &= consume_hex(8, &input, &time_low); |
| valid &= consume_char('-', &input); |
| valid &= consume_hex(4, &input, &time_mid); |
| valid &= consume_char('-', &input); |
| valid &= consume_hex(4, &input, &time_high); |
| valid &= consume_char('-', &input); |
| valid &= consume_hex(4, &input, &sequence); |
| valid &= consume_char('-', &input); |
| valid &= consume_hex(12, &input, &node); |
| valid &= input.empty(); |
| |
| if (!valid) { |
| return false; |
| } |
| |
| const uint64_t high64 = |
| ((node & 0xffffffffffffULL) << 16) | (sequence & 0xffffULL); |
| const uint64_t low64 = ((time_high & 0xffffULL) << 48) | |
| ((time_mid & 0xffffULL) << 32) | |
| (time_low & 0xffffffffULL); |
| *uuid = Uuid(high64, low64); |
| return true; |
| } |
| |
| // We reserve the node 00:00:00:00:00:00, time [0, 999] for invalid |
| // Uuids (constants) |
| static constexpr uint64_t ValidMinLo() { return 1000; } |
| static constexpr uint64_t InvalidMinLo() { return 0; } |
| |
| uint64_t high_{0}; |
| uint64_t low_{0}; |
| }; |
| |
| // Comparison operators. |
| inline bool operator==(Uuid a, Uuid b) { |
| return a.high64() == b.high64() && a.low64() == b.low64(); |
| } |
| inline bool operator!=(Uuid a, Uuid b) { return !(a == b); } |
| inline bool operator<(Uuid a, Uuid b) { |
| return a.high64() < b.high64() || |
| (a.high64() == b.high64() && a.low64() < b.low64()); |
| } |
| inline bool operator<=(Uuid a, Uuid b) { |
| return a.high64() < b.high64() || |
| (a.high64() == b.high64() && a.low64() <= b.low64()); |
| } |
| |
| // Formatting. |
| inline std::ostream& operator<<(std::ostream& o, Uuid id) { |
| return o << id.ToString(); |
| } |
| |
| class UuidGenerator { |
| public: |
| virtual ~UuidGenerator() = default; |
| virtual Uuid Generate() const = 0; |
| // Generates a V4 UUID as documented in |
| // https://tools.ietf.org/html/rfc4122#section-4.4 |
| static const UuidGenerator& Default(); |
| }; |
| |
| } // namespace credentio |
| |
| #endif // THIRD_PARTY_CREDENTIO_UUID_UUID_H_ |