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;
319 std::vector<int> numbers;
320 bool hasPrefix =
false;
322 for(
int num : data.m_usedNumbers )
325 numbers.push_back( num );
330 if( numbers.empty() && !hasPrefix )
334 std::vector<std::pair<int, int>> ranges;
336 if( !numbers.empty() )
338 int start = numbers[0];
339 int end = numbers[0];
341 for(
size_t i = 1; i < numbers.size(); ++i )
343 if( numbers[i] ==
end + 1 )
349 ranges.push_back( { start,
end } );
350 start =
end = numbers[i];
353 ranges.push_back( { start,
end } );
356 bool firstRange =
true;
357 for(
const auto& [start,
end] : ranges )
388 std::unique_lock<std::mutex> lock;
391 lock = std::unique_lock<std::mutex>(
m_mutex );
402 auto parsePositiveInt = [](
const std::ssub_match& aMatch,
int& aOut ) ->
bool
404 const char* first = std::to_address( aMatch.first );
405 const char* last = std::to_address( aMatch.second );
407 auto [ptr, ec] = std::from_chars( first, last, value );
409 if( ec != std::errc() || ptr != last || value <= 0 )
420 const std::regex rangePattern( R
"(^(.*\D)(\d+)-(\d+)$)" );
421 const std::regex numberedPattern( R
"(^(.*\D)(\d+)$)" );
422 const std::regex prefixOnlyPattern( R
"(^(.+)$)" );
424 for(
const std::string& part : parts )
429 if( std::regex_match( unescaped, match, rangePattern ) )
431 std::string prefix = match[1].str();
435 if( !parsePositiveInt( match[2], start ) || !parsePositiveInt( match[3],
end ) )
441 for(
int i = start; i <=
end; ++i )
444 else if( std::regex_match( unescaped, match, numberedPattern ) )
446 std::string prefix = match[1].str();
449 if( !parsePositiveInt( match[2], number ) )
455 insertImpl( prefix + std::to_string( number ) );
457 else if( std::regex_match( unescaped, match, prefixOnlyPattern ) )
459 std::string prefix = match[1].str();
476 std::unique_lock<std::mutex> lock;
479 lock = std::unique_lock<std::mutex>(
m_mutex );
493 std::unique_lock<std::mutex> lock;
496 lock = std::unique_lock<std::mutex>(
m_mutex );
504 result.reserve( aStr.length() * 2 );
508 if( c ==
'\\' || c ==
',' || c ==
'-' )
518 result.reserve( aStr.length() );
520 bool escaped =
false;
542 std::vector<std::string>
result;
544 bool escaped =
false;
558 else if( c == aDelimiter )
560 result.push_back( current );
569 if( !current.empty() )
570 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.