| //===-- UUID.cpp ----------------------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "lldb/Utility/UUID.h" | |
| #include "lldb/Utility/Stream.h" | |
| #include "llvm/ADT/StringRef.h" | |
| #include "llvm/Support/Format.h" | |
| #include "llvm/Support/RandomNumberGenerator.h" | |
| #include <cctype> | |
| #include <chrono> | |
| #include <climits> | |
| #include <cstdint> | |
| #include <cstdio> | |
| #include <cstring> | |
| #include <random> | |
| using namespace lldb_private; | |
| // Whether to put a separator after count uuid bytes. | |
| // For the first 16 bytes we follow the traditional UUID format. After that, we | |
| // simply put a dash after every 6 bytes. | |
| static inline bool separate(size_t count) { | |
| if (count >= 10) | |
| return (count - 10) % 6 == 0; | |
| switch (count) { | |
| case 4: | |
| case 6: | |
| case 8: | |
| return true; | |
| default: | |
| return false; | |
| } | |
| } | |
| UUID::UUID(UUID::CvRecordPdb70 debug_info) { | |
| llvm::sys::swapByteOrder(debug_info.Uuid.Data1); | |
| llvm::sys::swapByteOrder(debug_info.Uuid.Data2); | |
| llvm::sys::swapByteOrder(debug_info.Uuid.Data3); | |
| llvm::sys::swapByteOrder(debug_info.Age); | |
| if (debug_info.Age) | |
| *this = UUID(&debug_info, sizeof(debug_info)); | |
| else | |
| *this = UUID(&debug_info.Uuid, sizeof(debug_info.Uuid)); | |
| } | |
| std::string UUID::GetAsString(llvm::StringRef separator) const { | |
| std::string result; | |
| llvm::raw_string_ostream os(result); | |
| for (auto B : llvm::enumerate(GetBytes())) { | |
| if (separate(B.index())) | |
| os << separator; | |
| os << llvm::format_hex_no_prefix(B.value(), 2, true); | |
| } | |
| return result; | |
| } | |
| void UUID::Dump(Stream &s) const { s.PutCString(GetAsString()); } | |
| static inline int xdigit_to_int(char ch) { | |
| ch = tolower(ch); | |
| if (ch >= 'a' && ch <= 'f') | |
| return 10 + ch - 'a'; | |
| return ch - '0'; | |
| } | |
| llvm::StringRef | |
| UUID::DecodeUUIDBytesFromString(llvm::StringRef p, | |
| llvm::SmallVectorImpl<uint8_t> &uuid_bytes) { | |
| uuid_bytes.clear(); | |
| while (p.size() >= 2) { | |
| if (isxdigit(p[0]) && isxdigit(p[1])) { | |
| int hi_nibble = xdigit_to_int(p[0]); | |
| int lo_nibble = xdigit_to_int(p[1]); | |
| // Translate the two hex nibble characters into a byte | |
| uuid_bytes.push_back((hi_nibble << 4) + lo_nibble); | |
| // Skip both hex digits | |
| p = p.drop_front(2); | |
| } else if (p.front() == '-') { | |
| // Skip dashes | |
| p = p.drop_front(); | |
| } else { | |
| // UUID values can only consist of hex characters and '-' chars | |
| break; | |
| } | |
| } | |
| return p; | |
| } | |
| bool UUID::SetFromStringRef(llvm::StringRef str) { | |
| llvm::StringRef p = str; | |
| // Skip leading whitespace characters | |
| p = p.ltrim(); | |
| llvm::SmallVector<uint8_t, 20> bytes; | |
| llvm::StringRef rest = UUID::DecodeUUIDBytesFromString(p, bytes); | |
| // Return false if we could not consume the entire string or if the parsed | |
| // UUID is empty. | |
| if (!rest.empty() || bytes.empty()) | |
| return false; | |
| *this = UUID(bytes); | |
| return true; | |
| } | |
| UUID UUID::Generate(uint32_t num_bytes) { | |
| llvm::SmallVector<uint8_t, 20> bytes(num_bytes); | |
| auto ec = llvm::getRandomBytes(bytes.data(), bytes.size()); | |
| // If getRandomBytes failed, fall back to a lower entropy source. | |
| if (ec) { | |
| auto seed = std::chrono::steady_clock::now().time_since_epoch().count(); | |
| std::independent_bits_engine<std::default_random_engine, CHAR_BIT, | |
| unsigned short> | |
| engine(seed); | |
| std::generate(bytes.begin(), bytes.end(), std::ref(engine)); | |
| } | |
| return UUID(bytes); | |
| } |