blob: 289468d7dd0a92e5036509de53870a01d11ad78e [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/lib/uuid/uuid.h"
#include <stddef.h>
#include <optional>
#include <ostream>
#include "src/lib/fxl/strings/string_printf.h"
#if defined(__Fuchsia__)
#include <zircon/syscalls.h>
#else
#include <algorithm>
#include <random>
#endif
namespace uuid {
namespace {
inline bool IsHexDigit(char c) {
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');
}
inline bool IsLowerHexDigit(char c) { return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'); }
bool IsValidInternal(const std::string& guid, bool strict) {
constexpr size_t kUUIDLength = 36U;
if (guid.length() != kUUIDLength)
return false;
for (size_t i = 0; i < guid.length(); ++i) {
char current = guid[i];
if (i == 8 || i == 13 || i == 18 || i == 23) {
if (current != '-')
return false;
} else {
if ((strict && !IsLowerHexDigit(current)) || !IsHexDigit(current))
return false;
}
}
return true;
}
void FillRandomly(RawUuid* raw) {
#if defined(__Fuchsia__)
zx_cprng_draw(raw, kUuidSize);
#else
std::random_device rd;
auto* begin = reinterpret_cast<std::random_device::result_type*>(raw);
auto* end = begin + kUuidSize / sizeof(std::random_device::result_type);
std::generate(begin, end, std::ref(rd));
#endif
}
} // namespace
Uuid Uuid::Generate() {
// We generate a 128-bit (pseudo) random UUID in the form of version 4 as described
// in RFC 4122, section 4.4.
// Generate 16 random bytes.
Uuid result;
FillRandomly(&result.raw_);
// Set the version field (bits 12 through 15 of |time_hi_and_version|) to 4.
result.raw_.time_hi_and_version = (result.raw_.time_hi_and_version & 0x0fffu) | 0x4000u;
// Set the reserved bits (bits 6 and 7) of |clock_seq_hi_and_reserved| to zero
// and one, respectively.
result.raw_.clock_seq_hi_and_reserved = (result.raw_.clock_seq_hi_and_reserved & 0x3fu) | 0x80u;
// Return the UUID.
return result;
}
std::optional<uuid::Uuid> Uuid::FromString(std::string_view uuid) {
RawUuid guid;
static constexpr int kUuidStringSize = 36;
if (uuid.size() != kUuidStringSize) {
// String can't be a valid UUID.
return std::nullopt;
}
// sscanf() will parse extra dashes as negative numbers (e.g. -ab => "negative 0xab").
// Check there's the expected number of dashes.
int num_dashes = 0;
for (char i : uuid) {
if (i == '-') {
num_dashes++;
}
}
if (num_dashes != 4) {
// We expect exactly 4 dashes.
return std::nullopt;
}
int matched = sscanf(uuid.data(), "%8x-%4hx-%4hx-%2hhx%2hhx-%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx",
&guid.time_low, &guid.time_mid, &guid.time_hi_and_version,
&guid.clock_seq_hi_and_reserved, &guid.clock_seq_low, &guid.node[0],
&guid.node[1], &guid.node[2], &guid.node[3], &guid.node[4], &guid.node[5]);
if (matched != 11) {
// Didn't match all the fields in sscanf() above.
return std::nullopt;
}
return uuid::Uuid(guid);
}
std::string Uuid::ToString() const {
// Print the string.
return fxl::StringPrintf("%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", raw_.time_low,
raw_.time_mid, raw_.time_hi_and_version, raw_.clock_seq_hi_and_reserved,
raw_.clock_seq_low, raw_.node[0], raw_.node[1], raw_.node[2],
raw_.node[3], raw_.node[4], raw_.node[5]);
}
std::ostream& operator<<(std::ostream& out, const Uuid& uuid) { return out << uuid.ToString(); }
std::string Generate() { return Uuid::Generate().ToString(); }
bool IsValid(const std::string& guid) { return IsValidInternal(guid, /*strict=*/false); }
bool IsValidOutputString(const std::string& guid) { return IsValidInternal(guid, /*strict=*/true); }
} // namespace uuid