YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/util/uuid.cc
Line
Count
Source (jump to first uncovered line)
1
// Copyright (c) YugaByte, Inc.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
4
// in compliance with the License.  You may obtain a copy of the License at
5
//
6
// http://www.apache.org/licenses/LICENSE-2.0
7
//
8
// Unless required by applicable law or agreed to in writing, software distributed under the License
9
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
10
// or implied.  See the License for the specific language governing permissions and limitations
11
// under the License.
12
//
13
14
#include "yb/util/uuid.h"
15
16
#include <boost/lexical_cast.hpp>
17
#include <boost/uuid/detail/sha1.hpp>
18
#include <boost/uuid/nil_generator.hpp>
19
#include <boost/uuid/random_generator.hpp>
20
#include <boost/uuid/uuid_io.hpp>
21
22
#include "yb/gutil/endian.h"
23
24
#include "yb/util/random_util.h"
25
#include "yb/util/result.h"
26
#include "yb/util/status.h"
27
#include "yb/util/status_format.h"
28
#include "yb/util/status_log.h"
29
30
namespace yb {
31
32
341M
Uuid::Uuid() {
33
341M
  memset(&boost_uuid_, 0, sizeof(boost_uuid_));
34
341M
}
35
36
352M
Uuid::Uuid(const Uuid& other) {
37
352M
  boost_uuid_ = other.boost_uuid_;
38
352M
}
39
40
60
Uuid::Uuid(const uuid_t copy) {
41
1.02k
  for(int it = 0; it < 16; it ++) {
42
960
    boost_uuid_.data[it] = copy[it];
43
960
  }
44
60
}
45
46
217M
Uuid Uuid::Nil() {
47
217M
  return Uuid(boost::uuids::nil_uuid());
48
217M
}
49
50
277k
Uuid Uuid::Generate() {
51
277k
  return Uuid::Generate(&ThreadLocalRandom());
52
277k
}
53
54
478k
Uuid Uuid::Generate(std::mt19937_64* rng) {
55
478k
  return Uuid(boost::uuids::basic_random_generator<std::mt19937_64>(rng)());
56
478k
}
57
58
142M
std::string Uuid::ToString() const {
59
142M
  std::string strval;
60
142M
  CHECK_OK(ToString(&strval));
61
142M
  return strval;
62
142M
}
63
64
142M
CHECKED_STATUS Uuid::ToString(std::string *strval) const {
65
142M
  *strval = boost::uuids::to_string(boost_uuid_);
66
142M
  return Status::OK();
67
142M
}
68
69
16.3M
void Uuid::EncodeToComparable(uint8_t* output) const {
70
16.3M
  if (boost_uuid_.version() == boost::uuids::uuid::version_time_based) {
71
    // Take the MSB of the UUID and get the timestamp ordered bytes.
72
1.04k
    ToTimestampBytes(output);
73
16.3M
  } else {
74
16.3M
    ToVersionFirstBytes(output);
75
16.3M
  }
76
16.3M
  memcpy(output + kUuidMsbSize, boost_uuid_.data + kUuidMsbSize, kUuidLsbSize);
77
16.3M
}
78
79
2.53M
void Uuid::EncodeToComparable(std::string* bytes) const {
80
2.53M
  uint8_t output[kUuidSize];
81
2.53M
  EncodeToComparable(output);
82
2.53M
  bytes->assign(reinterpret_cast<char *>(output), kUuidSize);
83
2.53M
}
84
85
1.28M
void Uuid::ToBytes(std::string* bytes) const {
86
1.28M
  bytes->assign(boost_uuid_.begin(), boost_uuid_.end());
87
1.28M
}
88
89
0
void Uuid::ToBytes(std::array<uint8_t, kUuidSize>* out) const {
90
0
  memcpy(out->data(), boost_uuid_.data, kUuidSize);
91
0
}
92
93
101M
Slice Uuid::AsSlice() const {
94
101M
  return Slice(boost_uuid_.data, boost_uuid_.size());
95
101M
}
96
97
640k
CHECKED_STATUS Uuid::FromSlice(const Slice& slice, size_t size_hint) {
98
640k
  size_t expected_size = (size_hint == 0) ? slice.size() : size_hint;
99
640k
  if (expected_size > slice.size()) {
100
0
    return STATUS_SUBSTITUTE(InvalidArgument, "Size of slice: $0 is smaller than provided "
101
0
        "size_hint: $1", slice.size(), expected_size);
102
0
  }
103
640k
  if (expected_size != kUuidSize) {
104
4
    return STATUS_SUBSTITUTE(InvalidArgument, "Size of slice is invalid: $0", expected_size);
105
4
  }
106
640k
  memcpy(boost_uuid_.data, slice.data(), kUuidSize);
107
640k
  return Status::OK();
108
640k
}
109
110
640k
CHECKED_STATUS Uuid::FromBytes(const std::string& bytes) {
111
640k
  return FromSlice(Slice(bytes.data(), bytes.size()));
112
640k
}
113
114
4
std::string Uuid::ToHexString() const {
115
4
  using Word = unsigned long long; // NOLINT
116
4
  constexpr size_t kWordSize = sizeof(Word);
117
4
  char buffer[kUuidSize * 2 + 1];
118
12
  for (size_t i = boost_uuid_.size(); i != 0;) {
119
8
    char* outpos = buffer + (kUuidSize - i) * 2;
120
8
    i -= kWordSize;
121
8
    Word value;
122
8
    memcpy(&value, boost_uuid_.data + i, kWordSize);
123
8
    static_assert(sizeof(Word) == 8, "Adjust little endian conversion below");
124
8
    snprintf(outpos, kWordSize * 2 + 1, "%016" PRIx64, LittleEndian::ToHost64(value));
125
8
  }
126
4
  return std::string(buffer, sizeof(buffer) - 1);
127
4
}
128
129
975k
CHECKED_STATUS Uuid::FromHexString(const std::string& hex_string) {
130
975k
  constexpr size_t kInputLen = kUuidSize * 2;
131
975k
  if (hex_string.length() != kInputLen) {
132
1
    return STATUS_SUBSTITUTE(InvalidArgument, "Size of hex_string is invalid: $0, expected: $1",
133
1
                             hex_string.size(), kInputLen);
134
1
  }
135
975k
  using Word = unsigned long long; // NOLINT
136
975k
  constexpr size_t kWordLen = sizeof(Word) * 2;
137
975k
  static_assert(kInputLen % kWordLen == 0, "Unexpected word size");
138
975k
  char buffer[kWordLen + 1];
139
975k
  buffer[kWordLen] = 0;
140
141
2.92M
  for (size_t i = 0; i != kInputLen;) {
142
1.94M
    memcpy(buffer, hex_string.c_str() + i, kWordLen);
143
1.94M
    char* endptr = nullptr;
144
1.94M
    auto value = strtoull(buffer, &endptr, 0x10);
145
1.94M
    if (endptr != buffer + kWordLen) {
146
1
      return STATUS_FORMAT(
147
1
          InvalidArgument, "$0 is not a valid uuid at $1", hex_string, i + endptr - buffer);
148
1
    }
149
1.94M
    static_assert(sizeof(Word) == 8, "Adjust little endian conversion below");
150
1.94M
    value = LittleEndian::FromHost64(value);
151
1.94M
    i += kWordLen;
152
1.94M
    memcpy(boost_uuid_.data + boost_uuid_.size() - i / 2, &value, sizeof(value));
153
1.94M
  }
154
155
975k
  return Status::OK();
156
975k
}
157
158
87.0M
CHECKED_STATUS Uuid::DecodeFromComparableSlice(const Slice& slice, size_t size_hint) {
159
18.4E
  size_t expected_size = (size_hint == 0) ? slice.size() : size_hint;
160
87.0M
  if (expected_size > slice.size()) {
161
0
    return STATUS_SUBSTITUTE(InvalidArgument, "Size of slice: $0 is smaller than provided "
162
0
        "size_hint: $1", slice.size(), expected_size);
163
0
  }
164
87.0M
  if (expected_size != kUuidSize) {
165
0
    return STATUS_SUBSTITUTE(InvalidArgument,
166
0
                             "Decode error: Size of slice is invalid: $0", expected_size);
167
0
  }
168
87.0M
  const uint8_t* bytes = slice.data();
169
87.0M
  if ((bytes[0] & 0xF0) == 0x10) {
170
    // Check the first byte to see if it is version 1.
171
6.90k
    FromTimestampBytes(bytes);
172
87.0M
  } else {
173
87.0M
    FromVersionFirstBytes(bytes);
174
87.0M
  }
175
87.0M
  memcpy(boost_uuid_.data + kUuidMsbSize, bytes + kUuidMsbSize, kUuidLsbSize);
176
87.0M
  return Status::OK();
177
87.0M
}
178
179
11.4k
CHECKED_STATUS Uuid::DecodeFromComparable(const std::string& bytes) {
180
11.4k
  Slice slice(bytes.data(), bytes.size());
181
11.4k
  return DecodeFromComparableSlice(slice);
182
11.4k
}
183
184
60
CHECKED_STATUS Uuid::HashMACAddress() {
185
60
  RETURN_NOT_OK(IsTimeUuid());
186
60
  boost::uuids::detail::sha1 sha1;
187
60
  unsigned int hash[kShaDigestSize];
188
60
  sha1.process_bytes(boost_uuid_.data + kTimeUUIDMacOffset, kTimeUUIDTotalMacBytes);
189
60
  uint8_t tmp[kTimeUUIDTotalMacBytes];
190
60
  sha1.get_digest(hash);
191
420
  for (size_t i = 0; i < kTimeUUIDTotalMacBytes; i ++) {
192
360
    tmp[i] = (hash[i % kShaDigestSize] & 255);
193
360
    hash[i % kShaDigestSize] = hash[i % kShaDigestSize] >> 8;
194
360
  }
195
60
  memcpy(boost_uuid_.data + kTimeUUIDMacOffset, tmp, kTimeUUIDTotalMacBytes);
196
60
  return Status::OK();
197
60
}
198
199
18
void Uuid::FromTimestamp(int64_t ts_hnanos) {
200
18
  uint64_t ts_byte_data = BigEndian::FromHost64((uint64_t)ts_hnanos);
201
18
  auto* ts_bytes = reinterpret_cast<uint8_t *>(&ts_byte_data);
202
18
  ts_bytes[0] = ((ts_bytes[0] & 0x0F) | 0x10); // Set the version to 1.
203
18
  FromTimestampBytes(ts_bytes);
204
18
}
205
206
8
CHECKED_STATUS Uuid::MaxFromUnixTimestamp(int64_t timestamp_ms) {
207
  // Since we are converting to a finer-grained precision (milliseconds to 100's nanoseconds) the
208
  // input milliseconds really corresponds to a range in 100's nanoseconds precision.
209
  // So, to get a logically correct max timeuuid, we need to use the upper bound of that range
210
  // (i.e. add '9999' at the end not '0000').
211
8
  int64_t ts_hnanos = (timestamp_ms + 1 - kGregorianOffsetMillis) * kMillisPerHundredNanos - 1;
212
213
8
  FromTimestamp(ts_hnanos); // Set most-significant bits (i.e. timestamp).
214
8
  memset(boost_uuid_.data + kUuidMsbSize, 0xFF, kUuidLsbSize); // Set least-significant bits.
215
8
  return Status::OK();
216
8
}
217
218
10
CHECKED_STATUS Uuid::MinFromUnixTimestamp(int64_t timestamp_ms) {
219
10
  int64_t timestamp = (timestamp_ms - kGregorianOffsetMillis) * kMillisPerHundredNanos;
220
10
  FromTimestamp(timestamp); // Set most-significant bits (i.e. timestamp).
221
10
  memset(boost_uuid_.data + kUuidMsbSize, 0x00, kUuidLsbSize); // Set least-significant bits.
222
10
  return Status::OK();
223
10
}
224
225
43
CHECKED_STATUS Uuid::ToUnixTimestamp(int64_t* timestamp_ms) const {
226
43
  RETURN_NOT_OK(IsTimeUuid());
227
43
  uint8_t output[kUuidMsbSize];
228
43
  ToTimestampBytes(output);
229
43
  output[0] = (output[0] & 0x0f);
230
43
  *timestamp_ms = 0;
231
387
  for (size_t i = 0; i < kUuidMsbSize; i++) {
232
344
    *timestamp_ms = (*timestamp_ms << 8) | (output[i] & 0xff);
233
344
  }
234
  // Convert from nano seconds since Gregorian calendar start to millis since unix epoch.
235
43
  *timestamp_ms = (*timestamp_ms / kMillisPerHundredNanos) + kGregorianOffsetMillis;
236
43
  return Status::OK();
237
43
}
238
239
1.96k
Status Uuid::IsTimeUuid() const {
240
1.96k
  if (boost_uuid_.version() == boost::uuids::uuid::version_time_based) {
241
1.96k
    return Status::OK();
242
1.96k
  }
243
244
1
  return STATUS_SUBSTITUTE(InvalidArgument,
245
1
                           "Not a type 1 UUID. Current type: $0", boost_uuid_.version());
246
1
}
247
248
4.11k
bool Uuid::operator<(const Uuid& other) const {
249
  // First compare the version, variant and then the timestamp bytes.
250
4.11k
  if (boost_uuid_.version() < other.boost_uuid_.version()) {
251
1.48k
    return true;
252
2.62k
  } else if (boost_uuid_.version() > other.boost_uuid_.version()) {
253
708
    return false;
254
708
  }
255
1.91k
  if (boost_uuid_.version() == boost::uuids::uuid::version_time_based) {
256
    // Compare the hi timestamp bits.
257
3.67k
    for (size_t i = 6; i < kUuidMsbSize; i++) {
258
2.74k
      if (boost_uuid_.data[i] < other.boost_uuid_.data[i]) {
259
443
        return true;
260
2.30k
      } else if (boost_uuid_.data[i] > other.boost_uuid_.data[i]) {
261
264
        return false;
262
264
      }
263
2.74k
    }
264
    // Compare the mid timestamp bits.
265
2.76k
    for (int i = 4; i < 6; i++) {
266
1.85k
      if (boost_uuid_.data[i] < other.boost_uuid_.data[i]) {
267
12
        return true;
268
1.83k
      } else if (boost_uuid_.data[i] > other.boost_uuid_.data[i]) {
269
0
        return false;
270
0
      }
271
1.85k
    }
272
    // Compare the low timestamp bits.
273
3.41k
    for (int i = 0; i < 4; i++) {
274
2.81k
      if (boost_uuid_.data[i] < other.boost_uuid_.data[i]) {
275
203
        return true;
276
2.60k
      } else if (boost_uuid_.data[i] > other.boost_uuid_.data[i]) {
277
105
        return false;
278
105
      }
279
2.81k
    }
280
282
  } else {
281
    // Compare all the other bits
282
2.52k
    for (size_t i = 0; i < kUuidMsbSize; i++) {
283
2.24k
      if (boost_uuid_.data[i] < other.boost_uuid_.data[i]) {
284
1
        return true;
285
2.24k
      } else if (boost_uuid_.data[i] > other.boost_uuid_.data[i]) {
286
1
        return false;
287
1
      }
288
2.24k
    }
289
282
  }
290
291
  // Then compare the remaining bytes.
292
7.99k
  for (size_t i = kUuidMsbSize; i < kUuidSize; i++) {
293
7.10k
    if (boost_uuid_.data[i] < other.boost_uuid_.data[i]) {
294
0
      return true;
295
7.10k
    } else if (boost_uuid_.data[i] > other.boost_uuid_.data[i]) {
296
0
      return false;
297
0
    }
298
7.10k
  }
299
888
  return false;
300
888
}
301
302
namespace {
303
304
// Makes transaction id from its binary representation.
305
// If check_exact_size is true, checks that slice contains only TransactionId.
306
Result<Uuid> DoDecodeUuid(
307
16.5M
    const Slice &slice, const bool check_exact_size, const char* name) {
308
16.5M
  if (check_exact_size ? slice.size() != boost::uuids::uuid::static_size()
309
6.63M
                       : slice.size() < boost::uuids::uuid::static_size()) {
310
0
    if (!name) {
311
0
      name = "UUID";
312
0
    }
313
0
    return STATUS_FORMAT(
314
0
        Corruption, "Invalid length of binary data with $4 '$0': $1 (expected $2$3)",
315
0
        slice.ToDebugHexString(), slice.size(), check_exact_size ? "" : "at least ",
316
0
        boost::uuids::uuid::static_size(), name);
317
0
  }
318
16.5M
  Uuid id;
319
16.5M
  memcpy(id.data(), slice.data(), boost::uuids::uuid::static_size());
320
16.5M
  return id;
321
16.5M
}
322
323
} // namespace
324
325
9.93M
Result<Uuid> Uuid::FullyDecode(const Slice& slice, const char* name) {
326
9.93M
  return DoDecodeUuid(slice, /* check_exact_size= */ true, name);
327
9.93M
}
328
329
892
Uuid Uuid::TryFullyDecode(const Slice& slice) {
330
892
  if (slice.size() != boost::uuids::uuid::static_size()) {
331
892
    return Uuid::Nil();
332
892
  }
333
0
  Uuid id;
334
0
  memcpy(id.data(), slice.data(), boost::uuids::uuid::static_size());
335
0
  return id;
336
0
}
337
338
6.64M
Result<Uuid> Uuid::Decode(Slice* slice, const char* name) {
339
6.64M
  auto id = VERIFY_RESULT(DoDecodeUuid(*slice, /* check_exact_size= */ false, name));
340
6.64M
  slice->remove_prefix(boost::uuids::uuid::static_size());
341
6.64M
  return id;
342
6.64M
}
343
344
166k
Result<Uuid> Uuid::FromString(const std::string& strval) {
345
166k
  if (strval.empty()) {
346
2
    return Uuid::Nil();
347
2
  }
348
166k
  try {
349
166k
    return Uuid(boost::lexical_cast<boost::uuids::uuid>(strval));
350
5
  } catch (std::exception& e) {
351
5
    return STATUS(Corruption, "Couldn't read Uuid from string!");
352
5
  }
353
166k
}
354
355
} // namespace yb