7
mirror of https://gitlab.com/kicad/code/kicad.git synced 2024-11-25 00:35:01 +00:00
kicad/pcbnew/pcb_io/altium/altium_pcb_compound_file.cpp
Seth Hillbrand 21900ad3e1 Maintain std::move to avoid copy penalty
Caching lib models is a slow point when loading libraries when many
footprints have embedded models.  We want to speed this by not copying
data unneccesarily.  Some compilers will invalidate the models structure
right-to-left in the function call (the order is unspecified by the
standard), leading to broken model names unless we cache the name before
hand.
2024-07-21 07:23:00 -07:00

191 lines
5.7 KiB
C++

#include <altium_pcb_compound_file.h>
#include <utf.h>
#include <wx/string.h>
#include <compoundfilereader.h>
#include <map>
ALTIUM_PCB_COMPOUND_FILE::ALTIUM_PCB_COMPOUND_FILE( const wxString& aFilePath )
: ALTIUM_COMPOUND_FILE( aFilePath )
{
}
ALTIUM_PCB_COMPOUND_FILE::ALTIUM_PCB_COMPOUND_FILE( const void* aBuffer, size_t aLen )
: ALTIUM_COMPOUND_FILE( aBuffer, aLen )
{
}
ALTIUM_PCB_COMPOUND_FILE::~ALTIUM_PCB_COMPOUND_FILE()
{
}
std::map<wxString, wxString> ALTIUM_PCB_COMPOUND_FILE::ListLibFootprints()
{
if( m_libFootprintDirNameCache.empty() )
cacheLibFootprintNames();
return m_libFootprintDirNameCache;
}
std::tuple<wxString, const CFB::COMPOUND_FILE_ENTRY*>
ALTIUM_PCB_COMPOUND_FILE::FindLibFootprintDirName( const wxString& aFpUnicodeName )
{
if( m_libFootprintNameCache.empty() )
cacheLibFootprintNames();
auto it = m_libFootprintNameCache.find( aFpUnicodeName );
if( it == m_libFootprintNameCache.end() )
return { wxEmptyString, nullptr };
return { it->first, it->second };
}
const std::pair<AMODEL, std::vector<char>>* ALTIUM_PCB_COMPOUND_FILE::GetLibModel( const wxString& aModelName ) const
{
auto it = m_libModelsCache.find( aModelName );
if( it == m_libModelsCache.end() )
return nullptr;
return &it->second;
}
void ALTIUM_PCB_COMPOUND_FILE::cacheLibFootprintNames()
{
m_libFootprintDirNameCache.clear();
m_libFootprintNameCache.clear();
if( !m_reader )
return;
const CFB::COMPOUND_FILE_ENTRY* root = m_reader->GetRootEntry();
if( !root )
return;
m_reader->EnumFiles( root, 1,
[this]( const CFB::COMPOUND_FILE_ENTRY* tentry, const CFB::utf16string& dir, int level ) -> int
{
if( m_reader->IsStream( tentry ) )
return 0;
m_reader->EnumFiles( tentry, 1,
[&]( const CFB::COMPOUND_FILE_ENTRY* entry, const CFB::utf16string&, int ) -> int
{
std::wstring fileName = UTF16ToWstring( entry->name, entry->nameLen );
if( m_reader->IsStream( entry ) && fileName == L"Parameters" )
{
ALTIUM_BINARY_PARSER parametersReader( *this, entry );
std::map<wxString, wxString> parameterProperties =
parametersReader.ReadProperties();
wxString key = ALTIUM_PROPS_UTILS::ReadString(
parameterProperties, wxT( "PATTERN" ), wxT( "" ) );
wxString fpName = ALTIUM_PROPS_UTILS::ReadUnicodeString(
parameterProperties, wxT( "PATTERN" ), wxT( "" ) );
m_libFootprintDirNameCache[key] = fpName;
m_libFootprintNameCache[fpName] = tentry;
}
return 0;
} );
return 0;
} );
}
bool ALTIUM_PCB_COMPOUND_FILE::CacheLibModels()
{
const CFB::COMPOUND_FILE_ENTRY* models_root = nullptr;
const CFB::COMPOUND_FILE_ENTRY* models_data = nullptr;
bool found = false;
if( !m_reader || !m_libModelsCache.empty() )
return false;
models_data = FindStream( { "Library", "Models", "Data" } );
if( !models_data )
return false;
ALTIUM_BINARY_PARSER parser( *this, models_data );
if( parser.GetRemainingBytes() == 0 )
return false;
std::vector<AMODEL> models;
// First, we parse and extract the model data from the Data stream
while( parser.GetRemainingBytes() >= 4 )
{
AMODEL elem( parser );
models.push_back( elem );
}
// Next, we need the model directory entry to read the compressed model streams
m_reader->EnumFiles( m_reader->GetRootEntry(), 2, [&]( const CFB::COMPOUND_FILE_ENTRY* entry, const CFB::utf16string& dir, int ) -> int
{
if( found )
return 1;
if( m_reader->IsStream( entry ) )
return 0;
std::string dir_str = UTF16ToUTF8( dir.c_str() );
std::string entry_str = UTF16ToUTF8( entry->name );
if( dir_str.compare( "Library" ) == 0 && entry_str.compare( "Models" ) == 0 )
{
models_root = entry;
found = true;
return 1;
}
return 0;
});
if( !models_root )
return false;
m_reader->EnumFiles(
models_root, 1,
[&]( const CFB::COMPOUND_FILE_ENTRY* stepEntry, const CFB::utf16string&, int ) -> int
{
long fileNumber;
wxString fileName = UTF16ToUTF8( stepEntry->name, stepEntry->nameLen );
if( !fileName.ToLong( &fileNumber ) )
return 0;
if( !m_reader->IsStream( stepEntry ) || fileNumber >= long( models.size() ) )
return 0;
size_t stepSize = static_cast<size_t>( stepEntry->size );
std::vector<char> stepContent( stepSize );
// read file into buffer
m_reader->ReadFile( stepEntry, 0, stepContent.data(), stepSize );
if( stepContent.empty() )
return 0;
// We store the models in their original compressed form so as to speed the caching process
// When we parse an individual footprint, we decompress and recompress the model data into
// our format
wxString modelName = models[fileNumber].id;
m_libModelsCache.emplace( modelName, std::make_pair( std::move( models[fileNumber] ),
std::move( stepContent ) ) );
return 0;
} );
return true;
}