blob: 4235c8d24b6be6a228ba177ba02bc61f1ecf5e93 [file] [edit]
// 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