kicad-source/eeschema/refdes_tracker.h
Seth Hillbrand e387953490 ADDED refdes tracker
Provide an option to track ever reference designator ever used in the
project to prevent its reuse even if the component has been removed

Fixes https://gitlab.com/kicad/code/kicad/-/issues/13052
2025-07-23 14:28:33 -07:00

234 lines
8.1 KiB
C++

/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
*
* KiCad is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* KiCad is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with KiCad. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include <unordered_map>
#include <unordered_set>
#include <string>
#include <mutex>
#include <set>
#include <sstream>
#include <map>
#include <vector>
// Forward declaration
class SCH_REFERENCE;
/**
* Class to efficiently track reference designators and provide next available designators.
*
* Maintains internal data structures for O(1) lookup of existing designators and efficient
* retrieval of next available numerical suffixes for any given prefix.
*/
class REFDES_TRACKER
{
public:
/**
* Constructor.
*
* @param aThreadSafe if true, enables mutex locking for thread-safe operation
*/
explicit REFDES_TRACKER( bool aThreadSafe = false );
/**
* Insert a reference designator into the tracker.
*
* @param aRefDes the reference designator to insert
* @return true if inserted successfully, false if already exists
*/
bool Insert( const std::string& aRefDes );
/**
* Check if a reference designator exists in the tracker.
*
* @param aRefDes the reference designator to check
* @return true if the reference designator exists
*/
bool Contains( const std::string& aRefDes ) const;
/**
* Get the next available reference designator for a given prefix and reserve it.
*
* The returned designator is automatically inserted into the tracker.
*
* @param aPrefix the alphabetic prefix (e.g., "R", "C", "IC")
* @param aMinValue the minimum numerical value to use (default 1)
* @return the next available number for the given prefix (now reserved)
*/
int GetNextRefDes( const std::string& aPrefix, int aMinValue = 1 );
/**
* Get the next available reference designator number for multi-unit symbols.
*
* Finds the smallest unused reference number for the given prefix, or the smallest
* number where all required units are available. The returned number is automatically
* inserted into the tracker as a base reference designator.
*
* @param aRef the schematic reference to use for prefix and unit filtering
* @param aRefNumberMap map from reference numbers to vectors of SCH_REFERENCE for currently used references
* @param aRequiredUnits vector of unit numbers needed (negative values are ignored)
* @param aMinValue the minimum value to start searching from
* @return the next available reference number
*/
int GetNextRefDesForUnits( const SCH_REFERENCE& aRef,
const std::map<int, std::vector<SCH_REFERENCE>>& aRefNumberMap,
const std::vector<int>& aRequiredUnits,
int aMinValue );
/**
* Serialize the tracker data to a compact string representation.
*
* Uses range notation for consecutive numbers (e.g., "R1-3,R5-7,R10").
*
* @return serialized string representation
*/
std::string Serialize() const;
/**
* Deserialize tracker data from string representation.
*
* @param aData the serialized data string
* @return true if deserialization was successful
*/
bool Deserialize( const std::string& aData );
/**
* Clear all stored reference designators.
*/
void Clear();
/**
* Get the total count of stored reference designators.
*
* @return number of reference designators stored
*/
size_t Size() const;
private:
/**
* Data structure for tracking used numbers and caching next available values.
*/
struct PREFIX_DATA
{
std::set<int> m_usedNumbers; ///< Sorted set of used numbers for this prefix
mutable std::map<int, int> m_nextCache; ///< Cache of next available number for given min values
mutable int m_baseNext; ///< Next available from 1 (cached)
mutable bool m_cacheValid; ///< True if m_baseNext cache is valid
PREFIX_DATA() : m_baseNext( 1 ), m_cacheValid( false ) {}
};
mutable std::mutex m_mutex; ///< Mutex for thread safety
bool m_threadSafe; ///< True if thread safety is enabled
/// Map from prefix to its tracking data
std::unordered_map<std::string, PREFIX_DATA> m_prefixData;
/// Set of all reference designators for O(1) lookup
std::unordered_set<std::string> m_allRefDes;
/**
* Internal implementation of Insert without locking.
*
* @param aRefDes reference designator to insert
* @return true if inserted, false if already exists
*/
bool insertImpl( const std::string& aRefDes );
/**
* Clear all internal data structures without locking.
*
* This is used internally to reset the tracker state.
*/
void clearImpl();
/**
* Parse a reference designator into prefix and numerical suffix.
*
* @param aRefDes the reference designator to parse
* @return pair of (prefix, number) where number is 0 if no numerical suffix
*/
std::pair<std::string, int> parseRefDes( const std::string& aRefDes ) const;
/**
* Update cached next available values when a number is inserted.
*
* @param aData the prefix data to update
* @param aInsertedNumber the number that was just inserted
*/
void updateCacheOnInsert( PREFIX_DATA& aData, int aInsertedNumber ) const;
/**
* Find next available number for a prefix starting from a minimum value.
*
* @param aData the prefix data
* @param aMinValue minimum value to start search from
* @return next available number >= aMinValue
*/
int findNextAvailable( const PREFIX_DATA& aData, int aMinValue ) const;
/**
* Check if all required units are available for a given reference number.
*
* @param aRef the SCH_REFERENCE object to check against
* @param aRefVector vector of SCH_REFERENCE objects for a specific reference number
* @param aRequiredUnits vector of unit numbers needed (negative values ignored)
* @return true if all required units are available (not conflicting)
*/
bool areUnitsAvailable( const SCH_REFERENCE& aRef,
const std::vector<SCH_REFERENCE>& aRefVector,
const std::vector<int>& aRequiredUnits ) const;
/**
* Insert a number for a specific prefix, updating internal structures.
*
* @param aPrefix the prefix
* @param aNumber the number to insert (0 for prefix-only)
* @return true if inserted, false if already exists
*/
bool insertNumber( const std::string& aPrefix, int aNumber );
/**
* Escape special characters for serialization.
*
* @param aStr string to escape
* @return escaped string
*/
std::string escapeForSerialization( const std::string& aStr ) const;
/**
* Unescape special characters from serialization.
*
* @param aStr escaped string
* @return unescaped string
*/
std::string unescapeFromSerialization( const std::string& aStr ) const;
/**
* Split string by delimiter, handling escaped characters.
*
* @param aStr string to split
* @param aDelimiter delimiter character
* @return vector of split parts
*/
std::vector<std::string> splitString( const std::string& aStr, char aDelimiter ) const;
void updateBaseNext( PREFIX_DATA& aData ) const;
};