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
15#include <cctype>
16#include <cstdio>
17#include <cstring>
18
19using namespace lldb_private;
20
21// Whether to put a separator after count uuid bytes.
22// For the first 16 bytes we follow the traditional UUID format. After that, we
23// simply put a dash after every 6 bytes.
24static inline bool separate(size_t count) {
25  if (count >= 10)
26    return (count - 10) % 6 == 0;
27
28  switch (count) {
29  case 4:
30  case 6:
31  case 8:
32    return true;
33  default:
34    return false;
35  }
36}
37
38UUID::UUID(UUID::CvRecordPdb70 debug_info) {
39  llvm::sys::swapByteOrder(debug_info.Uuid.Data1);
40  llvm::sys::swapByteOrder(debug_info.Uuid.Data2);
41  llvm::sys::swapByteOrder(debug_info.Uuid.Data3);
42  llvm::sys::swapByteOrder(debug_info.Age);
43  if (debug_info.Age)
44    *this = UUID(&debug_info, sizeof(debug_info));
45  else
46    *this = UUID(&debug_info.Uuid, sizeof(debug_info.Uuid));
47}
48
49std::string UUID::GetAsString(llvm::StringRef separator) const {
50  std::string result;
51  llvm::raw_string_ostream os(result);
52
53  for (auto B : llvm::enumerate(GetBytes())) {
54    if (separate(B.index()))
55      os << separator;
56
57    os << llvm::format_hex_no_prefix(B.value(), 2, true);
58  }
59  os.flush();
60
61  return result;
62}
63
64void UUID::Dump(Stream &s) const { s.PutCString(GetAsString()); }
65
66static inline int xdigit_to_int(char ch) {
67  ch = tolower(ch);
68  if (ch >= 'a' && ch <= 'f')
69    return 10 + ch - 'a';
70  return ch - '0';
71}
72
73llvm::StringRef
74UUID::DecodeUUIDBytesFromString(llvm::StringRef p,
75                                llvm::SmallVectorImpl<uint8_t> &uuid_bytes) {
76  uuid_bytes.clear();
77  while (p.size() >= 2) {
78    if (isxdigit(p[0]) && isxdigit(p[1])) {
79      int hi_nibble = xdigit_to_int(p[0]);
80      int lo_nibble = xdigit_to_int(p[1]);
81      // Translate the two hex nibble characters into a byte
82      uuid_bytes.push_back((hi_nibble << 4) + lo_nibble);
83
84      // Skip both hex digits
85      p = p.drop_front(2);
86    } else if (p.front() == '-') {
87      // Skip dashes
88      p = p.drop_front();
89    } else {
90      // UUID values can only consist of hex characters and '-' chars
91      break;
92    }
93  }
94  return p;
95}
96
97bool UUID::SetFromStringRef(llvm::StringRef str) {
98  llvm::StringRef p = str;
99
100  // Skip leading whitespace characters
101  p = p.ltrim();
102
103  llvm::SmallVector<uint8_t, 20> bytes;
104  llvm::StringRef rest = UUID::DecodeUUIDBytesFromString(p, bytes);
105
106  // Return false if we could not consume the entire string or if the parsed
107  // UUID is empty.
108  if (!rest.empty() || bytes.empty())
109    return false;
110
111  *this = UUID(bytes);
112  return true;
113}
114