Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 1 | #pragma once |
| 2 | |
| 3 | #include <stdint.h> |
Zane Shelley | ca9f625 | 2019-10-25 21:17:30 -0500 | [diff] [blame] | 4 | |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 5 | #include <algorithm> |
Zane Shelley | c11f23c | 2020-05-11 12:14:41 -0500 | [diff] [blame] | 6 | #include <memory> |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 7 | #include <vector> |
| 8 | |
| 9 | namespace libhei |
| 10 | { |
| 11 | |
| 12 | /** @brief A generic flyweight factory for objects of type T. */ |
| 13 | template <class T> |
| 14 | class Flyweight |
| 15 | { |
Zane Shelley | fc7ab19 | 2019-09-27 15:45:16 -0500 | [diff] [blame] | 16 | private: // This class cannot be instantiated. Use getSingleton() instead. |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 17 | /** @brief Default constructor. */ |
| 18 | Flyweight() = default; |
| 19 | |
| 20 | /** @brief Destructor. */ |
Zane Shelley | c11f23c | 2020-05-11 12:14:41 -0500 | [diff] [blame] | 21 | ~Flyweight() = default; |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 22 | |
| 23 | /** @brief Default copy constructor. */ |
Zane Shelley | fe27b65 | 2019-10-28 11:33:07 -0500 | [diff] [blame] | 24 | Flyweight(const Flyweight&) = delete; |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 25 | |
| 26 | /** @brief Default assignment operator. */ |
Zane Shelley | fe27b65 | 2019-10-28 11:33:07 -0500 | [diff] [blame] | 27 | Flyweight& operator=(const Flyweight&) = delete; |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 28 | |
Zane Shelley | fc7ab19 | 2019-09-27 15:45:16 -0500 | [diff] [blame] | 29 | public: |
Zane Shelley | fc7ab19 | 2019-09-27 15:45:16 -0500 | [diff] [blame] | 30 | /** @brief Provides access to a singleton instance of this object. */ |
Zane Shelley | fe27b65 | 2019-10-28 11:33:07 -0500 | [diff] [blame] | 31 | static Flyweight& getSingleton() |
Zane Shelley | fc7ab19 | 2019-09-27 15:45:16 -0500 | [diff] [blame] | 32 | { |
| 33 | static Flyweight theFlyweight; |
| 34 | return theFlyweight; |
| 35 | } |
| 36 | |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 37 | /** |
Zane Shelley | c11f23c | 2020-05-11 12:14:41 -0500 | [diff] [blame] | 38 | * @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 Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 44 | */ |
Zane Shelley | c11f23c | 2020-05-11 12:14:41 -0500 | [diff] [blame] | 45 | template <class... Args> |
| 46 | std::shared_ptr<T> get(Args&&... i_args) |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 47 | { |
Zane Shelley | c11f23c | 2020-05-11 12:14:41 -0500 | [diff] [blame] | 48 | // Create a new instance with the given arguments. |
| 49 | std::shared_ptr<T> newEntry = std::make_shared<T>(i_args...); |
| 50 | |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 51 | // The index is sorted by the value of the T objects. Check to see if |
Zane Shelley | c11f23c | 2020-05-11 12:14:41 -0500 | [diff] [blame] | 52 | // 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 Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 58 | |
| 59 | // std::lower_bound() will return the first element that does not |
Zane Shelley | c11f23c | 2020-05-11 12:14:41 -0500 | [diff] [blame] | 60 | // 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 Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 63 | { |
Zane Shelley | c11f23c | 2020-05-11 12:14:41 -0500 | [diff] [blame] | 64 | // Store the new enty in the sorted index. |
| 65 | itr = iv_index.insert(itr, newEntry); |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 66 | } |
| 67 | |
Zane Shelley | c11f23c | 2020-05-11 12:14:41 -0500 | [diff] [blame] | 68 | // 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 Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 74 | } |
| 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 Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 83 | 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 Shelley | 7f7a42d | 2019-10-28 13:28:31 -0500 | [diff] [blame] | 96 | void compact() |
| 97 | { |
| 98 | iv_index.shrink_to_fit(); |
| 99 | } |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 100 | |
Zane Shelley | c11f23c | 2020-05-11 12:14:41 -0500 | [diff] [blame] | 101 | /** @return The number of entries in this flyweight factory. */ |
| 102 | size_t size() |
| 103 | { |
| 104 | return iv_index.size(); |
| 105 | } |
| 106 | |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 107 | private: |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 108 | /** |
| 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 Shelley | c11f23c | 2020-05-11 12:14:41 -0500 | [diff] [blame] | 124 | std::vector<std::shared_ptr<T>> iv_index; |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 125 | }; |
| 126 | |
| 127 | } // end namespace libhei |