KiCad PCB EDA Suite
Loading...
Searching...
No Matches
conn_indexes.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#include "conn_publish.h"
21#include <algorithm>
22#include <stdexcept>
23
24namespace SCH_CONNECTIVITY
25{
26std::optional<NODE_ID> PUBLICATION::FindByName( const wxString& aName ) const
27{
28 const auto name = m_keys.FindName( aName );
29
30 if( !name )
31 return std::nullopt;
32
33 const auto found = m_byName.find( *name );
34 return found == m_byName.end() ? std::nullopt : std::optional<NODE_ID>( found->second );
35}
36
37std::span<const NODE_ID> PUBLICATION::MembersOf( NODE_ID aBundle ) const
38{
39 const auto found = m_busMembers.find( aBundle );
40 return found == m_busMembers.end() ? std::span<const NODE_ID>() : found->second;
41}
42
43std::span<const NODE_ID> PUBLICATION::ParentsOf( NODE_ID aSignal ) const
44{
45 const auto found = m_busParents.find( aSignal );
46 return found == m_busParents.end() ? std::span<const NODE_ID>() : found->second;
47}
48
49std::vector<wxString> PUBLICATION::EquivalentBusNames( const wxString& aName ) const
50{
51 const auto node = FindByName( aName );
52
53 if( !node )
54 return {};
55
56 const auto signature = m_busSignatures.find( *node );
57
58 if( signature == m_busSignatures.end() )
59 return {};
60
61 std::vector<NAME_ID> names;
62
63 for( NODE_ID other : signature->second->second )
64 {
65 if( other != *node )
66 names.push_back( m_components.at( other ).name );
67 }
68
69 std::ranges::sort( names, NAME_LESS{ &m_keys } );
70 std::vector<wxString> result;
71 result.reserve( names.size() );
72
73 for( NAME_ID name : names )
74 result.push_back( m_keys.Name( name ) );
75
76 return result;
77}
78
79void PUBLICATION::UpdateBusSignatures( const COMPONENTS& aCurrent, const SLOT_INPUTS& aSlots )
80{
81 for( auto it = m_busSignatures.begin(); it != m_busSignatures.end(); )
82 {
83 const auto current = aCurrent.find( it->first );
84
85 if( current != aCurrent.end() && current->second.content == m_components.at( it->first ).content )
86 {
87 ++it;
88 continue;
89 }
90
91 const auto group = it->second;
92 group->second.erase( it->first );
93
94 if( group->second.empty() )
95 m_equivalentBuses.erase( group );
96
97 it = m_busSignatures.erase( it );
98 }
99
100 for( const auto& [node, component] : aCurrent )
101 {
102 if( component.content->kind != KIND::BUNDLE || m_busSignatures.contains( node ) )
103 continue;
104
105 const auto& best = component.content->best;
106
107 if( !best || !best->schema || best->schema->shape != BUS_SCHEMA::SHAPE::GROUP
108 || !best->schema->prefix.IsEmpty() )
109 continue;
110
111 BUS_SIGNATURE signature{ best->path, {} };
112 signature.second.reserve( component.content->members.size() );
113
114 for( const SLOT_KEY& slot : component.content->members )
115 signature.second.push_back( aSlots.at( slot )->claim.name );
116
117 // Private lookup identity only; query results use canonical published-name ordering
118 std::sort( signature.second.begin(), signature.second.end() );
119 const auto group = m_equivalentBuses.try_emplace( std::move( signature ) ).first;
120 group->second.insert( node );
121 m_busSignatures.emplace( node, group );
122 }
123}
124
125void PUBLICATION::UpdateIndexes( const COMPONENTS& aCurrent, const SLOT_INPUTS& aSlots )
126{
127 const auto nodeLess = [&]( NODE_ID a, NODE_ID b )
128 {
129 return m_keys.Less( m_keys.Node( a ), m_keys.Node( b ) );
130 };
131 const auto setParents = [&]( NODE_ID node, std::vector<NODE_ID> parents )
132 {
133 const auto before = ParentsOf( node );
134
135 if( std::ranges::equal( before, parents ) )
136 return;
137
138 for( NODE_ID parent : before )
139 {
140 if( !std::binary_search( parents.begin(), parents.end(), parent, nodeLess ) )
141 {
142 auto found = m_busMembers.find( parent );
143
144 if( found == m_busMembers.end() )
145 throw std::invalid_argument( "Published bundle membership is missing its reverse entry" );
146
147 std::erase( found->second, node );
148
149 if( found->second.empty() )
150 m_busMembers.erase( found );
151 }
152 }
153
154 for( NODE_ID parent : parents )
155 {
156 if( !std::binary_search( before.begin(), before.end(), parent, nodeLess ) )
157 {
158 auto& members = m_busMembers[parent];
159 members.insert( std::lower_bound( members.begin(), members.end(), node, nodeLess ), node );
160 }
161 }
162
163 if( parents.empty() )
164 m_busParents.erase( node );
165 else
166 m_busParents.insert_or_assign( node, std::move( parents ) );
167 };
168
169 // Remove old keys first so names and slots can transfer between surviving components
170 for( const auto& [node, component] : m_components )
171 {
172 const auto current = aCurrent.find( node );
173
174 if( component.name != INVALID_ID && ( current == aCurrent.end() || current->second.name != component.name ) )
175 m_byName.erase( component.name );
176
177 if( component.content->kind != KIND::SIGNAL )
178 continue;
179
180 if( current == aCurrent.end()
181 || ( component.content != current->second.content
182 && component.content->slots != current->second.content->slots ) )
183 {
184 for( const SLOT_KEY& slot : component.content->slots )
185 m_slotComponents.erase( slot );
186 }
187
188 if( current == aCurrent.end() )
189 setParents( node, {} );
190 }
191
192 for( const auto& [node, component] : aCurrent )
193 {
194 const auto old = m_components.find( node );
195
196 if( component.name != INVALID_ID && ( old == m_components.end() || old->second.name != component.name ) )
197 {
198 const auto [found, inserted] = m_byName.emplace( component.name, node );
199
200 if( !inserted && found->second != node )
201 throw std::invalid_argument( "Published name belongs to multiple components: "
202 + m_keys.Name( component.name ).utf8_string() );
203 }
204
205 if( component.content->kind != KIND::SIGNAL
206 || ( old != m_components.end() && old->second.content == component.content ) )
207 continue;
208
209 const bool slotsChanged = old == m_components.end() || old->second.content->slots != component.content->slots;
210 std::vector<NODE_ID> parents;
211 parents.reserve( component.content->slots.size() );
212
213 for( const SLOT_KEY& slot : component.content->slots )
214 {
215 if( slotsChanged )
216 {
217 const auto [found, inserted] = m_slotComponents.emplace( slot, node );
218
219 if( !inserted && found->second != node )
220 throw std::invalid_argument( "Published slot belongs to multiple signal components" );
221 }
222
223 const NODE_ID parent = aSlots.at( slot )->parentBundle;
224 const auto bundle = aCurrent.find( parent );
225
226 if( bundle == aCurrent.end() || bundle->second.content->kind != KIND::BUNDLE )
227 throw std::invalid_argument( "Published slot has no current bundle parent" );
228
229 parents.push_back( parent );
230 }
231
232 std::sort( parents.begin(), parents.end(), nodeLess );
233 parents.erase( std::unique( parents.begin(), parents.end() ), parents.end() );
234 setParents( node, std::move( parents ) );
235 }
236
237 UpdateBusSignatures( aCurrent, aSlots );
238}
239} // namespace SCH_CONNECTIVITY
const char * name
std::span< const NODE_ID > MembersOf(NODE_ID aBundle) const
EQUIVALENT_BUSES m_equivalentBuses
std::pair< NAME_ID, std::vector< NAME_ID > > BUS_SIGNATURE
std::vector< wxString > EquivalentBusNames(const wxString &aName) const
std::map< SLOT_KEY, const SLOT_INPUT *, KEY_LESS > SLOT_INPUTS
std::map< NODE_ID, PUBLISHED_COMPONENT > COMPONENTS
std::span< const NODE_ID > ParentsOf(NODE_ID aSignal) const
void UpdateBusSignatures(const COMPONENTS &aCurrent, const SLOT_INPUTS &aSlots)
void UpdateIndexes(const COMPONENTS &aCurrent, const SLOT_INPUTS &aSlots)
std::optional< NODE_ID > FindByName(const wxString &aName) const
Value keys and the key session of the schematic connectivity engine.
uint32_t NAME_ID
Session handle of a name, ordered by UTF-8 value through NAME_LESS.
Definition conn_keys.h:42
uint32_t NODE_ID
Session handle of a NODE_KEY graph node.
Definition conn_keys.h:43
constexpr uint32_t INVALID_ID
Marks an unset handle.
Definition conn_keys.h:47
@ GROUP
A list such as I2C{SDA SCL}. Members align by name with another group.
Definition conn_bus.h:52
Orders name handles by UTF-8 value.
Definition conn_keys.h:246
One member position of a bus.
Definition conn_keys.h:127
wxString result
Test unit parsing edge cases and error handling.