← Back to team overview

kicad-developers team mailing list archive

LIB_PART data storage refactor

 

Hi,

In the attachment you will find a patch that refactors data storage in
LIB_PART. Instead of keeping all drawings in a
boost::ptr_vector<LIB_ITEM>, the items are stored in an integer to
LIB_ITEMS map. Thanks to that, it is no longer necessary to call
LIB_ITEMS::sort() on every modification, so the result is ~33% library
loading. I see some other places where the performance could be enhanced
thanks to the refactor, so if this change works then I will try to
improve other areas.

I would appreciate a bit of help from brave testers that could take the
patch for a test drive and confirm whether it satisfies expectations.

Regards,
Orson
>From 97b150e113d3922e24913378cbcf4f341d25b1fe Mon Sep 17 00:00:00 2001
From: Maciej Suminski <maciej.suminski@xxxxxxx>
Date: Mon, 14 Aug 2017 16:53:25 +0200
Subject: [PATCH] Refactored LIB_PART data storage

Instead of keeping all items in a boost::ptr_vector(), LIB_ITEMs are now
stored in an integer (KICAD_T) to LIB_ITEMS map.

The map allows to quickly access a subset of items of given type.
As the items are stored per type, there is no need to call
LIB_ITEMS::sort() to assure the correct drawing order. As a result,
libraries load faster.

To retain the old interface, there is a LIB_ITEMS_LIST wrapper for
the map, allowing the developers to access the items as if it was a flat
list-like structure.
---
 eeschema/CMakeLists.txt     |   1 +
 eeschema/class_libentry.cpp | 114 +++++++-----------------
 eeschema/class_libentry.h   |  20 ++++-
 eeschema/lib_collectors.cpp |   3 +-
 eeschema/lib_collectors.h   |   3 +-
 eeschema/lib_draw_item.h    |  10 ---
 eeschema/lib_items.cpp      | 211 ++++++++++++++++++++++++++++++++++++++++++++
 eeschema/lib_items.h        | 128 +++++++++++++++++++++++++++
 eeschema/symbdraw.cpp       |   3 -
 eeschema/symbedit.cpp       |   4 +-
 10 files changed, 396 insertions(+), 101 deletions(-)
 create mode 100644 eeschema/lib_items.cpp
 create mode 100644 eeschema/lib_items.h

