YugabyteDB (2.13.1.0-b60, 21121d69985fbf76aa6958d8f04a9bfa936293b5)

Coverage Report

Created: 2022-03-22 16:43

/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
987M
Uuid::Uuid() {
33
987M
  memset(&boost_uuid_, 0, sizeof(boost_uuid_));
34
987M
}
35
36
1.04G
Uuid::Uuid(const Uuid& other) {
37
1.04G
  boost_uuid_ = other.boost_uuid_;
38
1.04G
}
39
40
60
Uuid::Uuid(const uuid_t copy) {
41
1.02k
  for(int it = 0; it < 16; 
it ++960
) {
42
960
    boost_uuid_.data[it] = copy[it];
43
960
  }
44
60
}
45
46
633M
Uuid Uuid::Nil() {
47
633M
  return Uuid(boost::uuids::nil_uuid());
48
633M
}
49
50
463k
Uuid Uuid::Generate() {
51
463k
  return Uuid::Generate(&ThreadLocalRandom());
52
463k
}
53
54
637k
Uuid Uuid::Generate(std::mt19937_64* rng) {
55
637k
  return Uuid(boost::uuids::basic_random_generator<std::mt19937_64>(rng)());
56
637k
}
57
58
303M
std::string Uuid::ToString() const {
59
303M
  std::string strval;
60
303M
  CHECK_OK(ToString(&strval));
61
303M
  return strval;
62
303M
}
63
64
303M
CHECKED_STATUS Uuid::ToString(std::string *strval) const {
65
303M
  *strval = boost::uuids::to_string(boost_uuid_);
66
303M
  return Status::OK();
67
303M
}
68
69
56.9M
void Uuid::EncodeToComparable(uint8_t* output) const {
70
56.9M
  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.01k
    ToTimestampBytes(output);
73
56.9M
  } else {
74
56.9M
    ToVersionFirstBytes(output);
75
56.9M
  }
76
56.9M
  memcpy(output + kUuidMsbSize, boost_uuid_.data + kUuidMsbSize, kUuidLsbSize);
