Don't generate duplicate IDs in line/wire/bus tool.

Also cleans up existing duplicate IDs when reading.

Fixes https://gitlab.com/kicad/code/kicad/issues/9749
This commit is contained in:
Jeff Young 2021-11-24 11:02:38 +00:00
parent b052d56c7b
commit 228edd4121
5 changed files with 71 additions and 21 deletions

View File

@ -38,21 +38,23 @@
#include <wx/log.h> #include <wx/log.h>
// boost:mt19937 is not thread-safe // boost:mt19937 is not thread-safe
static std::mutex rng_mutex; static std::mutex rng_mutex;
// Create only once, as seeding is *very* expensive // Create only once, as seeding is *very* expensive
static boost::mt19937 rng; static boost::mt19937 rng;
static boost::uuids::basic_random_generator<boost::mt19937> randomGenerator( rng ); static boost::uuids::basic_random_generator<boost::mt19937> randomGenerator( rng );
// These don't have the same performance penalty, but might as well be consistent // These don't have the same performance penalty, but we might as well be consistent
static boost::uuids::string_generator stringGenerator; static boost::uuids::string_generator stringGenerator;
static boost::uuids::nil_generator nilGenerator; static boost::uuids::nil_generator nilGenerator;
// Global nil reference // Global nil reference
KIID niluuid( 0 ); KIID niluuid( 0 );
// When true, always create nil uuids for performance, when valid ones aren't needed // When true, always create nil uuids for performance, when valid ones aren't needed
static bool createNilUuids = false; static bool g_createNilUuids = false;
// For static initialization // For static initialization
@ -72,7 +74,7 @@ KIID::KIID()
{ {
#endif #endif
if( createNilUuids ) if( g_createNilUuids )
{ {
m_uuid = nilGenerator(); m_uuid = nilGenerator();
} }
@ -247,9 +249,24 @@ void KIID::ConvertTimestampToUuid()
} }
void KIID::Increment()
{
// This obviously destroys uniform distribution, but it can be useful when a
// deterministic replacement for a duplicate ID is required.
for( int i = 15; i >= 0; --i )
{
m_uuid.data[i]++;
if( m_uuid.data[i] != 0 )
break;
}
}
void KIID::CreateNilUuids( bool aNil ) void KIID::CreateNilUuids( bool aNil )
{ {
createNilUuids = aNil; g_createNilUuids = aNil;
} }

View File

