7
mirror of https://gitlab.com/kicad/code/kicad.git synced 2025-04-11 16:10:10 +00:00

Move LSET to new BASE_SET class

Next step in large layer refactoring.  Added multiple unit tests as well
to check behavior
This commit is contained in:
Seth Hillbrand 2024-07-17 10:31:17 -07:00
parent ae458d0421
commit 7ecde84a94
9 changed files with 385 additions and 68 deletions

View File

@ -43,7 +43,7 @@ static inline size_t hash_board_item( const BOARD_ITEM* aItem, int aFlags )
size_t ret = 0;
if( aFlags & HASH_LAYER )
ret = hash<unsigned long long>{}( aItem->GetLayerSet().to_ullong() );
ret = hash<BASE_SET>{}( aItem->GetLayerSet() );
return ret;
}

View File

@ -22,6 +22,7 @@
#include <vector>
#include <stdexcept>
#include <iterator>
#include <limits>
#if defined( _MSC_VER )
// ssize_t is a POSIX extension
@ -37,30 +38,60 @@ public:
using iterator = std::vector<int>::iterator;
using const_iterator = std::vector<int>::const_iterator;
BASE_SET( size_t size = 0 ) : m_bits( size, 0 ) {}
BASE_SET( size_t size = 64 ) : m_bits( size, 0 ) {}
bool test( size_t pos ) const
{
if( pos >= m_bits.size() )
throw std::out_of_range( "Index out of range" );
return false;
return m_bits[pos] == 1;
}
void set( size_t pos )
{
if( pos >= m_bits.size() )
throw std::out_of_range( "Index out of range" );
bool any() const { return std::any_of( m_bits.begin(), m_bits.end(), []( int bit ) { return bit == 1; } ); }
m_bits[pos] = 1;
bool all() const { return std::all_of( m_bits.begin(), m_bits.end(), []( int bit ) { return bit == 1; } ); }
bool none() const { return std::none_of( m_bits.begin(), m_bits.end(), []( int bit ) { return bit == 1; } ); }
BASE_SET& set( size_t pos = std::numeric_limits<size_t>::max(), bool value = true )
{
if( pos == std::numeric_limits<size_t>::max() )
{
std::fill( m_bits.begin(), m_bits.end(), value ? 1 : 0 );
return *this;
}
if( pos >= m_bits.size() )
m_bits.resize( pos + 1, 0 );
m_bits[pos] = value ? 1 : 0;
return *this;
}
void reset( size_t pos )
BASE_SET& reset( size_t pos = std::numeric_limits<size_t>::max() )
{
if( pos == std::numeric_limits<size_t>::max() )
{
std::fill( m_bits.begin(), m_bits.end(), 0 );
return *this;
}
if( pos >= m_bits.size() )
throw std::out_of_range( "Index out of range" );
m_bits.resize( pos + 1, 0 );
m_bits[pos] = 0;
return *this;
}
BASE_SET& flip( size_t pos = std::numeric_limits<size_t>::max() )
{
if( pos == std::numeric_limits<size_t>::max() )
std::transform( m_bits.begin(), m_bits.end(), m_bits.begin(), []( int bit ) { return bit ^ 1; } );
else
m_bits[pos] ^= 1;
return *this;
}
size_t count() const { return std::count( m_bits.begin(), m_bits.end(), 1 ); }
@ -69,11 +100,101 @@ public:
void resize( size_t newSize ) { m_bits.resize( newSize, 0 ); }
int& operator[]( size_t pos ) { return m_bits[pos]; }
const int& operator[]( size_t pos ) const { return m_bits[pos]; }
int compare( const BASE_SET& other ) const
{
auto result = std::lexicographical_compare_three_way(
m_bits.begin(), m_bits.end(), other.m_bits.begin(), other.m_bits.end() );
return result == std::strong_ordering::equal ? 0 : ( result == std::strong_ordering::less ? -1 : 1 );
}
iterator begin() { return m_bits.begin(); }
iterator end() { return m_bits.end(); }
const_iterator begin() const { return m_bits.begin(); }
const_iterator end() const { return m_bits.end(); }
// Define equality operator
bool operator==( const BASE_SET& other ) const
{
std::size_t minSize = std::min( size(), other.size() );
if( !std::equal( m_bits.begin(), m_bits.begin() + minSize, other.m_bits.begin() ) )
return false;
if( std::any_of( m_bits.begin() + minSize, m_bits.end(), []( int bit ) { return bit != 0; } ) )
return false;
if( std::any_of( other.m_bits.begin() + minSize, other.m_bits.end(), []( int bit ) { return bit != 0; } ) )
return false;
return true;
}
// Define less-than operator for comparison
bool operator<( const BASE_SET& other ) const
{
return std::lexicographical_compare( m_bits.begin(), m_bits.end(), other.m_bits.begin(), other.m_bits.end() );
}
// Define output operator
friend std::ostream& operator<<( std::ostream& os, const BASE_SET& set )
{
return os << set.to_string();
}
// to_string method
template <typename CharT = char>
std::basic_string<CharT> to_string( CharT zero = CharT( '0' ), CharT one = CharT( '1' ) ) const
{
std::basic_string<CharT> result( size(), zero );
for( size_t i = 0; i < size(); ++i )
{
if( test( i ) )
{
result[size() - 1 - i] = one; // Reverse order to match std::bitset behavior
}
}
return result;
}
// Boolean operators
BASE_SET& operator&=( const BASE_SET& rhs )
{
for( size_t i = 0; i < m_bits.size(); ++i )
m_bits[i] &= rhs.m_bits[i];
return *this;
}
BASE_SET& operator|=( const BASE_SET& rhs )
{
for( size_t i = 0; i < m_bits.size(); ++i )
m_bits[i] |= rhs.m_bits[i];
return *this;
}
BASE_SET& operator^=( const BASE_SET& rhs )
{
for( size_t i = 0; i < m_bits.size(); ++i )
m_bits[i] ^= rhs.m_bits[i];
return *this;
}
BASE_SET operator~() const
{
BASE_SET result = *this;
for( size_t i = 0; i < m_bits.size(); ++i )
result.m_bits[i] = !m_bits[i];
return result;
}
// Custom iterator to iterate over set bits
class set_bits_iterator
{
@ -178,4 +299,41 @@ private:
std::vector<int> m_bits;
};
inline BASE_SET operator&( const BASE_SET& lhs, const BASE_SET& rhs )
{
BASE_SET result = lhs;
result &= rhs;
return result;
}
inline BASE_SET operator|( const BASE_SET& lhs, const BASE_SET& rhs )
{
BASE_SET result = lhs;
result |= rhs;
return result;
}
inline BASE_SET operator^( const BASE_SET& lhs, const BASE_SET& rhs )
{
BASE_SET result = lhs;
result ^= rhs;
return result;
}
namespace std
{
template <>
struct hash<BASE_SET>
{
size_t operator()( const BASE_SET& bs ) const
{
size_t hash = 0;
for( const auto& bit : bs )
hash = hash * 31 + std::hash<int>()( bit );
return hash;
}
};
} // namespace std
#endif // BASE_SET_H

View File

@ -20,12 +20,10 @@
#ifndef LSET_H
#define LSET_H
#include <bitset>
#include <layer_ids.h>
#include <base_set.h>
class LSEQ;
typedef std::bitset<PCB_LAYER_ID_COUNT> BASE_SET;
/**
* LSET is a set of PCB_LAYER_IDs. It can be converted to numerous purpose LSEQs using
@ -51,7 +49,7 @@ public:
* Create an empty (cleared) set.
*/
LSET() :
BASE_SET() // all bits are set to zero in BASE_SET()
BASE_SET(PCB_LAYER_ID_COUNT) // all bits are set to zero in BASE_SET()
{
}
@ -61,7 +59,7 @@ public:
}
LSET( PCB_LAYER_ID aLayer ) :
BASE_SET()
BASE_SET(PCB_LAYER_ID_COUNT)
{
set( aLayer );
}