diff --git a/eeschema/CMakeLists.txt b/eeschema/CMakeLists.txt
index dc7c0c050..b369a7ab4 100644
--- a/eeschema/CMakeLists.txt
+++ b/eeschema/CMakeLists.txt
@@ -138,6 +138,7 @@ set( EESCHEMA_SRCS
     lib_draw_item.cpp
     lib_export.cpp
     lib_field.cpp
+    lib_items.cpp
     lib_pin.cpp
     lib_polyline.cpp
     lib_rectangle.cpp
diff --git a/eeschema/class_libentry.cpp b/eeschema/class_libentry.cpp
index fd01fff55..7b2bbc886 100644
--- a/eeschema/class_libentry.cpp
+++ b/eeschema/class_libentry.cpp
@@ -178,7 +178,7 @@ struct null_deleter
 
 LIB_PART::LIB_PART( const wxString& aName, PART_LIB* aLibrary ) :
     EDA_ITEM( LIB_PART_T ),
-    m_me( this, null_deleter() )
+    m_me( this, null_deleter() ), drawings( drawingsMap )
 {
     m_name                = aName;
     m_library             = aLibrary;
@@ -198,17 +198,17 @@ LIB_PART::LIB_PART( const wxString& aName, PART_LIB* aLibrary ) :
     // when the field editors are invoked.
     LIB_FIELD* value = new LIB_FIELD( this, VALUE );
     value->SetText( aName );
-    drawings.push_back( value );
+    drawingsMap[LIB_FIELD_T].push_back( value );
 
-    drawings.push_back( new LIB_FIELD( this, REFERENCE ) );
-    drawings.push_back( new LIB_FIELD( this, FOOTPRINT ) );
-    drawings.push_back( new LIB_FIELD( this, DATASHEET ) );
+    drawingsMap[LIB_FIELD_T].push_back( new LIB_FIELD( this, REFERENCE ) );
+    drawingsMap[LIB_FIELD_T].push_back( new LIB_FIELD( this, FOOTPRINT ) );
+    drawingsMap[LIB_FIELD_T].push_back( new LIB_FIELD( this, DATASHEET ) );
 }
 
 
 LIB_PART::LIB_PART( LIB_PART& aPart, PART_LIB* aLibrary ) :
     EDA_ITEM( aPart ),
-    m_me( this, null_deleter() )
+    m_me( this, null_deleter() ), drawings( drawingsMap )
 {
     LIB_ITEM* newItem;
 
@@ -223,7 +223,7 @@ LIB_PART::LIB_PART( LIB_PART& aPart, PART_LIB* aLibrary ) :
     m_dateModified        = aPart.m_dateModified;
     m_options             = aPart.m_options;
 
-    for( LIB_ITEM& oldItem : aPart.GetDrawItemList() )
+    for( LIB_ITEM& oldItem : aPart.drawings )
     {
         if( oldItem.IsNew() )
             continue;
@@ -351,7 +351,7 @@ void LIB_PART::Draw( EDA_DRAW_PANEL* aPanel, wxDC* aDc, const wxPoint& aOffset,
             if( drawItem.Type() == LIB_FIELD_T )
                 continue;
 
-            if( drawItem.Type() == LIB_FIELD_T )
+            if( drawItem.Type() == LIB_FIELD_T )        // TODO dead code?
             {
                 drawItem.Draw( aPanel, aDc, aOffset, aOpts.color,
                                aOpts.draw_mode, (void*) NULL, aOpts.transform );
@@ -428,7 +428,6 @@ void LIB_PART::Draw( EDA_DRAW_PANEL* aPanel, wxDC* aDc, const wxPoint& aOffset,
                            aOpts.draw_mode, (void*) forceNoFill,
                            aOpts.transform );
         }
-
     }
 
     // Enable this to draw the anchor of the component.
@@ -553,9 +552,9 @@ void LIB_PART::RemoveDrawItem( LIB_ITEM* aItem, EDA_DRAW_PANEL* aPanel, wxDC* aD
         }
     }
 
-    LIB_ITEMS::iterator i;
+    LIB_ITEMS& items = drawingsMap[aItem->Type()];
 
-    for( i = drawings.begin(); i != drawings.end(); i++ )
+    for( LIB_ITEMS::iterator i = items.begin(); i != items.end(); i++ )
     {
         if( *i == aItem )
         {
@@ -563,7 +562,7 @@ void LIB_PART::RemoveDrawItem( LIB_ITEM* aItem, EDA_DRAW_PANEL* aPanel, wxDC* aD
                 aItem->Draw( aPanel, aDc, wxPoint( 0, 0 ), COLOR4D::UNSPECIFIED,
                              g_XorMode, NULL, DefaultTransform );
 
-            drawings.erase( i );
+            items.erase( i );
             SetModified();
             break;
         }
@@ -576,7 +575,6 @@ void LIB_PART::AddDrawItem( LIB_ITEM* aItem )
     wxASSERT( aItem != NULL );
 
     drawings.push_back( aItem );
-    drawings.sort();
 }
 
 
@@ -619,17 +617,17 @@ LIB_ITEM* LIB_PART::GetNextDrawItem( LIB_ITEM* aItem, KICAD_T aType )
 
 void LIB_PART::GetPins( LIB_PINS& aList, int aUnit, int aConvert )
 {
+    if( drawingsMap.count( LIB_PIN_T ) == 0 )
+        return;
+
     /* Notes:
      * when aUnit == 0: no unit filtering
      * when aConvert == 0: no convert (shape selection) filtering
      * when .m_Unit == 0, the body item is common to units
      * when .m_Convert == 0, the body item is common to shapes
      */
-    for( LIB_ITEM& item : drawings )
+    for( LIB_ITEM& item : drawingsMap[LIB_PIN_T] )
     {
-        if( item.Type() != LIB_PIN_T )    // we search pins only
-            continue;
-
         // Unit filtering:
         if( aUnit && item.m_Unit && ( item.m_Unit != aUnit ) )
              continue;
@@ -994,7 +992,6 @@ bool LIB_PART::Load( LINE_READER& aLineReader, wxString& aErrorMsg )
 
 ok:
     // If we are here, this part is O.k. - put it in:
-    drawings.sort();
 
     return true;
 }
@@ -1132,7 +1129,7 @@ bool LIB_PART::LoadField( LINE_READER& aLineReader, wxString& aErrorMsg )
     }
     else
     {
-        drawings.push_back( field );
+        drawingsMap[LIB_FIELD_T].push_back( field );
     }
 
     return true;
@@ -1169,10 +1166,8 @@ const EDA_RECT LIB_PART::GetUnitBoundingBox( int aUnit, int aConvert ) const
     EDA_RECT bBox;
     bool initialized = false;
 
-    for( unsigned ii = 0; ii < drawings.size(); ii++  )
+    for( const LIB_ITEM& item : drawings )
     {
-        const LIB_ITEM& item = drawings[ii];
-
         if( ( item.m_Unit > 0 ) && ( ( m_unitCount > 1 ) && ( aUnit > 0 )
                                      && ( aUnit != item.m_Unit ) ) )
             continue;
@@ -1201,10 +1196,8 @@ const EDA_RECT LIB_PART::GetBodyBoundingBox( int aUnit, int aConvert ) const
     EDA_RECT bBox;
     bool initialized = false;
 
-    for( unsigned ii = 0; ii < drawings.size(); ii++  )
+    for( const LIB_ITEM& item : drawings )
     {
-        const LIB_ITEM& item = drawings[ii];
-
         if( ( item.m_Unit > 0 ) && ( ( m_unitCount > 1 ) && ( aUnit > 0 )
                                      && ( aUnit != item.m_Unit ) ) )
             continue;
@@ -1230,19 +1223,7 @@ const EDA_RECT LIB_PART::GetBodyBoundingBox( int aUnit, int aConvert ) const
 
 void LIB_PART::deleteAllFields()
 {
-    LIB_ITEMS::iterator it;
-
-    for( it = drawings.begin();  it != drawings.end();  /* deleting */  )
-    {
-        if( it->Type() != LIB_FIELD_T  )
-        {
-            ++it;
-            continue;
-        }
-
-        // 'it' is not advanced, but should point to next in list after erase()
-        it = drawings.erase( it );
-    }
+    drawingsMap[LIB_FIELD_T].clear();
 }
 
 
@@ -1256,12 +1237,8 @@ void LIB_PART::SetFields( const std::vector <LIB_FIELD>& aFields )
         LIB_FIELD* field = new LIB_FIELD( aFields[i] );
 
         field->SetParent( this );
-        drawings.push_back( field );
+        drawingsMap[LIB_FIELD_T].push_back( field );
     }
-
-    // Reorder drawings: transparent polygons first, pins and text last.
-    // so texts have priority on screen.
-    drawings.sort();
 }
 
 
@@ -1285,11 +1262,8 @@ void LIB_PART::GetFields( LIB_FIELDS& aList )
     }
 
     // Now grab all the rest of fields.
-    for( LIB_ITEM& item : drawings )
+    for( LIB_ITEM& item : drawingsMap[LIB_FIELD_T] )
     {
-        if( item.Type() != LIB_FIELD_T )
-            continue;
-
         field = ( LIB_FIELD* ) &item;
 
         if( (unsigned) field->GetId() < MANDATORY_FIELDS )
@@ -1302,11 +1276,8 @@ void LIB_PART::GetFields( LIB_FIELDS& aList )
 
 LIB_FIELD* LIB_PART::GetField( int aId )
 {
-    for( LIB_ITEM& item : drawings )
+    for( LIB_ITEM& item : drawingsMap[LIB_FIELD_T] )
     {
-        if( item.Type() != LIB_FIELD_T )
-            continue;
-
         LIB_FIELD* field = ( LIB_FIELD* ) &item;
 
         if( field->GetId() == aId )
@@ -1319,11 +1290,8 @@ LIB_FIELD* LIB_PART::GetField( int aId )
 
 LIB_FIELD* LIB_PART::FindField( const wxString& aFieldName )
 {
-    for( LIB_ITEM& item : drawings )
+    for( LIB_ITEM& item : drawingsMap[LIB_FIELD_T] )
     {
-        if( item.Type() != LIB_FIELD_T )
-            continue;
-
         LIB_FIELD* field = ( LIB_FIELD* ) &item;
 
         if( field->GetName() == aFieldName )
@@ -1400,23 +1368,21 @@ bool LIB_PART::LoadDateAndTime( char* aLine )
 void LIB_PART::SetOffset( const wxPoint& aOffset )
 {
     for( LIB_ITEM& item : drawings )
-    {
         item.SetOffset( aOffset );
-    }
 }
 
 
 void LIB_PART::RemoveDuplicateDrawItems()
 {
-    drawings.unique();
+    for( auto& itemTypes : drawingsMap )
+        itemTypes.second.unique();
 }
 
 
 bool LIB_PART::HasConversion() const
 {
-    for( unsigned ii = 0; ii < drawings.size(); ii++  )
+    for( const LIB_ITEM& item : drawings )
     {
-        const LIB_ITEM& item = drawings[ii];
         if( item.m_Convert > 1 )
             return true;
     }
@@ -1475,8 +1441,6 @@ void LIB_PART::MoveSelectedItems( const wxPoint& aOffset )
         item.SetOffset( aOffset );
         item.m_Flags = 0;
     }
-
-    drawings.sort();
 }
 
 
@@ -1491,7 +1455,7 @@ void LIB_PART::ClearSelectedItems()
 
 void LIB_PART::DeleteSelectedItems()
 {
-    LIB_ITEMS::iterator item = drawings.begin();
+    LIB_ITEMS_LIST::ITERATOR item = drawings.begin();
 
     // We *do not* remove the 2 mandatory fields: reference and value
     // so skip them (do not remove) if they are flagged selected.
@@ -1512,7 +1476,7 @@ void LIB_PART::DeleteSelectedItems()
         }
 
         if( !item->IsSelected() )
-            item++;
+            ++item;
         else
             item = drawings.erase( item );
     }
@@ -1547,11 +1511,9 @@ void LIB_PART::CopySelectedItems( const wxPoint& aOffset )
     }
 
     MoveSelectedItems( aOffset );
-    drawings.sort();
 }
 
 
-
 void LIB_PART::MirrorSelectedItemsH( const wxPoint& aCenter )
 {
     for( LIB_ITEM& item : drawings )
@@ -1562,10 +1524,9 @@ void LIB_PART::MirrorSelectedItemsH( const wxPoint& aCenter )
         item.MirrorHorizontal( aCenter );
         item.m_Flags = 0;
     }
-
-    drawings.sort();
 }
 
+
 void LIB_PART::MirrorSelectedItemsV( const wxPoint& aCenter )
 {
     for( LIB_ITEM& item : drawings )
@@ -1576,10 +1537,9 @@ void LIB_PART::MirrorSelectedItemsV( const wxPoint& aCenter )
         item.MirrorVertical( aCenter );
         item.m_Flags = 0;
     }
-
-    drawings.sort();
 }
 
+
 void LIB_PART::RotateSelectedItems( const wxPoint& aCenter )
 {
     for( LIB_ITEM& item : drawings )
@@ -1590,12 +1550,9 @@ void LIB_PART::RotateSelectedItems( const wxPoint& aCenter )
         item.Rotate( aCenter );
         item.m_Flags = 0;
     }
-
-    drawings.sort();
 }
 
 
