blob: a81fe306e228adeef4897db2bf4360b60ee43fda [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>
Zane Shelleyc11f23c2020-05-11 12:14:41 -05006#include <memory>
Zane Shelley200c3452019-09-26 11:46:30 -05007#include <vector>
8
9namespace libhei
10{
11
12/** @brief A generic flyweight factory for objects of type T. */
13template <class T>
14class Flyweight
15{
Zane Shelleyfc7ab192019-09-27 15:45:16 -050016 private: // This class cannot be instantiated. Use getSingleton() instead.
Zane Shelley200c3452019-09-26 11:46:30 -050017 /** @brief Default constructor. */
18 Flyweight() = default;
19
20 /** @brief Destructor. */
Zane Shelley72fd2e42022-11-12 12:14:53 -060021 ~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:
Zane Shelleyfc7ab192019-09-27 15:45:16 -050033 /** @brief Provides access to a singleton instance of this object. */
Zane Shelleyfe27b652019-10-28 11:33:07 -050034 static Flyweight& getSingleton()
Zane Shelleyfc7ab192019-09-27 15:45:16 -050035 {
36 static Flyweight theFlyweight;
37 return theFlyweight;
38 }
39
Zane Shelley200c3452019-09-26 11:46:30 -050040 /**
Zane Shelleyc11f23c2020-05-11 12:14:41 -050041 * @brief Does an emplace add of a new entry to the factory, if the entry
42 * does not already exist, and returns a pointer to the entry in the
43 * factory.
44 * @param An variable argument list that would be passed to a contructor of
45 * class T.
46 * @return A pointer to this entry in the factory.
Zane Shelley200c3452019-09-26 11:46:30 -050047 */
Zane Shelleyc11f23c2020-05-11 12:14:41 -050048 template <class... Args>
49 std::shared_ptr<T> get(Args&&... i_args)
Zane Shelley200c3452019-09-26 11:46:30 -050050 {
Zane Shelleyc11f23c2020-05-11 12:14:41 -050051 // Create a new instance with the given arguments.
52 std::shared_ptr<T> newEntry = std::make_shared<T>(i_args...);
53
Zane Shelley200c3452019-09-26 11:46:30 -050054 // The index is sorted by the value of the T objects. Check to see if
Zane Shelleyc11f23c2020-05-11 12:14:41 -050055 // newEntry already exists in the factory.
56 auto itr = std::lower_bound(
57 iv_index.begin(), iv_index.end(), newEntry,
58 [](const std::shared_ptr<T> a, const std::shared_ptr<T> b) {
59 return *a < *b;
60 });
Zane Shelley200c3452019-09-26 11:46:30 -050061
62 // std::lower_bound() will return the first element that does not
Zane Shelleyc11f23c2020-05-11 12:14:41 -050063 // compare less than newEntry. So if an element is found, we must make
64 // sure it does not have the same value as newEntry.
65 if (iv_index.end() == itr || !(*newEntry == *(*itr)))
Zane Shelley200c3452019-09-26 11:46:30 -050066 {
Zane Shelleyc11f23c2020-05-11 12:14:41 -050067 // Store the new enty in the sorted index.
68 itr = iv_index.insert(itr, newEntry);
Zane Shelley200c3452019-09-26 11:46:30 -050069 }
70
Zane Shelleyc11f23c2020-05-11 12:14:41 -050071 // It is important to note that if newEntry was not inserted into the
72 // index above because a duplicate already existed, it will be deleted
73 // as soon as it goes out of scope.
74
75 // Return a pointer to the this entry in the factory.
76 return *itr;
Zane Shelley200c3452019-09-26 11:46:30 -050077 }
78
79 /**
80 * @brief Deletes all entries in the factory.
81 *
82 * This is called in the destructor. So it cannot throw an exception.
83 */
84 void clear()
85 {
Zane Shelley200c3452019-09-26 11:46:30 -050086 iv_index.clear();
87 }
88
89 /**
90 * @brief Shrinks the index capacity to eliminate unused memory space.
91 *
92 * The index is a vector where the capacity will grow as items are added to
93 * it. Each time more capacity is needed, the vector will double the current
94 * capacity to make room for new entries. The general use of this class is
95 * expected that all needed entries will be added during the isolator
96 * initialization. Afterwards, the extra capacity is not needed. So this
97 * function will shrink the capacity to the size of the vector.
98 */
Zane Shelley7f7a42d2019-10-28 13:28:31 -050099 void compact()
100 {
101 iv_index.shrink_to_fit();
102 }
Zane Shelley200c3452019-09-26 11:46:30 -0500103
Zane Shelleyc11f23c2020-05-11 12:14:41 -0500104 /** @return The number of entries in this flyweight factory. */
105 size_t size()
106 {
107 return iv_index.size();
108 }
109
Zane Shelley200c3452019-09-26 11:46:30 -0500110 private:
Zane Shelley200c3452019-09-26 11:46:30 -0500111 /**
112 * Each new T is allocated on the memory heap and a pointer to each of those
113 * objects is stored in this vector. The vector will not contain duplicates
114 * and is kept sorted by the value of the objects. Inserting a new element
115 * is O(n). However, since the vector is sorted, searching for existing
116 * entries is O(log n). Considering the expected use of this class,
117 * insertion should only exist during initialization of the isolator, where
118 * searching will be done at the time of isolation when performance is more
119 * important.
120 *
121 * An alternative to this approach is to use std::set, where insertion and
122 * searching are both O(log n). However, std::set is typically designed with
123 * a red-black tree which carries a lot of extra memory overhead to maintain
124 * the structure. Also, the Hostboot user application does not support
125 * std::set at this time.
126 */
Zane Shelleyc11f23c2020-05-11 12:14:41 -0500127 std::vector<std::shared_ptr<T>> iv_index;
Zane Shelley200c3452019-09-26 11:46:30 -0500128};
129
130} // end namespace libhei