View File

@ -144,7 +144,7 @@ static wxString escapeString( const wxString& aString )
static std::string fmt_mask( LSET aSet )
{
return StrPrintf( "%08x", (unsigned) ( aSet & LSET::AllCuMask() ).to_ulong() );
return ( aSet & LSET::AllCuMask() ).to_string();
}

View File

@ -1377,21 +1377,7 @@ int PAD::Compare( const PAD* aPadRef, const PAD* aPadCmp )
// Dick: specctra_export needs this
// Lorenzo: gencad also needs it to implement padstacks!
#if __cplusplus >= 201103L
long long d = aPadRef->GetLayerSet().to_ullong() - aPadCmp->GetLayerSet().to_ullong();
if( d < 0 )
return -1;
else if( d > 0 )
return 1;
return 0;
#else
// these strings are not typically constructed, since we don't get here often.
std::string s1 = aPadRef->GetLayerSet().to_string();
std::string s2 = aPadCmp->GetLayerSet().to_string();
return s1.compare( s2 );
#endif
return aPadRef->GetLayerSet().compare( aPadCmp->GetLayerSet() );
}
@ -1410,7 +1396,7 @@ wxString PAD::ShowPadShape() const
{
case PAD_SHAPE::CIRCLE: return _( "Circle" );
case PAD_SHAPE::OVAL: return _( "Oval" );
case PAD_SHAPE::RECTANGLE: return _( "Rect" );
case PAD_SHAPE::RECTANGLE: return _( "Rect" );
case PAD_SHAPE::TRAPEZOID: return _( "Trap" );
case PAD_SHAPE::ROUNDRECT: return _( "Roundrect" );
case PAD_SHAPE::CHAMFERED_RECT: return _( "Chamferedrect" );

View File

@ -32,7 +32,6 @@
#include <richio.h>
#include <string>
#include <layer_ids.h>
#include <lset.h>
#include <boost/ptr_container/ptr_map.hpp>
#include <wx_filename.h>
#include "widgets/report_severity.h"
@ -40,6 +39,7 @@
class BOARD;
class BOARD_ITEM;
class FP_CACHE;
class LSET;
class PCB_IO_KICAD_SEXPR_PARSER;
class NETINFO_MAPPING;
class BOARD_DESIGN_SETTINGS;

View File

@ -24,58 +24,233 @@ BOOST_AUTO_TEST_SUITE( BaseSetTests )
BOOST_AUTO_TEST_CASE( ConstructionAndSize )
{
BASE_SET bs(10);
BOOST_CHECK_EQUAL(bs.size(), 10);
BOOST_CHECK_EQUAL(bs.count(), 0);
BASE_SET bs( 10 );
BOOST_CHECK_EQUAL( bs.size(), 10 );
BOOST_CHECK_EQUAL( bs.count(), 0 );
bs.resize(20);
BOOST_CHECK_EQUAL(bs.size(), 20);
BOOST_CHECK_EQUAL(bs.count(), 0);
bs.resize( 20 );
BOOST_CHECK_EQUAL( bs.size(), 20 );
BOOST_CHECK_EQUAL( bs.count(), 0 );
}
BOOST_AUTO_TEST_CASE( BitSettingAndResetting )
{
BASE_SET bs(10);
bs.set(2);
BOOST_CHECK(bs.test(2));
BOOST_CHECK_EQUAL(bs.count(), 1);
BASE_SET bs( 10 );
bs.set( 2 );
BOOST_CHECK( bs.test( 2 ) );
BOOST_CHECK_EQUAL( bs.count(), 1 );
bs.reset(2);
BOOST_CHECK(!bs.test(2));
BOOST_CHECK_EQUAL(bs.count(), 0);
bs.reset( 2 );
BOOST_CHECK( !bs.test( 2 ) );
BOOST_CHECK_EQUAL( bs.count(), 0 );
}
BOOST_AUTO_TEST_CASE( OutOfRange )
BOOST_AUTO_TEST_CASE( SetOutOfRange )
{
BASE_SET bs(10);
BOOST_CHECK_THROW(bs.set(10), std::out_of_range);
BOOST_CHECK_THROW(bs.reset(10), std::out_of_range);
BOOST_CHECK_THROW(bs.test(10), std::out_of_range);
BASE_SET bs( 10 );
BOOST_CHECK_EQUAL( bs.size(), 10 );
BOOST_CHECK_EQUAL( bs.count(), 0 );
bs.set( 10 );
BOOST_CHECK_EQUAL( bs.size(), 11 );
BOOST_CHECK_EQUAL( bs.count(), 1 );
bs.reset( 10 );
BOOST_CHECK_EQUAL( bs.size(), 11 );
BOOST_CHECK_EQUAL( bs.count(), 0 );
bs.reset( 20 );
BOOST_CHECK_EQUAL( bs.size(), 21 );
BOOST_CHECK_EQUAL( bs.count(), 0 );
}
BOOST_AUTO_TEST_CASE( IteratingSetBits )
{
BASE_SET bs(10);
bs.set(2);
bs.set(4);
BASE_SET bs( 10 );
bs.set( 2 );
bs.set( 4 );
auto it = bs.set_bits_begin();
BOOST_CHECK_EQUAL(*it, 2);
BOOST_CHECK_EQUAL( *it, 2 );
++it;
BOOST_CHECK_EQUAL(*it, 4);
BOOST_CHECK_EQUAL( *it, 4 );
++it;
BOOST_CHECK(it == bs.set_bits_end());
BOOST_CHECK( it == bs.set_bits_end() );
// Custom reverse iterator test
std::vector<size_t> reverse_set_bits;
for (auto rit = bs.set_bits_rbegin(); rit != bs.set_bits_rend(); ++rit)
for( auto rit = bs.set_bits_rbegin(); rit != bs.set_bits_rend(); ++rit )
{
reverse_set_bits.push_back(*rit);
reverse_set_bits.push_back( *rit );
}
BOOST_CHECK_EQUAL(reverse_set_bits.size(), 2);
BOOST_CHECK_EQUAL(reverse_set_bits[0], 4);
BOOST_CHECK_EQUAL(reverse_set_bits[1], 2);
BOOST_CHECK_EQUAL( reverse_set_bits.size(), 2 );
BOOST_CHECK_EQUAL( reverse_set_bits[0], 4 );
BOOST_CHECK_EQUAL( reverse_set_bits[1], 2 );
}
// Test equality operator
BOOST_AUTO_TEST_CASE( BASE_SETEqualityOperator )
{
BASE_SET set1( 10 );
BASE_SET set2( 10 );
BASE_SET set3( 15 );
set1.set( 2 );
set1.set( 4 );
set2.set( 2 );
set2.set( 4 );
set3.set( 2 );
BOOST_CHECK( set1 == set2 );
BOOST_CHECK( !( set1 == set3 ) );
BOOST_CHECK( !( set2 == set3 ) );
}
// Test less-than operator
BOOST_AUTO_TEST_CASE(BASE_SETComparisonOperator)
{
BASE_SET set1( 10 );
BASE_SET set2( 10 );
BASE_SET set3( 15 );
set1.set( 2 );
set1.set( 5 );
set2.set( 2 );
set3.set( 2 );
BOOST_CHECK( set3 < set1 ); // Although set3 is larger, set1 has a 1 at position 5, so set3 is less
BOOST_CHECK( set2 < set3 ); // set2 and set3 both have the same values set but set3 has more positions available
BOOST_CHECK( !( set1 < set3 ) );
BOOST_CHECK( !( set1 < set2 ) ); // Although sizes are equal, elements in set2 are subsets of set1
}
// Test boolean operator&=
BOOST_AUTO_TEST_CASE(BASE_SETAndAssignment)
{
BASE_SET bs1( 10 );
BASE_SET bs2( 10 );
bs1.set( 1 );
bs1.set( 3 );
bs2.set( 2 );
bs2.set( 3 );
bs1 &= bs2;
BOOST_CHECK_EQUAL( bs1.test( 1 ), false );
BOOST_CHECK_EQUAL( bs1.test( 2 ), false );
BOOST_CHECK_EQUAL( bs1.test( 3 ), true );
}
// Test boolean operator|=
BOOST_AUTO_TEST_CASE(BASE_SETOrAssignment)
{
BASE_SET bs1( 10 );
BASE_SET bs2( 10 );
bs1.set( 1 );
bs2.set( 2 );
bs2.set( 3 );
bs1 |= bs2;
BOOST_CHECK_EQUAL( bs1.test( 1 ), true );
BOOST_CHECK_EQUAL( bs1.test( 2 ), true );
BOOST_CHECK_EQUAL( bs1.test( 3 ), true );
}
// Test boolean operator^=
BOOST_AUTO_TEST_CASE(BASE_SETXorAssignment)
{
BASE_SET bs1( 10 );
BASE_SET bs2( 10 );
bs1.set( 1 );
bs1.set( 3 );
bs2.set( 2 );
bs2.set( 3 );
bs1 ^= bs2;
BOOST_CHECK_EQUAL( bs1.test( 1 ), true );
BOOST_CHECK_EQUAL( bs1.test( 2 ), true );
BOOST_CHECK_EQUAL( bs1.test( 3 ), false );
}
// Test boolean operator~
BOOST_AUTO_TEST_CASE(BASE_SETNotOperator)
{
BASE_SET bs1( 4 );
bs1.set( 1 );
bs1.set( 3 );
BASE_SET bs2 = ~bs1;
BOOST_CHECK_EQUAL( bs2.test( 0 ), true );
BOOST_CHECK_EQUAL( bs2.test( 1 ), false );
BOOST_CHECK_EQUAL( bs2.test( 2 ), true );
BOOST_CHECK_EQUAL( bs2.test( 3 ), false );
}
// Test non-member operator&
BOOST_AUTO_TEST_CASE(BASE_SETAndOperator)
{
BASE_SET bs1( 10 );
BASE_SET bs2( 10 );
bs1.set( 1 );
bs1.set( 3 );
bs2.set( 2 );
bs2.set( 3 );
BASE_SET result = bs1 & bs2;
BOOST_CHECK_EQUAL( result.test( 1 ), false );
BOOST_CHECK_EQUAL( result.test( 2 ), false );
BOOST_CHECK_EQUAL( result.test( 3 ), true );
}
// Test non-member operator|
BOOST_AUTO_TEST_CASE(BASE_SETOrOperator)
{
BASE_SET bs1( 10 );
BASE_SET bs2( 10 );
bs1.set( 1 );
bs2.set( 2 );
bs2.set( 3 );
BASE_SET result = bs1 | bs2;
BOOST_CHECK_EQUAL( result.test( 1 ), true );
BOOST_CHECK_EQUAL( result.test( 2 ), true );
BOOST_CHECK_EQUAL( result.test( 3 ), true );
}
// Test non-member operator^
BOOST_AUTO_TEST_CASE(BASE_SETXorOperator)
{
BASE_SET bs1( 10 );
BASE_SET bs2( 10 );
bs1.set( 1 );
bs1.set( 3 );
bs2.set( 2 );
bs2.set( 3 );
BASE_SET result = bs1 ^ bs2;
BOOST_CHECK_EQUAL( result.test( 1 ), true );
BOOST_CHECK_EQUAL( result.test( 2 ), true );
BOOST_CHECK_EQUAL( result.test( 3 ), false );
}
// Test std::hash specialization
BOOST_AUTO_TEST_CASE( BASE_SETHash )
{
BASE_SET bs1( 10 );
bs1.set( 1 );
bs1.set( 3 );
std::hash<BASE_SET> hashFn;
size_t hash = hashFn( bs1 );
BASE_SET bs2( 10 );
bs2.set( 1 );
bs2.set( 3 );
BOOST_CHECK_EQUAL( hash, hashFn( bs2 ) );
bs2.set( 2 );
BOOST_CHECK_NE( hash, hashFn( bs2 ) );
}
BOOST_AUTO_TEST_SUITE_END()

View File

@ -104,12 +104,12 @@ BOOST_AUTO_TEST_CASE(LSETFormatting)
LSET set({F_Cu, In1_Cu, In2_Cu});
std::string hexString = set.FmtHex();
std::string expectedHexString = "0000000_00000007"; // depends on bit ordering
std::string expectedHexString = "00000000_00000007"; // depends on bit ordering
BOOST_CHECK_EQUAL(hexString, expectedHexString);
std::string binString = set.FmtBin();
std::string expectedBinString = "0000|0000_0000|0000_0000|0000_0000|0000_0000|0000_0000|0000_0000|0000_0111"; // depends on bit ordering
std::string expectedBinString = "0000_0000|0000_0000|0000_0000|0000_0000|0000_0000|0000_0000|0000_0000|0000_0111"; // depends on bit ordering
BOOST_CHECK_EQUAL(binString, expectedBinString);
}

View File

@ -39,10 +39,10 @@ struct LSETS_TO_TEST
const static std::vector<LSETS_TO_TEST> type_to_ext_cases = {
{ LSET( { F_Cu, F_Fab } ), "0020000_00000001",
"0000|0000_0010|0000_0000|0000_0000|0000_0000|0000_0000|0000_0000|0000_0001" },
{ LSET( { In14_Cu, B_Adhes, Rescue } ), "8000001_00004000",
"1000|0000_0000|0000_0000|0000_0001|0000_0000|0000_0000|0100_0000|0000_0000" }
{ LSET( { F_Cu, F_Fab } ), "00020000_00000001",
"0000_0000|0000_0010|0000_0000|0000_0000|0000_0000|0000_0000|0000_0000|0000_0001" },
{ LSET( { In14_Cu, B_Adhes, Rescue } ), "08000001_00004000",
"0000_1000|0000_0000|0000_0000|0000_0001|0000_0000|0000_0000|0100_0000|0000_0000" }
};