The Sparta Modeling Framework
Loading...
Searching...
No Matches
FastList.hpp
Go to the documentation of this file.
1// <FastList.hpp> -*- C++ -*-
2
3
11#pragma once
12
13#include <vector>
14#include <iostream>
15#include <exception>
16#include <iterator>
17#include <cinttypes>
18#include <cassert>
19#include <cstddef>
20#include <type_traits>
21
24
25namespace sparta::utils
26{
46 template <class DataT>
48 {
49 struct Node
50 {
51 using NodeIdx = int;
52
53 // Where this node is in the vector
54 const NodeIdx index;
55
56 // Points to the next element or the next free
57 // element if this node has been removed.
58 NodeIdx next = -1;
59
60 // Points to the previous element.
61 NodeIdx prev = -1;
62
63 // Stores the memory for an instance of 'T'.
64 // Use placement new to construct the object and
65 // manually invoke its dtor as necessary.
66 alignas(DataT) std::byte type_storage[sizeof(DataT)];
67
68 Node(NodeIdx _index) :
69 index(_index)
70 {}
71 };
72
73 typename Node::NodeIdx advanceNode_(typename Node::NodeIdx node_idx) const {
74 typename Node::NodeIdx ret_idx = (node_idx == -1 ? -1 : nodes_[node_idx].next);
75 return ret_idx;
76 }
77
78 auto getStorage(typename Node::NodeIdx node_idx) {
79 return &nodes_[node_idx].type_storage;
80 }
81
82 auto getStorage(typename Node::NodeIdx node_idx) const {
83 return &nodes_[node_idx].type_storage;
84 }
85
86 public:
87 using value_type = DataT;
88
94 template<bool is_const = true>
95 class NodeIterator : public sparta::utils::IteratorTraits<std::forward_iterator_tag, value_type>
96 {
97 typedef std::conditional_t<is_const, const value_type &, value_type &> RefIteratorType;
98 typedef std::conditional_t<is_const, const value_type *, value_type *> PtrIteratorType;
99 typedef std::conditional_t<is_const, const FastList *, FastList *> FastListPtrType;
100 public:
101
102 NodeIterator() = default;
103
104 NodeIterator(const NodeIterator<false> & iter) :
105 flist_(iter.flist_),
106 node_idx_(iter.node_idx_)
107 {}
108
113 bool isValid() const { return (node_idx_ != -1); }
114
116 PtrIteratorType operator->() {
117 assert(isValid());
118 return reinterpret_cast<PtrIteratorType>(flist_->getStorage(node_idx_));
119 }
120
122 PtrIteratorType operator->() const {
123 assert(isValid());
124 return reinterpret_cast<PtrIteratorType>(flist_->getStorage(node_idx_));
125 }
126
128 RefIteratorType operator* () {
129 assert(isValid());
130 return *reinterpret_cast<PtrIteratorType>(flist_->getStorage(node_idx_));
131 }
132
134 RefIteratorType operator* () const {
135 assert(isValid());
136 return *reinterpret_cast<PtrIteratorType>(flist_->getStorage(node_idx_));
137 }
138
140 int getIndex() const { return node_idx_; }
141
144 {
145 assert(isValid());
146 node_idx_ = flist_->advanceNode_(node_idx_);
147 return *this;
148 }
149
152 {
153 NodeIterator orig = *this;
154 assert(isValid());
155 node_idx_ = flist_->advanceNode_(node_idx_);
156 return orig;
157 }
158
160 bool operator!=(const NodeIterator &rhs)
161 {
162 return (rhs.flist_ != flist_) ||
163 (rhs.node_idx_ != node_idx_);
164 }
165
167 bool operator==(const NodeIterator & node) const noexcept {
168 return (node.flist_ == flist_) && (node.node_idx_ == node_idx_);
169 }
170
172 NodeIterator& operator=(const NodeIterator &rhs) = default;
173 NodeIterator& operator=( NodeIterator &&rhs) = default;
174
175 private:
176 friend class FastList<DataT>;
177
178 NodeIterator(FastListPtrType flist, typename Node::NodeIdx node_idx) :
179 flist_(flist),
180 node_idx_(node_idx)
181 { }
182
183 FastListPtrType flist_ = nullptr;
184 typename Node::NodeIdx node_idx_ = -1;
185 };
186
192 {
193 sparta_assert(size != 0,
194 "Cannot create a sparta::utils::FastList of size 0");
195 int node_idx = 0;
196 nodes_.reserve(size);
197 for(size_t i = 0; i < size; ++i) {
198 Node n(node_idx);
199 n.prev = node_idx - 1;
200 n.next = node_idx + 1;
201 ++node_idx;
202 nodes_.emplace_back(n);
203 }
204 nodes_.back().next = -1;
205 }
206
208 ~FastList() { clear(); }
209
212
215 return iterator(this, first_node_);
216 }
217
220 return const_iterator(this, first_node_);
221 }
222
224 iterator end() { return iterator(this, -1); }
225
227 const_iterator end() const { return const_iterator(this, -1); }
228
230 DataT & front() { return *begin(); }
231
233 const DataT & front() const { return *begin(); }
234
236 bool empty() const { return size_ == 0; }
237
239 size_t size() const { return size_; };
240
242 size_t max_size() const { return nodes_.capacity(); };
243
245 // Modifiers
246 void clear() noexcept {
247 const auto my_end = end();
248 for(auto it = begin(); it != my_end;) {
249 erase(it++);
250 }
251 }
252
258 {
259 const auto node_idx = entry.getIndex();
260 auto & node_to_erase = nodes_[node_idx];
261 reinterpret_cast<DataT*>(&node_to_erase.type_storage)->~DataT();
262 int next_elem = -1;
263
264 if(first_node_ == node_idx) {
265 first_node_ = node_to_erase.next;
266 }
267 if(last_node_ == node_idx) {
268 last_node_ = node_to_erase.prev;
269 }
270
271 if(SPARTA_EXPECT_FALSE(node_to_erase.next != -1))
272 {
273 auto & next_node = nodes_[node_to_erase.next];
274 next_node.prev = node_to_erase.prev;
275 next_elem = node_to_erase.next;
276 }
277
278 if(SPARTA_EXPECT_FALSE(node_to_erase.prev != -1))
279 {
280 auto & prev_node = nodes_[node_to_erase.prev];
281 prev_node.next = node_to_erase.next;
282 }
283
284 node_to_erase.prev = -1;
285 node_to_erase.next = -1;
286 if(SPARTA_EXPECT_TRUE(free_head_ != -1)) {
287 nodes_[free_head_].prev = node_idx;
288 node_to_erase.next = free_head_;
289 }
290 free_head_ = node_idx;
291 --size_;
292 return iterator(this, next_elem);
293 }
294
295 template<class ...ArgsT>
296 iterator emplace(const const_iterator & pos, ArgsT&&...args)
297 {
298 sparta_assert(free_head_ != -1,
299 "FastList is out of element room");
300 const auto index_pos = pos.getIndex();
301
302 // If the index pos is -1, it's either end() or begin() on
303 // an empty list. Just emplace_back (or front, don't matter)
304 if(index_pos == -1) {
305 return emplace_back(std::forward<ArgsT>(args)...);
306 }
307
308 auto & new_node = nodes_[free_head_];
309 free_head_ = new_node.next;
310 new (&new_node.type_storage) DataT(args...);
311 // Update pointers. Start with a clean slate
312 new_node.next = -1;
313 new_node.prev = -1;
314
315 // Insert before the given pt
316 auto & insert_pt = nodes_[index_pos];
317 new_node.next = insert_pt.index;
318 new_node.prev = insert_pt.prev;
319 insert_pt.prev = new_node.index;
320 if(new_node.prev != -1) {
321 // update the previous node's next
322 nodes_[new_node.prev].next = new_node.index;
323 }
324
325 if((first_node_ == index_pos) || (first_node_ == -1))
326 {
327 first_node_ = new_node.index;
328 }
329 ++size_;
330
331 return iterator(this, new_node.index);
332 }
333
339 template<class ...ArgsT>
340 iterator emplace_front(ArgsT&&...args)
341 {
342 sparta_assert(free_head_ != -1,
343 "FastList is out of element room");
344
345 auto & new_node = nodes_[free_head_];
346 free_head_ = new_node.next;
347 new (&new_node.type_storage) DataT(args...);
348
349 // Update pointers. Start with a clean slate
350 new_node.next = -1;
351 new_node.prev = -1;
352 if(SPARTA_EXPECT_TRUE(first_node_ != -1))
353 {
354 auto & old_first = nodes_[first_node_];
355 old_first.prev = new_node.index;
356 new_node.next = old_first.index;
357 }
358 first_node_ = new_node.index;
359 if(SPARTA_EXPECT_FALSE(last_node_ == -1)) {
360 last_node_ = first_node_;
361 }
362
363 ++size_;
364 return iterator(this, new_node.index);
365 }
366
372 template<class ...ArgsT>
373 iterator emplace_back(ArgsT&&...args) {
374 sparta_assert(free_head_ != -1,
375 "FastList is out of element room");
376
377 auto & new_node = nodes_[free_head_];
378 free_head_ = new_node.next;
379 new (&new_node.type_storage) DataT(args...);
380
381 // Update pointers. Start with a clean slate
382 new_node.next = -1;
383 new_node.prev = -1;
384 if(SPARTA_EXPECT_TRUE(last_node_ != -1))
385 {
386 auto & old_last = nodes_[last_node_];
387 old_last.next = new_node.index;
388 new_node.prev = old_last.index;
389 }
390 last_node_ = new_node.index;
391 if(SPARTA_EXPECT_FALSE(first_node_ == -1)) {
392 first_node_ = last_node_;
393 }
394
395 ++size_;
396 return iterator(this, new_node.index);
397 }
398
401 template<class ...ArgsT>
402 iterator insert(const const_iterator & pos, ArgsT&&...args) {
403 return emplace(pos, args...);
404 }
405
407 void pop_back() {
408 sparta_assert(last_node_ != -1,
409 "Can't pop_back on an empty list");
410 erase(iterator(this, last_node_));
411 }
412
414 void pop_front() {
415 sparta_assert(first_node_ != -1,
416 "Can't pop_front on an empty list");
417 erase(iterator(this, first_node_));
418 }
419
420 private:
421
422 // Friendly printer
423 friend std::ostream & operator<<(std::ostream & os, const FastList<DataT> & fl)
424 {
425 int next_node = fl.first_node_;
426 if(next_node == -1) {
427 os << "<empty>" << std::endl;
428 }
429 else {
430 int index = fl.size_ - 1;
431 do
432 {
433 const auto & n = fl.nodes_[next_node];
434 os << index << " elem=" << *reinterpret_cast<const DataT*>(&n.type_storage)
435 << " n.next=" << n.next
436 << " n.prev=" << n.prev << std::endl;
437 next_node = n.next;
438 --index;
439 } while(next_node != -1);
440 }
441 return os;
442 }
443
444 // Stores all the nodes.
445 std::vector<Node> nodes_;
446
447 int free_head_ = 0;
448 int first_node_ = -1;
449 int last_node_ = -1;
450 size_t size_ = 0;
451 };
452}
Defines a few handy (and now deprecated) C++ iterator traits.
Set of macros for Sparta assertions. Caught by the framework.
#define sparta_assert(...)
Simple variadic assertion that will throw a sparta_exception if the condition fails.
#define SPARTA_EXPECT_TRUE(x)
A macro for hinting to the compiler a particular condition should be considered most likely true.
#define SPARTA_EXPECT_FALSE(x)
A macro for hinting to the compiler a particular condition should be considered most likely false.
The internal iterator type of FastList. Use FastList<T>::[const_]iterator instead.
Definition FastList.hpp:96
bool operator!=(const NodeIterator &rhs)
Equality of iterator (not the underlying object)
Definition FastList.hpp:160
bool isValid() const
Determine if the iteartor is valid.
Definition FastList.hpp:113
PtrIteratorType operator->()
Iterator dereference.
Definition FastList.hpp:116
bool operator==(const NodeIterator &node) const noexcept
Equality of iterator (not the underlying object)
Definition FastList.hpp:167
NodeIterator & operator=(const NodeIterator &rhs)=default
Assignments.
NodeIterator operator++(int)
Move to the next iterator (post)
Definition FastList.hpp:151
int getIndex() const
Get the index in the list where this iterator points.
Definition FastList.hpp:140
NodeIterator & operator++()
Move to the next iterator (pre)
Definition FastList.hpp:143
PtrIteratorType operator->() const
Iterator dereference (const)
Definition FastList.hpp:122
RefIteratorType operator*()
Iterator dereference.
Definition FastList.hpp:128
An alternative to std::list, about 70% faster.
Definition FastList.hpp:48
iterator emplace_front(ArgsT &&...args)
Add an element to the front of the list.
Definition FastList.hpp:340
void pop_front()
Pop the first element off of the list.
Definition FastList.hpp:414
~FastList()
Destroy (clear) the list.
Definition FastList.hpp:208
iterator end()
Obtain an end iterator.
Definition FastList.hpp:224
iterator begin()
Obtain a beginning iterator.
Definition FastList.hpp:214
iterator emplace_back(ArgsT &&...args)
emplace an object at the back
Definition FastList.hpp:373
size_t size() const
Definition FastList.hpp:239
iterator insert(const const_iterator &pos, ArgsT &&...args)
Definition FastList.hpp:402
DataT value_type
Handy using.
Definition FastList.hpp:87
const_iterator end() const
Obtain an end const_iterator.
Definition FastList.hpp:227
NodeIterator< true > const_iterator
Iterator type, const.
Definition FastList.hpp:211
NodeIterator< false > iterator
Iterator type.
Definition FastList.hpp:210
iterator erase(const const_iterator &entry)
Erase an element with the given iterator.
Definition FastList.hpp:257
size_t max_size() const
Definition FastList.hpp:242
DataT & front()
Get the front of the fast list non-const.
Definition FastList.hpp:230
FastList(size_t size)
Construct FastList of a given size.
Definition FastList.hpp:191
const_iterator begin() const
Obtain a beginning const_iterator.
Definition FastList.hpp:219
const DataT & front() const
Get the front of the fast list, const.
Definition FastList.hpp:233
void pop_back()
Pop the last element off of the list.
Definition FastList.hpp:407