KiCad PCB EDA Suite
Loading...
Searching...
No Matches
allegro_db_utils.h
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
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU 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, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24#pragma once
25
31
32#include <concepts>
33#include <cstdint>
34#include <functional>
35#include <optional>
36#include <type_traits>
37#include <unordered_set>
38#include <variant>
39
40#include <ki_exception.h>
41
43#include <convert/allegro_db.h>
44
45#include <wx/log.h>
46#include <wx/string.h>
47
48
49namespace ALLEGRO
50{
51
52inline const wxChar* const traceAllegroUtils = wxT( "KICAD_ALLEGRO_BUILDER" );
53
54
58template <ALLEGRO_BLOCK_DATA BLK_T>
59constexpr bool BlockTypeMatches( uint8_t aType )
60{
61 // Segments can be any of three code (H/oblique/V)
62 if constexpr( std::is_same_v<BLK_T, BLK_0x15_16_17_SEGMENT> )
63 return aType == 0x15 || aType == 0x16 || aType == 0x17;
64 else
65 return aType == BLK_T::BLOCK_TYPE_CODE;
66}
67
73template <ALLEGRO_BLOCK_DATA BLK_T>
74const BLK_T& BlockDataAs( const BLOCK_BASE& aBlock )
75{
76 wxASSERT( BlockTypeMatches<BLK_T>( aBlock.GetBlockType() ) );
77 return static_cast<const BLOCK<BLK_T>&>( aBlock ).GetData();
78}
79
80
87template <ALLEGRO_BLOCK_DATA BLK_T>
89{
90public:
92 m_block( nullptr )
93 {
94 }
95
96 explicit BLOCK_REF( const BLOCK_BASE* aBlock ) :
97 m_block( aBlock )
98 {
99 }
100
105 explicit operator bool() const { return m_block != nullptr; }
106
107 const BLK_T& operator*() const
108 {
109 wxASSERT( m_block != nullptr );
110 return BlockDataAs<BLK_T>( *m_block );
111 }
112
113 const BLK_T* operator->() const
114 {
115 wxASSERT( m_block != nullptr );
116 return &BlockDataAs<BLK_T>( *m_block );
117 }
118
119 const BLOCK_BASE* Block() const { return m_block; }
120
121private:
123};
124
125
134uint32_t GetPrimaryNext( const BLOCK_BASE& aBlock );
135
136
145{
146public:
147 using NEXT_FUNC_T = std::function<uint32_t( const BLOCK_BASE& )>;
148
150 {
151 public:
156 m_current( 0 ),
157 m_currBlock( nullptr ),
158 m_tail( 0 ),
159 m_board( nullptr ),
160 m_nextFunc( nullptr )
161 {
162 }
163
167 ITERATOR( uint32_t aCurrent, uint32_t aTail, const BRD_DB& aBoard,
168 const NEXT_FUNC_T& aNextFunc ) :
169 m_current( aCurrent ),
170 m_tail( aTail ),
171 m_board( &aBoard ),
172 m_nextFunc( &aNextFunc )
173 {
174 m_currBlock = m_board->GetObjectByKey( m_current );
175
176 if( !m_currBlock )
177 m_current = 0;
178 else
179 m_visited.insert( m_current );
180 }
181
182 const BLOCK_BASE* operator*() const
183 {
184 return m_currBlock;
185 }
186
188 {
189 if( m_current == m_tail || !m_currBlock )
190 {
191 m_current = 0;
192 }
193 else
194 {
195 m_current = ( *m_nextFunc )( *m_currBlock );
196
197 if( m_current == m_tail || m_board->IsSentinel( m_current ) )
198 {
199 m_current = 0;
200 }
201 else if( !m_visited.insert( m_current ).second )
202 {
203 THROW_IO_ERROR( wxString::Format( "Cycle detected in linked list at key %#010x", m_current ) );
204 }
205 else
206 {
207 m_currBlock = m_board->GetObjectByKey( m_current );
208
209 if( m_currBlock == nullptr )
210 m_current = 0;
211 }
212 }
213
214 return *this;
215 }
216
217 friend bool operator==( const ITERATOR& aLhs, const ITERATOR& aRhs )
218 {
219 return aLhs.m_current == aRhs.m_current;
220 }
221
222 private:
223 uint32_t m_current;
225 uint32_t m_tail;
228 std::unordered_set<uint32_t> m_visited;
229 };
230
240 LL_WALKER( uint32_t aHead, uint32_t aTail, const BRD_DB& aBoard,
241 NEXT_FUNC_T aNextFunc = GetPrimaryNext ) :
242 m_head( aHead ),
243 m_tail( aTail ),
244 m_board( aBoard ),
245 m_nextFunction( std::move( aNextFunc ) )
246 {
247 }
248
252 LL_WALKER( const FILE_HEADER::LINKED_LIST& aList, const BRD_DB& aBoard,
253 NEXT_FUNC_T aNextFunc = GetPrimaryNext ) :
254 LL_WALKER( aList.m_Head, aList.m_Tail, aBoard, std::move( aNextFunc ) )
255 {
256 }
257
259 {
261 }
262
263 ITERATOR end() const
264 {
265 return ITERATOR{};
266 }
267
268private:
269 uint32_t m_head;
270 uint32_t m_tail;
273};
274
275
286
287
294using MISMATCH_REPORTER = std::function<void( uint8_t aGotType, const BLOCK_BASE& aBlock )>;
295
302template <ALLEGRO_BLOCK_DATA T>
304{
305public:
307 {
308 public:
310 ITERATOR() = default;
311
312 ITERATOR( LL_WALKER::ITERATOR aCurrent, MISMATCH_POLICY aPolicy, const MISMATCH_REPORTER* aReporter ) :
313 m_baseIter( aCurrent ),
314 m_policy( aPolicy ),
315 m_reporter( aReporter )
316 {
318 }
319
320 const T& operator*() const { return BlockDataAs<T>( **m_baseIter ); }
321 const T* operator->() const { return &BlockDataAs<T>( **m_baseIter ); }
322
324 size_t GetBlockOffset() const { return ( *m_baseIter )->GetOffset(); }
325
327 {
328 ++m_baseIter;
330 return *this;
331 }
332
333 friend bool operator==( const ITERATOR& aLhs, const ITERATOR& aRhs )
334 {
335 return aLhs.m_baseIter == aRhs.m_baseIter;
336 }
337
338 private:
340 {
341 while( m_baseIter != LL_WALKER::ITERATOR{} )
342 {
343 const BLOCK_BASE* block = *m_baseIter;
344 const uint8_t blockType = block->GetBlockType();
345
346 if( BlockTypeMatches<T>( blockType ) )
347 break;
348
349 const auto blockTypeDesc = []() -> wxString
350 {
351 if constexpr( std::is_same_v<T, BLK_0x15_16_17_SEGMENT> )
352 return wxS( "0x15/0x16/0x17 (segment)" );
353 else
354 return wxString::Format( wxS( "%#04x" ), T::BLOCK_TYPE_CODE );
355 };
356
357 switch( m_policy )
358 {
361 wxString::Format( "Expected block type %s, got %#04x", blockTypeDesc(), blockType ) );
363 wxLogTrace( traceAllegroUtils, "Expected block type %s, got %#04x, skipping", blockTypeDesc(),
364 blockType );
365 break;
367 if( m_reporter )
368 ( *m_reporter )( blockType, *block );
369 break;
370 case MISMATCH_POLICY::SKIP: break;
371 }
372
373 ++m_baseIter;
374 }
375 }
376
380 };
381
382 TYPED_LL_WALKER( uint32_t aHead, uint32_t aTail, const BRD_DB& aBoard,
385 m_walker( aHead, aTail, aBoard, std::move( aNextFunc ) ),
386 m_policy( aPolicy )
387 {
388 }
389
390 TYPED_LL_WALKER( const FILE_HEADER::LINKED_LIST& aList, const BRD_DB& aBoard,
393 TYPED_LL_WALKER( aList.m_Head, aList.m_Tail, aBoard, aPolicy, std::move( aNextFunc ) )
394 {
395 }
396
398 {
399 m_mismatchReporter = std::move( aReporter );
400 }
401
403 {
404 return ITERATOR( m_walker.begin(), m_policy, &m_mismatchReporter );
405 }
406
407 ITERATOR end() const
408 {
409 return {};
410 }
411
412private:
416};
417
418
422using FIELD_VALUE = std::variant<wxString, uint32_t>;
423
433std::optional<FIELD_VALUE> GetFirstFieldOfType( const BRD_DB& aDb, uint32_t aFieldsPtr, uint32_t aEndKey,
434 uint16_t aFieldCode );
435
439std::optional<int> GetFirstFieldOfTypeInt( const BRD_DB& aDb, uint32_t aFieldsPtr, uint32_t aEndKey,
440 uint16_t aFieldCode );
441
442} // namespace ALLEGRO
The base class for all blocks in the main body of an Allegro file.
uint8_t GetBlockType() const
This is the actual type code as read from the file.
const BLOCK_BASE * m_block
const BLK_T & operator*() const
BLOCK_REF(const BLOCK_BASE *aBlock)
const BLK_T * operator->() const
const BLOCK_BASE * Block() const
An Allegro board database representing the contents of a .brd (and presumably .dra) file.
Definition allegro_db.h:47
const BLOCK_BASE * operator*() const
std::unordered_set< uint32_t > m_visited
friend bool operator==(const ITERATOR &aLhs, const ITERATOR &aRhs)
ITERATOR()
Usually constructed as an end-of-list sentinel.
ITERATOR(uint32_t aCurrent, uint32_t aTail, const BRD_DB &aBoard, const NEXT_FUNC_T &aNextFunc)
Actual object ITERATOR.
Range-for-compatible walker over a linked list of BLOCK_BASE objects in a BRD_DB.
ITERATOR begin() const
const BRD_DB & m_board
LL_WALKER(const FILE_HEADER::LINKED_LIST &aList, const BRD_DB &aBoard, NEXT_FUNC_T aNextFunc=GetPrimaryNext)
Convenience constructor for linked lists in the file header.
std::function< uint32_t(const BLOCK_BASE &)> NEXT_FUNC_T
LL_WALKER(uint32_t aHead, uint32_t aTail, const BRD_DB &aBoard, NEXT_FUNC_T aNextFunc=GetPrimaryNext)
General constructor for walking any linked list given head/tail keys, a database, and a next-function...
ITERATOR end() const
const MISMATCH_REPORTER * m_reporter
size_t GetBlockOffset() const
File offset of the current underlying block (for diagnostics).
friend bool operator==(const ITERATOR &aLhs, const ITERATOR &aRhs)
ITERATOR()=default
End sentinel.
ITERATOR(LL_WALKER::ITERATOR aCurrent, MISMATCH_POLICY aPolicy, const MISMATCH_REPORTER *aReporter)
TYPED_LL_WALKER(const FILE_HEADER::LINKED_LIST &aList, const BRD_DB &aBoard, MISMATCH_POLICY aPolicy=MISMATCH_POLICY::SKIP, LL_WALKER::NEXT_FUNC_T aNextFunc=GetPrimaryNext)
void SetMismatchReporter(MISMATCH_REPORTER aReporter)
TYPED_LL_WALKER(uint32_t aHead, uint32_t aTail, const BRD_DB &aBoard, MISMATCH_POLICY aPolicy=MISMATCH_POLICY::SKIP, LL_WALKER::NEXT_FUNC_T aNextFunc=GetPrimaryNext)
MISMATCH_REPORTER m_mismatchReporter
#define THROW_IO_ERROR(msg)
macro which captures the "call site" values of FILE_, __FUNCTION & LINE
constexpr bool BlockTypeMatches(uint8_t aType)
Check whether a runtime block type code matches the expected type for T.
MISMATCH_POLICY
Policy for TYPED_LL_WALKER when a block with an unexpected type code is encountered.
@ THROW
THROW_IO_ERROR on mismatch.
@ LOG_TRACE
wxLogTrace the mismatch and skip
@ SKIP
silently skip blocks with the wrong type
@ REPORT
invoke a user-supplied callback, then skip
std::optional< FIELD_VALUE > GetFirstFieldOfType(const BRD_DB &aDb, uint32_t aFieldsPtr, uint32_t aEndKey, uint16_t aFieldCode)
Look up the first 0x03 FIELD value of a given type in a linked field chain.
std::optional< int > GetFirstFieldOfTypeInt(const BRD_DB &aDb, uint32_t aFieldsPtr, uint32_t aEndKey, uint16_t aFieldCode)
Convenience wrapper around GetFirstFieldOfType() for integer-valued fields.
uint32_t GetPrimaryNext(const BLOCK_BASE &aBlock)
Get the next block key in the linked list for a given block.
const wxChar *const traceAllegroUtils
std::function< void(uint8_t aGotType, const BLOCK_BASE &aBlock)> MISMATCH_REPORTER
Callback signature for MISMATCH_POLICY::REPORT.
const BLK_T & BlockDataAs(const BLOCK_BASE &aBlock)
Cast a BLOCK_BASE to a typed BLOCK<T> and return the data.
std::variant< wxString, uint32_t > FIELD_VALUE
Some value stored in an 0x03 FIELD block.
STL namespace.
This is apparently some kind of linked list that chains though subsets objects in the file.