Blender V4.3
uuid.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "BLI_assert.h"
10#include "BLI_string.h"
11#include "BLI_uuid.h"
12
13#include <cstdio>
14#include <cstring>
15#include <ctime>
16#include <random>
17#include <sstream>
18#include <string>
19#include <tuple>
20
21/* Ensure the UUID struct doesn't have any padding, to be compatible with memcmp(). */
22static_assert(sizeof(bUUID) == 16, "expect UUIDs to be 128 bit exactly");
23
25{
26 static std::mt19937_64 rng = []() {
27 std::mt19937_64 rng;
28
29 /* Ensure the RNG really can output 64-bit values. */
30 static_assert(std::mt19937_64::min() == 0LL);
31 static_assert(std::mt19937_64::max() == 0xffffffffffffffffLL);
32
33 timespec ts;
34#ifdef __APPLE__
35 /* `timespec_get()` is only available on macOS 10.15+, so until that's the minimum version
36 * supported by Blender, use another function to get the timespec.
37 *
38 * `clock_gettime()` is only available on POSIX, so not on Windows; Linux uses the newer C++11
39 * function `timespec_get()` as well. */
40 clock_gettime(CLOCK_REALTIME, &ts);
41#else
42 timespec_get(&ts, TIME_UTC);
43#endif
44 /* XOR the nanosecond and second fields, just in case the clock only has seconds resolution. */
45 uint64_t seed = ts.tv_nsec;
46 seed ^= ts.tv_sec;
47 rng.seed(seed);
48
49 return rng;
50 }();
51
52 bUUID uuid;
53
54 /* RFC4122 suggests setting certain bits to a fixed value, and then randomizing the remaining
55 * bits. The opposite is easier to implement, though, so that's what's done here. */
56
57 /* Read two 64-bit numbers to randomize all 128 bits of the UUID. */
58 uint64_t *uuid_as_int64 = reinterpret_cast<uint64_t *>(&uuid);
59 uuid_as_int64[0] = rng();
60 uuid_as_int64[1] = rng();
61
62 /* Set the most significant four bits to 0b0100 to indicate version 4 (random UUID). */
63 uuid.time_hi_and_version &= ~0xF000;
64 uuid.time_hi_and_version |= 0x4000;
65
66 /* Set the most significant two bits to 0b10 to indicate compatibility with RFC4122. */
67 uuid.clock_seq_hi_and_reserved &= ~0x40;
68 uuid.clock_seq_hi_and_reserved |= 0x80;
69
70 return uuid;
71}
72
74{
75 const bUUID nil = {0, 0, 0, 0, 0, {0}};
76 return nil;
77}
78
80{
81 return BLI_uuid_equal(BLI_uuid_nil(), uuid);
82}
83
84bool BLI_uuid_equal(const bUUID uuid1, const bUUID uuid2)
85{
86 return std::memcmp(&uuid1, &uuid2, sizeof(uuid1)) == 0;
87}
88
89void BLI_uuid_format(char *buffer, const bUUID uuid)
90{
91 const size_t buffer_len_unclamped = BLI_snprintf(
92 buffer,
94 "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
95 uuid.time_low,
96 uuid.time_mid,
99 uuid.clock_seq_low,
100 uuid.node[0],
101 uuid.node[1],
102 uuid.node[2],
103 uuid.node[3],
104 uuid.node[4],
105 uuid.node[5]);
106
107 /* Assert the string length is not clamped. */
108 BLI_assert(buffer_len_unclamped == UUID_STRING_SIZE - 1);
109 UNUSED_VARS_NDEBUG(buffer_len_unclamped);
110}
111
112bool BLI_uuid_parse_string(bUUID *uuid, const char *buffer)
113{
114 const int fields_parsed_num = std::sscanf(
115 buffer,
116 "%8x-%4hx-%4hx-%2hhx%2hhx-%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx",
117 &uuid->time_low,
118 &uuid->time_mid,
119 &uuid->time_hi_and_version,
121 &uuid->clock_seq_low,
122 &uuid->node[0],
123 &uuid->node[1],
124 &uuid->node[2],
125 &uuid->node[3],
126 &uuid->node[4],
127 &uuid->node[5]);
128 return fields_parsed_num == 11;
129}
130
131std::ostream &operator<<(std::ostream &stream, bUUID uuid)
132{
133 std::string buffer(36, '\0');
134 BLI_uuid_format(buffer.data(), uuid);
135 stream << buffer;
136 return stream;
137}
138
139namespace blender {
140
141bUUID::bUUID(const std::initializer_list<uint32_t> field_values)
142{
143 BLI_assert_msg(field_values.size() == 11, "bUUID requires 5 regular fields + 6 `node` values");
144
145 const auto *field_iter = field_values.begin();
146
147 this->time_low = *field_iter++;
148 this->time_mid = uint16_t(*field_iter++);
149 this->time_hi_and_version = uint16_t(*field_iter++);
150 this->clock_seq_hi_and_reserved = uint8_t(*field_iter++);
151 this->clock_seq_low = uint8_t(*field_iter++);
152
153 std::copy(field_iter, field_values.end(), this->node);
154}
155
156bUUID::bUUID(const StringRefNull string_formatted_uuid)
157{
158 const bool parsed_ok = BLI_uuid_parse_string(this, string_formatted_uuid.c_str());
159 if (!parsed_ok) {
160 std::stringstream ss;
161 ss << "invalid UUID string " << string_formatted_uuid;
162 throw std::runtime_error(ss.str());
163 }
164}
165
166bUUID::bUUID(const ::bUUID &struct_uuid)
167{
168 *(static_cast<::bUUID *>(this)) = struct_uuid;
169}
170
171std::string bUUID::str() const
172{
173 std::string string(36, '\0');
174 BLI_uuid_format(string.data(), *this);
175 return string;
176}
177
178uint64_t bUUID::hash() const
179{
180 /* Convert the struct into two 64-bit numbers, and XOR them to get the hash. */
181 const uint64_t *uuid_as_int64 = reinterpret_cast<const uint64_t *>(this);
182 return uuid_as_int64[0] ^ uuid_as_int64[1];
183}
184
185bool operator==(const bUUID uuid1, const bUUID uuid2)
186{
187 return BLI_uuid_equal(uuid1, uuid2);
188}
189
190bool operator!=(const bUUID uuid1, const bUUID uuid2)
191{
192 return !(uuid1 == uuid2);
193}
194
195bool operator<(const bUUID uuid1, const bUUID uuid2)
196{
197 auto simple_fields1 = std::tie(uuid1.time_low,
198 uuid1.time_mid,
201 uuid1.clock_seq_low);
202 auto simple_fields2 = std::tie(uuid2.time_low,
203 uuid2.time_mid,
206 uuid2.clock_seq_low);
207 if (simple_fields1 == simple_fields2) {
208 return std::memcmp(uuid1.node, uuid2.node, sizeof(uuid1.node)) < 0;
209 }
210 return simple_fields1 < simple_fields2;
211}
212
213} // namespace blender
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:57
size_t BLI_snprintf(char *__restrict dst, size_t dst_maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
#define UNUSED_VARS_NDEBUG(...)
bool BLI_uuid_parse_string(bUUID *uuid, const char *buffer) ATTR_NONNULL()
Definition uuid.cc:112
bool BLI_uuid_equal(bUUID uuid1, bUUID uuid2)
Definition uuid.cc:84
void BLI_uuid_format(char *buffer, bUUID uuid) ATTR_NONNULL()
Definition uuid.cc:89
struct bUUID bUUID
Universally Unique Identifier according to RFC4122.
#define UUID_STRING_SIZE
static unsigned long seed
Definition btSoftBody.h:39
constexpr bool operator!=(StringRef a, StringRef b)
constexpr bool operator==(StringRef a, StringRef b)
constexpr bool operator<(StringRef a, StringRef b)
unsigned short uint16_t
Definition stdint.h:79
unsigned char uint8_t
Definition stdint.h:78
unsigned __int64 uint64_t
Definition stdint.h:90
Universally Unique Identifier according to RFC4122.
uint8_t clock_seq_hi_and_reserved
uint8_t clock_seq_low
uint16_t time_mid
uint32_t time_low
uint8_t node[6]
uint16_t time_hi_and_version
void BLI_uuid_format(char *buffer, const bUUID uuid)
Definition uuid.cc:89
bUUID BLI_uuid_nil()
Definition uuid.cc:73
std::ostream & operator<<(std::ostream &stream, bUUID uuid)
Definition uuid.cc:131
bool BLI_uuid_equal(const bUUID uuid1, const bUUID uuid2)
Definition uuid.cc:84
bool BLI_uuid_parse_string(bUUID *uuid, const char *buffer)
Definition uuid.cc:112
bool BLI_uuid_is_nil(bUUID uuid)
Definition uuid.cc:79
bUUID BLI_uuid_generate_random()
Definition uuid.cc:24