blob: f308e3c112611b4555fbe8e949c4bafdca3a8f6a [file] [log] [blame]
Zane Shelley200c3452019-09-26 11:46:30 -05001#pragma once
2
3#include <stdint.h>
4#include <algorithm>
5#include <vector>
6
7namespace libhei
8{
9
10/** @brief A generic flyweight factory for objects of type T. */
11template <class T>
12class Flyweight
13{
Zane Shelleyfc7ab192019-09-27 15:45:16 -050014 private: // This class cannot be instantiated. Use getSingleton() instead.
Zane Shelley200c3452019-09-26 11:46:30 -050015
16 /** @brief Default constructor. */
17 Flyweight() = default;
18
19 /** @brief Destructor. */
20 ~Flyweight() { clear(); }
21
22 /** @brief Default copy constructor. */
23 Flyweight( const Flyweight & ) = delete;
24
25 /** @brief Default assignment operator. */
26 Flyweight & operator=( const Flyweight & ) = delete;
27
Zane Shelleyfc7ab192019-09-27 15:45:16 -050028 public:
29
30 /** @brief Provides access to a singleton instance of this object. */
31 static Flyweight & getSingleton()
32 {
33 static Flyweight theFlyweight;
34 return theFlyweight;
35 }
36
Zane Shelley200c3452019-09-26 11:46:30 -050037 /**
38 * @brief Adds the given entry to the factory, if it does not already
39 * exist. Then returns a reference to that entry in the factory.
40 * @param The target entry.
41 * @return A reference to this entry in the factory.
42 */
43 T & get( const T & i_entry )
44 {
45 // The index is sorted by the value of the T objects. Check to see if
46 // this entry already exists in the factory.
47 auto itr = std::lower_bound( iv_index.begin(), iv_index.end(), &i_entry,
48 [](const T * a, const T * b) { return *a < *b; } );
49
50 // std::lower_bound() will return the first element that does not
51 // compare less than i_entry. So if an entry is found, we must make sure
52 // it does not have the same value as i_entry.
53 if ( iv_index.end() == itr || !(i_entry == *(*itr)) )
54 {
55 // Create a new entry and store the pointer in the sorted index.
56 itr = iv_index.insert( itr, new T { i_entry } );
57 }
58
59 // Return a reference to this entry in the factory.
60 return *(*itr);
61 }
62
63 /**
64 * @brief Deletes all entries in the factory.
65 *
66 * This is called in the destructor. So it cannot throw an exception.
67 */
68 void clear()
69 {
70 for ( auto i : iv_index ) { delete i; }
71 iv_index.clear();
72 }
73
74 /**
75 * @brief Shrinks the index capacity to eliminate unused memory space.
76 *
77 * The index is a vector where the capacity will grow as items are added to
78 * it. Each time more capacity is needed, the vector will double the current
79 * capacity to make room for new entries. The general use of this class is
80 * expected that all needed entries will be added during the isolator
81 * initialization. Afterwards, the extra capacity is not needed. So this
82 * function will shrink the capacity to the size of the vector.
83 */
84 void compact() { iv_index.shrink_to_fit(); }
85
86 private:
87
88 /**
89 * Each new T is allocated on the memory heap and a pointer to each of those
90 * objects is stored in this vector. The vector will not contain duplicates
91 * and is kept sorted by the value of the objects. Inserting a new element
92 * is O(n). However, since the vector is sorted, searching for existing
93 * entries is O(log n). Considering the expected use of this class,
94 * insertion should only exist during initialization of the isolator, where
95 * searching will be done at the time of isolation when performance is more
96 * important.
97 *
98 * An alternative to this approach is to use std::set, where insertion and
99 * searching are both O(log n). However, std::set is typically designed with
100 * a red-black tree which carries a lot of extra memory overhead to maintain
101 * the structure. Also, the Hostboot user application does not support
102 * std::set at this time.
103 */
104 std::vector<T*> iv_index;
105};
106
107} // end namespace libhei
108