DetectorGraph  2.0
statictypedallocator-lite.hpp
Go to the documentation of this file.
1 // Copyright 2018 Nest Labs, Inc.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #ifndef DETECTORGRAPH_INCLUDE_STATICTYPEDALLOCATOR_LITE_HPP_
16 #define DETECTORGRAPH_INCLUDE_STATICTYPEDALLOCATOR_LITE_HPP_
17 
18 #include "dgassert.hpp"
19 #include "dgstdincludes.hpp"
20 
21 namespace DetectorGraph
22 {
23 
24 /**
25  * @brief Internal - Static allocator for child types of arbitrary sizes.
26  *
27  * Memory-constrained devices often don't provide a general purpose heap (new
28  * & delete). In those cases it's desirable to have an application limit its
29  * memory allocations purely to statically allocated memory.
30  * DetectorGraph uses the `class Child<T> : class Base` pattern a lot to
31  * provide it's richly typed APIs. And in some places it relies on runtime
32  * allocation of such `Child<T>` objects. This class provides an allocator
33  * for such objects that complies with the no-heap requirement.
34  *
35  * The one limitation is that it's only able to store 1 object per `T` of
36  * `Child<T>`.
37  *
38  * Usage sample:
39  * @code
40 
41 struct SomeBase { virtual ~SomeBase() {} };
42 template<class T> struct SomeChild : public SomeBase { };
43 
44 StaticTypedAllocator<SomeBase, 0> allocator;
45 
46 SomeBase* objT1 = allocator.New<SomeChild<T1>>();
47 SomeBase* objT2 = allocator.New<SomeChild<T2>>();
48 allocator.Delete(objT1);
49 allocator.Delete(objT2);
50 
51  * @endcode
52  *
53  * Different values for the Ctxt template parameter must be used on separate
54  * instances if they store the same types:
55  * @code
56 // Given
57 
58 StaticTypedAllocator<SomeBase, 0> allocatorA;
59 SomeBase* objA = allocator.New<SomeChild<T1>>();
60 // SomeBase* objB = allocator.New<SomeChild<T1>>(); // Will throw a 'busy' assert.
61 
62 StaticTypedAllocator<SomeBase, 1> allocatorB;
63 SomeBase* objB = allocatorB.New<SomeChild<T1>>(); // ok
64 
65  * @endcode
66  *
67  *
68  */
69 namespace {
70  struct DefaultStaticTypedAllocatorCtxt {};
71 }
72 
73 template< class TBase, class Ctxt = DefaultStaticTypedAllocatorCtxt>
75 {
76  struct NodeHeader
77  {
78  NodeHeader(uint8_t* aStorage)
79  : storage(aStorage)
80  , next(NULL), busy(false)
81  {
82  }
83 
84  uint8_t* storage;
85  TBase* GetObjectPtr()
86  {
87  return reinterpret_cast<TBase*>(storage);
88  }
89  NodeHeader* next;
90  bool busy;
91  };
92 public:
93  StaticTypedAllocator() : mHeadNode(NULL)
94  {
95  }
96 
97  /* Below there are two versions of the New() method.
98  * The top one uses C++11 variadic template args and rvalue perfect
99  * forwarding. This is both more convenient and more efficient as it
100  * constructs the type only once vs constructing & copy constructing. */
101 
102  /**
103  * @brief Constructs & Allocates new object of type TChild with arguments.
104  */
105  template <class TChild, typename... TChildArgs>
106 #if defined(BUILD_FEATURE_DETECTORGRAPH_CONFIG_PERFECT_FORWARDING)
107  TChild* New(TChildArgs&&... constructor_args)
108 #else
109  TChild* New(TChildArgs&... constructor_args)
110 #endif
111  {
112  NodeHeader* node = GetNodeHeader<TChild>();
113 
114  DG_ASSERT(!node->busy);
115  // NOTE: Cannot store more than one object at the same time.
116 
117 #if defined(BUILD_FEATURE_DETECTORGRAPH_CONFIG_PERFECT_FORWARDING)
118  TChild* newObjPtr =
119  new(node->storage) TChild(std::forward<TChildArgs>(constructor_args)...);
120 #else
121  TChild* newObjPtr =
122  new(node->storage) TChild(constructor_args...);
123 #endif
124 
125  node->busy = true;
126  LinkNode(node);
127  return newObjPtr;
128  }
129 
130  /**
131  * @brief Deletes/Frees an allocated object via the base ptr.
132  */
133  void Delete(TBase* targetObject)
134  {
135  // O(N) deletion
136  DG_ASSERT(mHeadNode); // Not empty
137 
138  // A reference to the .next ptr on the previous node
139  NodeHeader** fromPtr = &mHeadNode;
140 
141  // Traversing node
142  NodeHeader* nodeIt = mHeadNode;
143 
144  // Target
145  TBase* storedObject = nodeIt->GetObjectPtr();
146 
147  while(nodeIt && storedObject != targetObject)
148  {
149  // Save .next ptr address before following it
150  fromPtr = &(nodeIt->next);
151  nodeIt = nodeIt->next;
152  storedObject = nodeIt->GetObjectPtr();
153  }
154 
155  DG_ASSERT(nodeIt && storedObject);
156 
157  // Bypass deleted node
158  *fromPtr = nodeIt->next;
159 
160  // Destroy deleted node
161  storedObject->~TBase();
162 
163  // Cleanup deleted node
164  nodeIt->busy = false;
165  nodeIt->next = NULL;
166  }
167 
168  /**
169  * @brief Deletes all allocated objects.
170  */
171  void clear()
172  {
173  // O(N) clear.
174  NodeHeader* nodeIt = mHeadNode;
175  while(nodeIt)
176  {
177  TBase* storedObject = nodeIt->GetObjectPtr();
178  storedObject->~TBase();
179  nodeIt->busy = false;
180 
181  NodeHeader* tmp = nodeIt; // only necessary to cleanup
182  nodeIt = nodeIt->next;
183  tmp->next = NULL;
184  }
185  mHeadNode = NULL;
186  }
187 
189  {
190  clear();
191  }
192 
193 private:
194  void LinkNode(NodeHeader* node)
195  {
196  // O(N) insertion.
197 
198  // Find insertion point
199  NodeHeader** nextPtr = &mHeadNode;
200  while(*nextPtr)
201  {
202  nextPtr = &((*nextPtr)->next);
203  }
204 
205  // Change last ptr to point to new node instead of NULL
206  *nextPtr = node;
207  node->next = NULL;
208  }
209 
210 private:
211  NodeHeader* mHeadNode;
212 
213  template<class TChild>
214  NodeHeader* GetNodeHeader()
215  {
216  static uint8_t mNodeStorage[sizeof(TChild)];
217  static NodeHeader node = NodeHeader(mNodeStorage);
218  return &node;
219  }
220 };
221 
222 }
223 
224 #endif // DETECTORGRAPH_INCLUDE_STATICTYPEDALLOCATOR_LITE_HPP_
TChild * New(TChildArgs &... constructor_args)
Constructs & Allocates new object of type TChild with arguments.
void Delete(TBase *targetObject)
Deletes/Frees an allocated object via the base ptr.
void clear()
Deletes all allocated objects.
#define DG_ASSERT(condition)
Definition: dgassert.hpp:20