37 std::unique_lock<std::mutex> lock;
40 lock = std::unique_lock<std::mutex>(
m_mutex );
81 std::unique_lock<std::mutex> lock;
84 lock = std::unique_lock<std::mutex>(
m_mutex );
91 const std::map<
int, std::vector<SCH_REFERENCE>>& aRefNumberMap,
92 const std::vector<int>& aRequiredUnits,
95 std::unique_lock<std::mutex> lock;
98 lock = std::unique_lock<std::mutex>(
m_mutex );
101 std::vector<int> validUnits;
102 std::copy_if( aRequiredUnits.begin(), aRequiredUnits.end(),
103 std::back_inserter( validUnits ),
104 [](
int unit ) { return unit >= 0; } );
106 int candidate = aMinValue;
111 auto mapIt = aRefNumberMap.find( candidate );
113 if( mapIt == aRefNumberMap.end() )
116 std::string candidateRefDes = aRef.
GetRef().ToStdString() + std::to_string( candidate );
135 if( validUnits.empty() )
160 const std::vector<SCH_REFERENCE>& aRefVector,
161 const std::vector<int>& aRequiredUnits )
const
163 for(
const int& unit : aRequiredUnits )
170 if( ref.CompareLibName( aRef ) != 0
171 || ref.CompareValue( aRef ) != 0
172 || ref.GetUnit() == unit )
185 if( aRefDes.empty() )
190 size_t pos = aRefDes.size();
192 while( pos > 0 && std::isdigit(
static_cast<unsigned char>( aRefDes[pos - 1] ) ) )
196 return { aRefDes, 0 };
198 if( pos == aRefDes.size() )
199 return { aRefDes, 0 };
202 const char* first = aRefDes.data() + pos;
203 const char* last = aRefDes.data() + aRefDes.size();
204 auto [ptr, ec] = std::from_chars( first, last, number );
206 if( ec != std::errc() || ptr != last )
207 return { aRefDes, 0 };
209 return { aRefDes.substr( 0, pos ), number };
223 if( used == candidate )
227 else if( used > candidate )
258 int cachedNext = cacheIt->second;
260 if( aInsertedNumber == cachedNext )
263 int candidate = cachedNext + 1;
268 cacheIt->second = candidate;
276 return cacheIt->second;
289 candidate = aMinValue;
303 std::unique_lock<std::mutex> lock;
305 lock = std::unique_lock<std::mutex>(
m_mutex );
307 std::ostringstream
result;
310 using PREFIX_ENTRY = std::pair<const std::string, PREFIX_DATA>;
312 std::vector<const PREFIX_ENTRY*> entries;
316 entries.push_back( &entry );
318 std::sort( entries.begin(), entries.end(),
319 [](
const auto* aLeft,
const auto* aRight )
321 return aLeft->first < aRight->first;
324 for(
const auto* entry : entries )
326 const std::string& prefix = entry->first;
336 std::vector<int> numbers;
337 bool hasPrefix =
false;
342 numbers.push_back( num );
347 if( numbers.empty() && !hasPrefix )
351 std::vector<std::pair<int, int>> ranges;
353 if( !numbers.empty() )
355 int start = numbers[0];
356 int end = numbers[0];
358 for(
size_t i = 1; i < numbers.size(); ++i )
360 if( numbers[i] ==
end + 1 )
366 ranges.push_back( { start,
end } );
367 start =
end = numbers[i];
370 ranges.push_back( { start,
end } );
373 bool firstRange =
true;
374 for(
const auto& [start,
end] : ranges )
405 std::unique_lock<std::mutex> lock;
408 lock = std::unique_lock<std::mutex>(
m_mutex );
419 auto parsePositiveInt = [](
const std::ssub_match& aMatch,
int& aOut ) ->
bool
421 const char* first = std::to_address( aMatch.first );
422 const char* last = std::to_address( aMatch.second );
424 auto [ptr, ec] = std::from_chars( first, last, value );
426 if( ec != std::errc() || ptr != last || value <= 0 )
437 const std::regex rangePattern( R
"(^(.*\D)(\d+)-(\d+)$)" );
438 const std::regex numberedPattern( R
"(^(.*\D)(\d+)$)" );
439 const std::regex prefixOnlyPattern( R
"(^(.+)$)" );
441 for(
const std::string& part : parts )
446 if( std::regex_match( unescaped, match, rangePattern ) )
448 std::string prefix = match[1].str();
452 if( !parsePositiveInt( match[2], start ) || !parsePositiveInt( match[3],
end ) )
458 for(
int i = start; i <=
end; ++i )
461 else if( std::regex_match( unescaped, match, numberedPattern ) )
463 std::string prefix = match[1].str();
466 if( !parsePositiveInt( match[2], number ) )
472 insertImpl( prefix + std::to_string( number ) );
474 else if( std::regex_match( unescaped, match, prefixOnlyPattern ) )
476 std::string prefix = match[1].str();
493 std::unique_lock<std::mutex> lock;
496 lock = std::unique_lock<std::mutex>(
m_mutex );
510 std::unique_lock<std::mutex> lock;
513 lock = std::unique_lock<std::mutex>(
m_mutex );
521 result.reserve( aStr.length() * 2 );
525 if( c ==
'\\' || c ==
',' || c ==
'-' )
535 result.reserve( aStr.length() );
537 bool escaped =
false;
559 std::vector<std::string>
result;
561 bool escaped =
false;
575 else if( c == aDelimiter )
577 result.push_back( current );
586 if( !current.empty() )
587 result.push_back( current );
void updateBaseNext(PREFIX_DATA &aData) const
std::string escapeForSerialization(const std::string &aStr) const
Escape special characters for serialization.
bool insertNumber(const std::string &aPrefix, int aNumber)
Insert a number for a specific prefix, updating internal structures.
bool Deserialize(const std::string &aData)
Deserialize tracker data from string representation.
std::vector< std::string > splitString(const std::string &aStr, char aDelimiter) const
Split string by delimiter, handling escaped characters.
bool m_reuseRefDes
If true, allows reusing existing reference designators.
std::mutex m_mutex
Mutex for thread safety.
int GetNextRefDesForUnits(const SCH_REFERENCE &aRef, const std::map< int, std::vector< SCH_REFERENCE > > &aRefNumberMap, const std::vector< int > &aRequiredUnits, int aMinValue)
Get the next available reference designator number for multi-unit symbols.
std::unordered_set< std::string > m_allRefDes
bool Insert(const std::string &aRefDes)
Insert a reference designator into the tracker.
void clearImpl()
Clear all internal data structures without locking.
size_t Size() const
Get the total count of stored reference designators.
std::string unescapeFromSerialization(const std::string &aStr) const
Unescape special characters from serialization.
bool insertImpl(const std::string &aRefDes)
Internal implementation of Insert without locking.
int findNextAvailable(const PREFIX_DATA &aData, int aMinValue) const
Find next available number for a prefix starting from a minimum value.
REFDES_TRACKER(bool aThreadSafe=false)
Constructor.
bool areUnitsAvailable(const SCH_REFERENCE &aRef, const std::vector< SCH_REFERENCE > &aRefVector, const std::vector< int > &aRequiredUnits) const
Check if all required units are available for a given reference number.
void updateCacheOnInsert(PREFIX_DATA &aData, int aInsertedNumber) const
Update cached next available values when a number is inserted.
std::string Serialize() const
Serialize the tracker data to a compact string representation.
std::unordered_map< std::string, PREFIX_DATA > m_prefixData
Map from prefix to its tracking data.
bool m_threadSafe
True if thread safety is enabled.
bool Contains(const std::string &aRefDes) const
Check if a reference designator exists in the tracker.
std::pair< std::string, int > parseRefDes(const std::string &aRefDes) const
Parse a reference designator into prefix and numerical suffix.
bool containsImpl(const std::string &aRefDes) const
Check if a reference designator exists in the tracker without locking.
void Clear()
Clear all stored reference designators.
A helper to define a symbol's reference designator in a schematic.
const char * GetRefStr() const
Data structure for tracking used numbers and caching next available values.
std::set< int > m_usedNumbers
Sorted set of used numbers for this prefix.
bool m_cacheValid
True if m_baseNext cache is valid.
int m_baseNext
Next available from 1 (cached)
std::map< int, int > m_nextCache
Cache of next available number for given min values.
wxString result
Test unit parsing edge cases and error handling.