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() )
173 const std::vector<SCH_REFERENCE>& aRefVector,
174 const std::vector<int>& aRequiredUnits )
const
176 for(
const int& unit : aRequiredUnits )
183 if( ref.CompareLibName( aRef ) != 0
184 || ref.CompareValue( aRef ) != 0
185 || ref.GetUnit() == unit )
198 if( aRefDes.empty() )
203 size_t pos = aRefDes.size();
205 while( pos > 0 && std::isdigit(
static_cast<unsigned char>( aRefDes[pos - 1] ) ) )
209 return { aRefDes, 0 };
211 if( pos == aRefDes.size() )
212 return { aRefDes, 0 };
215 const char* first = aRefDes.data() + pos;
216 const char* last = aRefDes.data() + aRefDes.size();
217 auto [ptr, ec] = std::from_chars( first, last, number );
219 if( ec != std::errc() || ptr != last )
220 return { aRefDes, 0 };
222 return { aRefDes.substr( 0, pos ), number };
236 if( used == candidate )
240 else if( used > candidate )
271 int cachedNext = cacheIt->second;
273 if( aInsertedNumber == cachedNext )
276 int candidate = cachedNext + 1;
281 cacheIt->second = candidate;
289 return cacheIt->second;
302 candidate = aMinValue;
316 std::unique_lock<std::mutex> lock;
318 lock = std::unique_lock<std::mutex>(
m_mutex );
320 std::ostringstream
result;
332 std::vector<int> numbers;
333 bool hasPrefix =
false;
335 for(
int num : data.m_usedNumbers )
338 numbers.push_back( num );
343 if( numbers.empty() && !hasPrefix )
347 std::vector<std::pair<int, int>> ranges;
349 if( !numbers.empty() )
351 int start = numbers[0];
352 int end = numbers[0];
354 for(
size_t i = 1; i < numbers.size(); ++i )
356 if( numbers[i] ==
end + 1 )
362 ranges.push_back( { start,
end } );
363 start =
end = numbers[i];
366 ranges.push_back( { start,
end } );
369 bool firstRange =
true;
370 for(
const auto& [start,
end] : ranges )
401 std::unique_lock<std::mutex> lock;
404 lock = std::unique_lock<std::mutex>(
m_mutex );
415 auto parsePositiveInt = [](
const std::ssub_match& aMatch,
int& aOut ) ->
bool
417 const char* first = std::to_address( aMatch.first );
418 const char* last = std::to_address( aMatch.second );
420 auto [ptr, ec] = std::from_chars( first, last, value );
422 if( ec != std::errc() || ptr != last || value <= 0 )
433 const std::regex rangePattern( R
"(^(.*\D)(\d+)-(\d+)$)" );
434 const std::regex numberedPattern( R
"(^(.*\D)(\d+)$)" );
435 const std::regex prefixOnlyPattern( R
"(^(.+)$)" );
437 for(
const std::string& part : parts )
442 if( std::regex_match( unescaped, match, rangePattern ) )
444 std::string prefix = match[1].str();
448 if( !parsePositiveInt( match[2], start ) || !parsePositiveInt( match[3],
end ) )
454 for(
int i = start; i <=
end; ++i )
457 else if( std::regex_match( unescaped, match, numberedPattern ) )
459 std::string prefix = match[1].str();
462 if( !parsePositiveInt( match[2], number ) )
468 insertImpl( prefix + std::to_string( number ) );
470 else if( std::regex_match( unescaped, match, prefixOnlyPattern ) )
472 std::string prefix = match[1].str();
489 std::unique_lock<std::mutex> lock;
492 lock = std::unique_lock<std::mutex>(
m_mutex );
506 std::unique_lock<std::mutex> lock;
509 lock = std::unique_lock<std::mutex>(
m_mutex );
517 result.reserve( aStr.length() * 2 );
521 if( c ==
'\\' || c ==
',' || c ==
'-' )
531 result.reserve( aStr.length() );
533 bool escaped =
false;
555 std::vector<std::string>
result;
557 bool escaped =
false;
571 else if( c == aDelimiter )
573 result.push_back( current );
582 if( !current.empty() )
583 result.push_back( current );
591 std::unique_lock<std::mutex> lock;
594 lock = std::unique_lock<std::mutex>(
m_mutex );
602 std::unique_lock<std::mutex> lock;
605 lock = std::unique_lock<std::mutex>(
m_mutex );
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.
void ClearUnitsChecker()
Clear the external units checker, reverting to default behavior.
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 SetUnitsChecker(const UNITS_CHECKER_FUNC< SCH_REFERENCE > &aChecker)
Set an external units checker function for SCH_REFERENCE objects.
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.
UNITS_CHECKER_FUNC< SCH_REFERENCE > m_externalUnitsChecker
External units checker function (optional)
void Clear()
Clear all stored reference designators.
A helper to define a symbol's reference designator in a schematic.
const char * GetRefStr() const
std::function< bool(const T &aTestRef, const std::vector< T > &aExistingRefs, const std::vector< int > &aRequiredUnits)> UNITS_CHECKER_FUNC
Function type for external units availability checking.
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.