blob: cd56d82121c61685de5d37260af0e2a45979b040 [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. */
Zane Shelley83da2452019-10-25 15:45:34 -050023 Flyweight(const Flyweight &) = delete;
Zane Shelley200c3452019-09-26 11:46:30 -050024
25 /** @brief Default assignment operator. */
Zane Shelley83da2452019-10-25 15:45:34 -050026 Flyweight & operator=(const Flyweight &) = delete;
Zane Shelley200c3452019-09-26 11:46:30 -050027
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 */
Zane Shelley83da2452019-10-25 15:45:34 -050043 T & get(const T & i_entry)
Zane Shelley200c3452019-09-26 11:46:30 -050044 {
45 // The index is sorted by the value of the T objects. Check to see if
46 // this entry already exists in the factory.
Zane Shelley83da2452019-10-25 15:45:34 -050047 auto itr =
48 std::lower_bound(iv_index.begin(), iv_index.end(), &i_entry,
49 [](const T * a, const T * b) { return *a < *b; });
Zane Shelley200c3452019-09-26 11:46:30 -050050
51 // std::lower_bound() will return the first element that does not
52 // compare less than i_entry. So if an entry is found, we must make sure
53 // it does not have the same value as i_entry.
Zane Shelley83da2452019-10-25 15:45:34 -050054 if (iv_index.end() == itr || !(i_entry == *(*itr)))
Zane Shelley200c3452019-09-26 11:46:30 -050055 {
56 // Create a new entry and store the pointer in the sorted index.
Zane Shelley83da2452019-10-25 15:45:34 -050057 itr = iv_index.insert(itr, new T { i_entry });
Zane Shelley200c3452019-09-26 11:46:30 -050058 }
59
60 // Return a reference to this entry in the factory.
61 return *(*itr);
62 }
63
64 /**
65 * @brief Deletes all entries in the factory.
66 *
67 * This is called in the destructor. So it cannot throw an exception.
68 */
69 void clear()
70 {
Zane Shelley83da2452019-10-25 15:45:34 -050071 for (auto i : iv_index) { delete i; }
Zane Shelley200c3452019-09-26 11:46:30 -050072 iv_index.clear();
73 }
74
75 /**
76 * @brief Shrinks the index capacity to eliminate unused memory space.
77 *
78 * The index is a vector where the capacity will grow as items are added to
79 * it. Each time more capacity is needed, the vector will double the current
80 * capacity to make room for new entries. The general use of this class is
81 * expected that all needed entries will be added during the isolator
82 * initialization. Afterwards, the extra capacity is not needed. So this
83 * function will shrink the capacity to the size of the vector.
84 */
85 void compact() { iv_index.shrink_to_fit(); }
86
87 private:
88
89 /**
90 * Each new T is allocated on the memory heap and a pointer to each of those
91 * objects is stored in this vector. The vector will not contain duplicates
92 * and is kept sorted by the value of the objects. Inserting a new element
93 * is O(n). However, since the vector is sorted, searching for existing
94 * entries is O(log n). Considering the expected use of this class,
95 * insertion should only exist during initialization of the isolator, where
96 * searching will be done at the time of isolation when performance is more
97 * important.
98 *
99 * An alternative to this approach is to use std::set, where insertion and
100 * searching are both O(log n). However, std::set is typically designed with
101 * a red-black tree which carries a lot of extra memory overhead to maintain
102 * the structure. Also, the Hostboot user application does not support
103 * std::set at this time.
104 */
105 std::vector<T*> iv_index;
106};
107
108} // end namespace libhei