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