-
 LIB_ITEM* LIB_PART::LocateDrawItem( int aUnit, int aConvert,
                                     KICAD_T aType, const wxPoint& aPoint )
 {
@@ -1642,15 +1599,14 @@ void LIB_PART::SetUnitCount( int aCount )
 
     if( aCount < m_unitCount )
     {
-        LIB_ITEMS::iterator i;
-        i = drawings.begin();
+        LIB_ITEMS_LIST::ITERATOR i = drawings.begin();
 
         while( i != drawings.end() )
         {
             if( i->m_Unit > aCount )
                 i = drawings.erase( i );
             else
-                i++;
+                ++i;
         }
     }
     else
@@ -1674,8 +1630,6 @@ void LIB_PART::SetUnitCount( int aCount )
                 drawings.push_back( newItem );
             }
         }
-
-        drawings.sort();
     }
 
     m_unitCount = aCount;
@@ -1714,14 +1668,14 @@ void LIB_PART::SetConversion( bool aSetConvert )
     {
         // Delete converted shape items because the converted shape does
         // not exist
-        LIB_ITEMS::iterator i = drawings.begin();
+        LIB_ITEMS_LIST::ITERATOR i = drawings.begin();
 
         while( i != drawings.end() )
         {
             if( i->m_Convert > 1 )
                 i = drawings.erase( i );
             else
-                i++;
+                ++i;
         }
     }
 }