77
56.9M
}
78
79
12.9M
void Uuid::EncodeToComparable(std::string* bytes) const {
80
12.9M
  uint8_t output[kUuidSize];
81
12.9M
  EncodeToComparable(output);
82
12.9M
  bytes->assign(reinterpret_cast<char *>(output), kUuidSize);
83
12.9M
}
84
85
42
void Uuid::AppendEncodedComparable(std::string* bytes) const {
86
42
  uint8_t output[kUuidSize];
87
42
  EncodeToComparable(output);
88
42
  bytes->append(reinterpret_cast<char *>(output), kUuidSize);
89
42
}
90
91
1.31M
void Uuid::ToBytes(std::string* bytes) const {
92
1.31M
  bytes->assign(boost_uuid_.begin(), boost_uuid_.end());
93
1.31M
}
94
95
0
void Uuid::ToBytes(std::array<uint8_t, kUuidSize>* out) const {
96
0
  memcpy(out->data(), boost_uuid_.data, kUuidSize);
97
0
}
98
99
303M
Slice Uuid::AsSlice() const {
100
303M
  return Slice(boost_uuid_.data, boost_uuid_.size());
101
303M
}
102
103
639k
CHECKED_STATUS Uuid::FromSlice(const Slice& slice, size_t size_hint) {
104
639k
  size_t expected_size = (size_hint == 0) ? 
slice.size()638k
:
size_hint328
;
105
639k
  if (expected_size > slice.size()) {
106
0
    return STATUS_SUBSTITUTE(InvalidArgument, "Size of slice: $0 is smaller than provided "
107
0
        "size_hint: $1", slice.size(), expected_size);
108
0
  }
109
639k
  if (expected_size != kUuidSize) {
110
4
    return STATUS_SUBSTITUTE(InvalidArgument, "Size of slice is invalid: $0", expected_size);
111
4
  }
112
639k
  memcpy(boost_uuid_.data, slice.data(), kUuidSize);
113
639k
  return Status::OK();
114
639k
}
115
116
639k
CHECKED_STATUS Uuid::FromBytes(const std::string& bytes) {
117
639k
  return FromSlice(Slice(bytes.data(), bytes.size()));
118
639k
}
119
120
8
std::string Uuid::ToHexString() const {
121
8
  using Word = unsigned long long; // NOLINT
122
8
  constexpr size_t kWordSize = sizeof(Word);
123
8
  char buffer[kUuidSize * 2 + 1];
124
24
  for (size_t i = boost_uuid_.size(); i != 0;) {
125
16
    char* outpos = buffer + (kUuidSize - i) * 2;
126
16
    i -= kWordSize;
127
16
    Word value;
128
16
    memcpy(&value, boost_uuid_.data + i, kWordSize);
129
16
    static_assert(sizeof(Word) == 8, "Adjust little endian conversion below");
130
16
    snprintf(outpos, kWordSize * 2 + 1, "%016" PRIx64, LittleEndian::ToHost64(value));
131
16
  }
132
8
  return std::string(buffer, sizeof(buffer) - 1);
133
8
}
134
135
1.53M
CHECKED_STATUS Uuid::FromHexString(const std::string& hex_string) {
136
1.53M
  constexpr size_t kInputLen = kUuidSize * 2;
137
1.53M
  if (hex_string.length() != kInputLen) {
138
1
    return STATUS_SUBSTITUTE(InvalidArgument, "Size of hex_string is invalid: $0, expected: $1",
139
1
                             hex_string.size(), kInputLen);
140
1
  }
141
1.53M
  using Word = unsigned long long; // NOLINT
142
1.53M
  constexpr size_t kWordLen = sizeof(Word) * 2;
143
1.53M
  static_assert(kInputLen % kWordLen == 0, "Unexpected word size");
144
1.53M
  char buffer[kWordLen + 1];
145
1.53M
  buffer[kWordLen] = 0;
146
147
4.60M
  for (size_t i = 0; i != kInputLen;) {
148
3.06M
    memcpy(buffer, hex_string.c_str() + i, kWordLen);
149
3.06M
    char* endptr = nullptr;
150
3.06M
    auto value = strtoull(buffer, &endptr, 0x10);
151
3.06M
    if (endptr != buffer + kWordLen) {
152
1
      return STATUS_FORMAT(
153
1
          InvalidArgument, "$0 is not a valid uuid at $1", hex_string, i + endptr - buffer);
154
1
    }
155
3.06M
    static_assert(sizeof(Word) == 8, "Adjust little endian conversion below");
156
3.06M
    value = LittleEndian::FromHost64(value);
157
3.06M
    i += kWordLen;
158
3.06M
    memcpy(boost_uuid_.data + boost_uuid_.size() - i / 2, &value, sizeof(value));
159
3.06M
  }
160
161
1.53M
  return Status::OK();
162
1.53M
}
163
164
389M
CHECKED_STATUS Uuid::DecodeFromComparableSlice(const Slice& slice, size_t size_hint) {
165
18.4E
  size_t expected_size = 
(size_hint == 0)389M
?
slice.size()389M
: size_hint;
166
389M
  if (expected_size > slice.size()) {
167
0
    return STATUS_SUBSTITUTE(InvalidArgument, "Size of slice: $0 is smaller than provided "
168
0
        "size_hint: $1", slice.size(), expected_size);
169
0
  }
170
389M
  if (expected_size != kUuidSize) {
171
0
    return STATUS_SUBSTITUTE(InvalidArgument,
172
0
                             "Decode error: Size of slice is invalid: $0", expected_size);
173
0
  }
174
389M
  const uint8_t* bytes = slice.data();
175
389M
  if ((bytes[0] & 0xF0) == 0x10) {
176
    // Check the first byte to see if it is version 1.
177
6.67k
    FromTimestampBytes(bytes);
178
389M
  } else {
179
389M
    FromVersionFirstBytes(bytes);
180
389M
  }
181
389M
  memcpy(boost_uuid_.data + kUuidMsbSize, bytes + kUuidMsbSize, kUuidLsbSize);
182
389M
  return Status::OK();
183
389M
}
184
185
10.8k
CHECKED_STATUS Uuid::DecodeFromComparable(const std::string& bytes) {
186
10.8k
  Slice slice(bytes.data(), bytes.size());
187
10.8k
  return DecodeFromComparableSlice(slice);
188
10.8k
}
189
190
60
CHECKED_STATUS Uuid::HashMACAddress() {
191
60
  RETURN_NOT_OK(IsTimeUuid());
192
60
  boost::uuids::detail::sha1 sha1;
193
60
  unsigned int hash[kShaDigestSize];
194
60
  sha1.process_bytes(boost_uuid_.data + kTimeUUIDMacOffset, kTimeUUIDTotalMacBytes);
195
60
  uint8_t tmp[kTimeUUIDTotalMacBytes];
196
60
  sha1.get_digest(hash);
197
420
  for (size_t i = 0; i < kTimeUUIDTotalMacBytes; 
i ++360
) {
198
360
    tmp[i] = (hash[i % kShaDigestSize] & 255);
199
360
    hash[i % kShaDigestSize] = hash[i % kShaDigestSize] >> 8;
200
360
  }
201
60
  memcpy(boost_uuid_.data + kTimeUUIDMacOffset, tmp, kTimeUUIDTotalMacBytes);
202
60
  return Status::OK();
203
60
}
204
205
18
void Uuid::FromTimestamp(int64_t ts_hnanos) {
206
18
  uint64_t ts_byte_data = BigEndian::FromHost64((uint64_t)ts_hnanos);
207
18
  auto* ts_bytes = reinterpret_cast<uint8_t *>(&ts_byte_data);
208
18
  ts_bytes[0] = ((ts_bytes[0] & 0x0F) | 0x10); // Set the version to 1.
209
18
  FromTimestampBytes(ts_bytes);
210
18
}
211
212
8
CHECKED_STATUS Uuid::MaxFromUnixTimestamp(int64_t timestamp_ms) {
213
  // Since we are converting to a finer-grained precision (milliseconds to 100's nanoseconds) the
214
  // input milliseconds really corresponds to a range in 100's nanoseconds precision.
215
  // So, to get a logically correct max timeuuid, we need to use the upper bound of that range
216
  // (i.e. add '9999' at the end not '0000').
217
8
  int64_t ts_hnanos = (timestamp_ms + 1 - kGregorianOffsetMillis) * kMillisPerHundredNanos - 1;
218
219
8
  FromTimestamp(ts_hnanos); // Set most-significant bits (i.e. timestamp).
220
8
  memset(boost_uuid_.data + kUuidMsbSize, 0xFF, kUuidLsbSize); // Set least-significant bits.
221
8
  return Status::OK();
222
8
}
223
224
10
CHECKED_STATUS Uuid::MinFromUnixTimestamp(int64_t timestamp_ms) {
225
10
  int64_t timestamp = (timestamp_ms - kGregorianOffsetMillis) * kMillisPerHundredNanos;
226
10
  FromTimestamp(timestamp); // Set most-significant bits (i.e. timestamp).
227
10
  memset(boost_uuid_.data + kUuidMsbSize, 0x00, kUuidLsbSize); // Set least-significant bits.
228
10
  return Status::OK();
229
10
}
230
231
43
CHECKED_STATUS Uuid::ToUnixTimestamp(int64_t* timestamp_ms) const {
232
43
  RETURN_NOT_OK(IsTimeUuid());
233
43
  uint8_t output[kUuidMsbSize];
234
43
  ToTimestampBytes(output);
235
43
  output[0] = (output[0] & 0x0f);
236
43
  *timestamp_ms = 0;
237
387
  for (size_t i = 0; i < kUuidMsbSize; 
i++344
) {
238
344
    *timestamp_ms = (*timestamp_ms << 8) | (output[i] & 0xff);
239
344
  }
240
  // Convert from nano seconds since Gregorian calendar start to millis since unix epoch.
241
43
  *timestamp_ms = (*timestamp_ms / kMillisPerHundredNanos) + kGregorianOffsetMillis;
242
43
  return Status::OK();
243
43
}
244
245
2.07k
Status Uuid::IsTimeUuid() const {
246
2.07k
  if (boost_uuid_.version() == boost::uuids::uuid::version_time_based) {
247
2.07k
    return Status::OK();
248
2.07k
  }
249
250
1
  return STATUS_SUBSTITUTE(InvalidArgument,
251
2.07k
                           "Not a type 1 UUID. Current type: $0", boost_uuid_.version());
252
2.07k
}
253
254
179k
bool Uuid::operator<(const Uuid& other) const {
255
  // First compare the version, variant and then the timestamp bytes.
256
179k
  if (boost_uuid_.version() < other.boost_uuid_.version()) {
257
1.32k
    return true;
258
178k
  } else if (boost_uuid_.version() > other.boost_uuid_.version()) {
259
624
    return false;
260
624
  }
261
177k
  if (boost_uuid_.version() == boost::uuids::uuid::version_time_based) {
262
    // Compare the hi timestamp bits.
263
3.51k
    for (size_t i = 6; i < kUuidMsbSize; 
i++1.97k
) {
264
2.60k
      if (boost_uuid_.data[i] < other.boost_uuid_.data[i]) {
265
395
        return true;
266
2.21k
      } else if (boost_uuid_.data[i] > other.boost_uuid_.data[i]) {
267
240
        return false;
268
240
      }
269
2.60k
    }
270
    // Compare the mid timestamp bits.
271
2.69k
    
for (int i = 4; 904
i < 6;
i++1.79k
) {
272
1.80k
      if (boost_uuid_.data[i] < other.boost_uuid_.data[i]) {
273
12
        return true;
274
1.79k
      } else if (boost_uuid_.data[i] > other.boost_uuid_.data[i]) {
275
0
        return false;
276
0
      }
277
1.80k
    }
278
    // Compare the low timestamp bits.
279
3.29k
    
for (int i = 0; 892
i < 4;
i++2.39k
) {
280
2.70k
      if (boost_uuid_.data[i] < other.boost_uuid_.data[i]) {
281
203
        return true;
282
2.50k
      } else if (boost_uuid_.data[i] > other.boost_uuid_.data[i]) {
283
105
        return false;
284
105
      }
285
2.70k
    }
286
176k
  } else {
287
    // Compare all the other bits
288
1.57M
    for (size_t i = 0; i < kUuidMsbSize; 
i++1.39M
) {
289
1.40M
      if (boost_uuid_.data[i] < other.boost_uuid_.data[i]) {
290
1.00k
        return true;
291
1.40M
      } else if (boost_uuid_.data[i] > other.boost_uuid_.data[i]) {
292
504
        return false;
293
504
      }
294
1.40M
    }
295
176k
  }
296
297
  // Then compare the remaining bytes.
298
1.57M
  
for (size_t i = kUuidMsbSize; 175k
i < kUuidSize;
i++1.40M
) {
299
1.40M
    if (boost_uuid_.data[i] < other.boost_uuid_.data[i]) {
300
0
      return true;
301
1.40M
    } else if (boost_uuid_.data[i] > other.boost_uuid_.data[i]) {
302
0
      return false;
303
0
    }
304
1.40M
  }
305
175k
  return false;
306
175k
}
307
308
namespace {
309
310
// Makes transaction id from its binary representation.
311
// If check_exact_size is true, checks that slice contains only TransactionId.
312
Result<Uuid> DoDecodeUuid(
313
34.1M
    const Slice &slice, const bool check_exact_size, const char* name) {
314
34.1M
  if (check_exact_size ? 
slice.size() != boost::uuids::uuid::static_size()18.1M
315
34.1M
                       : 
slice.size() < boost::uuids::uuid::static_size()15.9M
) {
316
0
    if (!name) {
317
0
      name = "UUID";
318
0
    }
319
0
    return STATUS_FORMAT(
320
0
        Corruption, "Invalid length of binary data with $4 '$0': $1 (expected $2$3)",
321
0
        slice.ToDebugHexString(), slice.size(), check_exact_size ? "" : "at least ",
322
0
        boost::uuids::uuid::static_size(), name);
323
0
  }
324
34.1M
  Uuid id;
325
34.1M
  memcpy(id.data(), slice.data(), boost::uuids::uuid::static_size());
326
34.1M
  return id;
327
34.1M
}
328
329
} // namespace
330
331
18.1M
Result<Uuid> Uuid::FullyDecode(const Slice& slice, const char* name) {
332
18.1M
  return DoDecodeUuid(slice, /* check_exact_size= */ true, name);
333
18.1M
}
334
335
2.89k
Uuid Uuid::TryFullyDecode(const Slice& slice) {
336
2.89k
  if (slice.size() != boost::uuids::uuid::static_size()) {
337
1.92k
    return Uuid::Nil();
338
1.92k
  }
339
975
  Uuid id;
340
975
  memcpy(id.data(), slice.data(), boost::uuids::uuid::static_size());
341
975
  return id;
342
2.89k
}
343
344
15.9M
Result<Uuid> Uuid::Decode(Slice* slice, const char* name) {
345
15.9M
  auto id = VERIFY_RESULT(DoDecodeUuid(*slice, /* check_exact_size= */ false, name));
346
0
  slice->remove_prefix(boost::uuids::uuid::static_size());
347
15.9M
  return id;
348
15.9M
}
349
350
173k
Result<Uuid> Uuid::FromString(const std::string& strval) {
351
173k
  if (strval.empty()) {
352
2
    return Uuid::Nil();
353
2
  }
354
173k
  try {
355
173k
    return Uuid(boost::lexical_cast<boost::uuids::uuid>(strval));
356
173k
  } catch (std::exception& e) {
357
5
    return STATUS(Corruption, "Couldn't read Uuid from string!");
358
5
  }
359
173k
}
360
361
} // namespace yb