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