diff --git a/eeschema/class_libentry.h b/eeschema/class_libentry.h
index c22b3e3cc..7659ccd99 100644
--- a/eeschema/class_libentry.h
+++ b/eeschema/class_libentry.h
@@ -34,8 +34,8 @@
 #include <lib_id.h>
 #include <lib_draw_item.h>
 #include <lib_field.h>
+#include <lib_items.h>
 #include <vector>
-#include <memory>
 
 class EDA_RECT;
 class LINE_READER;
@@ -236,7 +236,8 @@ class LIB_PART : public EDA_ITEM
     long                m_dateModified;     ///< Date the part was last modified.
     LIBRENTRYOPTIONS    m_options;          ///< Special part features such as POWER or NORMAL.)
     int                 m_unitCount;        ///< Number of units (parts) per package.
-    LIB_ITEMS           drawings;           ///< How to draw this part.
+    LIB_ITEMS_MAP       drawingsMap;        ///< How to draw this part.
+    LIB_ITEMS_LIST      drawings;           ///< Wrapper to drawingsMap to present as a flat structure
     wxArrayString       m_FootprintList;    /**< List of suitable footprint names for the
                                                  part (wild card names accepted). */
     LIB_ALIASES         m_aliases;          ///< List of alias object pointers associated with the
@@ -682,9 +683,20 @@ public:
     /**
      * Return a reference to the draw item list.
      *
-     * @return LIB_ITEMS& - Reference to the draw item object list.
+     * @return LIB_ITEMS_LIST& - Reference to the draw item object list.
      */
