Simulant  21.12-574
A portable game engine for Windows, OSX, Linux, Dreamcast, and PSP
contiguous_map.h
1 #pragma once
2 
3 /*
4  * ContiguousMap<T> is a container with the following properties:
5  *
6  * - Sorted on insertion
7  * - Clears without dealloc
8  * - Nodes stored in contiguous memory
9  *
10  * Internally it's a simple binary search tree that's stored
11  * in a vector. Ideally it would be a red-black tree to
12  * balance it. FUTURE IMPROVEMENT IF ANYONE WANTS TO PICK IT UP!
13  */
14 
15 
16 #include <vector>
17 #include <utility>
18 #include <cstdint>
19 #include <cassert>
20 #include <stdexcept>
21 #include <string>
22 
23 namespace smlt {
24 
25 namespace _contiguous_map {
26 
27 template<typename K, typename V>
28 struct NodeMeta {
29  NodeMeta(const K& key, const V& value):
30  pair(std::make_pair(key, value)) {}
31 
32  std::pair<const K, V> pair;
33 
34  bool is_black = false;
35 
36  /* If this is not -1, then this is the start
37  * of a linked list of equal values */
38  int32_t equal_index_ = -1;
39  int32_t last_equal_index_ = -1;
40 
41  int32_t parent_index_ = -1;
42  int32_t left_index_ = -1;
43  int32_t right_index_ = -1;
44 } __attribute__((aligned(8)));
45 
46 }
47 
48 template<typename T>
50 public:
51  enum Result : int8_t {
52  RESULT_LESS = -1,
53  RESULT_EQUAL = 0,
54  RESULT_GREATER = 1
55  };
56 
57  Result operator()(const T& a, const T& b) const {
58  return (a < b) ? RESULT_LESS : (b < a) ? RESULT_GREATER : RESULT_EQUAL;
59  }
60 };
61 
62 
63 template<typename K, typename V, typename Compare=ThreeWayCompare<K>>
65 public:
66  typedef K key_type;
67  typedef V value_type;
69 
70  class iterator_base {
71  protected:
72  /* This is the equivalent of begin */
74  map_(map) {
75 
76  current_node_ = map->leftmost_index_;
77  }
78 
79  /* Passing -1 means end(), anything else points to the specified node */
80  iterator_base(ContiguousMultiMap* map, int32_t index):
81  map_(map) {
82 
83  current_node_ = index;
84 
85  if(index > -1) {
86  auto current = _node(current_node_);
87  if(current->left_index_ > -1) {
88  previous_node_index_ = current->left_index_;
89  }
90  } else {
91  previous_node_index_ = -1;
92  }
93  }
94 
95  iterator_base(const iterator_base& other) = default;
96 
97  inline typename ContiguousMultiMap::node_type* _node(int32_t index) const {
98  assert(index > -1);
99  return &map_->nodes_[index];
100  }
101 
102  void increment() {
103  if(current_node_ < 0) return; // Do nothing
104 
105  auto current = _node(current_node_);
106 
107  /* We have a pointer to an equal value node, but we haven't started iterating */
108  if(current->equal_index_ > -1 && list_head_index_ == -1) {
109  list_head_index_ = current_node_;
110  current_node_ = current->equal_index_;
111  return;
112  } else if(list_head_index_ > -1) {
113  /* We're iterating equal value nodes */
114  current_node_ = current->equal_index_;
115  if(current_node_ == -1) {
116  /* We've finished iterating the list, now back to recursing */
117  current_node_ = list_head_index_; // Restore back to the head node
118  list_head_index_ = -1; // We're no longer iterating the list
119  current = _node(current_node_); // Set current so we can continue normally
120  } else {
121  /* We've updated the current node */
122  return;
123  }
124  }
125 
126  auto previous = current_node_;
127 
128  if(current->right_index_ == -1) {
129  /* We've reached the end, now we go up until we come from
130  * the left branch */
131 
132  current_node_ = current->parent_index_;
133  while(current_node_ > -1) {
134  current = _node(current_node_);
135  if(previous == current->left_index_) {
136  /* We came from the left, so break */
137  break;
138  } else {
139  previous = current_node_;
140  current_node_ = current->parent_index_;
141  }
142  }
143 
144  } else {
145  current_node_ = current->right_index_;
146  if(current_node_ > -1) {
147  /* Go down the left branch to start iterating this one */
148  current = _node(current_node_);
149  while(current->left_index_ > -1) {
150  current_node_ = current->left_index_;
151  current = _node(current_node_);
152  }
153  }
154  }
155 
156  previous_node_index_ = previous;
157  }
158 
159  bool is_equal(const iterator_base& other) const {
160  return (
161  map_ == other.map_ &&
162  current_node_ == other.current_node_
163  );
164  }
165 
166  int32_t current_node_ = -1;
167 
168  private:
169  ContiguousMultiMap* map_ = nullptr;
170 
171  int32_t list_head_index_ = -1;
172  int32_t previous_node_index_ = -1;
173  };
174 
175  class iterator : private iterator_base {
176  private:
178  iterator_base(map) {}
179 
180  iterator(ContiguousMultiMap* map, int32_t index):
181  iterator_base(map, index) {}
182 
183  public:
184  friend class ContiguousMultiMap;
185 
186  using iterator_category = std::forward_iterator_tag;
187  using value_type = std::pair<const K, V>;
188  using difference_type = uint32_t;
189  using pointer = std::pair<const K, V>*;
190  using reference = std::pair<const K, V>&;
191 
192  iterator(const iterator& other) = default;
193  iterator& operator=(const iterator&) = default;
194  iterator& operator=(iterator&&) = default;
195 
196  iterator& operator++() {
197  this->increment();
198  return *this;
199  }
200 
201  bool operator==(const iterator& other) const {
202  return this->is_equal(other);
203  }
204 
205  bool operator!=(const iterator& other) const {
206  return !this->is_equal(other);
207  }
208 
209  reference operator*() const {
210  return this->_node(this->current_node_)->pair;
211  }
212 
213  pointer operator->() const {
214  return &this->_node(this->current_node_)->pair;
215  }
216  };
217 
218  class const_iterator : private iterator_base {
219  private:
220  friend class ContiguousMultiMap;
221 
223  iterator_base(map) {}
224 
225  const_iterator(ContiguousMultiMap* map, int32_t index):
226  iterator_base(map, index) {}
227 
228  public:
229 
230  using iterator_category = std::forward_iterator_tag;
231  using value_type = std::pair<const K, V>;
232  using difference_type = uint32_t;
233  using pointer = const std::pair<const K, V>*;
234  using reference = const std::pair<const K, V>&;
235 
236  const_iterator(const const_iterator& other) = default;
237  const_iterator& operator=(const const_iterator&) = default;
238  const_iterator& operator=(const_iterator&&) = default;
239 
240  const_iterator& operator++() {
241  this->increment();
242  return *this;
243  }
244 
245  bool operator==(const const_iterator& other) const {
246  return this->is_equal(other);
247  }
248 
249  bool operator!=(const const_iterator& other) const {
250  return !this->is_equal(other);
251  }
252 
253  reference operator*() const {
254  return this->_node(this->current_node_)->pair;
255  }
256 
257  pointer operator->() const {
258  return &this->_node(this->current_node_)->pair;
259  }
260  };
261 
262  ContiguousMultiMap() = default;
263 
264  ContiguousMultiMap(std::size_t reserve_count):
265  root_index_(-1),
266  leftmost_index_(-1) {
267  nodes_.reserve(reserve_count);
268  }
269 
270  ContiguousMultiMap(const ContiguousMultiMap&) = delete; // Avoid copies for now, slow!
271  ContiguousMultiMap& operator=(const ContiguousMultiMap&) = delete;
272 
273  bool insert(const K& key, V&& element) {
274  K k = key; // Copy K to leverage the move of _insert
275  return _insert(std::move(k), std::move(element));
276  }
277 
278  bool insert(const K& key, const V& element) {
279  K k = key;
280  V v = element;
281  return _insert(std::move(k), std::move(v));
282  }
283 
284  void clear() {
285  nodes_.clear();
286  root_index_ = -1;
287  leftmost_index_ = -1;
288  }
289 
290  void shrink_to_fit() {
291  nodes_.shrink_to_fit();
292  }
293 
294  std::size_t size() const {
295  return nodes_.size();
296  }
297 
298  void reserve(std::size_t size) {
299  nodes_.reserve(size);
300  }
301 
302  bool empty() const {
303  return nodes_.empty();
304  }
305 
306  iterator find(const K& key) {
307  int32_t index = _find(key);
308  if(index == -1) {
309  return end();
310  }
311  return iterator(this, index);
312  }
313 
314  const_iterator find(const K &key) const {
315  int32_t index = _find(key);
316  if(index == -1) {
317  return end();
318  }
319 
320  return const_iterator(
321  const_cast<ContiguousMultiMap*>(this),
322  index
323  );
324  }
325 
326  const_iterator upper_bound(const K& key) const {
327  auto it = find(key);
328 
329  while(it != end() && it->first == key) {
330  ++it;
331  }
332 
333  return it;
334  }
335 
336  iterator begin() {
337  return iterator(this);
338  }
339 
340  iterator end() {
341  return end_;
342  }
343 
344  const_iterator begin() const {
345  return const_iterator(
346  const_cast<ContiguousMultiMap*>(this)
347  );
348  }
349 
350  const_iterator end() const {
351  return cend_;
352  }
353 
354  /* This is a helper function for debugging the contiguousmap.
355  * Given a path of L or R characters, returns the key at that path
356  * or throws an error if the path is invalid */
357 
358  const K& path_key(const std::string& path) const {
359  const node_type* node = &nodes_[root_index_];
360 
361  for(auto c: path) {
362  if(c == 'L') {
363  if(node->left_index_ == -1) {
364  throw std::logic_error("Hit end of branch");
365  }
366  node = &nodes_[node->left_index_];
367  } else if(c == 'R') {
368  if(node->right_index_ == -1) {
369  throw std::logic_error("Hit end of branch");
370  }
371  node = &nodes_[node->right_index_];
372  } else {
373  throw std::logic_error("Invalid path");
374  }
375  }
376 
377  /* Return the key */
378  return node->pair.first;
379  }
380 private:
381  friend class iterator_base;
382 
383  iterator end_ = iterator(this, -1);
384  const_iterator cend_ = const_iterator(
385  const_cast<ContiguousMultiMap*>(this), -1
386  );
387 
388  int32_t _find(const K& key) const {
389  if(root_index_ == -1) return -1;
390 
391  int32_t current_index = root_index_;
392  const node_type* current = &nodes_[current_index];
393 
394  while(current) {
395  auto order = compare_(key, current->pair.first);
396 
397  if(order == ThreeWayCompare<K>::RESULT_EQUAL) {
398  return current_index;
399  } else if(order == ThreeWayCompare<K>::RESULT_LESS) {
400  if(current->left_index_ != -1) {
401  current_index = current->left_index_;
402  current = &nodes_[current_index];
403  } else {
404  return -1;
405  }
406  } else {
407  if(current->right_index_ != -1) {
408  current_index = current->right_index_;
409  current = &nodes_[current_index];
410  } else {
411  return -1;
412  }
413  }
414  }
415 
416  return current_index;
417  }
418 
419  void _rotate_left(node_type* node, int32_t node_index, node_type* parent=nullptr) {
420  int32_t nnew_index = node->right_index_;
421  assert(nnew_index != -1);
422 
423  /* Fetch the parent if it wasn't passed */
424  int32_t parent_index = node->parent_index_;
425  parent = (parent) ? parent : (parent_index == -1) ? nullptr : &nodes_[parent_index];
426 
427  node_type* nnew = &nodes_[nnew_index];
428 
429  node->right_index_ = nnew->left_index_;
430  nnew->left_index_ = node_index;
431  node->parent_index_ = nnew_index;
432 
433  if(node->right_index_ != -1) {
434  nodes_[node->right_index_].parent_index_ = node_index;
435  }
436 
437  if(parent) {
438  if(node_index == parent->left_index_) {
439  parent->left_index_ = nnew_index;
440  } else if(node_index == parent->right_index_) {
441  parent->right_index_ = nnew_index;
442  }
443  }
444 
445  nnew->parent_index_ = parent_index;
446  }
447 
448  void _rotate_right(node_type* node, int32_t node_index, node_type* parent=nullptr) {
449  int32_t nnew_index = node->left_index_;
450  assert(nnew_index != -1);
451 
452  /* Fetch the parent if it wasn't passed */
453  int32_t parent_index = node->parent_index_;
454  parent = (parent) ? parent : (parent_index == -1) ? nullptr : &nodes_[parent_index];
455 
456  node_type* nnew = &nodes_[nnew_index];
457 
458  node->left_index_ = nnew->right_index_;
459  nnew->right_index_ = node_index;
460  node->parent_index_ = nnew_index;
461 
462  if(node->left_index_ != -1) {
463  nodes_[node->left_index_].parent_index_ = node_index;
464  }
465 
466  if(parent) {
467  if(node_index == parent->left_index_) {
468  parent->left_index_ = nnew_index;
469  } else if(node_index == parent->right_index_) {
470  parent->right_index_ = nnew_index;
471  }
472  }
473 
474  nnew->parent_index_ = parent_index;
475  }
476 
477  void _insert_repair_tree(node_type* node, int32_t node_index) {
478 
479  /* Case 1: Parent is the root, so just mark the parent as black */
480  if(node->parent_index_ == -1) {
481  node->is_black = true;
482  return;
483  }
484 
485  assert(node->parent_index_ != -1);
486  node_type* parent = &nodes_[node->parent_index_];
487 
488  /* Case 2: Parent is black, we're all good */
489  if(parent->is_black) {
490  return;
491  }
492 
493  node_type* grandparent = (parent->parent_index_ == -1) ?
494  nullptr : &nodes_[parent->parent_index_];
495 
496  int32_t parent_sibling_index = (!grandparent) ? -1 :
497  (grandparent->left_index_ == node->parent_index_) ?
498  grandparent->right_index_ : grandparent->left_index_;
499 
500  node_type* parent_sibling = (parent_sibling_index == -1) ? nullptr : &nodes_[parent_sibling_index];
501 
502  /* Case 3: parent has a sibling, and it's red */
503  if(parent_sibling && !parent_sibling->is_black) {
504  /* Recolor and recurse upwards */
505  parent->is_black = true;
506  parent_sibling->is_black = true;
507  grandparent->is_black = false;
508 
509  /* Recurse up! */
510  _insert_repair_tree(grandparent, parent->parent_index_);
511  } else {
512  /* Case 4: Parent is red, but the parent sibling doesn't exist, or is black */
513  if(node_index == parent->right_index_ && node->parent_index_ == grandparent->left_index_) {
514  _rotate_left(parent, node->parent_index_, grandparent);
515 
516  node_index = node->left_index_;
517  node = &nodes_[node->left_index_];
518 
519  assert(node->parent_index_ != -1);
520  parent = &nodes_[node->parent_index_];
521 
522  assert(parent->parent_index_ != -1);
523  grandparent = &nodes_[parent->parent_index_];
524 
525  } else if(node_index == parent->left_index_ && node->parent_index_ == grandparent->right_index_) {
526  _rotate_right(parent, node->parent_index_, grandparent);
527 
528  node_index = node->right_index_;
529  node = &nodes_[node->right_index_];
530 
531  assert(node->parent_index_ != -1);
532  parent = &nodes_[node->parent_index_];
533 
534  assert(parent->parent_index_ != -1);
535  grandparent = &nodes_[parent->parent_index_];
536  }
537 
538  if(node_index == parent->left_index_) {
539  _rotate_right(grandparent, parent->parent_index_);
540  } else {
541  _rotate_left(grandparent, parent->parent_index_);
542  }
543 
544  assert(parent);
545  assert(grandparent);
546 
547  parent->is_black = true;
548  grandparent->is_black = false;
549  }
550  }
551 
552  bool _insert(K&& key, V&& value) {
553  if(root_index_ == -1) {
554  /* Root case, just set to black */
555  leftmost_index_ = root_index_ = new_node(-1, std::move(key), std::move(value));
556  nodes_.back().is_black = true;
557  return true;
558  } else {
559  /* Returns new index, and whether or not the
560  * parent is a black node */
561  int32_t new_index;
562  bool is_black = _insert_recurse(
563  root_index_, std::move(key), std::move(value), &new_index
564  );
565 
566  node_type* inserted = (new_index > -1) ? &nodes_[new_index] : nullptr;
567 
568  auto ret = new_index > 1;
569  if(new_index > -1 && !is_black) {
570  /* Parent was red! rebalance! */
571  _insert_repair_tree(inserted, new_index);
572 
573  /* Reset root index */
574  if(inserted->parent_index_ == -1) {
575  root_index_ = new_index;
576  } else {
577  node_type* parent = inserted;
578  while(parent->parent_index_ != -1) {
579  node_type* next_parent = &nodes_[parent->parent_index_];
580 
581  if(next_parent->parent_index_ == -1) {
582  root_index_ = parent->parent_index_;
583  break;
584  }
585 
586  parent = next_parent;
587  }
588  }
589 
590  ret = true;
591  }
592 
593  assert(leftmost_index_ > -1);
594 
595  /* If we inserted, and the parent is the leftmost index
596  * then we check to see if this is now the leftmost index */
597  if(inserted && inserted->parent_index_ == leftmost_index_) {
598  node_type* p = &nodes_[inserted->parent_index_];
599  if(p && p->left_index_ == new_index) {
600  leftmost_index_ = new_index;
601  }
602  }
603 
604  return ret;
605  }
606  }
607 
608  inline int32_t new_node(int32_t parent_index, K&& key, V&& value) {
609  auto ret = (int32_t) nodes_.size();
610 
611  node_type n(key, value);
612  n.parent_index_ = parent_index;
613  nodes_.push_back(std::move(n));
614  return ret;
615  }
616 
617  /* Returns inserted, parent_is_black */
618  bool _insert_recurse(int32_t root_index, K&& key, V&& value, int32_t* new_index) {
619  assert(root_index > -1);
620 
621  node_type* root = &nodes_[root_index];
622 
623  auto order = compare_(key, root->pair.first);
624  if(order == ThreeWayCompare<K>::RESULT_EQUAL) {
625 
626  /* We're inserting a duplicate, so we use the equal_index_ */
627 
628  auto new_idx = new_node(-1, std::move(key), std::move(value));
629  root = &nodes_[root_index]; // new_node could realloc
630 
631  /* We could insert immediately after the root, but, this
632  * makes insertion unstable; iteration won't iterate in the
633  * inserted order so instead we go through to the last
634  * equal index before inserting */
635 
636  auto left = root;
637  if(root->last_equal_index_ > -1) {
638  left = &nodes_[left->last_equal_index_];
639  }
640 
641  left->equal_index_ = new_idx;
642  root->last_equal_index_ = new_idx;
643 
644  /* We return the new node index, and we say the parent
645  * is black (because this node was added to a linked list
646  * not the tree itself and we don't want any recursive fixing
647  * of the red-black tree) */
648  if(new_index) {
649  *new_index = new_idx;
650  }
651 
652  return true;
653  } else if(order == ThreeWayCompare<K>::RESULT_LESS) {
654  if(root->left_index_ == -1) {
655  auto new_idx = new_node(root_index, std::move(key), std::move(value));
656  /* The insert could have invalidated the root pointer */
657  root = &nodes_[root_index];
658  root->left_index_ = new_idx;
659 
660  if(new_index) {
661  *new_index = new_idx;
662  }
663 
664  return root->is_black;
665  } else {
666  return _insert_recurse(
667  root->left_index_, std::move(key), std::move(value), new_index
668  );
669  }
670  } else {
671  if(root->right_index_ == -1) {
672  auto new_idx = new_node(root_index, std::move(key), std::move(value));
673  /* The insert could have invalidated the root pointer */
674  root = &nodes_[root_index];
675  root->right_index_ = new_idx;
676 
677  if(new_index) {
678  *new_index = new_idx;
679  }
680 
681  return root->is_black;
682  } else {
683  return _insert_recurse(
684  root->right_index_, std::move(key), std::move(value), new_index
685  );
686  }
687  }
688  }
689 
690  std::vector<node_type> nodes_;
691 
692  int32_t root_index_ = -1;
693  int32_t leftmost_index_ = -1;
694 
695  Compare compare_;
696 };
697 
698 
699 template<typename K, typename V, typename Compare=ThreeWayCompare<K>>
701 public:
704 
705  ContiguousMap() = default;
706  ContiguousMap(std::size_t reserve):
707  map_(reserve) {}
708 
709  bool empty() const {
710  return map_.empty();
711  }
712 
713  std::size_t size() const {
714  return map_.size();
715  }
716 
717  bool insert(const K& key, V&& element) {
718  if(map_.find(key) == map_.end()) {
719  map_.insert(key, std::move(element));
720  return true;
721  } else {
722  return false;
723  }
724  }
725 
726  V& at(const K& key) {
727  auto it = map_.find(key);
728 
729  if(it == map_.end()) {
730  throw std::out_of_range("Key not found");
731  }
732 
733  return (*it).second;
734  }
735 
736  const V& at(const K& key) const {
737  auto it = map_.find(key);
738 
739  if(it == map_.end()) {
740  throw std::out_of_range("Key not found");
741  }
742 
743  return (*it).second;
744  }
745 
746  bool insert(const K& key, const V& element) {
747  if(map_.find(key) == map_.end()) {
748  map_.insert(key, element);
749  return true;
750  } else {
751  return false;
752  }
753  }
754 
755  void shrink_to_fit() {
756  map_.shrink_to_fit();
757  }
758 
759  void clear() {
760  map_.clear();
761  }
762 
763  iterator find(const K& key) {
764  return map_.find(key);
765  }
766 
767  const_iterator find(const K &key) const {
768  return map_.find(key);
769  }
770 
771  iterator begin() {
772  return map_.begin();
773  }
774 
775  iterator end() {
776  return map_.end();
777  }
778 
779  const_iterator begin() const {
780  return map_.begin();
781  }
782 
783  const_iterator end() const {
784  return map_.end();
785  }
786 private:
788 };
789 
790 
791 }
smlt::ContiguousMultiMap::const_iterator
Definition: contiguous_map.h:218
smlt::ThreeWayCompare
Definition: contiguous_map.h:49
smlt
Definition: animation.cpp:25
smlt::ContiguousMap
Definition: contiguous_map.h:700
smlt::_contiguous_map::NodeMeta
Definition: contiguous_map.h:28
smlt::ContiguousMultiMap::iterator_base
Definition: contiguous_map.h:70
smlt::ContiguousMultiMap::iterator
Definition: contiguous_map.h:175
smlt::ContiguousMultiMap
Definition: contiguous_map.h:64