KiCad PCB EDA Suite
Loading...
Searching...
No Matches
conn_publish.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
22#include <algorithm>
23#include <iterator>
24#include <limits>
25#include <ranges>
26#include <set>
27#include <stdexcept>
28#include <wx/thread.h>
29
30namespace SCH_CONNECTIVITY
31{
32wxString ApplyNameSuffix( const wxString& aName, const BUS_SCHEMA* aSchema, uint32_t aSuffix )
33{
34 wxString result = aName;
35
36 if( aSuffix )
37 {
38 const size_t offset = aSchema && aSchema->shape == BUS_SCHEMA::SHAPE::GROUP && !aSchema->prefix.IsEmpty()
39 ? aSchema->prefixEnd
40 : result.length();
41 result.insert( offset, wxString::Format( "_%u", aSuffix ) );
42 }
43
44 return result;
45}
46
47namespace
48{
49 struct PREDECESSOR
50 {
51 NODE_ID node;
52 size_t weight;
53 };
54
55 struct SUCCESSION
56 {
57 std::vector<NODE_ID> oldNodes;
58 std::vector<NODE_ID> newNodes;
59 };
60
61 using PREDECESSORS = std::map<NODE_ID, PREDECESSOR>;
62
63 struct NODE_LESS
64 {
65 const SESSION_KEYS& keys;
66
67 bool operator()( NODE_ID aLeft, NODE_ID aRight ) const
68 {
69 return keys.Less( keys.Node( aLeft ), keys.Node( aRight ) );
70 }
71 };
72
73 std::vector<NODE_ID> OrderedNodes( const PUBLICATION::COMPONENTS& aComponents, const SESSION_KEYS& aKeys )
74 {
75 const auto nodes = std::views::keys( aComponents );
76 std::vector<NODE_ID> result( nodes.begin(), nodes.end() );
77 std::ranges::sort( result, NODE_LESS{ aKeys } );
78 return result;
79 }
80
81 std::vector<RECORD_KEY> SortedRecords( const PARTITION& aPartition, const SESSION_KEYS& aKeys )
82 {
83 std::vector<RECORD_KEY> result;
84
85 for( const auto& [node, version] : aPartition.identity )
86 {
87 if( const auto* record = std::get_if<RECORD_NODE>( &aKeys.Node( node ) ) )
88 result.push_back( record->record );
89 }
90
91 std::ranges::sort( result, KEY_LESS{ aKeys } );
92 return result;
93 }
94
95 std::vector<SUCCESSION> FindSuccessions( const PUBLICATION::COMPONENTS& aOld, const PUBLICATION::COMPONENTS& aNew,
96 const std::set<NODE_ID>& aUnchanged, PREDECESSORS& aPredecessors,
97 const SESSION_KEYS& aKeys )
98 {
99 std::vector<SUCCESSION> result;
100
101 for( KIND kind : { KIND::BUNDLE, KIND::SIGNAL } )
102 {
103 std::map<ITEM_KEY, NODE_ID, KEY_LESS> items( KEY_LESS{ aKeys } );
104 std::map<SLOT_KEY, NODE_ID, KEY_LESS> slots( KEY_LESS{ aKeys } );
105 std::map<NODE_ID, std::vector<NODE_ID>> oldEdges;
106 std::map<NODE_ID, std::vector<NODE_ID>> newEdges;
107
108 for( const auto& [node, component] : aOld )
109 {
110 if( component.content->kind != kind || aUnchanged.contains( node ) )
111 continue;
112
113 oldEdges[node];
114
115 for( const ITEM_KEY& item : component.content->items )
116 items.emplace( item, node );
117
118 for( const SLOT_KEY& slot : component.content->slots )
119 slots.emplace( slot, node );
120 }
121
122 for( const auto& [node, component] : aNew )
123 {
124 if( component.content->kind != kind )
125 continue;
126
127 if( aUnchanged.contains( node ) )
128 {
129 aPredecessors.emplace( node, PREDECESSOR{ node, component.content->items.size()
130 + component.content->slots.size() } );
131 continue;
132 }
133
134 auto& edges = newEdges[node];
135 std::map<NODE_ID, size_t> weights;
136
137 for( const ITEM_KEY& item : component.content->items )
138 {
139 if( const auto found = items.find( item ); found != items.end() )
140 ++weights[found->second];
141 }
142
143 for( const SLOT_KEY& slot : component.content->slots )
144 {
145 if( const auto found = slots.find( slot ); found != slots.end() )
146 ++weights[found->second];
147 }
148
149 for( const auto& [old, weight] : weights )
150 {
151 edges.push_back( old );
152 oldEdges[old].push_back( node );
153 auto [found, inserted] = aPredecessors.emplace( node, PREDECESSOR{ old, weight } );
154
155 if( !inserted
156 && ( weight > found->second.weight
157 || ( weight == found->second.weight && NODE_LESS{ aKeys }( old, found->second.node ) ) ) )
158 found->second = { old, weight };
159 }
160 }
161
162 std::set<NODE_ID> oldVisited;
163 std::set<NODE_ID> newVisited;
164 const auto visit = [&]( bool aOldSide, NODE_ID aNode )
165 {
166 SUCCESSION group;
167 std::vector<std::pair<bool, NODE_ID>> pending{ { aOldSide, aNode } };
168
169 while( !pending.empty() )
170 {
171 const auto [oldSide, node] = pending.back();
172 pending.pop_back();
173 auto& visited = oldSide ? oldVisited : newVisited;
174
175 if( !visited.insert( node ).second )
176 continue;
177
178 ( oldSide ? group.oldNodes : group.newNodes ).push_back( node );
179
180 for( NODE_ID adjacent : ( oldSide ? oldEdges : newEdges ).at( node ) )
181 pending.emplace_back( !oldSide, adjacent );
182 }
183
184 result.push_back( std::move( group ) );
185 };
186
187 for( const auto& [node, edges] : oldEdges )
188 {
189 if( !oldVisited.contains( node ) )
190 visit( true, node );
191 }
192
193 for( const auto& [node, edges] : newEdges )
194 {
195 if( !newVisited.contains( node ) )
196 visit( false, node );
197 }
198 }
199
200 return result;
201 }
202
203 wxString Suffix( uint32_t aSuffix )
204 {
205 return aSuffix ? wxString::Format( "_%u", aSuffix ) : wxString();
206 }
207
208 wxString BundleScope( const CLAIM& aClaim, const SESSION_KEYS& aKeys )
209 {
210 const wxString& full = aKeys.Name( aClaim.fullName );
211 return full.Left( full.length() - aKeys.Name( aClaim.name ).length() );
212 }
213
214 wxString RenderName( const PUBLISHED_COMPONENT& aComponent, uint32_t aSuffix, const SESSION_KEYS& aKeys )
215 {
216 if( aComponent.content->kind == KIND::BUNDLE
217 && aComponent.content->best->schema->shape == BUS_SCHEMA::SHAPE::GROUP
218 && !aComponent.content->best->schema->prefix.IsEmpty() )
219 {
220 const CLAIM& claim = *aComponent.content->best;
221 return BundleScope( claim, aKeys )
222 + ApplyNameSuffix( aKeys.Name( claim.name ), claim.schema.get(), aSuffix );
223 }
224
225 return aKeys.Name( aComponent.baseName ) + Suffix( aSuffix );
226 }
227
228 void AssignNames( KIND aKind, PUBLICATION::COMPONENTS& aCurrent, const PUBLICATION::COMPONENTS& aOld,
229 const PREDECESSORS& aPredecessors, const std::vector<NODE_ID>& aOrder, SESSION_KEYS& aKeys )
230 {
231 std::map<NAME_ID, std::vector<NODE_ID>> buckets;
232 std::set<NAME_ID> occupied;
233
234 for( NODE_ID node : aOrder )
235 {
236 const auto& component = aCurrent.at( node );
237
238 if( component.content->kind == aKind && component.content->best )
239 buckets[component.collisionBase].push_back( node );
240 else if( component.name != INVALID_ID )
241 occupied.insert( component.name );
242 }
243
244 const auto previous = [&]( NODE_ID node ) -> const PUBLISHED_COMPONENT*
245 {
246 const auto found = aPredecessors.find( node );
247 return found == aPredecessors.end() ? nullptr : &aOld.at( found->second.node );
248 };
249 const auto rendered = [&]( NODE_ID node, uint32_t suffix )
250 {
251 const auto& component = aCurrent.at( node );
252 const auto* old = previous( node );
253
254 if( old && old->baseName == component.baseName && old->collisionBase == component.collisionBase
255 && old->suffix == suffix )
256 {
257 if( aKind == KIND::SIGNAL
258 || ( old->content->best->name == component.content->best->name
259 && old->content->best->schema->shape == component.content->best->schema->shape
260 && old->content->best->schema->prefix == component.content->best->schema->prefix ) )
261 return old->name;
262 }
263
264 return aKeys.InternName( RenderName( component, suffix, aKeys ) );
265 };
266 const CLAIM_LESS less{ aKeys };
267 const NODE_LESS nodeLess{ aKeys };
268 std::vector<NODE_ID> remaining;
269
270 for( const auto& [base, members] : buckets )
271 {
272 const auto preferred = [&]( NODE_ID a, NODE_ID b )
273 {
274 const CLAIM& left = *aCurrent.at( a ).content->best;
275 const CLAIM& right = *aCurrent.at( b ).content->best;
276
277 if( left.Strong() != right.Strong() )
278 return left.Strong();
279
280 if( left.Strong() )
281 {
282 if( less( right, left ) )
283 return true;
284
285 if( less( left, right ) )
286 return false;
287 }
288 else
289 {
290 const auto weight = [&]( NODE_ID node ) -> size_t
291 {
292 const auto* old = previous( node );
293 return old && old->collisionBase == base && old->suffix == 0 ? aPredecessors.at( node ).weight
294 : 0;
295 };
296
297 if( weight( a ) != weight( b ) )
298 return weight( a ) > weight( b );
299 }
300
301 return nodeLess( a, b );
302 };
303
304 const NODE_ID holder = *std::ranges::min_element( members, preferred );
305 auto& component = aCurrent.at( holder );
306 const NAME_ID name = rendered( holder, 0 );
307
308 if( !occupied.insert( name ).second )
309 throw std::invalid_argument( "Distinct collision bases render name: "
310 + std::string( aKeys.Name( name ).utf8_str() ) );
311
312 component.name = name;
313 std::ranges::remove_copy( members, std::back_inserter( remaining ), holder );
314 }
315
316 std::ranges::sort( remaining, nodeLess );
317 const auto assign = [&]( NODE_ID node, uint32_t suffix )
318 {
319 auto& component = aCurrent.at( node );
320 const NAME_ID name = rendered( node, suffix );
321
322 if( !occupied.insert( name ).second )
323 return false;
324
325 component.suffix = suffix;
326 component.name = name;
327 return true;
328 };
329
330 for( NODE_ID node : remaining )
331 {
332 if( const auto* old = previous( node );
333 old && old->suffix && old->collisionBase == aCurrent.at( node ).collisionBase )
334 assign( node, old->suffix );
335 }
336
337 std::map<NAME_ID, uint32_t> nextSuffix;
338
339 for( NODE_ID node : remaining )
340 {
341 if( aCurrent.at( node ).name != INVALID_ID )
342 continue;
343
344 // Different bus expressions can share a collision bucket but have independent rendered suffixes
345 uint32_t& suffix = nextSuffix.try_emplace( aCurrent.at( node ).baseName, 1 ).first->second;
346
347 while( !assign( node, suffix ) )
348 {
349 if( suffix == std::numeric_limits<uint32_t>::max() )
350 throw std::overflow_error( "Connectivity suffix space exhausted" );
351
352 ++suffix;
353 }
354 }
355 }
356
357 CHANGE_SET DescribeChanges( const std::vector<SUCCESSION>& aGroups, const std::set<NODE_ID>& aUnchanged,
358 const PUBLICATION::COMPONENTS& aOld, const PUBLICATION::COMPONENTS& aNew,
359 const SESSION_KEYS& aKeys )
360 {
362 const NAME_LESS nameLess{ &aKeys };
363 const auto names = [&]( const auto& nodes, const auto& components )
364 {
365 std::vector<NAME_ID> values;
366
367 for( NODE_ID node : nodes )
368 {
369 if( const NAME_ID name = components.at( node ).name; name != INVALID_ID )
370 values.push_back( name );
371 }
372
373 std::ranges::sort( values, nameLess );
374 return values;
375 };
376
377 const auto changedNames = [&]( const auto& nodes, const auto& before, const auto& after )
378 {
379 for( NODE_ID node : nodes )
380 {
381 const auto& previous = before.at( node );
382 const auto next = after.find( node );
383
384 if( previous.name != INVALID_ID && ( next == after.end() || previous != next->second ) )
385 result.netsChanged.push_back( previous.name );
386 }
387 };
388
389 for( const SUCCESSION& group : aGroups )
390 {
391 auto old = names( group.oldNodes, aOld );
392 auto current = names( group.newNodes, aNew );
393 changedNames( group.oldNodes, aOld, aNew );
394 changedNames( group.newNodes, aNew, aOld );
395
396 if( old.size() == 1 && current.size() == 1 )
397 {
398 if( old.front() != current.front() )
399 result.renamedNets.emplace_back( old.front(), current.front() );
400 }
401 else if( old.size() > 1 && current.size() == 1 )
402 {
403 result.mergedNets.emplace_back( std::move( old ), current.front() );
404 }
405 else if( old.size() == 1 && current.size() > 1 )
406 {
407 result.splitNets.emplace_back( old.front(), std::move( current ) );
408 }
409 else
410 {
411 result.netsRemoved.insert( result.netsRemoved.end(), old.begin(), old.end() );
412 result.netsAdded.insert( result.netsAdded.end(), current.begin(), current.end() );
413 }
414 }
415
416 for( NODE_ID node : aUnchanged )
417 {
418 const NAME_ID old = aOld.at( node ).name;
419 const NAME_ID current = aNew.at( node ).name;
420
421 if( old != current )
422 {
423 result.renamedNets.emplace_back( old, current );
424 result.netsChanged.push_back( old );
425 result.netsChanged.push_back( current );
426 }
427 }
428
429 std::ranges::sort( result.netsAdded, nameLess );
430 std::ranges::sort( result.netsRemoved, nameLess );
431 std::ranges::sort( result.renamedNets, nameLess, &std::pair<NAME_ID, NAME_ID>::first );
432 std::ranges::sort( result.mergedNets, nameLess, &std::pair<std::vector<NAME_ID>, NAME_ID>::second );
433 std::ranges::sort( result.splitNets, nameLess, &std::pair<NAME_ID, std::vector<NAME_ID>>::first );
434 return result;
435 }
436} // namespace
437
439 const COMPONENT_CACHE<SIGNAL_RESULT>& aSignals,
440 const RECORD_STORE::RECORD_CACHE& aRecords, std::span<const FRAME_INSTANCE> aFrame,
441 const INPUT_STORE& aInputs )
442{
443 wxASSERT( wxThread::IsMain() );
444 COMPONENTS current;
445 std::map<NODE_ID, uint64_t> versions;
446 std::set<NODE_ID> unchanged;
447 SLOT_INPUTS slots( KEY_LESS{ m_keys } );
448 const auto retained = [&]( NODE_ID node, uint64_t version )
449 {
450 versions.emplace( node, version );
451 const auto found = m_versions.find( node );
452
453 if( found != m_versions.end() && found->second == version )
454 {
455 unchanged.insert( node );
456 return m_components.at( node ).content;
457 }
458
459 return std::shared_ptr<const COMPONENT_CONTENT>();
460 };
461
462 for( const auto& entry : aBundles.Entries() )
463 {
464 auto& component = current[entry->input.anchor];
465 component.content = retained( entry->input.anchor, entry->version );
466
467 if( !component.content )
468 {
469 auto content = std::make_shared<COMPONENT_CONTENT>();
470 content->kind = KIND::BUNDLE;
471 content->best = entry->value.canonical;
472 content->items = entry->value.items;
473 content->records = SortedRecords( entry->input, m_keys );
474 content->netclasses = entry->value.netclasses;
475 content->members = entry->value.members;
476 std::ranges::transform( entry->value.slots, std::back_inserter( content->slots ), &SLOT_INPUT::key );
477
478 if( content->best )
479 content->baseName = content->best->fullName;
480
481 component.content = std::move( content );
482 }
483
484 for( const SLOT_INPUT& slot : entry->value.slots )
485 slots.emplace( slot.key, &slot );
486
487 component.baseName = component.content->baseName;
488
489 if( component.content->best )
490 {
491 const CLAIM& claim = *component.content->best;
492
493 if( !claim.schema )
494 throw std::invalid_argument( "Driven bundle publication requires a schema" );
495
496 // Vectors sharing a prefix collide across ranges, but groups collide only on the whole name
497 component.collisionBase =
498 claim.schema->shape == BUS_SCHEMA::SHAPE::VECTOR
499 ? m_keys.InternName( BundleScope( claim, m_keys ) + claim.schema->prefix + "[]" )
500 : claim.fullName;
501 }
502 }
503
504 for( const auto& entry : aSignals.Entries() )
505 {
506 auto& component = current[entry->input.anchor];
507 component.content = retained( entry->input.anchor, entry->version );
508
509 if( !component.content )
510 {
511 auto content = std::make_shared<COMPONENT_CONTENT>();
512 content->best = entry->value.summary.best;
513 content->items = entry->value.items;
514 content->slots = entry->value.slots;
515 content->nameSlot = entry->value.nameSlot;
516 content->netclasses = entry->value.summary.netclasses;
517 content->baseName = entry->value.baseName;
518 content->records = SortedRecords( entry->input, m_keys );
519 component.content = std::move( content );
520 }
521
522 component.baseName = component.content->baseName;
523 }
524
525 PREDECESSORS predecessors;
526 const auto groups = FindSuccessions( m_components, current, unchanged, predecessors, m_keys );
527 const auto order = OrderedNodes( current, m_keys );
528 AssignNames( KIND::BUNDLE, current, m_components, predecessors, order, m_keys );
529
530 for( auto& [node, component] : current )
531 {
532 if( component.content->kind != KIND::SIGNAL )
533 continue;
534
535 if( component.content->nameSlot )
536 {
537 const SLOT_INPUT* naming = slots.at( *component.content->nameSlot );
538 const CLAIM& best = *component.content->best;
539 const auto memberSuffix = [&]( const SLOT_INPUT& slot )
540 {
541 const auto& parent = current.at( slot.parentBundle );
542 const auto& schema = *parent.content->best->schema;
543 return schema.shape == BUS_SCHEMA::SHAPE::GROUP && !schema.prefix.IsEmpty() ? parent.suffix : 0;
544 };
545
546 // Driver ties precede publication, so they cannot see which bundle retained the base
547 for( const SLOT_KEY& key : component.content->slots )
548 {
549 const SLOT_INPUT* candidate = slots.at( key );
550 CLAIM comparable = candidate->claim;
551 comparable.source = best.source;
552
553 if( comparable != best )
554 continue;
555
556 if( memberSuffix( *candidate ) < memberSuffix( *naming )
557 || ( memberSuffix( *candidate ) == memberSuffix( *naming )
558 && m_keys.Less( candidate->key, naming->key ) ) )
559 naming = candidate;
560 }
561
562 component.nameSlot = naming->key;
563 const auto& parent = current.at( naming->parentBundle );
564 const CLAIM& claim = *parent.content->best;
565
566 if( claim.schema->shape == BUS_SCHEMA::SHAPE::GROUP )
567 {
568 const auto old = m_components.find( node );
569 const auto oldParent = m_components.find( naming->parentBundle );
570
571 if( old != m_components.end() && oldParent != m_components.end()
572 && old->second.content == component.content && old->second.nameSlot == component.nameSlot
573 && oldParent->second.content == parent.content && oldParent->second.suffix == parent.suffix )
574 {
575 component.baseName = old->second.baseName;
576 component.collisionBase = component.baseName;
577 continue;
578 }
579
580 wxString prefix = claim.schema->prefix;
581
582 if( !prefix.IsEmpty() )
583 prefix += Suffix( parent.suffix ) + ".";
584
585 // An extra member slot keeps the sheet path of its own declaration, not the canonical one
586 component.baseName = m_keys.InternName( m_keys.Name( naming->claim.path ) + prefix
587 + m_keys.Name( naming->localName ) );
588 }
589 }
590
591 component.collisionBase = component.baseName;
592 }
593
594 AssignNames( KIND::SIGNAL, current, m_components, predecessors, order, m_keys );
595 std::map<NODE_ID, NODE_ID> continuations;
596
597 for( NODE_ID node : order )
598 {
599 const auto pred = predecessors.find( node );
600
601 if( pred == predecessors.end() || !current.at( node ).content->best )
602 continue;
603
604 auto [found, inserted] = continuations.emplace( pred->second.node, node );
605
606 if( !inserted && pred->second.weight > predecessors.at( found->second ).weight )
607 found->second = node;
608 }
609
610 int nextNetCode = m_nextNetCode;
611 uint32_t nextSubgraphCode = m_nextSubgraphCode;
612
613 for( NODE_ID node : order )
614 {
615 auto& component = current.at( node );
616
617 if( !component.content->best )
618 continue;
619
620 const auto pred = predecessors.find( node );
621
622 if( pred != predecessors.end() && continuations.at( pred->second.node ) == node )
623 {
624 const auto& old = m_components.at( pred->second.node );
625 component.netCode = old.netCode;
626 component.subgraphCode = old.subgraphCode;
627 }
628
629 if( !component.subgraphCode )
630 {
631 if( nextSubgraphCode == std::numeric_limits<uint32_t>::max() )
632 throw std::overflow_error( "Connectivity subgraph code space exhausted" );
633
634 component.subgraphCode = nextSubgraphCode++;
635 }
636
637 if( component.content->kind == KIND::SIGNAL && !component.netCode )
638 {
639 if( nextNetCode == std::numeric_limits<int>::max() )
640 throw std::overflow_error( "Connectivity net code space exhausted" );
641
642 component.netCode = nextNetCode++;
643 }
644 }
645
646 CHANGE_SET changes = DescribeChanges( groups, unchanged, m_components, current, m_keys );
647 std::set<NAME_ID, NAME_LESS> changedNames( NAME_LESS{ &m_keys } );
648 const auto collectDependents = [&]( const COMPONENTS& components )
649 {
650 std::vector<NODE_ID> pending;
651 std::set<NODE_ID> visited;
652 const auto add = [&]( NODE_ID node )
653 {
654 if( visited.insert( node ).second )
655 pending.push_back( node );
656 };
657
658 for( NAME_ID name : changes.netsChanged )
659 {
660 if( name == INVALID_ID )
661 continue;
662
663 changedNames.insert( name );
664
665 if( const auto found = m_byName.find( name ); found != m_byName.end() )
666 add( found->second );
667 }
668
669 for( size_t i = 0; i < pending.size(); ++i )
670 {
671 const NODE_ID node = pending[i];
672 const NAME_ID name = components.at( node ).name;
673
674 if( name != INVALID_ID )
675 changedNames.insert( name );
676
677 for( NODE_ID member : MembersOf( node ) )
678 add( member );
679
680 if( const auto signature = m_busSignatures.find( node ); signature != m_busSignatures.end() )
681 {
682 for( NODE_ID equivalent : signature->second->second )
683 add( equivalent );
684 }
685 }
686 };
687
688 ROW_UPDATE rows = PrepareRows( current, aRecords, slots, changes );
689 m_auxiliary.Update( aFrame, aInputs, aRecords, changes );
690
691 for( const ITEM_KEY& item : changes.changedItems )
692 {
693 if( const auto old = m_rows.find( item ); old != m_rows.end() )
694 changes.netsChanged.push_back( old->second.name );
695
696 if( const auto replacement = rows.upserts.find( item ); replacement != rows.upserts.end() )
697 changes.netsChanged.push_back( replacement->second.name );
698 }
699
700 // Removed bus memberships still invalidate the member's containing-bus navigation
701 collectDependents( m_components );
702 ApplyRows( std::move( rows ) );
703 UpdateIndexes( current, slots );
704 collectDependents( current );
705 changes.netsChanged.assign( changedNames.begin(), changedNames.end() );
706 m_components = std::move( current );
707 m_versions = std::move( versions );
708 m_changes = std::move( changes );
709 m_nextNetCode = nextNetCode;
710 m_nextSubgraphCode = nextSubgraphCode;
711}
712
714{
715 m_auxiliary.Clear();
716 m_byName.clear();
717 m_slotComponents.clear();
718 m_busMembers.clear();
719 m_busParents.clear();
720 m_busSignatures.clear();
721 m_equivalentBuses.clear();
722 m_rows.clear();
723 m_rowInputs.clear();
724 m_netclasses.clear();
725 m_components.clear();
726 m_versions.clear();
727 m_changes = {};
728}
729} // namespace SCH_CONNECTIVITY
const char * name
Cache current component evaluations by exact node/version identity, within one stratum and key sessio...
Main-thread extraction cache.
Definition conn_inputs.h:41
ROW_UPDATE PrepareRows(const COMPONENTS &aCurrent, const RECORD_STORE::RECORD_CACHE &aRecords, const SLOT_INPUTS &aSlots, CHANGE_SET &aChanges)
Definition conn_rows.cpp:62
std::span< const NODE_ID > MembersOf(NODE_ID aBundle) const
std::map< NODE_ID, uint64_t > m_versions
void Update(const COMPONENT_CACHE< BUNDLE_BINDING > &aBundles, const COMPONENT_CACHE< SIGNAL_RESULT > &aSignals, const RECORD_STORE::RECORD_CACHE &aRecords, std::span< const FRAME_INSTANCE > aFrame, const INPUT_STORE &aInputs)
EQUIVALENT_BUSES m_equivalentBuses
uint32_t m_nextSubgraphCode
Never reset, so codes do not repeat.
CLASS_ASSIGNMENTS m_netclasses
void ApplyRows(ROW_UPDATE &&aUpdate)
std::map< SLOT_KEY, const SLOT_INPUT *, KEY_LESS > SLOT_INPUTS
std::map< NODE_ID, PUBLISHED_COMPONENT > COMPONENTS
int m_nextNetCode
Never reset, so codes do not repeat.
void UpdateIndexes(const COMPONENTS &aCurrent, const SLOT_INPUTS &aSlots)
CACHE_TABLE< RECORD_KEY, ISLAND_RECORD, KEY_LESS > RECORD_CACHE
Session IDs are dense handles, never a canonical ordering.
Definition conn_keys.h:146
bool equivalent(SIM_MODEL::DEVICE_T a, SIM_MODEL::DEVICE_T b)
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
KIND
The electrical type of a record or component.
Definition conn_keys.h:55
constexpr uint32_t INVALID_ID
Marks an unset handle.
Definition conn_keys.h:47
wxString ApplyNameSuffix(const wxString &aName, const BUS_SCHEMA *aSchema, uint32_t aSuffix)
Add "_N" to aName.
CITER next(CITER it)
Definition ptree.cpp:120
The parsed form of one bus text.
Definition conn_bus.h:48
@ GROUP
A list such as I2C{SDA SCL}. Members align by name with another group.
Definition conn_bus.h:52
@ VECTOR
A range such as D[0..3]. Members align by ordinal.
Definition conn_bus.h:51
wxString prefix
Root prefix, such as D or I2C. It is empty for an unnamed group.
Definition conn_bus.h:92
size_t prefixEnd
End of the root prefix in the escaped input text.
Definition conn_bus.h:97
Difference between two publications.
std::vector< NAME_ID > netsChanged
Old and current names whose membership or presentation inputs changed, with their bus dependents.
std::vector< ITEM_KEY > changedItems
Items whose row, island, rule area, source or text changed.
Strict weak order on claims by value.
Definition conn_claims.h:77
The claim of one item for the name of its island.
Definition conn_claims.h:53
ITEM_KEY source
The claiming item, the last tie-break.
Definition conn_claims.h:60
bool Strong() const
True for a name that the user placed.
Definition conn_claims.h:69
std::shared_ptr< const BUS_SCHEMA > schema
Parsed bus, or null for a signal.
Definition conn_claims.h:65
NAME_ID path
Sheet prefix, or the empty name if unscoped.
Definition conn_claims.h:56
NAME_ID fullName
path followed by name.
Definition conn_claims.h:59
One item or pin in one sheet instance.
Definition conn_keys.h:77
Orders keys by value through SESSION_KEYS::Less().
Definition conn_keys.h:255
Orders name handles by UTF-8 value.
Definition conn_keys.h:246
Exact identity of one connected component.
One published net or bus with its identity.
One bus member as a node of the signal stratum.
SLOT_KEY key
Source item of the declaring claim and the leaf ordinal in its schema.
CLAIM claim
PRIORITY::BUS_MEMBER claim with the leaf name and the path of its owning claim.
NAME_ID localName
Group path and leaf local name joined with dots.
NODE_ID parentBundle
Anchor node of the bus component.
One member position of a bus.
Definition conn_keys.h:127
wxString result
Test unit parsing edge cases and error handling.