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 | 72fd2e4 | 2022-11-12 12:14:53 -0600 | [diff] [blame] | 21 | ~Flyweight() |
| 22 | { |
| 23 | clear(); |
| 24 | } |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 25 | |
| 26 | /** @brief Default copy constructor. */ |
Zane Shelley | fe27b65 | 2019-10-28 11:33:07 -0500 | [diff] [blame] | 27 | Flyweight(const Flyweight&) = delete; |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 28 | |
| 29 | /** @brief Default assignment operator. */ |
Zane Shelley | fe27b65 | 2019-10-28 11:33:07 -0500 | [diff] [blame] | 30 | Flyweight& operator=(const Flyweight&) = delete; |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 31 | |
Zane Shelley | fc7ab19 | 2019-09-27 15:45:16 -0500 | [diff] [blame] | 32 | public: |
Zane Shelley | fc7ab19 | 2019-09-27 15:45:16 -0500 | [diff] [blame] | 33 | /** @brief Provides access to a singleton instance of this object. */ |
Zane Shelley | fe27b65 | 2019-10-28 11:33:07 -0500 | [diff] [blame] | 34 | static Flyweight& getSingleton() |
Zane Shelley | fc7ab19 | 2019-09-27 15:45:16 -0500 | [diff] [blame] | 35 | { |
| 36 | static Flyweight theFlyweight; |
| 37 | return theFlyweight; |
| 38 | } |
| 39 | |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 40 | /** |
Zane Shelley | c11f23c | 2020-05-11 12:14:41 -0500 | [diff] [blame] | 41 | * @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 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 | template <class... Args> |
| 49 | std::shared_ptr<T> get(Args&&... i_args) |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 50 | { |
Zane Shelley | c11f23c | 2020-05-11 12:14:41 -0500 | [diff] [blame] | 51 | // Create a new instance with the given arguments. |
| 52 | std::shared_ptr<T> newEntry = std::make_shared<T>(i_args...); |
| 53 | |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 54 | // 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] | 55 | // 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) { |
Patrick Williams | 8db65db | 2024-08-16 15:22:30 -0400 | [diff] [blame^] | 59 | return *a < *b; |
| 60 | }); |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 61 | |
| 62 | // std::lower_bound() will return the first element that does not |
Zane Shelley | c11f23c | 2020-05-11 12:14:41 -0500 | [diff] [blame] | 63 | // 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 Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 66 | { |
Zane Shelley | c11f23c | 2020-05-11 12:14:41 -0500 | [diff] [blame] | 67 | // Store the new enty in the sorted index. |
| 68 | itr = iv_index.insert(itr, newEntry); |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 69 | } |
| 70 | |
Zane Shelley | c11f23c | 2020-05-11 12:14:41 -0500 | [diff] [blame] | 71 | // 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 Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 77 | } |
| 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 Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 86 | 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 Shelley | 7f7a42d | 2019-10-28 13:28:31 -0500 | [diff] [blame] | 99 | void compact() |
| 100 | { |
| 101 | iv_index.shrink_to_fit(); |
| 102 | } |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 103 | |
Zane Shelley | c11f23c | 2020-05-11 12:14:41 -0500 | [diff] [blame] | 104 | /** @return The number of entries in this flyweight factory. */ |
| 105 | size_t size() |
| 106 | { |
| 107 | return iv_index.size(); |
| 108 | } |
| 109 | |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 110 | private: |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 111 | /** |
| 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 Shelley | c11f23c | 2020-05-11 12:14:41 -0500 | [diff] [blame] | 127 | std::vector<std::shared_ptr<T>> iv_index; |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 128 | }; |
| 129 | |
| 130 | } // end namespace libhei |