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 <nlohmann/json.hpp>
37
38#include <cctype>
39#include <mutex>
40#include <utility>
41#include <stdlib.h>
42
43#include <wx/log.h>
44
45// boost:mt19937 is not thread-safe
46static std::mutex rng_mutex;
47
48// Static rng and generators are used because the overhead of constant seeding is expensive
49// We rely on the default non-arg constructor of basic_random_generator to provide a random seed.
50// We use a separate rng object for cases where we want to control the basic_random_generator
51// initial seed by calling SeedGenerator from unit tests and other special cases.
52static boost::mt19937 rng;
53static boost::uuids::basic_random_generator<boost::mt19937> randomGenerator;
54
55// These don't have the same performance penalty, but we might as well be consistent
56static boost::uuids::string_generator stringGenerator;
57static boost::uuids::nil_generator nilGenerator;
58
59
60// Global nil reference
62
63
64// When true, always create nil uuids for performance, when valid ones aren't needed
65static bool g_createNilUuids = false;
66
67
68// For static initialization
70{
71 static KIID nil( 0 );
72 return nil;
73}
74
75
77{
78#if BOOST_VERSION >= 106700
79 try
80 {
81#endif
82
84 {
86 }
87 else
88 {
89 std::lock_guard<std::mutex> lock( rng_mutex );
91 }
92
93#if BOOST_VERSION >= 106700
94 }
95 catch( const boost::uuids::entropy_error& )
96 {
97 wxLogFatalError( "A Boost UUID entropy exception was thrown in %s:%s.",
98 __FILE__, __FUNCTION__ );
99 }
100#endif
101}
102
103
104KIID::KIID( int null ) :
105 m_uuid( nilGenerator() )
106{
107 wxASSERT( null == 0 );
108}
109
110
111KIID::KIID( const std::string& aString ) :
112 m_uuid()
113{
114 if( !aString.empty() && aString.length() <= 8
115 && std::all_of( aString.begin(), aString.end(),
116 []( unsigned char c )
117 {
118 return std::isxdigit( c );
119 } ) )
120 {
121 // A legacy-timestamp-based UUID has only the last 4 octets filled in.
122 // Convert them individually to avoid stepping in the little-endian/big-endian
123 // doo-doo.
124 for( int i = 0; i < 4; i++ )
125 {
126 int start = static_cast<int>( aString.length() ) - 8 + i * 2;
127 int end = start + 2;
128
129 start = std::max( 0, start );
130 int len = std::max( 0, end - start );
131
132 std::string octet = aString.substr( start, len );
133 m_uuid.data[i + 12] = strtol( octet.data(), nullptr, 16 );
134 }
135 }
136 else
137 {
138 try
139 {
140 m_uuid = stringGenerator( aString );
141 }
142 catch( ... )
143 {
144 // Failed to parse string representation; best we can do is assign a new
145 // random one.
146#if BOOST_VERSION >= 106700
147 try
148 {
149#endif
150
152
153#if BOOST_VERSION >= 106700
154 }
155 catch( const boost::uuids::entropy_error& )
156 {
157 wxLogFatalError( "A Boost UUID entropy exception was thrown in %s:%s.",
158 __FILE__, __FUNCTION__ );
159 }
160#endif
161 }
162 }
163}
164
165
166KIID::KIID( const char* aString ) :
167 KIID( std::string( aString ) )
168{
169}
170
171
172KIID::KIID( const wxString& aString ) :
173 KIID( std::string( aString.ToUTF8() ) )
174{
175}
176
177
178bool KIID::SniffTest( const wxString& aCandidate )
179{
180 static wxString niluuidStr = niluuid.AsString();
181
182 if( aCandidate.Length() != niluuidStr.Length() )
183 return false;
184
185 for( wxChar c : aCandidate )
186 {
187 if( c >= '0' && c <= '9' )
188 continue;
189
190 if( c >= 'a' && c <= 'f' )
191 continue;
192
193 if( c >= 'A' && c <= 'F' )
194 continue;
195
196 if( c == '-' )
197 continue;
198
199 return false;
200 }
201
202 return true;
203}
204
205
207{
208 m_uuid.data[12] = static_cast<uint8_t>( aTimestamp >> 24 );
209 m_uuid.data[13] = static_cast<uint8_t>( aTimestamp >> 16 );
210 m_uuid.data[14] = static_cast<uint8_t>( aTimestamp >> 8 );
211 m_uuid.data[15] = static_cast<uint8_t>( aTimestamp );
212}
213
214
216{
217 return !m_uuid.data[8] && !m_uuid.data[9] && !m_uuid.data[10] && !m_uuid.data[11];
218}
219
220
222{
223 timestamp_t ret = 0;
224
225 ret |= m_uuid.data[12] << 24;
226 ret |= m_uuid.data[13] << 16;
227 ret |= m_uuid.data[14] << 8;
228 ret |= m_uuid.data[15];
229
230 return ret;
231}
232
233
234size_t KIID::Hash() const
235{
236 return boost::uuids::hash_value( m_uuid );
237}
238
239
240void KIID::Clone( const KIID& aUUID )
241{
242 m_uuid = aUUID.m_uuid;
243}
244
245
246wxString KIID::AsString() const
247{
248 return boost::uuids::to_string( m_uuid );
249}
250
251
252std::string KIID::AsStdString() const
253{
254 return boost::uuids::to_string( m_uuid );
255}
256
257
259{
260 return wxString::Format( "%8.8lX", (unsigned long) AsLegacyTimestamp() );
261}
262
263
265{
266 if( !IsLegacyTimestamp() )
267 return;
268
270}
271
272
274{
275 // This obviously destroys uniform distribution, but it can be useful when a
276 // deterministic replacement for a duplicate ID is required.
277
278 for( int i = 15; i >= 0; --i )
279 {
280 m_uuid.data[i]++;
281
282 if( m_uuid.data[i] != 0 )
283 break;
284 }
285}
286
287
288void KIID::CreateNilUuids( bool aNil )
289{
290 g_createNilUuids = aNil;
291}
292
293
294void KIID::SeedGenerator( unsigned int aSeed )
295{
296 rng.seed( aSeed );
297 randomGenerator = boost::uuids::basic_random_generator<boost::mt19937>( rng );
298}
299
300
301KIID_PATH::KIID_PATH( const wxString& aString )
302{
303 for( const wxString& pathStep : wxSplit( aString, '/' ) )
304 {
305 if( !pathStep.empty() )
306 emplace_back( KIID( pathStep ) );
307 }
308}
309
310
312{
313 KIID_PATH copy = *this;
314 clear();
315
316 if( aPath.size() > copy.size() )
317 return false; // this path is not contained within aPath
318
319 for( size_t i = 0; i < aPath.size(); ++i )
320 {
321 if( copy.at( i ) != aPath.at( i ) )
322 {
323 *this = copy;
324 return false; // this path is not contained within aPath
325 }
326 }
327
328 for( size_t i = aPath.size(); i < copy.size(); ++i )
329 push_back( copy.at( i ) );
330
331 return true;
332}
333
334
335bool KIID_PATH::EndsWith( const KIID_PATH& aPath ) const
336{
337 if( aPath.size() > size() )
338 return false; // this path can not end aPath
339
340 KIID_PATH copyThis = *this;
341 KIID_PATH copyThat = aPath;
342
343 while( !copyThat.empty() )
344 {
345 if( *std::prev( copyThis.end() ) != *std::prev( copyThat.end() ) )
346 return false;
347
348 copyThis.pop_back();
349 copyThat.pop_back();
350 }
351
352 return true;
353}
354
355
356wxString KIID_PATH::AsString() const
357{
358 wxString path;
359
360 for( const KIID& pathStep : *this )
361 path += '/' + pathStep.AsString();
362
363 return path;
364}
365
366
367void to_json( nlohmann::json& aJson, const KIID& aKIID )
368{
369 aJson = aKIID.AsString().ToUTF8();
370}
371
372
373void from_json( const nlohmann::json& aJson, KIID& aKIID )
374{
375 aKIID = KIID( aJson.get<std::string>() );
376}
bool EndsWith(const KIID_PATH &aPath) const
Test if aPath from the last path towards the first path.
Definition: kiid.cpp:335
bool MakeRelativeTo(const KIID_PATH &aPath)
Definition: kiid.cpp:311
KIID_PATH()
Definition: kiid.h:142
wxString AsString() const
Definition: kiid.cpp:356
Definition: kiid.h:49
KIID()
Definition: kiid.cpp:76
static void SeedGenerator(unsigned int aSeed)
Re-initialize the UUID generator with a given seed (for testing or QA purposes)
Definition: kiid.cpp:294
size_t Hash() const
Definition: kiid.cpp:234
wxString AsString() const
Definition: kiid.cpp:246
boost::uuids::uuid m_uuid
Definition: kiid.h:128
void Increment()
Generates a deterministic replacement for a given ID.
Definition: kiid.cpp:273
std::string AsStdString() const
Definition: kiid.cpp:252
wxString AsLegacyTimestampString() const
Definition: kiid.cpp:258
timestamp_t AsLegacyTimestamp() const
Definition: kiid.cpp:221
static void CreateNilUuids(bool aNil=true)
A performance optimization which disables/enables the generation of pseudo-random UUIDs.
Definition: kiid.cpp:288
static bool SniffTest(const wxString &aCandidate)
Returns true if a string has the correct formatting to be a KIID.
Definition: kiid.cpp:178
void Clone(const KIID &aUUID)
Definition: kiid.cpp:240
bool IsLegacyTimestamp() const
Definition: kiid.cpp:215
void ConvertTimestampToUuid()
Change an existing time stamp based UUID into a true UUID.
Definition: kiid.cpp:264
static boost::uuids::nil_generator nilGenerator
Definition: kiid.cpp:57
static bool g_createNilUuids
Definition: kiid.cpp:65
KIID & NilUuid()
Definition: kiid.cpp:69
static std::mutex rng_mutex
Definition: kiid.cpp:46
static boost::mt19937 rng
Definition: kiid.cpp:52
void from_json(const nlohmann::json &aJson, KIID &aKIID)
Definition: kiid.cpp:373
static boost::uuids::string_generator stringGenerator
Definition: kiid.cpp:56
static boost::uuids::basic_random_generator< boost::mt19937 > randomGenerator
Definition: kiid.cpp:53
void to_json(nlohmann::json &aJson, const KIID &aKIID)
Definition: kiid.cpp:367
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.