blob: 6228759beb302011e0a83865c0387b8cf87f80f1 [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 Shelleyc11f23c2020-05-11 12:14:41 -050021 ~Flyweight() = default;
Zane Shelley200c3452019-09-26 11:46:30 -050022
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:
Zane Shelleyfc7ab192019-09-27 15:45:16 -050030 /** @brief Provides access to a singleton instance of this object. */
Zane Shelleyfe27b652019-10-28 11:33:07 -050031 static Flyweight& getSingleton()
Zane Shelleyfc7ab192019-09-27 15:45:16 -050032 {
33 static Flyweight theFlyweight;
34 return theFlyweight;
35 }
36
Zane Shelley200c3452019-09-26 11:46:30 -050037 /**
Zane Shelleyc11f23c2020-05-11 12:14:41 -050038 * @brief Does an emplace add of a new entry to the factory, if the entry
39 * does not already exist, and returns a pointer to the entry in the
40 * factory.
41 * @param An variable argument list that would be passed to a contructor of
42 * class T.
43 * @return A pointer to this entry in the factory.
Zane Shelley200c3452019-09-26 11:46:30 -050044 */
Zane Shelleyc11f23c2020-05-11 12:14:41 -050045 template <class... Args>
46 std::shared_ptr<T> get(Args&&... i_args)
Zane Shelley200c3452019-09-26 11:46:30 -050047 {
Zane Shelleyc11f23c2020-05-11 12:14:41 -050048 // Create a new instance with the given arguments.
49 std::shared_ptr<T> newEntry = std::make_shared<T>(i_args...);
50
Zane Shelley200c3452019-09-26 11:46:30 -050051 // The index is sorted by the value of the T objects. Check to see if
Zane Shelleyc11f23c2020-05-11 12:14:41 -050052 // newEntry already exists in the factory.
53 auto itr = std::lower_bound(
54 iv_index.begin(), iv_index.end(), newEntry,
55 [](const std::shared_ptr<T> a, const std::shared_ptr<T> b) {
56 return *a < *b;
57 });
Zane Shelley200c3452019-09-26 11:46:30 -050058
59 // std::lower_bound() will return the first element that does not
Zane Shelleyc11f23c2020-05-11 12:14:41 -050060 // compare less than newEntry. So if an element is found, we must make
61 // sure it does not have the same value as newEntry.
62 if (iv_index.end() == itr || !(*newEntry == *(*itr)))
Zane Shelley200c3452019-09-26 11:46:30 -050063 {
Zane Shelleyc11f23c2020-05-11 12:14:41 -050064 // Store the new enty in the sorted index.
65 itr = iv_index.insert(itr, newEntry);
Zane Shelley200c3452019-09-26 11:46:30 -050066 }
67
Zane Shelleyc11f23c2020-05-11 12:14:41 -050068 // It is important to note that if newEntry was not inserted into the
69 // index above because a duplicate already existed, it will be deleted
70 // as soon as it goes out of scope.
71
72 // Return a pointer to the this entry in the factory.
73 return *itr;
Zane Shelley200c3452019-09-26 11:46:30 -050074 }
75
76 /**
77 * @brief Deletes all entries in the factory.
78 *
79 * This is called in the destructor. So it cannot throw an exception.
80 */
81 void clear()
82 {
Zane Shelley200c3452019-09-26 11:46:30 -050083 iv_index.clear();
84 }
85
86 /**
87 * @brief Shrinks the index capacity to eliminate unused memory space.
88 *
89 * The index is a vector where the capacity will grow as items are added to
90 * it. Each time more capacity is needed, the vector will double the current
91 * capacity to make room for new entries. The general use of this class is
92 * expected that all needed entries will be added during the isolator
93 * initialization. Afterwards, the extra capacity is not needed. So this
94 * function will shrink the capacity to the size of the vector.
95 */
Zane Shelley7f7a42d2019-10-28 13:28:31 -050096 void compact()
97 {
98 iv_index.shrink_to_fit();
99 }
Zane Shelley200c3452019-09-26 11:46:30 -0500100
Zane Shelleyc11f23c2020-05-11 12:14:41 -0500101 /** @return The number of entries in this flyweight factory. */
102 size_t size()
103 {
104 return iv_index.size();
105 }
106
Zane Shelley200c3452019-09-26 11:46:30 -0500107 private:
Zane Shelley200c3452019-09-26 11:46:30 -0500108 /**
109 * Each new T is allocated on the memory heap and a pointer to each of those
110 * objects is stored in this vector. The vector will not contain duplicates
111 * and is kept sorted by the value of the objects. Inserting a new element
112 * is O(n). However, since the vector is sorted, searching for existing
113 * entries is O(log n). Considering the expected use of this class,
114 * insertion should only exist during initialization of the isolator, where
115 * searching will be done at the time of isolation when performance is more
116 * important.
117 *
118 * An alternative to this approach is to use std::set, where insertion and
119 * searching are both O(log n). However, std::set is typically designed with
120 * a red-black tree which carries a lot of extra memory overhead to maintain
121 * the structure. Also, the Hostboot user application does not support
122 * std::set at this time.
123 */
Zane Shelleyc11f23c2020-05-11 12:14:41 -0500124 std::vector<std::shared_ptr<T>> iv_index;
Zane Shelley200c3452019-09-26 11:46:30 -0500125};
126
127} // end namespace libhei