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, see <https://www.gnu.org/licenses/>.
20 */
21
22#include <kiid.h>
23
24#include <boost/random/mersenne_twister.hpp>
25#include <boost/uuid/uuid_generators.hpp>
26#include <boost/uuid/uuid_io.hpp>
27
28#if BOOST_VERSION >= 106700
29#include <boost/uuid/entropy_error.hpp>
30#endif
31
32#include <json_common.h>
33
34#include <cctype>
35#include <cstdint>
36#include <mutex>
37#include <utility>
38#include <stdlib.h>
39
40#include <wx/log.h>
41#include <wx/string.h>
42
43// Use thread_local because boost:mt19937 is not thread-safe
44// Static rng and generators are used because the overhead of constant seeding is expensive
45// We rely on the default non-arg constructor of basic_random_generator to provide a random seed.
46// We use a separate rng object for cases where we want to control the basic_random_generator
47// initial seed by calling SeedGenerator from unit tests and other special cases.
48static thread_local boost::mt19937 rng;
49static thread_local boost::uuids::basic_random_generator<boost::mt19937> randomGenerator;
50
51// These don't have the same performance penalty, but we might as well be consistent
52static boost::uuids::string_generator stringGenerator;
53static boost::uuids::nil_generator nilGenerator;
54
55
56// Global nil reference
58
59
60// When true, always create nil uuids for performance, when valid ones aren't needed.
61// Thread-local to prevent background library loading from affecting other threads.
62static thread_local bool g_createNilUuids = false;
63
64
65// For static initialization
67{
68 static KIID nil( 0 );
69 return nil;
70}
71
72
74{
75#if BOOST_VERSION >= 106700
76 try
77 {
78#endif
79
81 {
83 }
84 else
85 {
87 }
88
89#if BOOST_VERSION >= 106700
90 }
91 catch( const boost::uuids::entropy_error& )
92 {
93 wxLogFatalError( "A Boost UUID entropy exception was thrown in %s:%s.",
94 __FILE__, __FUNCTION__ );
95 }
96#endif
97}
98
99
100KIID::KIID( int null ) :
102{
103 wxASSERT( null == 0 );
104}
105
106
107KIID::KIID( const std::string& aString ) :
108 m_uuid()
109{
110 if( !aString.empty() && aString.length() <= 8
111 && std::all_of( aString.begin(), aString.end(),
112 []( unsigned char c )
113 {
114 return std::isxdigit( c );
115 } ) )
116 {
117 // A legacy-timestamp-based UUID has only the last 4 octets filled in.
118 // Convert them individually to avoid stepping in the little-endian/big-endian
119 // doo-doo.
120 for( int i = 0; i < 4; i++ )
121 {
122 int start = static_cast<int>( aString.length() ) - 8 + i * 2;
123 int end = start + 2;
124
125 start = std::max( 0, start );
126 int len = std::max( 0, end - start );
127
128 std::string octet = aString.substr( start, len );
129 m_uuid.data[i + 12] = strtol( octet.data(), nullptr, 16 );
130 }
131 }
132 else
133 {
134 try
135 {
136 m_uuid = stringGenerator( aString );
137 }
138 catch( ... )
139 {
140 // Failed to parse string representation; best we can do is assign a new
141 // random one.
142#if BOOST_VERSION >= 106700
143 try
144 {
145#endif
146
147 m_uuid = randomGenerator();
148
149#if BOOST_VERSION >= 106700
150 }
151 catch( const boost::uuids::entropy_error& )
152 {
153 wxLogFatalError( "A Boost UUID entropy exception was thrown in %s:%s.",
154 __FILE__, __FUNCTION__ );
155 }
156#endif
157 }
158 }
159}
160
161
162KIID::KIID( const char* aString ) :
163 KIID( std::string( aString ) )
164{
165}
166
167
168KIID::KIID( const wxString& aString ) :
169 KIID( std::string( aString.ToUTF8() ) )
170{
171}
172
173
174bool KIID::SniffTest( const wxString& aCandidate )
175{
176 static wxString niluuidStr = niluuid.AsString();
177
178 if( aCandidate.Length() != niluuidStr.Length() )
179 return false;
180
181 for( wxChar c : aCandidate )
182 {
183 if( c >= '0' && c <= '9' )
184 continue;
185
186 if( c >= 'a' && c <= 'f' )
187 continue;
188
189 if( c >= 'A' && c <= 'F' )
190 continue;
191
192 if( c == '-' )
193 continue;
194
195 return false;
196 }
197
198 return true;
199}
200
201
203{
204 m_uuid.data[12] = static_cast<uint8_t>( aTimestamp >> 24 );
205 m_uuid.data[13] = static_cast<uint8_t>( aTimestamp >> 16 );
206 m_uuid.data[14] = static_cast<uint8_t>( aTimestamp >> 8 );
207 m_uuid.data[15] = static_cast<uint8_t>( aTimestamp );
208}
209
210
212{
213 return !m_uuid.data[8] && !m_uuid.data[9] && !m_uuid.data[10] && !m_uuid.data[11];
214}
215
216
218{
219 timestamp_t ret = 0;
220
221 ret |= m_uuid.data[12] << 24;
222 ret |= m_uuid.data[13] << 16;
223 ret |= m_uuid.data[14] << 8;
224 ret |= m_uuid.data[15];
225
226 return ret;
227}
228
229
230size_t KIID::Hash() const
231{
232 return boost::uuids::hash_value( m_uuid );
233}
234
235
236void KIID::Clone( const KIID& aUUID )
237{
238 m_uuid = aUUID.m_uuid;
239}
240
241
242wxString KIID::AsString() const
243{
244 return boost::uuids::to_string( m_uuid );
245}
246
247
248std::string KIID::AsStdString() const
249{
250 return boost::uuids::to_string( m_uuid );
251}
252
253
255{
256 return wxString::Format( "%8.8lX", (unsigned long) AsLegacyTimestamp() );
257}
258
259
261{
262 if( !IsLegacyTimestamp() )
263 return;
264
266}
267
268
270{
271 // This obviously destroys uniform distribution, but it can be useful when a
272 // deterministic replacement for a duplicate ID is required.
273
274 for( int i = 15; i >= 0; --i )
275 {
276 m_uuid.data[i]++;
277
278 if( m_uuid.data[i] != 0 )
279 break;
280 }
281}
282
283
284KIID KIID::Combine( const KIID& aFirst, const KIID& aSecond )
285{
286 KIID result( 0 );
287
288 for( int i = 0; i < 16; ++i )
289 result.m_uuid.data[i] = aFirst.m_uuid.data[i] ^ aSecond.m_uuid.data[i];
290
291 return result;
292}
293
294
295KIID KIID::FromDeterministicString( const wxString& aName )
296{
297 // Dual FNV-1a accumulators fold the name into two 64-bit values, which are
298 // then laid out as a v4-shaped UUID string and parsed back into a KIID so
299 // boost's string_generator produces identical bytes on every call. Do not
300 // alter the constants or bit layout: this output is embedded in persisted
301 // diff/merge artifacts and must stay byte-identical.
302 std::uint64_t h1 = 0xcbf29ce484222325ULL;
303 std::uint64_t h2 = 0x84222325cbf29ce4ULL;
304
305 for( wxChar c : aName )
306 {
307 h1 ^= static_cast<std::uint64_t>( c );
308 h1 *= 0x100000001b3ULL;
309 h2 ^= static_cast<std::uint64_t>( c ) * 0x9E3779B97F4A7C15ULL;
310 h2 = ( h2 << 13 ) | ( h2 >> 51 );
311 }
312
313 const wxString uuid = wxString::Format(
314 wxS( "%08x-%04x-%04x-%04x-%012llx" ),
315 static_cast<unsigned>( h1 >> 32 ),
316 static_cast<unsigned>( ( h1 >> 16 ) & 0xffff ),
317 static_cast<unsigned>( h1 & 0xffff ),
318 static_cast<unsigned>( h2 & 0xffff ),
319 static_cast<unsigned long long>( h2 >> 16 ) & 0xffffffffffffULL );
320
321 return KIID( uuid );
322}
323
324
325void KIID::CreateNilUuids( bool aNil )
326{
327 g_createNilUuids = aNil;
328}
329
330
331void KIID::SeedGenerator( unsigned int aSeed )
332{
333 rng.seed( aSeed );
334 randomGenerator = boost::uuids::basic_random_generator<boost::mt19937>( rng );
335}
336
337
338KIID_PATH::KIID_PATH( const wxString& aString )
339{
340 for( const wxString& pathStep : wxSplit( aString, '/' ) )
341 {
342 if( !pathStep.empty() )
343 emplace_back( KIID( pathStep ) );
344 }
345}
346
347
349{
350 KIID_PATH copy = *this;
351 clear();
352
353 if( aPath.size() > copy.size() )
354 return false; // this path is not contained within aPath
355
356 for( size_t i = 0; i < aPath.size(); ++i )
357 {
358 if( copy.at( i ) != aPath.at( i ) )
359 {
360 *this = copy;
361 return false; // this path is not contained within aPath
362 }
363 }
364
365 for( size_t i = aPath.size(); i < copy.size(); ++i )
366 push_back( copy.at( i ) );
367
368 return true;
369}
370
371
372bool KIID_PATH::EndsWith( const KIID_PATH& aPath ) const
373{
374 if( aPath.size() > size() )
375 return false; // this path can not end aPath
376
377 KIID_PATH copyThis = *this;
378 KIID_PATH copyThat = aPath;
379
380 while( !copyThat.empty() )
381 {
382 if( *std::prev( copyThis.end() ) != *std::prev( copyThat.end() ) )
383 return false;
384
385 copyThis.pop_back();
386 copyThat.pop_back();
387 }
388
389 return true;
390}
391
392
393wxString KIID_PATH::AsString() const
394{
395 wxString path;
396
397 for( const KIID& pathStep : *this )
398 path += '/' + pathStep.AsString();
399
400 return path;
401}
402
403
404void to_json( nlohmann::json& aJson, const KIID& aKIID )
405{
406 aJson = aKIID.AsString().ToUTF8();
407}
408
409
410void from_json( const nlohmann::json& aJson, KIID& aKIID )
411{
412 aKIID = KIID( aJson.get<std::string>() );
413}
bool EndsWith(const KIID_PATH &aPath) const
Test if aPath from the last path towards the first path.
Definition kiid.cpp:372
bool MakeRelativeTo(const KIID_PATH &aPath)
Definition kiid.cpp:348
KIID_PATH()
Definition kiid.h:167
wxString AsString() const
Definition kiid.cpp:393
Definition kiid.h:44
KIID()
Definition kiid.cpp:73
static void SeedGenerator(unsigned int aSeed)
Re-initialize the UUID generator with a given seed (for testing or QA purposes)
Definition kiid.cpp:331
size_t Hash() const
Definition kiid.cpp:230
wxString AsString() const
Definition kiid.cpp:242
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:284
boost::uuids::uuid m_uuid
Definition kiid.h:156
void Increment()
Generates a deterministic replacement for a given ID.
Definition kiid.cpp:269
std::string AsStdString() const
Definition kiid.cpp:248
wxString AsLegacyTimestampString() const
Definition kiid.cpp:254
timestamp_t AsLegacyTimestamp() const
Definition kiid.cpp:217
static void CreateNilUuids(bool aNil=true)
A performance optimization which disables/enables the generation of pseudo-random UUIDs.
Definition kiid.cpp:325
static bool SniffTest(const wxString &aCandidate)
Returns true if a string has the correct formatting to be a KIID.
Definition kiid.cpp:174
static KIID FromDeterministicString(const wxString &aName)
Build a deterministic UUID from an arbitrary name string.
Definition kiid.cpp:295
void Clone(const KIID &aUUID)
Definition kiid.cpp:236
bool IsLegacyTimestamp() const
Definition kiid.cpp:211
void ConvertTimestampToUuid()
Change an existing time stamp based UUID into a true UUID.
Definition kiid.cpp:260
static boost::uuids::nil_generator nilGenerator
Definition kiid.cpp:53
static thread_local boost::uuids::basic_random_generator< boost::mt19937 > randomGenerator
Definition kiid.cpp:49
KIID & NilUuid()
Definition kiid.cpp:66
void from_json(const nlohmann::json &aJson, KIID &aKIID)
Definition kiid.cpp:410
static thread_local boost::mt19937 rng
Definition kiid.cpp:48
static boost::uuids::string_generator stringGenerator
Definition kiid.cpp:52
static thread_local bool g_createNilUuids
Definition kiid.cpp:62
void to_json(nlohmann::json &aJson, const KIID &aKIID)
Definition kiid.cpp:404
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:41
STL namespace.
netlist clear()
std::string path
table push_back({ "Source", "Layer", "Vertices", "Strategy", "Build(us)", "ns/query", "Mquery/s", "Inside" })
wxString result
Test unit parsing edge cases and error handling.