Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 1 | #pragma once |
| 2 | |
| 3 | #include <stdint.h> |
| 4 | #include <algorithm> |
| 5 | #include <vector> |
| 6 | |
| 7 | namespace libhei |
| 8 | { |
| 9 | |
| 10 | /** @brief A generic flyweight factory for objects of type T. */ |
| 11 | template <class T> |
| 12 | class Flyweight |
| 13 | { |
Zane Shelley | fc7ab19 | 2019-09-27 15:45:16 -0500 | [diff] [blame] | 14 | private: // This class cannot be instantiated. Use getSingleton() instead. |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 15 | |
| 16 | /** @brief Default constructor. */ |
| 17 | Flyweight() = default; |
| 18 | |
| 19 | /** @brief Destructor. */ |
| 20 | ~Flyweight() { clear(); } |
| 21 | |
| 22 | /** @brief Default copy constructor. */ |
Zane Shelley | 83da245 | 2019-10-25 15:45:34 -0500 | [diff] [blame^] | 23 | Flyweight(const Flyweight &) = delete; |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 24 | |
| 25 | /** @brief Default assignment operator. */ |
Zane Shelley | 83da245 | 2019-10-25 15:45:34 -0500 | [diff] [blame^] | 26 | Flyweight & operator=(const Flyweight &) = delete; |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 27 | |
Zane Shelley | fc7ab19 | 2019-09-27 15:45:16 -0500 | [diff] [blame] | 28 | public: |
| 29 | |
| 30 | /** @brief Provides access to a singleton instance of this object. */ |
| 31 | static Flyweight & getSingleton() |
| 32 | { |
| 33 | static Flyweight theFlyweight; |
| 34 | return theFlyweight; |
| 35 | } |
| 36 | |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 37 | /** |
| 38 | * @brief Adds the given entry to the factory, if it does not already |
| 39 | * exist. Then returns a reference to that entry in the factory. |
| 40 | * @param The target entry. |
| 41 | * @return A reference to this entry in the factory. |
| 42 | */ |
Zane Shelley | 83da245 | 2019-10-25 15:45:34 -0500 | [diff] [blame^] | 43 | T & get(const T & i_entry) |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 44 | { |
| 45 | // The index is sorted by the value of the T objects. Check to see if |
| 46 | // this entry already exists in the factory. |
Zane Shelley | 83da245 | 2019-10-25 15:45:34 -0500 | [diff] [blame^] | 47 | auto itr = |
| 48 | std::lower_bound(iv_index.begin(), iv_index.end(), &i_entry, |
| 49 | [](const T * a, const T * b) { return *a < *b; }); |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 50 | |
| 51 | // std::lower_bound() will return the first element that does not |
| 52 | // compare less than i_entry. So if an entry is found, we must make sure |
| 53 | // it does not have the same value as i_entry. |
Zane Shelley | 83da245 | 2019-10-25 15:45:34 -0500 | [diff] [blame^] | 54 | if (iv_index.end() == itr || !(i_entry == *(*itr))) |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 55 | { |
| 56 | // Create a new entry and store the pointer in the sorted index. |
Zane Shelley | 83da245 | 2019-10-25 15:45:34 -0500 | [diff] [blame^] | 57 | itr = iv_index.insert(itr, new T { i_entry }); |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 58 | } |
| 59 | |
| 60 | // Return a reference to this entry in the factory. |
| 61 | return *(*itr); |
| 62 | } |
| 63 | |
| 64 | /** |
| 65 | * @brief Deletes all entries in the factory. |
| 66 | * |
| 67 | * This is called in the destructor. So it cannot throw an exception. |
| 68 | */ |
| 69 | void clear() |
| 70 | { |
Zane Shelley | 83da245 | 2019-10-25 15:45:34 -0500 | [diff] [blame^] | 71 | for (auto i : iv_index) { delete i; } |
Zane Shelley | 200c345 | 2019-09-26 11:46:30 -0500 | [diff] [blame] | 72 | iv_index.clear(); |
| 73 | } |
| 74 | |
| 75 | /** |
| 76 | * @brief Shrinks the index capacity to eliminate unused memory space. |
| 77 | * |
| 78 | * The index is a vector where the capacity will grow as items are added to |
| 79 | * it. Each time more capacity is needed, the vector will double the current |
| 80 | * capacity to make room for new entries. The general use of this class is |
| 81 | * expected that all needed entries will be added during the isolator |
| 82 | * initialization. Afterwards, the extra capacity is not needed. So this |
| 83 | * function will shrink the capacity to the size of the vector. |
| 84 | */ |
| 85 | void compact() { iv_index.shrink_to_fit(); } |
| 86 | |
| 87 | private: |
| 88 | |
| 89 | /** |
| 90 | * Each new T is allocated on the memory heap and a pointer to each of those |
| 91 | * objects is stored in this vector. The vector will not contain duplicates |
| 92 | * and is kept sorted by the value of the objects. Inserting a new element |
| 93 | * is O(n). However, since the vector is sorted, searching for existing |
| 94 | * entries is O(log n). Considering the expected use of this class, |
| 95 | * insertion should only exist during initialization of the isolator, where |
| 96 | * searching will be done at the time of isolation when performance is more |
| 97 | * important. |
| 98 | * |
| 99 | * An alternative to this approach is to use std::set, where insertion and |
| 100 | * searching are both O(log n). However, std::set is typically designed with |
| 101 | * a red-black tree which carries a lot of extra memory overhead to maintain |
| 102 | * the structure. Also, the Hostboot user application does not support |
| 103 | * std::set at this time. |
| 104 | */ |
| 105 | std::vector<T*> iv_index; |
| 106 | }; |
| 107 | |
| 108 | } // end namespace libhei |