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> |
| 6 | #include <vector> |
| 7 | |
| 8 | namespace libhei |
| 9 | { |
| 10 | |
| 11 | /** @brief A generic flyweight factory for objects of type T. */ |
| 12 | template <class T> |
| 13 | class Flyweight |
| 14 | { |
Zane Shelley | fc7ab19 | 2019-09-27 15:45:16 -0500 | [diff] [blame] | 15 | private: // This class cannot be instantiated. Use getSingleton() instead. |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 16 | /** @brief Default constructor. */ |
| 17 | Flyweight() = default; |
| 18 | |
| 19 | /** @brief Destructor. */ |
Zane Shelley | 7f7a42d | 2019-10-28 13:28:31 -0500 | [diff] [blame] | 20 | ~Flyweight() |
| 21 | { |
| 22 | clear(); |
| 23 | } |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 24 | |
| 25 | /** @brief Default copy constructor. */ |
Zane Shelley | fe27b65 | 2019-10-28 11:33:07 -0500 | [diff] [blame] | 26 | Flyweight(const Flyweight&) = delete; |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 27 | |
| 28 | /** @brief Default assignment operator. */ |
Zane Shelley | fe27b65 | 2019-10-28 11:33:07 -0500 | [diff] [blame] | 29 | Flyweight& operator=(const Flyweight&) = delete; |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 30 | |
Zane Shelley | fc7ab19 | 2019-09-27 15:45:16 -0500 | [diff] [blame] | 31 | public: |
Zane Shelley | fc7ab19 | 2019-09-27 15:45:16 -0500 | [diff] [blame] | 32 | /** @brief Provides access to a singleton instance of this object. */ |
Zane Shelley | fe27b65 | 2019-10-28 11:33:07 -0500 | [diff] [blame] | 33 | static Flyweight& getSingleton() |
Zane Shelley | fc7ab19 | 2019-09-27 15:45:16 -0500 | [diff] [blame] | 34 | { |
| 35 | static Flyweight theFlyweight; |
| 36 | return theFlyweight; |
| 37 | } |
| 38 | |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 39 | /** |
| 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 Shelley | fe27b65 | 2019-10-28 11:33:07 -0500 | [diff] [blame] | 45 | T& get(const T& i_entry) |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 46 | { |
| 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 Shelley | 83da245 | 2019-10-25 15:45:34 -0500 | [diff] [blame] | 49 | auto itr = |
| 50 | std::lower_bound(iv_index.begin(), iv_index.end(), &i_entry, |
Zane Shelley | fe27b65 | 2019-10-28 11:33:07 -0500 | [diff] [blame] | 51 | [](const T* a, const T* b) { return *a < *b; }); |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 52 | |
| 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 Shelley | 83da245 | 2019-10-25 15:45:34 -0500 | [diff] [blame] | 56 | if (iv_index.end() == itr || !(i_entry == *(*itr))) |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 57 | { |
| 58 | // Create a new entry and store the pointer in the sorted index. |
Zane Shelley | c477199 | 2019-10-28 22:01:49 -0500 | [diff] [blame] | 59 | itr = iv_index.insert(itr, new T{i_entry}); |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 60 | } |
| 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 Shelley | 7f7a42d | 2019-10-28 13:28:31 -0500 | [diff] [blame] | 73 | for (auto i : iv_index) |
| 74 | { |
| 75 | delete i; |
| 76 | } |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 77 | 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 Shelley | 7f7a42d | 2019-10-28 13:28:31 -0500 | [diff] [blame] | 90 | void compact() |
| 91 | { |
| 92 | iv_index.shrink_to_fit(); |
| 93 | } |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 94 | |
| 95 | private: |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 96 | /** |
| 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 |