-    LIB_ITEMS& GetDrawItemList() { return drawings; }
+    LIB_ITEMS_LIST& GetDrawItemList()
+    {
+        return drawings;
+    }
+
+    /**
+     * Returns a reference to the draw item map that stores items per their type.
+     */
+    LIB_ITEMS_MAP& GetDrawItemMap()
+    {
+        return drawingsMap;
+    }
 
     /**
      * Set the units per part count.
diff --git a/eeschema/lib_collectors.cpp b/eeschema/lib_collectors.cpp
index 60dd95c17..2190388ba 100644
--- a/eeschema/lib_collectors.cpp
+++ b/eeschema/lib_collectors.cpp
@@ -26,6 +26,7 @@
 #include <general.h>
 #include <transform.h>
 #include <lib_collectors.h>
+#include <lib_items.h>
 
 
 const KICAD_T LIB_COLLECTOR::AllItems[] = {
@@ -110,7 +111,7 @@ SEARCH_RESULT LIB_COLLECTOR::Inspect( EDA_ITEM* aItem, void* testData )
 }
 
 
-void LIB_COLLECTOR::Collect( LIB_ITEMS& aItems, const KICAD_T aFilterList[],
+void LIB_COLLECTOR::Collect( LIB_ITEMS_LIST& aItems, const KICAD_T aFilterList[],
                              const wxPoint& aPosition, int aUnit, int aConvert )
 {
     Empty();        // empty the collection just in case
diff --git a/eeschema/lib_collectors.h b/eeschema/lib_collectors.h
index 6f08341e2..c875c452d 100644
--- a/eeschema/lib_collectors.h
+++ b/eeschema/lib_collectors.h
@@ -30,6 +30,7 @@
 #include <lib_draw_item.h>
 
 
+class LIB_ITEMS_LIST;
 class LIB_COLLECTOR;
 
 
@@ -127,7 +128,7 @@ public:
      * @param aUnit The unit of the items to collect or zero if all units.
      * @param aConvert The convert of the items to collect or zero if all conversions.
      */
-    void Collect( LIB_ITEMS& aItem, const KICAD_T aFilterList[], const wxPoint& aPosition,
+    void Collect( LIB_ITEMS_LIST& aItem, const KICAD_T aFilterList[], const wxPoint& aPosition,
                   int aUnit = 0, int aConvert = 0 );
 };
 
