xref: /freebsd/contrib/llvm-project/lldb/source/Utility/UUID.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
1 //===-- UUID.cpp ----------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "lldb/Utility/UUID.h"
10 
11 #include "lldb/Utility/Stream.h"
12 #include "llvm/ADT/StringRef.h"
13 #include "llvm/Support/Format.h"
14 #include "llvm/Support/RandomNumberGenerator.h"
15 
16 #include <cctype>
17 #include <chrono>
18 #include <climits>
19 #include <cstdint>
20 #include <cstdio>
21 #include <cstring>
22 #include <random>
23 
24 using namespace lldb_private;
25 
26 // Whether to put a separator after count uuid bytes.
27 // For the first 16 bytes we follow the traditional UUID format. After that, we
28 // simply put a dash after every 6 bytes.
separate(size_t count)29 static inline bool separate(size_t count) {
30   if (count >= 10)
31     return (count - 10) % 6 == 0;
32 
33   switch (count) {
34   case 4:
35   case 6:
36   case 8:
37     return true;
38   default:
39     return false;
40   }
41 }
42 
UUID(UUID::CvRecordPdb70 debug_info)43 UUID::UUID(UUID::CvRecordPdb70 debug_info) {
44   llvm::sys::swapByteOrder(debug_info.Uuid.Data1);
45   llvm::sys::swapByteOrder(debug_info.Uuid.Data2);
46   llvm::sys::swapByteOrder(debug_info.Uuid.Data3);
47   llvm::sys::swapByteOrder(debug_info.Age);
48   if (debug_info.Age)
49     *this = UUID(&debug_info, sizeof(debug_info));
50   else
51     *this = UUID(&debug_info.Uuid, sizeof(debug_info.Uuid));
52 }
53 
GetAsString(llvm::StringRef separator) const54 std::string UUID::GetAsString(llvm::StringRef separator) const {
55   std::string result;
56   llvm::raw_string_ostream os(result);
57 
58   for (auto B : llvm::enumerate(GetBytes())) {
59     if (separate(B.index()))
60       os << separator;
61 
62     os << llvm::format_hex_no_prefix(B.value(), 2, true);
63   }
64 
65   return result;
66 }
67 
Dump(Stream & s) const68 void UUID::Dump(Stream &s) const { s.PutCString(GetAsString()); }
69 
xdigit_to_int(char ch)70 static inline int xdigit_to_int(char ch) {
71   ch = tolower(ch);
72   if (ch >= 'a' && ch <= 'f')
73     return 10 + ch - 'a';
74   return ch - '0';
75 }
76 
77 llvm::StringRef
DecodeUUIDBytesFromString(llvm::StringRef p,llvm::SmallVectorImpl<uint8_t> & uuid_bytes)78 UUID::DecodeUUIDBytesFromString(llvm::StringRef p,
79                                 llvm::SmallVectorImpl<uint8_t> &uuid_bytes) {
80   uuid_bytes.clear();
81   while (p.size() >= 2) {
82     if (isxdigit(p[0]) && isxdigit(p[1])) {
83       int hi_nibble = xdigit_to_int(p[0]);
84       int lo_nibble = xdigit_to_int(p[1]);
85       // Translate the two hex nibble characters into a byte
86       uuid_bytes.push_back((hi_nibble << 4) + lo_nibble);
87 
88       // Skip both hex digits
89       p = p.drop_front(2);
90     } else if (p.front() == '-') {
91       // Skip dashes
92       p = p.drop_front();
93     } else {
94       // UUID values can only consist of hex characters and '-' chars
95       break;
96     }
97   }
98   return p;
99 }
100 
SetFromStringRef(llvm::StringRef str)101 bool UUID::SetFromStringRef(llvm::StringRef str) {
102   llvm::StringRef p = str;
103 
104   // Skip leading whitespace characters
105   p = p.ltrim();
106 
107   llvm::SmallVector<uint8_t, 20> bytes;
108   llvm::StringRef rest = UUID::DecodeUUIDBytesFromString(p, bytes);
109 
110   // Return false if we could not consume the entire string or if the parsed
111   // UUID is empty.
112   if (!rest.empty() || bytes.empty())
113     return false;
114 
115   *this = UUID(bytes);
116   return true;
117 }
118 
Generate(uint32_t num_bytes)119 UUID UUID::Generate(uint32_t num_bytes) {
120   llvm::SmallVector<uint8_t, 20> bytes(num_bytes);
121   auto ec = llvm::getRandomBytes(bytes.data(), bytes.size());
122 
123   // If getRandomBytes failed, fall back to a lower entropy source.
124   if (ec) {
125     auto seed = std::chrono::steady_clock::now().time_since_epoch().count();
126     std::independent_bits_engine<std::default_random_engine, CHAR_BIT,
127                                  unsigned short>
128         engine(seed);
129     std::generate(bytes.begin(), bytes.end(), std::ref(engine));
130   }
131 
132   return UUID(bytes);
133 }
134