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')