diff --git a/eeschema/lib_draw_item.h b/eeschema/lib_draw_item.h
index f1c0f8604..1e72a85dd 100644
--- a/eeschema/lib_draw_item.h
+++ b/eeschema/lib_draw_item.h
@@ -36,8 +36,6 @@
 #include <transform.h>
 #include <gr_basic.h>
 
-#include <boost/ptr_container/ptr_vector.hpp>
-
 
 class LINE_READER;
 class OUTPUTFORMATTER;
@@ -55,14 +53,6 @@ extern const int fill_tab[];
 
 
 /**
- * Helper for defining a list of library draw object pointers.  The Boost
- * pointer containers are responsible for deleting object pointers placed
- * in them.  If you access a object pointer from the list, do not delete
- * it directly.
- */
-typedef boost::ptr_vector< LIB_ITEM > LIB_ITEMS;
-
-/**
  * Helper for defining a list of pin object pointers.  The list does not
  * use a Boost pointer class so the object pointers do not accidentally get
  * deleted when the container is deleted.
diff --git a/eeschema/lib_items.cpp b/eeschema/lib_items.cpp
new file mode 100644
index 000000000..a49738bbe
--- /dev/null
+++ b/eeschema/lib_items.cpp
@@ -0,0 +1,211 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright 2017 CERN
+ * @author Maciej Suminski <maciej.suminski@xxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+
+#include <lib_items.h>
+#include <lib_draw_item.h>
+#include <stdexcept>
+#include <wx/debug.h>
+
+
+LIB_ITEMS_LIST::ITERATOR::ITERATOR( LIB_ITEMS_MAP& aItems, int aType )
+    : m_parent( &aItems )
+{
+    m_filter = ( aType != TYPE_NOT_INIT );
+    m_type = m_filter ? aType : FIRST_TYPE;
+    m_it = (*m_parent)[m_type].begin();
+
+    // be sure the enum order is correct for the type assert
+    static_assert( (int) ( ITERATOR::FIRST_TYPE ) < (int) ( ITERATOR::LAST_TYPE ),
+            "fix FIRST_TYPE and LAST_TYPE definitions" );
+    wxASSERT( m_type >= ITERATOR::FIRST_TYPE && m_type <= ITERATOR::LAST_TYPE );
+}
+
+
+LIB_ITEMS_LIST::ITERATOR LIB_ITEMS_LIST::begin( int aType ) const
+{
+    LIB_ITEMS_LIST::ITERATOR it( m_data, aType );
+
+    if( it.m_filter )       // iterates over a specific type
+    {
+        it.m_it = (*it.m_parent)[it.m_type].begin();
+    }
+    else                    // iterates over all items
+    {
+        // find a not empty container
+        auto i = m_data.begin();
+
+        while( i->second.empty() && i != m_data.end() )
+            ++i;
+
+        if( i == m_data.end() )
+            --i;
+
+        it.m_it = i->second.begin();
+        it.m_type = i->first;
+    }
+
+    return it;
+}
+
+
+LIB_ITEMS_LIST::ITERATOR LIB_ITEMS_LIST::end( int aType ) const
+{
+    LIB_ITEMS_LIST::ITERATOR it( m_data, aType );
+
+    if( it.m_filter )       // iterates over a specific type
+    {
+        it.m_it = (*it.m_parent)[it.m_type].end();
+    }
+    else                    // iterates over all items
+    {
+        // find a not empty container
+        auto i = m_data.rbegin();
+
+        while( i->second.empty() && i != m_data.rend() )
+            ++i;
+
+        if( i == m_data.rend() )
+            --i;
+
+        it.m_it = i->second.end();
+        it.m_type = i->first;
+    }
+
+    return it;
+}
+
+
+void LIB_ITEMS_LIST::push_back( LIB_ITEM* aItem )
+{
+    wxASSERT( aItem->Type() >= ITERATOR::FIRST_TYPE && aItem->Type() <= ITERATOR::LAST_TYPE );
+    m_data[aItem->Type()].push_back( aItem );
+}
+
+
+LIB_ITEMS_LIST::ITERATOR LIB_ITEMS_LIST::erase( const ITERATOR& aIterator )
+{
+    LIB_ITEMS_LIST::ITERATOR it( aIterator );
+    it.m_it = (*aIterator.m_parent)[aIterator.m_type].erase( aIterator.m_it );
+
+    // if we reached the end, then move to the next type
+    if( it.m_it == (*it.m_parent)[it.m_type].end() && !it.m_filter )
+        ++it;
+
+    return it;
+}
+
+
+size_t LIB_ITEMS_LIST::size() const
+{
+    size_t counter = 0;
+
+    for( auto& type : m_data )
+        counter += type.second.size();
+
+    return counter;
+}
+
+
+bool LIB_ITEMS_LIST::empty() const
+{
+    for( auto& type : m_data )
+    {
+        if( !type.second.empty() )
+            return false;
+    }
+
+    return true;
+}
+
+
+void LIB_ITEMS_LIST::sort()
+{
+    for( auto& itemType : m_data )
+        itemType.second.sort();
+}
+
+
+LIB_ITEM& LIB_ITEMS_LIST::operator[]( unsigned int aIdx )
+{
+    int counter = 0;
+
+    for( auto& type : m_data )
+    {
+        if( aIdx < counter + type.second.size() )
+            return type.second[aIdx - counter];
+        else
+            counter += type.second.size();
+    }
+
+    throw std::out_of_range( "LIB_ITEMS_LIST out of range" );
+}
+
+
+const LIB_ITEM& LIB_ITEMS_LIST::operator[]( unsigned int aIdx ) const
+{
+    int counter = 0;
+
+    for( const auto& type : m_data )
+    {
+        if( counter + type.second.size() < aIdx )
+            return type.second[aIdx - counter];
+        else
+            counter += type.second.size();
+    }
+
+    throw std::out_of_range( "LIB_ITEMS_LIST out of range" );
+}
+
+
+LIB_ITEMS_LIST::ITERATOR& LIB_ITEMS_LIST::ITERATOR::operator++()
+{
+    if( m_it != (*m_parent)[m_type].end() )
+        ++m_it;
+
+    if( m_it == (*m_parent)[m_type].end() && !m_filter && m_type < LAST_TYPE )
+    {
+        // switch to the next type for not filtered iterator
+        do
+            ++m_type;
+        while( ( !m_parent->count( m_type ) || (*m_parent)[m_type].empty() ) && m_type < LAST_TYPE );
+
+        // is it the real end of the list?
+        if( m_type == LAST_TYPE && (*m_parent)[LAST_TYPE].empty() )
+            m_it = (*m_parent)[LAST_TYPE].end();
+        else
+            m_it = (*m_parent)[m_type].begin();
+    }
+
+    return *this;
+}
+
+
+bool LIB_ITEMS_LIST::ITERATOR::operator!=( const LIB_ITEMS_LIST::ITERATOR& aOther ) const
+{
+    wxASSERT( aOther.m_parent == m_parent );
+    wxASSERT( aOther.m_filter == m_filter );
+    wxASSERT( !m_filter || aOther.m_type == m_type );
+
+    return aOther.m_it != m_it;
+}
diff --git a/eeschema/lib_items.h b/eeschema/lib_items.h
new file mode 100644
index 000000000..a1c207ed6
--- /dev/null
+++ b/eeschema/lib_items.h
@@ -0,0 +1,128 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright 2017 CERN
+ * @author Maciej Suminski <maciej.suminski@xxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+
+#ifndef LIB_ITEMS_H
+#define LIB_ITEMS_H
+
+/**
+ * LIB_ITEM containers.
+ */
+
+#include <core/typeinfo.h>
+#include <boost/ptr_container/ptr_vector.hpp>
+#include <map>
+
+class LIB_ITEM;
+
+/**
+ * Helper for defining a list of library draw object pointers.  The Boost
+ * pointer containers are responsible for deleting object pointers placed
+ * in them.  If you access a object pointer from the list, do not delete
+ * it directly.
+ */
+typedef boost::ptr_vector<LIB_ITEM> LIB_ITEMS;
+
+/**
+ * LIB_ITEM container to keep them sorted by their type.
+ */
+typedef std::map<int, LIB_ITEMS> LIB_ITEMS_MAP;
+
+/**
+ * Wrapper for LIB_ITEMS_MAP to provide flat (list-like) access to the items
+ * stored in the map (@see LIB_ITEMS_MAP).
+ */
+class LIB_ITEMS_LIST
+{
+public:
+    class ITERATOR
+    {
+    public:
+        ITERATOR& operator++();
+
+        LIB_ITEM& operator*()
+        {
+            return *m_it;
+        }
+
+        LIB_ITEM* operator->()
+        {
+            return &(*m_it);
+        }
+
+        bool operator!=( const ITERATOR& aOther ) const;
+
+    private:
+        /**
+         * Constructor.
+         * @param aItems is the container to wrap.
+         * @param aType enables item type filtering. When aType is TYPE_NOT_INIT, there is no
+         * filtering and all item types are accessible by the iterator.
+         */
+        ITERATOR( LIB_ITEMS_MAP& aItems, int aType = TYPE_NOT_INIT );
+
+        ///> Wrapped container
+        LIB_ITEMS_MAP* m_parent;
+
+        ///> Flag indicating whether type filtering is enabled
+        bool m_filter;
+
+        ///> Type of the currently iterated items (@see KICAD_T)
+        int m_type;
+
+        ///> Iterator for one of the LIB_ITEMS containers stored in the map
+        LIB_ITEMS::iterator m_it;
+
+        // Range of valid types handled by the iterator
+        static constexpr KICAD_T FIRST_TYPE = LIB_ARC_T;
+        static constexpr KICAD_T LAST_TYPE = LIB_FIELD_T;
+
+        friend class LIB_ITEMS_LIST;
+    };
+
+
+    LIB_ITEMS_LIST( LIB_ITEMS_MAP& aData )
+        : m_data( aData )
+    {
+    }
+
+    ITERATOR begin( int aType = TYPE_NOT_INIT ) const;
+    ITERATOR end( int aType = TYPE_NOT_INIT ) const;
+
+    void push_back( LIB_ITEM* aItem );
+    ITERATOR erase( const ITERATOR& aIterator );
+
+    size_t size() const;
+    bool empty() const;
+    void sort();
+
+    LIB_ITEM& operator[]( unsigned int aIdx );
+    const LIB_ITEM& operator[]( unsigned int aIdx ) const;
+
+private:
+    ///> Wrapped container
+    LIB_ITEMS_MAP& m_data;
+};
+
+
+#endif /* LIB_ITEMS_H */
diff --git a/eeschema/symbdraw.cpp b/eeschema/symbdraw.cpp
index 01f5775f9..ca1f2bb2c 100644
--- a/eeschema/symbdraw.cpp
+++ b/eeschema/symbdraw.cpp
@@ -113,9 +113,6 @@ void LIB_EDIT_FRAME::EditGraphicSymbol( wxDC* DC, LIB_ITEM* DrawItem )
 
     DrawItem->SetWidth( m_drawLineWidth );
 
