KiCad PCB EDA Suite
Loading...
Searching...
No Matches
kiid.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2020 Ian McInerney <[email protected]>
5 * Copyright (C) 2007-2014 Jean-Pierre Charras, jp.charras at wanadoo.fr
6 * Copyright The KiCad Developers, see AUTHORS.TXT for contributors.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
26#include <kiid.h>
27
28#include <boost/random/mersenne_twister.hpp>
29#include <boost/uuid/uuid_generators.hpp>
30#include <boost/uuid/uuid_io.hpp>
31
32#if BOOST_VERSION >= 106700
33#include <boost/uuid/entropy_error.hpp>
34#endif
35
36#include <json_common.h>
37
38#include <cctype>
39#include <mutex>
40#include <utility>
41#include <stdlib.h>
42
43#include <wx/log.h>
44
45// Use thread_local because boost:mt19937 is not thread-safe
46// Static rng and generators are used because the overhead of constant seeding is expensive
47// We rely on the default non-arg constructor of basic_random_generator to provide a random seed.
48// We use a separate rng object for cases where we want to control the basic_random_generator
49// initial seed by calling SeedGenerator from unit tests and other special cases.
50static thread_local boost::mt19937 rng;
51static thread_local boost::uuids::basic_random_generator<boost::mt19937> randomGenerator;
52
53// These don't have the same performance penalty, but we might as well be consistent
54static boost::uuids::string_generator stringGenerator;
55static boost::uuids::nil_generator nilGenerator;
56
57
58// Global nil reference
60
61
62// When true, always create nil uuids for performance, when valid ones aren't needed.
63// Thread-local to prevent background library loading from affecting other threads.
64static thread_local bool g_createNilUuids = false;
65
66
67// For static initialization
69{
70 static KIID nil( 0 );
71 return nil;
72}
73
74
76{
77#if BOOST_VERSION >= 106700
78 try
79 {
80#endif
81
83 {
85 }
86 else
87 {
89 }
90
91#if BOOST_VERSION >= 106700
92 }
93 catch( const boost::uuids::entropy_error& )
94 {
95 wxLogFatalError( "A Boost UUID entropy exception was thrown in %s:%s.",
96 __FILE__, __FUNCTION__ );
97 }
98#endif
99}
100
101
102KIID::KIID( int null ) :
104{
105 wxASSERT( null == 0 );
106}
107
108
109KIID::KIID( const std::string& aString ) :
110 m_uuid()
111{
112 if( !aString.empty() && aString.length() <= 8
113 && std::all_of( aString.begin(), aString.end(),
114 []( unsigned char c )
115 {
116 return std::isxdigit( c );
117 } ) )
118 {
119 // A legacy-timestamp-based UUID has only the last 4 octets filled in.
120 // Convert them individually to avoid stepping in the little-endian/big-endian
121 // doo-doo.
122 for( int i = 0; i < 4; i++ )
123 {
124 int start = static_cast<int>( aString.length() ) - 8 + i * 2;
125 int end = start + 2;
126
127 start = std::max( 0, start );
128 int len = std::max( 0, end - start );
129
130 std::string octet = aString.substr( start, len );
131 m_uuid.data[i + 12] = strtol( octet.data(), nullptr, 16 );
132 }
133 }
134 else
135 {
136 try
137 {
138 m_uuid = stringGenerator( aString );
139 }
140 catch( ... )
141 {
142 // Failed to parse string representation; best we can do is assign a new
143 // random one.
144#if BOOST_VERSION >= 106700
145 try
146 {
147#endif
148
149 m_uuid = randomGenerator();
150
151#if BOOST_VERSION >= 106700
152 }
153 catch( const boost::uuids::entropy_error& )
154 {
155 wxLogFatalError( "A Boost UUID entropy exception was thrown in %s:%s.",
156 __FILE__, __FUNCTION__ );
157 }
158#endif
159 }
160 }
161}
162
163
164KIID::KIID( const char* aString ) :
165 KIID( std::string( aString ) )
166{
167}
168
169
170KIID::KIID( const wxString& aString ) :
171 KIID( std::string( aString.ToUTF8() ) )
172{
173}
174
175
176bool KIID::SniffTest( const wxString& aCandidate )
177{
178 static wxString niluuidStr = niluuid.AsString();
179
180 if( aCandidate.Length() != niluuidStr.Length() )
181 return false;
182
183 for( wxChar c : aCandidate )
184 {
185 if( c >= '0' && c <= '9' )
186 continue;
187
188 if( c >= 'a' && c <= 'f' )
189 continue;
190
191 if( c >= 'A' && c <= 'F' )
192 continue;
193
194 if( c == '-' )
195 continue;
196
197 return false;
198 }
199
200 return true;
201}
202
203
205{
206 m_uuid.data[12] = static_cast<uint8_t>( aTimestamp >> 24 );
207 m_uuid.data[13] = static_cast<uint8_t>( aTimestamp >> 16 );
208 m_uuid.data[14] = static_cast<uint8_t>( aTimestamp >> 8 );
209 m_uuid.data[15] = static_cast<uint8_t>( aTimestamp );
210}
211
212
214{
215 return !m_uuid.data[8] && !m_uuid.data[9] && !m_uuid.data[10] && !m_uuid.data[11];
216}
217
218
220{
221 timestamp_t ret = 0;
222
223 ret |= m_uuid.data[12] << 24;
224 ret |= m_uuid.data[13] << 16;
225 ret |= m_uuid.data[14] << 8;
226 ret |= m_uuid.data[15];
227
228 return ret;
229}
230
231
232size_t KIID::Hash() const
233{
234 return boost::uuids::hash_value( m_uuid );
235}
236
237
238void KIID::Clone( const KIID& aUUID )
239{
240 m_uuid = aUUID.m_uuid;
241}
242
243
244wxString KIID::AsString() const
245{
246 return boost::uuids::to_string( m_uuid );
247}
248
249
250std::string KIID::AsStdString() const
251{
252 return boost::uuids::to_string( m_uuid );
253}
254
255
257{
258 return wxString::Format( "%8.8lX", (unsigned long) AsLegacyTimestamp() );
259}
260
261
263{
264 if( !IsLegacyTimestamp() )
265 return;
266
268}
269
270
272{
273 // This obviously destroys uniform distribution, but it can be useful when a
274 // deterministic replacement for a duplicate ID is required.
275
276 for( int i = 15; i >= 0; --i )
277 {
278 m_uuid.data[i]++;
279
280 if( m_uuid.data[i] != 0 )
281 break;
282 }
283}
284
285
286KIID KIID::Combine( const KIID& aFirst, const KIID& aSecond )
287{
288 KIID result( 0 );
289
290 for( int i = 0; i < 16; ++i )
291 result.m_uuid.data[i] = aFirst.m_uuid.data[i] ^ aSecond.m_uuid.data[i];
292
293 return result;
294}
295
296
297void KIID::CreateNilUuids( bool aNil )
298{
299 g_createNilUuids = aNil;
300}
301
302
303void KIID::SeedGenerator( unsigned int aSeed )
304{
305 rng.seed( aSeed );
306 randomGenerator = boost::uuids::basic_random_generator<boost::mt19937>( rng );
307}
308
309
310KIID_PATH::KIID_PATH( const wxString& aString )
311{
312 for( const wxString& pathStep : wxSplit( aString, '/' ) )
313 {
314 if( !pathStep.empty() )
315 emplace_back( KIID( pathStep ) );
316 }
317}
318
319
321{
322 KIID_PATH copy = *this;
323 clear();
324
325 if( aPath.size() > copy.size() )
326 return false; // this path is not contained within aPath
327
328 for( size_t i = 0; i < aPath.size(); ++i )
329 {
330 if( copy.at( i ) != aPath.at( i ) )
331 {
332 *this = copy;
333 return false; // this path is not contained within aPath
334 }
335 }
336
337 for( size_t i = aPath.size(); i < copy.size(); ++i )
338 push_back( copy.at( i ) );
339
340 return true;
341}
342
343
344bool KIID_PATH::EndsWith( const KIID_PATH& aPath ) const
345{
346 if( aPath.size() > size() )
347 return false; // this path can not end aPath
348
349 KIID_PATH copyThis = *this;
350 KIID_PATH copyThat = aPath;
351
352 while( !copyThat.empty() )
353 {
354 if( *std::prev( copyThis.end() ) != *std::prev( copyThat.end() ) )
355 return false;
356
357 copyThis.pop_back();
358 copyThat.pop_back();
359 }
360
361 return true;
362}
363
364
365wxString KIID_PATH::AsString() const
366{
367 wxString path;
368
369 for( const KIID& pathStep : *this )
370 path += '/' + pathStep.AsString();
371
372 return path;
373}
374
375
376void to_json( nlohmann::json& aJson, const KIID& aKIID )
377{
378 aJson = aKIID.AsString().ToUTF8();
379}
380
381
382void from_json( const nlohmann::json& aJson, KIID& aKIID )
383{
384 aKIID = KIID( aJson.get<std::string>() );
385}
bool EndsWith(const KIID_PATH &aPath) const
Test if aPath from the last path towards the first path.
Definition kiid.cpp:344
bool MakeRelativeTo(const KIID_PATH &aPath)
Definition kiid.cpp:320
KIID_PATH()
Definition kiid.h:154
wxString AsString() const
Definition kiid.cpp:365
Definition kiid.h:49
KIID()
Definition kiid.cpp:75
static void SeedGenerator(unsigned int aSeed)
Re-initialize the UUID generator with a given seed (for testing or QA purposes)
Definition kiid.cpp:303
size_t Hash() const
Definition kiid.cpp:232
wxString AsString() const
Definition kiid.cpp:244
static KIID Combine(const KIID &aFirst, const KIID &aSecond)
Creates a deterministic KIID from two input KIIDs by XORing their underlying UUIDs.
Definition kiid.cpp:286
boost::uuids::uuid m_uuid
Definition kiid.h:140
void Increment()
Generates a deterministic replacement for a given ID.
Definition kiid.cpp:271
std::string AsStdString() const
Definition kiid.cpp:250
wxString AsLegacyTimestampString() const
Definition kiid.cpp:256
timestamp_t AsLegacyTimestamp() const
Definition kiid.cpp:219
static void CreateNilUuids(bool aNil=true)
A performance optimization which disables/enables the generation of pseudo-random UUIDs.
Definition kiid.cpp:297
static bool SniffTest(const wxString &aCandidate)
Returns true if a string has the correct formatting to be a KIID.
Definition kiid.cpp:176
void Clone(const KIID &aUUID)
Definition kiid.cpp:238
bool IsLegacyTimestamp() const
Definition kiid.cpp:213
void ConvertTimestampToUuid()
Change an existing time stamp based UUID into a true UUID.
Definition kiid.cpp:262
static boost::uuids::nil_generator nilGenerator
Definition kiid.cpp:55
static thread_local boost::uuids::basic_random_generator< boost::mt19937 > randomGenerator
Definition kiid.cpp:51
KIID & NilUuid()
Definition kiid.cpp:68
void from_json(const nlohmann::json &aJson, KIID &aKIID)
Definition kiid.cpp:382
static thread_local boost::mt19937 rng
Definition kiid.cpp:50
static boost::uuids::string_generator stringGenerator
Definition kiid.cpp:54
static thread_local bool g_createNilUuids
Definition kiid.cpp:64
void to_json(nlohmann::json &aJson, const KIID &aKIID)
Definition kiid.cpp:376
KIID niluuid(0)
KICOMMON_API KIID niluuid
uint32_t timestamp_t
timestamp_t is our type to represent unique IDs for all kinds of elements; historically simply the ti...
Definition kiid.h:46
STL namespace.
std::string path
wxString result
Test unit parsing edge cases and error handling.