@ -94,6 +94,19 @@ void SCH_SEXPR_PARSER::checkpoint()
} }
KIID SCH_SEXPR_PARSER::parseKIID()
{
KIID id( FromUTF8() );
while( m_uuids.count( id ) )
id.Increment();
m_uuids.insert( id );
return id;
}
bool SCH_SEXPR_PARSER::parseBool() bool SCH_SEXPR_PARSER::parseBool()
{ {
T token = NextTok(); T token = NextTok();
@ -1838,7 +1851,7 @@ SCH_SHEET_PIN* SCH_SEXPR_PARSER::parseSchSheetPin( SCH_SHEET* aSheet )
case T_uuid: case T_uuid:
NeedSYMBOL(); NeedSYMBOL();
const_cast<KIID&>( sheetPin->m_Uuid ) = KIID( FromUTF8() ); const_cast<KIID&>( sheetPin->m_Uuid ) = parseKIID();
NeedRIGHT(); NeedRIGHT();
break; break;
@ -2050,7 +2063,7 @@ void SCH_SEXPR_PARSER::ParseSchematic( SCH_SHEET* aSheet, bool aIsCopyableOnly,
{ {
case T_uuid: case T_uuid:
NeedSYMBOL(); NeedSYMBOL();
screen->m_uuid = KIID( FromUTF8() ); screen->m_uuid = parseKIID();
NeedRIGHT(); NeedRIGHT();
break; break;
@ -2308,7 +2321,7 @@ SCH_SYMBOL* SCH_SEXPR_PARSER::parseSchematicSymbol()
case T_uuid: case T_uuid:
NeedSYMBOL(); NeedSYMBOL();
const_cast<KIID&>( symbol->m_Uuid ) = KIID( FromUTF8() ); const_cast<KIID&>( symbol->m_Uuid ) = parseKIID();
NeedRIGHT(); NeedRIGHT();
break; break;
@ -2395,7 +2408,7 @@ SCH_SYMBOL* SCH_SEXPR_PARSER::parseSchematicSymbol()
// First version to write out pin uuids accidentally wrote out the symbol's // First version to write out pin uuids accidentally wrote out the symbol's
// uuid for each pin, so ignore uuids coming from that version. // uuid for each pin, so ignore uuids coming from that version.
if( m_requiredVersion >= 20210126 ) if( m_requiredVersion >= 20210126 )
uuid = KIID( FromUTF8() ); uuid = parseKIID();
NeedRIGHT(); NeedRIGHT();
break; break;
@ -2460,7 +2473,7 @@ SCH_BITMAP* SCH_SEXPR_PARSER::parseImage()
case T_uuid: case T_uuid:
NeedSYMBOL(); NeedSYMBOL();
const_cast<KIID&>( bitmap->m_Uuid ) = KIID( FromUTF8() ); const_cast<KIID&>( bitmap->m_Uuid ) = parseKIID();
NeedRIGHT(); NeedRIGHT();
break; break;
@ -2560,7 +2573,7 @@ SCH_SHEET* SCH_SEXPR_PARSER::parseSheet()
case T_uuid: case T_uuid:
NeedSYMBOL(); NeedSYMBOL();
const_cast<KIID&>( sheet->m_Uuid ) = KIID( FromUTF8() ); const_cast<KIID&>( sheet->m_Uuid ) = parseKIID();
NeedRIGHT(); NeedRIGHT();
break; break;
@ -2652,7 +2665,7 @@ SCH_JUNCTION* SCH_SEXPR_PARSER::parseJunction()
case T_uuid: case T_uuid:
NeedSYMBOL(); NeedSYMBOL();
const_cast<KIID&>( junction->m_Uuid ) = KIID( FromUTF8() ); const_cast<KIID&>( junction->m_Uuid ) = parseKIID();
NeedRIGHT(); NeedRIGHT();
break; break;
@ -2689,7 +2702,7 @@ SCH_NO_CONNECT* SCH_SEXPR_PARSER::parseNoConnect()
case T_uuid: case T_uuid:
NeedSYMBOL(); NeedSYMBOL();
const_cast<KIID&>( no_connect->m_Uuid ) = KIID( FromUTF8() ); const_cast<KIID&>( no_connect->m_Uuid ) = parseKIID();
NeedRIGHT(); NeedRIGHT();
break; break;
@ -2743,7 +2756,7 @@ SCH_BUS_WIRE_ENTRY* SCH_SEXPR_PARSER::parseBusEntry()
case T_uuid: case T_uuid:
NeedSYMBOL(); NeedSYMBOL();
const_cast<KIID&>( busEntry->m_Uuid ) = KIID( FromUTF8() ); const_cast<KIID&>( busEntry->m_Uuid ) = parseKIID();
NeedRIGHT(); NeedRIGHT();
break; break;
@ -2808,7 +2821,7 @@ SCH_LINE* SCH_SEXPR_PARSER::parseLine()
case T_uuid: case T_uuid:
NeedSYMBOL(); NeedSYMBOL();
const_cast<KIID&>( line->m_Uuid ) = KIID( FromUTF8() ); const_cast<KIID&>( line->m_Uuid ) = parseKIID();
NeedRIGHT(); NeedRIGHT();
break; break;
@ -2934,7 +2947,7 @@ SCH_TEXT* SCH_SEXPR_PARSER::parseSchText()
case T_uuid: case T_uuid:
NeedSYMBOL(); NeedSYMBOL();
const_cast<KIID&>( text->m_Uuid ) = KIID( FromUTF8() ); const_cast<KIID&>( text->m_Uuid ) = parseKIID();
NeedRIGHT(); NeedRIGHT();
break; break;

View File

@ -81,6 +81,8 @@ class SCH_SEXPR_PARSER : public SCHEMATIC_LEXER
int m_convert; ///< The current body style being parsed. int m_convert; ///< The current body style being parsed.
wxString m_symbolName; ///< The current symbol name. wxString m_symbolName; ///< The current symbol name.
std::set<KIID> m_uuids;
PROGRESS_REPORTER* m_progressReporter; // optional; may be nullptr PROGRESS_REPORTER* m_progressReporter; // optional; may be nullptr
const LINE_READER* m_lineReader; // for progress reporting const LINE_READER* m_lineReader; // for progress reporting
unsigned m_lastProgressLine; unsigned m_lastProgressLine;
@ -88,6 +90,8 @@ class SCH_SEXPR_PARSER : public SCHEMATIC_LEXER
void checkpoint(); void checkpoint();
KIID parseKIID();
void parseHeader( TSCHEMATIC_T::T aHeaderType, int aFileVersion ); void parseHeader( TSCHEMATIC_T::T aHeaderType, int aFileVersion );
inline long parseHex() inline long parseHex()

View File

@ -651,7 +651,7 @@ int SCH_LINE_WIRE_BUS_TOOL::doDrawSegments( const std::string& aTool, int aType,
segment->SetEndPoint( cursorPos ); segment->SetEndPoint( cursorPos );
// Create a new segment, and chain it after the current segment. // Create a new segment, and chain it after the current segment.
segment = new SCH_LINE( *segment ); segment = static_cast<SCH_LINE*>( segment->Duplicate() );
segment->SetFlags( IS_NEW | IS_MOVING ); segment->SetFlags( IS_NEW | IS_MOVING );
segment->SetStartPoint( cursorPos ); segment->SetStartPoint( cursorPos );
m_wires.push_back( segment ); m_wires.push_back( segment );
@ -813,7 +813,7 @@ SCH_LINE* SCH_LINE_WIRE_BUS_TOOL::startSegments( int aType, const VECTOR2D& aPos
// horizontal and vertical lines only switch is on. // horizontal and vertical lines only switch is on.
if( m_frame->eeconfig()->m_Drawing.hv_lines_only ) if( m_frame->eeconfig()->m_Drawing.hv_lines_only )
{ {
segment = new SCH_LINE( *segment ); segment = static_cast<SCH_LINE*>( segment->Duplicate() );
segment->SetFlags( IS_NEW | IS_MOVING ); segment->SetFlags( IS_NEW | IS_MOVING );
m_wires.push_back( segment ); m_wires.push_back( segment );

View File

@ -59,8 +59,16 @@ public:
wxString AsString() const; wxString AsString() const;
wxString AsLegacyTimestampString() const; wxString AsLegacyTimestampString() const;
/**
* Returns true if a string has the correct formatting to be a KIID.
*/
static bool SniffTest( const wxString& aCandidate ); static bool SniffTest( const wxString& aCandidate );
/**
* A performance optimization which disables/enables the generation of pseudo-random UUIDs.
*
* NB: uses a global. Not thread safe!
*/
static void CreateNilUuids( bool aNil = true ); static void CreateNilUuids( bool aNil = true );
/** /**
@ -81,6 +89,14 @@ public:
*/ */
void ConvertTimestampToUuid(); void ConvertTimestampToUuid();
/**
* Generates a deterministic replacement for a given ID.
*
* NB: destroys uniform distribution! But it's the only thing we have when a deterministic
* replacement for a duplicate ID is required.
*/
void Increment();
bool operator==( KIID const& rhs ) const bool operator==( KIID const& rhs ) const
{ {
return m_uuid == rhs.m_uuid; return m_uuid == rhs.m_uuid;