blob: 4aeb952627d906b48bab77fe82fd7530a015bc56 [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. */
Zane Shelley7f7a42d2019-10-28 13:28:31 -050021 ~Flyweight()
22 {
23 clear();
24 }
Zane Shelley200c3452019-09-26 11:46:30 -050025
26 /** @brief Default copy constructor. */
Zane Shelleyfe27b652019-10-28 11:33:07 -050027 Flyweight(const Flyweight&) = delete;
Zane Shelley200c3452019-09-26 11:46:30 -050028
29 /** @brief Default assignment operator. */
Zane Shelleyfe27b652019-10-28 11:33:07 -050030 Flyweight& operator=(const Flyweight&) = delete;
Zane Shelley200c3452019-09-26 11:46:30 -050031
Zane Shelleyfc7ab192019-09-27 15:45:16 -050032 public:
33
34 /** @brief Provides access to a singleton instance of this object. */
Zane Shelleyfe27b652019-10-28 11:33:07 -050035 static Flyweight& getSingleton()
Zane Shelleyfc7ab192019-09-27 15:45:16 -050036 {
37 static Flyweight theFlyweight;
38 return theFlyweight;
39 }
40
Zane Shelley200c3452019-09-26 11:46:30 -050041 /**
42 * @brief Adds the given entry to the factory, if it does not already
43 * exist. Then returns a reference to that entry in the factory.
44 * @param The target entry.
45 * @return A reference to this entry in the factory.
46 */
Zane Shelleyfe27b652019-10-28 11:33:07 -050047 T& get(const T& i_entry)
Zane Shelley200c3452019-09-26 11:46:30 -050048 {
49 // The index is sorted by the value of the T objects. Check to see if
50 // this entry already exists in the factory.
Zane Shelley83da2452019-10-25 15:45:34 -050051 auto itr =
52 std::lower_bound(iv_index.begin(), iv_index.end(), &i_entry,
Zane Shelleyfe27b652019-10-28 11:33:07 -050053 [](const T* a, const T* b) { return *a < *b; });
Zane Shelley200c3452019-09-26 11:46:30 -050054
55 // std::lower_bound() will return the first element that does not
56 // compare less than i_entry. So if an entry is found, we must make sure
57 // it does not have the same value as i_entry.
Zane Shelley83da2452019-10-25 15:45:34 -050058 if (iv_index.end() == itr || !(i_entry == *(*itr)))
Zane Shelley200c3452019-09-26 11:46:30 -050059 {
60 // Create a new entry and store the pointer in the sorted index.
Zane Shelley83da2452019-10-25 15:45:34 -050061 itr = iv_index.insert(itr, new T { i_entry });
Zane Shelley200c3452019-09-26 11:46:30 -050062 }
63
64 // Return a reference to this entry in the factory.
65 return *(*itr);
66 }
67
68 /**
69 * @brief Deletes all entries in the factory.
70 *
71 * This is called in the destructor. So it cannot throw an exception.
72 */
73 void clear()
74 {
Zane Shelley7f7a42d2019-10-28 13:28:31 -050075 for (auto i : iv_index)
76 {
77 delete i;
78 }
Zane Shelley200c3452019-09-26 11:46:30 -050079 iv_index.clear();
80 }
81
82 /**
83 * @brief Shrinks the index capacity to eliminate unused memory space.
84 *
85 * The index is a vector where the capacity will grow as items are added to
86 * it. Each time more capacity is needed, the vector will double the current
87 * capacity to make room for new entries. The general use of this class is
88 * expected that all needed entries will be added during the isolator
89 * initialization. Afterwards, the extra capacity is not needed. So this
90 * function will shrink the capacity to the size of the vector.
91 */
Zane Shelley7f7a42d2019-10-28 13:28:31 -050092 void compact()
93 {
94 iv_index.shrink_to_fit();
95 }
Zane Shelley200c3452019-09-26 11:46:30 -050096
97 private:
98
99 /**
100 * Each new T is allocated on the memory heap and a pointer to each of those
101 * objects is stored in this vector. The vector will not contain duplicates
102 * and is kept sorted by the value of the objects. Inserting a new element
103 * is O(n). However, since the vector is sorted, searching for existing
104 * entries is O(log n). Considering the expected use of this class,
105 * insertion should only exist during initialization of the isolator, where
106 * searching will be done at the time of isolation when performance is more
107 * important.
108 *
109 * An alternative to this approach is to use std::set, where insertion and
110 * searching are both O(log n). However, std::set is typically designed with
111 * a red-black tree which carries a lot of extra memory overhead to maintain
112 * the structure. Also, the Hostboot user application does not support
113 * std::set at this time.
114 */
115 std::vector<T*> iv_index;
116};
117
118} // end namespace libhei