-    if( component )
-        component->GetDrawItemList().sort();
-
     OnModify( );
 
     MSG_PANEL_ITEMS items;
diff --git a/eeschema/symbedit.cpp b/eeschema/symbedit.cpp
index 49139fbde..d02a618a6 100644
--- a/eeschema/symbedit.cpp
+++ b/eeschema/symbedit.cpp
@@ -110,7 +110,7 @@ void LIB_EDIT_FRAME::LoadOneSymbol()
     wxCHECK_RET( !aliasNames.IsEmpty(), "No aliases found in library " + filename );
 
     LIB_PART*   first = lib->FindAlias( aliasNames[0] )->GetPart();
-    LIB_ITEMS&  drawList = first->GetDrawItemList();
+    LIB_ITEMS_LIST&  drawList = first->GetDrawItemList();
 
     for( LIB_ITEM& item : drawList )
     {
@@ -213,7 +213,7 @@ void LIB_EDIT_FRAME::SaveOneSymbol()
             part->GetValueField().Save( formatter );
             formatter.Print( 0, "DRAW\n" );
 
-            LIB_ITEMS& drawList = part->GetDrawItemList();
+            LIB_ITEMS_LIST& drawList = part->GetDrawItemList();
 
             for( LIB_ITEM& item : drawList )
             {
-- 
2.13.1


Follow ups