| // 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 |