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, see <https://www.gnu.org/licenses/>.
18 */
19
20#pragma once
21
27
28#include <concepts>
29#include <cstdint>
30#include <functional>
31#include <optional>
32#include <type_traits>
33#include <unordered_set>
34#include <variant>
35
36#include <ki_exception.h>
37
39#include <convert/allegro_db.h>
40
41#include <wx/log.h>
42#include <wx/string.h>
43
44
45namespace ALLEGRO
46{
47
48inline const wxChar* const traceAllegroUtils = wxT( "KICAD_ALLEGRO_BUILDER" );
49
50
54template <ALLEGRO_BLOCK_DATA BLK_T>
55constexpr bool BlockTypeMatches( uint8_t aType )
56{
57 // Segments can be any of three code (H/oblique/V)
58 if constexpr( std::is_same_v<BLK_T, BLK_0x15_16_17_SEGMENT> )
59 return aType == 0x15 || aType == 0x16 || aType == 0x17;
60 else
61 return aType == BLK_T::BLOCK_TYPE_CODE;
62}
63
69template <ALLEGRO_BLOCK_DATA BLK_T>
70const BLK_T& BlockDataAs( const BLOCK_BASE& aBlock )
71{
72 wxASSERT( BlockTypeMatches<BLK_T>( aBlock.GetBlockType() ) );
73 return static_cast<const BLOCK<BLK_T>&>( aBlock ).GetData();
74}
75
76
83template <ALLEGRO_BLOCK_DATA BLK_T>
85{
86public:
88 m_block( nullptr )
89 {
90 }
91
92 explicit BLOCK_REF( const BLOCK_BASE* aBlock ) :
93 m_block( aBlock )
94 {
95 }
96
101 explicit operator bool() const { return m_block != nullptr; }
102
103 const BLK_T& operator*() const
104 {
105 wxASSERT( m_block != nullptr );
106 return BlockDataAs<BLK_T>( *m_block );
107 }
108
109 const BLK_T* operator->() const
110 {
111 wxASSERT( m_block != nullptr );
112 return &BlockDataAs<BLK_T>( *m_block );
113 }
114
115 const BLOCK_BASE* Block() const { return m_block; }
116
117private:
119};
120
121
130uint32_t GetPrimaryNext( const BLOCK_BASE& aBlock );
131
132
141{
142public:
143 using NEXT_FUNC_T = std::function<uint32_t( const BLOCK_BASE& )>;
144
146 {
147 public:
152 m_current( 0 ),
153 m_currBlock( nullptr ),
154 m_tail( 0 ),
155 m_board( nullptr ),
156 m_nextFunc( nullptr )
157 {
158 }
159
163 ITERATOR( uint32_t aCurrent, uint32_t aTail, const BRD_DB& aBoard,
164 const NEXT_FUNC_T& aNextFunc ) :
165 m_current( aCurrent ),
166 m_tail( aTail ),
167 m_board( &aBoard ),
168 m_nextFunc( &aNextFunc )
169 {
170 m_currBlock = m_board->GetObjectByKey( m_current );
171
172 if( !m_currBlock )
173 m_current = 0;
174 else
175 m_visited.insert( m_current );
176 }
177
178 const BLOCK_BASE* operator*() const
179 {
180 return m_currBlock;
181 }
182
184 {
185 if( m_current == m_tail || !m_currBlock )
186 {
187 m_current = 0;
188 }
189 else
190 {
191 m_current = ( *m_nextFunc )( *m_currBlock );
192
193 if( m_current == m_tail || m_board->IsSentinel( m_current ) )
194 {
195 m_current = 0;
196 }
197 else if( !m_visited.insert( m_current ).second )
198 {
199 THROW_IO_ERROR( wxString::Format( "Cycle detected in linked list at key %#010x", m_current ) );
200 }
201 else
202 {
203 m_currBlock = m_board->GetObjectByKey( m_current );
204
205 if( m_currBlock == nullptr )
206 m_current = 0;
207 }
208 }
209
210 return *this;
211 }
212
213 friend bool operator==( const ITERATOR& aLhs, const ITERATOR& aRhs )
214 {
215 return aLhs.m_current == aRhs.m_current;
216 }
217
218 private:
219 uint32_t m_current;
221 uint32_t m_tail;
224 std::unordered_set<uint32_t> m_visited;
225 };
226
236 LL_WALKER( uint32_t aHead, uint32_t aTail, const BRD_DB& aBoard,
237 NEXT_FUNC_T aNextFunc = GetPrimaryNext ) :
238 m_head( aHead ),
239 m_tail( aTail ),
240 m_board( aBoard ),
241 m_nextFunction( std::move( aNextFunc ) )
242 {
243 }
244
248 LL_WALKER( const FILE_HEADER::LINKED_LIST& aList, const BRD_DB& aBoard,
249 NEXT_FUNC_T aNextFunc = GetPrimaryNext ) :
250 LL_WALKER( aList.m_Head, aList.m_Tail, aBoard, std::move( aNextFunc ) )
251 {
252 }
253
255 {
257 }
258
259 ITERATOR end() const
260 {
261 return ITERATOR{};
262 }
263
264private:
265 uint32_t m_head;
266 uint32_t m_tail;
269};
270
271
282
283
290using MISMATCH_REPORTER = std::function<void( uint8_t aGotType, const BLOCK_BASE& aBlock )>;
291
298template <ALLEGRO_BLOCK_DATA T>
300{
301public:
303 {
304 public:
306 ITERATOR() = default;
307
308 ITERATOR( LL_WALKER::ITERATOR aCurrent, MISMATCH_POLICY aPolicy, const MISMATCH_REPORTER* aReporter ) :
309 m_baseIter( aCurrent ),
310 m_policy( aPolicy ),
311 m_reporter( aReporter )
312 {
314 }
315
316 const T& operator*() const { return BlockDataAs<T>( **m_baseIter ); }
317 const T* operator->() const { return &BlockDataAs<T>( **m_baseIter ); }
318
320 size_t GetBlockOffset() const { return ( *m_baseIter )->GetOffset(); }
321
323 {
324 ++m_baseIter;
326 return *this;
327 }
328
329 friend bool operator==( const ITERATOR& aLhs, const ITERATOR& aRhs )
330 {
331 return aLhs.m_baseIter == aRhs.m_baseIter;
332 }
333
334 private:
336 {
337 while( m_baseIter != LL_WALKER::ITERATOR{} )
338 {
339 const BLOCK_BASE* block = *m_baseIter;
340 const uint8_t blockType = block->GetBlockType();
341
342 if( BlockTypeMatches<T>( blockType ) )
343 break;
344
345 const auto blockTypeDesc = []() -> wxString
346 {
347 if constexpr( std::is_same_v<T, BLK_0x15_16_17_SEGMENT> )
348 return wxS( "0x15/0x16/0x17 (segment)" );
349 else
350 return wxString::Format( wxS( "%#04x" ), T::BLOCK_TYPE_CODE );
351 };
352
353 switch( m_policy )
354 {
357 wxString::Format( "Expected block type %s, got %#04x", blockTypeDesc(), blockType ) );
359 wxLogTrace( traceAllegroUtils, "Expected block type %s, got %#04x, skipping", blockTypeDesc(),
360 blockType );
361 break;
363 if( m_reporter )
364 ( *m_reporter )( blockType, *block );
365 break;
366 case MISMATCH_POLICY::SKIP: break;
367 }
368
369 ++m_baseIter;
370 }
371 }
372
376 };
377
378 TYPED_LL_WALKER( uint32_t aHead, uint32_t aTail, const BRD_DB& aBoard,
381 m_walker( aHead, aTail, aBoard, std::move( aNextFunc ) ),
382 m_policy( aPolicy )
383 {
384 }
385
386 TYPED_LL_WALKER( const FILE_HEADER::LINKED_LIST& aList, const BRD_DB& aBoard,
389 TYPED_LL_WALKER( aList.m_Head, aList.m_Tail, aBoard, aPolicy, std::move( aNextFunc ) )
390 {
391 }
392
394 {
395 m_mismatchReporter = std::move( aReporter );
396 }
397
399 {
400 return ITERATOR( m_walker.begin(), m_policy, &m_mismatchReporter );
401 }
402
403 ITERATOR end() const
404 {
405 return {};
406 }
407
408private:
412};
413
414
418using FIELD_VALUE = std::variant<wxString, uint32_t>;
419
429std::optional<FIELD_VALUE> GetFirstFieldOfType( const BRD_DB& aDb, uint32_t aFieldsPtr, uint32_t aEndKey,
430 uint16_t aFieldCode );
431
435std::optional<int> GetFirstFieldOfTypeInt( const BRD_DB& aDb, uint32_t aFieldsPtr, uint32_t aEndKey,
436 uint16_t aFieldCode );
437
438} // 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:43
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.