blob: 0561b699a754df3e715a564f09804c04d83a152e [file]
// 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_