| // 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. |
| // |
| |
| #include "uuid/uuid.h" |
| |
| #include <net/if.h> |
| #include <netinet/in.h> |
| #include <sys/ioctl.h> |
| #include <sys/socket.h> |
| #include <unistd.h> |
| |
| #include <cstdint> |
| #include <string> |
| |
| #include "absl/base/no_destructor.h" |
| #include "absl/log/check.h" |
| #include "absl/log/log.h" |
| #include "absl/random/distributions.h" |
| #include "absl/random/random.h" |
| #include "absl/status/status.h" |
| #include "absl/status/statusor.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/string_view.h" |
| #include "riegeli/endian/endian_reading.h" |
| #include "riegeli/endian/endian_writing.h" |
| |
| namespace credentio { |
| |
| namespace { |
| class UuidGeneratorImpl : public UuidGenerator { |
| public: |
| // Generates a V4 UUID as documented in |
| // https://tools.ietf.org/html/rfc4122#section-4.4 |
| Uuid Generate() const override { |
| absl::BitGen gen; |
| const auto lo = absl::Uniform<uint64_t>(gen); |
| const auto hi = absl::Uniform<uint64_t>(gen); |
| return Uuid((lo & 0x0fffffffffffffffUL) | 0x4000000000000000UL, |
| (hi & 0xffffffffffffbfffUL) | 0x0000000000008000UL); |
| }; |
| }; |
| } // namespace |
| |
| //////////////////////////////////////////////////////////////////////// |
| // Uuid and related helpers |
| |
| // Canonical Uids. |
| const Uuid Uuid::kInvalid; |
| |
| // We reserve the node 00:00:00:00:00:00, time [0, 999] for invalid |
| // Uuids (constants) |
| const Uuid Uuid::kValidMin(0, ValidMinLo()); |
| const Uuid Uuid::kValidMax(~0ULL, ~0ULL); |
| |
| std::string Uuid::ToString() const { |
| if (*this == kInvalid) return std::string(kInvalidRepr); |
| |
| auto to_hex = [](uint64_t v, int num_chars, char* out) { |
| static constexpr char hex_char[] = {'0', '1', '2', '3', '4', '5', '6', '7', |
| '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; |
| for (int i = num_chars - 1; i >= 0; --i) { |
| *out++ = hex_char[(v >> (i * 4)) & 0xf]; |
| } |
| }; |
| uint32_t time_low = low64() & 0xffffffff; |
| uint32_t time_mid = (low64() >> 32) & 0xffff; |
| uint32_t time_high = (low64() >> 48) & 0xffff; |
| uint32_t sequence = high64() & 0xffff; |
| uint64_t node = (high64() >> 16) & 0xffffffffffffULL; |
| char buf[36]; |
| char* ptr = buf; |
| to_hex(time_low, 8, ptr); |
| ptr += 8; |
| *ptr++ = '-'; |
| to_hex(time_mid, 4, ptr); |
| ptr += 4; |
| *ptr++ = '-'; |
| to_hex(time_high, 4, ptr); |
| ptr += 4; |
| *ptr++ = '-'; |
| to_hex(sequence, 4, ptr); |
| ptr += 4; |
| *ptr++ = '-'; |
| to_hex(node, 12, ptr); |
| return std::string(buf, 36); |
| } |
| |
| absl::StatusOr<Uuid> Uuid::FromString(absl::string_view s) { |
| Uuid uuid; |
| if (!ParseFromString(s, &uuid)) { |
| return absl::InvalidArgumentError( |
| absl::StrCat("Syntax error: string '", s, "' is not a Uuid")); |
| } |
| return uuid; |
| } |
| |
| std::string Uuid::ToProtoBytes() const { |
| std::string s; |
| ToProtoBytes(&s); |
| return s; |
| } |
| |
| // The UUid byte format is a mixed-endian format specified by RFC 4122. |
| void Uuid::ToProtoBytes(std::string* s) const { |
| // Special case for kInvalidUID, which is represented as the empty string. |
| if (*this == kInvalid) { |
| s->assign(""); |
| return; |
| } |
| |
| char bytes[16]; |
| uint32_t time_low = low64() & 0xffffffff; |
| uint16_t time_mid = (low64() >> 32) & 0xffff; |
| uint16_t time_hi_and_version = (low64() >> 48) & 0xffff; |
| uint16_t clock_seq = high64() & 0xffff; |
| uint16_t node_low = (high64() >> 16) & 0xffff; |
| uint32_t node_hi = (high64() >> 32) & 0xffffffff; |
| |
| riegeli::WriteBigEndian<uint32_t>(time_low, &bytes[0]); |
| riegeli::WriteBigEndian<uint16_t>(time_mid, &bytes[4]); |
| riegeli::WriteBigEndian<uint16_t>(time_hi_and_version, &bytes[6]); |
| riegeli::WriteBigEndian<uint16_t>(clock_seq, &bytes[8]); |
| riegeli::WriteBigEndian<uint32_t>(node_hi, &bytes[10]); |
| riegeli::WriteBigEndian<uint16_t>(node_low, &bytes[14]); |
| s->assign(bytes, 16); |
| } |
| |
| absl::StatusOr<Uuid> Uuid::FromProtoBytes(absl::string_view bytes) { |
| if (bytes.empty()) { |
| return Uuid(kInvalid); |
| } else if (bytes.size() == 16) { |
| const char* data = bytes.data(); |
| uint64_t time_low = riegeli::ReadBigEndian<uint32_t>(&data[0]); |
| uint64_t time_mid = riegeli::ReadBigEndian<uint16_t>(&data[4]); |
| uint64_t time_hi_and_version = riegeli::ReadBigEndian<uint16_t>(&data[6]); |
| uint64_t clock_seq = riegeli::ReadBigEndian<uint16_t>(&data[8]); |
| uint64_t node_hi = riegeli::ReadBigEndian<uint32_t>(&data[10]); |
| uint64_t node_low = riegeli::ReadBigEndian<uint16_t>(&data[14]); |
| uint64_t low64 = (time_hi_and_version << 48) | (time_mid << 32) | time_low; |
| uint64_t high64 = (node_hi << 32) | (node_low << 16) | clock_seq; |
| return Uuid(high64, low64); |
| } |
| return ::absl::InvalidArgumentError("Syntax error: bytes are not a Uuid"); |
| } |
| |
| bool Uuid::IsValid() const { return kValidMin <= *this && *this <= kValidMax; } |
| |
| bool Uuid::IsMagic() const { return high_ == 0 && low_ < ValidMinLo(); } |
| |
| const UuidGenerator& UuidGenerator::Default() { |
| static const absl::NoDestructor<UuidGeneratorImpl> kDefaultUuidGenerator; |
| return *kDefaultUuidGenerator; |
| } |
| |
| } // namespace credentio |