26namespace sparta::utils
47 template <
class DataT>
67 alignas(DataT) std::byte type_storage[
sizeof(DataT)];
69 Node(NodeIdx _index) :
74 typename Node::NodeIdx advanceNode_(
typename Node::NodeIdx node_idx)
const {
75 typename Node::NodeIdx ret_idx = (node_idx == -1 ? -1 : nodes_[node_idx].next);
79 auto getStorage(
typename Node::NodeIdx node_idx) {
80 return &nodes_[node_idx].type_storage;
83 auto getStorage(
typename Node::NodeIdx node_idx)
const {
84 return &nodes_[node_idx].type_storage;
95 template<
bool is_const = true>
98 typedef std::conditional_t<is_const, const value_type &, value_type &> RefIteratorType;
99 typedef std::conditional_t<is_const, const value_type *, value_type *> PtrIteratorType;
100 typedef std::conditional_t<is_const, const FastList *, FastList *> FastListPtrType;
107 node_idx_(iter.node_idx_)
114 bool isValid()
const {
return (node_idx_ != -1); }
119 return reinterpret_cast<PtrIteratorType
>(flist_->getStorage(node_idx_));
125 return reinterpret_cast<PtrIteratorType
>(flist_->getStorage(node_idx_));
131 return *
reinterpret_cast<PtrIteratorType
>(flist_->getStorage(node_idx_));
137 return *
reinterpret_cast<PtrIteratorType
>(flist_->getStorage(node_idx_));
147 node_idx_ = flist_->advanceNode_(node_idx_);
156 node_idx_ = flist_->advanceNode_(node_idx_);
163 return (rhs.flist_ != flist_) ||
164 (rhs.node_idx_ != node_idx_);
169 return (node.flist_ == flist_) && (node.node_idx_ == node_idx_);
179 NodeIterator(FastListPtrType flist,
typename Node::NodeIdx node_idx) :
184 FastListPtrType flist_ =
nullptr;
185 typename Node::NodeIdx node_idx_ = -1;
209 "Cannot create a sparta::utils::FastList of size 0");
211 "sparta::utils::FastList initial size " << initial_size <<
212 " is larger than its max size " <<
max_size);
215 "sparta::utils::FastList max size " <<
max_size <<
216 " exceeds the addressable node index range");
218 nodes_.reserve(max_size_);
219 for(
size_t i = 0; i < initial_size; ++i) {
221 n.prev = node_idx - 1;
222 n.next = node_idx + 1;
224 nodes_.emplace_back(n);
226 nodes_.back().next = -1;
258 bool empty()
const {
return size_ == 0; }
261 size_t size()
const {
return size_; };
268 void clear() noexcept {
269 const auto my_end =
end();
270 for(
auto it =
begin(); it != my_end;) {
281 const auto node_idx = entry.
getIndex();
282 auto & node_to_erase = nodes_[node_idx];
283 reinterpret_cast<DataT*
>(&node_to_erase.type_storage)->~DataT();
286 if(first_node_ == node_idx) {
287 first_node_ = node_to_erase.next;
289 if(last_node_ == node_idx) {
290 last_node_ = node_to_erase.prev;
295 auto & next_node = nodes_[node_to_erase.next];
296 next_node.prev = node_to_erase.prev;
297 next_elem = node_to_erase.next;
302 auto & prev_node = nodes_[node_to_erase.prev];
303 prev_node.next = node_to_erase.next;
306 node_to_erase.prev = -1;
307 node_to_erase.next = -1;
309 nodes_[free_head_].prev = node_idx;
310 node_to_erase.next = free_head_;
312 free_head_ = node_idx;
317 template<
class ...ArgsT>
321 const auto index_pos = pos.getIndex();
325 if(index_pos == -1) {
329 auto & new_node = nodes_[free_head_];
330 free_head_ = new_node.next;
331 new (&new_node.type_storage) DataT(args...);
337 auto & insert_pt = nodes_[index_pos];
338 new_node.next = insert_pt.index;
339 new_node.prev = insert_pt.prev;
340 insert_pt.prev = new_node.index;
341 if(new_node.prev != -1) {
343 nodes_[new_node.prev].next = new_node.index;
346 if((first_node_ == index_pos) || (first_node_ == -1))
348 first_node_ = new_node.index;
352 return iterator(
this, new_node.index);
360 template<
class ...ArgsT>
365 auto & new_node = nodes_[free_head_];
366 free_head_ = new_node.next;
367 new (&new_node.type_storage) DataT(args...);
374 auto & old_first = nodes_[first_node_];
375 old_first.prev = new_node.index;
376 new_node.next = old_first.index;
378 first_node_ = new_node.index;
380 last_node_ = first_node_;
384 return iterator(
this, new_node.index);
392 template<
class ...ArgsT>
396 auto & new_node = nodes_[free_head_];
397 free_head_ = new_node.next;
398 new (&new_node.type_storage) DataT(args...);
405 auto & old_last = nodes_[last_node_];
406 old_last.next = new_node.index;
407 new_node.prev = old_last.index;
409 last_node_ = new_node.index;
411 first_node_ = last_node_;
415 return iterator(
this, new_node.index);
420 template<
class ...ArgsT>
422 return emplace(pos, args...);
428 "Can't pop_back on an empty list");
435 "Can't pop_front on an empty list");
442 friend std::ostream & operator<<(std::ostream & os,
const FastList<DataT> & fl)
444 int next_node = fl.first_node_;
445 if(next_node == -1) {
446 os <<
"<empty>" << std::endl;
449 int index = fl.size_ - 1;
452 const auto & n = fl.nodes_[next_node];
453 os << index <<
" elem=" << *
reinterpret_cast<const DataT*
>(&n.type_storage)
454 <<
" n.next=" << n.next
455 <<
" n.prev=" << n.prev << std::endl;
458 }
while(next_node != -1);
464 void ensureFreeNode_() {
467 "FastList is out of element room (max_size=" << max_size_ <<
")");
469 const typename Node::NodeIdx new_idx =
470 static_cast<typename Node::NodeIdx
>(nodes_.size());
471 nodes_.emplace_back(new_idx);
472 free_head_ = new_idx;
476 std::vector<Node> nodes_;
477 const size_t max_size_;
480 int first_node_ = -1;
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.
bool operator!=(const NodeIterator &rhs)
Equality of iterator (not the underlying object)
bool isValid() const
Determine if the iteartor is valid.
PtrIteratorType operator->()
Iterator dereference.
bool operator==(const NodeIterator &node) const noexcept
Equality of iterator (not the underlying object)
NodeIterator & operator=(const NodeIterator &rhs)=default
Assignments.
NodeIterator operator++(int)
Move to the next iterator (post)
int getIndex() const
Get the index in the list where this iterator points.
NodeIterator & operator++()
Move to the next iterator (pre)
PtrIteratorType operator->() const
Iterator dereference (const)
RefIteratorType operator*()
Iterator dereference.
An alternative to std::list, about 70% faster.
iterator emplace_front(ArgsT &&...args)
Add an element to the front of the list.
void pop_front()
Pop the first element off of the list.
~FastList()
Destroy (clear) the list.
iterator end()
Obtain an end iterator.
iterator begin()
Obtain a beginning iterator.
iterator emplace_back(ArgsT &&...args)
emplace an object at the back
iterator insert(const const_iterator &pos, ArgsT &&...args)
FastList(size_t initial_size, size_t max_size)
Construct a FastList that grows on demand up to a maximum.
DataT value_type
Handy using.
const_iterator end() const
Obtain an end const_iterator.
NodeIterator< true > const_iterator
Iterator type, const.
NodeIterator< false > iterator
Iterator type.
iterator erase(const const_iterator &entry)
Erase an element with the given iterator.
DataT & front()
Get the front of the fast list non-const.
FastList(size_t size)
Construct FastList of a given size.
const_iterator begin() const
Obtain a beginning const_iterator.
const DataT & front() const
Get the front of the fast list, const.
void pop_back()
Pop the last element off of the list.