Added Flyweight utility class

Signed-off-by: Zane Shelley <zshelle@us.ibm.com>
Change-Id: I30ca984f914d782174292a1c673f62a60f506f7b
diff --git a/src/register/prdfScanFacility.C b/src/register/prdfScanFacility.C
index a0aadac..079efe9 100755
--- a/src/register/prdfScanFacility.C
+++ b/src/register/prdfScanFacility.C
@@ -32,10 +32,9 @@
 //----------------------------------------------------------------------
 
 #include <register/hei_register.hpp>
+#include <util/hei_flyweight.hpp>
 
 #include <prdfScanFacility.H>
-#include <prdfFlyWeight.C>
-#include <prdfFlyWeightS.C>
 
 namespace libhei
 {
diff --git a/src/register/prdfScanFacility.H b/src/register/prdfScanFacility.H
index 501f72b..1b22e16 100755
--- a/src/register/prdfScanFacility.H
+++ b/src/register/prdfScanFacility.H
@@ -37,9 +37,8 @@
 
 #include <register/hei_hardware_register.hpp>
 #include <register/hei_operator_register.hpp>
+#include <util/hei_flyweight.hpp>
 
-#include <prdfFlyWeight.H>
-#include <prdfFlyWeightS.H>
 #include <vector>
 #include <prdfPlatServices.H>
 
@@ -204,18 +203,16 @@
 
 
 private:  // Data
-  typedef FlyWeightS<HardwareRegister,50>           ScanCommRegisters;
-  //FIXME RTC 64345 Investigate benefit of changing below from FlyWeight to
-  //FlyWeightS
-  typedef FlyWeight<AttnTypeRegister,50>        AttnTypeRegisters;
-  typedef FlyWeightS<AndRegister,50>            AndRegisters;
-  typedef FlyWeightS<OrRegister,10>             OrRegisters;
-  typedef FlyWeightS<NotRegister,50>            NotRegisters;
-  typedef FlyWeightS<LeftShiftRegister,10>      LeftShiftRegisters;
-  typedef FlyWeightS<RightShiftRegister, 10>    RightShiftRegisters;
-  typedef FlyWeightS<SummaryRegister,10>        SummaryRegisters;
-  typedef FlyWeight<ConstantRegister, 10>       ConstantRegisters;
-  typedef FlyWeightS<ScomRegisterAccess, 10>    PluginRegisters;
+  typedef Flyweight<HardwareRegister>           ScanCommRegisters;
+  typedef Flyweight<AttnTypeRegister>        AttnTypeRegisters;
+  typedef Flyweight<AndRegister>            AndRegisters;
+  typedef Flyweight<OrRegister>             OrRegisters;
+  typedef Flyweight<NotRegister>            NotRegisters;
+  typedef Flyweight<LeftShiftRegister>      LeftShiftRegisters;
+  typedef Flyweight<RightShiftRegister>    RightShiftRegisters;
+  typedef Flyweight<SummaryRegister>        SummaryRegisters;
+  typedef Flyweight<ConstantRegister>       ConstantRegisters;
+  typedef Flyweight<ScomRegisterAccess>    PluginRegisters;
 
   ScanCommRegisters     iv_scomRegFw;
   AttnTypeRegisters     iv_attnRegFw;
diff --git a/src/util/hei_flyweight.hpp b/src/util/hei_flyweight.hpp
new file mode 100644
index 0000000..45d6727
--- /dev/null
+++ b/src/util/hei_flyweight.hpp
@@ -0,0 +1,99 @@
+#pragma once
+
+#include <stdint.h>
+#include <algorithm>
+#include <vector>
+
+namespace libhei
+{
+
+/** @brief A generic flyweight factory for objects of type T. */
+template <class T>
+class Flyweight
+{
+  public:
+
+    /** @brief Default constructor. */
+    Flyweight() = default;
+
+    /** @brief Destructor. */
+    ~Flyweight() { clear(); }
+
+    /** @brief Default copy constructor. */
+    Flyweight( const Flyweight & ) = delete;
+
+    /** @brief Default assignment operator. */
+    Flyweight & operator=( const Flyweight & ) = delete;
+
+    /**
+     * @brief  Adds the given entry to the factory, if it does not already
+     *         exist. Then returns a reference to that entry in the factory.
+     * @param  The target entry.
+     * @return A reference to this entry in the factory.
+     */
+    T & get( const T & i_entry )
+    {
+        // The index is sorted by the value of the T objects. Check to see if
+        // this entry already exists in the factory.
+        auto itr = std::lower_bound( iv_index.begin(), iv_index.end(), &i_entry,
+                             [](const T * a, const T * b) { return *a < *b; } );
+
+        // std::lower_bound() will return the first element that does not
+        // compare less than i_entry. So if an entry is found, we must make sure
+        // it does not have the same value as i_entry.
+        if ( iv_index.end() == itr || !(i_entry == *(*itr)) )
+        {
+            // Create a new entry and store the pointer in the sorted index.
+            itr = iv_index.insert( itr, new T { i_entry } );
+        }
+
+        // Return a reference to this entry in the factory.
+        return *(*itr);
+    }
+
+    /**
+     * @brief Deletes all entries in the factory.
+     *
+     * This is called in the destructor. So it cannot throw an exception.
+     */
+    void clear()
+    {
+        for ( auto i : iv_index ) { delete i; }
+        iv_index.clear();
+    }
+
+    /**
+     * @brief Shrinks the index capacity to eliminate unused memory space.
+     *
+     * The index is a vector where the capacity will grow as items are added to
+     * it. Each time more capacity is needed, the vector will double the current
+     * capacity to make room for new entries. The general use of this class is
+     * expected that all needed entries will be added during the isolator
+     * initialization. Afterwards, the extra capacity is not needed. So this
+     * function will shrink the capacity to the size of the vector.
+     */
+    void compact() { iv_index.shrink_to_fit(); }
+
+  private:
+
+    /**
+     * Each new T is allocated on the memory heap and a pointer to each of those
+     * objects is stored in this vector. The vector will not contain duplicates
+     * and is kept sorted by the value of the objects. Inserting a new element
+     * is O(n). However, since the vector is sorted, searching for existing
+     * entries is O(log n). Considering the expected use of this class,
+     * insertion should only exist during initialization of the isolator, where
+     * searching will be done at the time of isolation when performance is more
+     * important.
+     *
+     * An alternative to this approach is to use std::set, where insertion and
+     * searching are both O(log n). However, std::set is typically designed with
+     * a red-black tree which carries a lot of extra memory overhead to maintain
+     * the structure. Also, the Hostboot user application does not support
+     * std::set at this time.
+     */
+    std::vector<T*> iv_index;
+};
+
+} // end namespace libhei
+
diff --git a/test/flyweight_test.cpp b/test/flyweight_test.cpp
new file mode 100644
index 0000000..1927360
--- /dev/null
+++ b/test/flyweight_test.cpp
@@ -0,0 +1,49 @@
+#include <util/hei_flyweight.hpp>
+
+#include "gtest/gtest.h"
+
+using namespace libhei;
+
+class Foo
+{
+  public:
+    Foo() = default;
+    explicit Foo( int i ) : iv_i(i) {}
+    int get() const { return iv_i; }
+    bool operator==( const Foo & i_r ) const { return iv_i == i_r.iv_i; }
+    bool operator<(  const Foo & i_r ) const { return iv_i <  i_r.iv_i; }
+  private:
+    int iv_i = 0;
+};
+
+Flyweight<Foo> factory {};
+
+Foo & addFoo( int i )
+{
+    Foo f { i };
+    return factory.get( f );
+}
+
+TEST( FlyweightTest, TestSet1 )
+{
+    // Add some unique entries in a random order and keep track of where those
+    // enties exist in memory.
+    Foo * a[5];
+    a[1] = &(addFoo(1));
+    a[2] = &(addFoo(2));
+    a[0] = &(addFoo(0));
+    a[4] = &(addFoo(4));
+    a[3] = &(addFoo(3));
+
+    // Now add more entries and verify the 'new' entries match the same
+    // addresses as the previously added entries.
+    for ( int i = 4; i >= 0; i-- )
+    {
+        ASSERT_EQ( a[i], &(addFoo(i)) );
+    }
+
+    // At this point, we have proven that duplicate entries will return
+    // references to the original unique entries. There is probably more we can
+    // do here, but this is enough to prove the Flyweight class follows the
+    // flyweight design pattern.
+}
diff --git a/test/meson.build b/test/meson.build
index a4e13ea..cdf8812 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -5,7 +5,10 @@
 test_src = ['../src/util/hei_bit_string.cpp']
 
 # build g-test framework unit tests
-gtests = ['bit_string_test']
+gtests = [
+    'bit_string_test',
+    'flyweight_test',
+]
 
 gtest = dependency('gtest', main : true, required : false, method : 'system')