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