General Purpose library for Freestanding C++ and POSIX systems
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

162 lines
3.1 KiB

  1. #pragma once
  2. #include <stddef.h>
  3. #include "gp/algorithm/tmp_manip.hpp"
  4. #include "gp/algorithm/modifiers.hpp"
  5. #include "gp_config.hpp"
  6. namespace gp {
  7. template<typename T, typename allocator, bool copy_allocator = false, bool may_contain_self = false>
  8. class ring_list{
  9. public:
  10. class explorer;
  11. class node {
  12. T* value;
  13. node* prev;
  14. node* next;
  15. public:
  16. node(T* p)
  17. : value{p}
  18. , prev{this}
  19. , next{this}
  20. {}
  21. friend class gp::ring_list<T, allocator, copy_allocator, may_contain_self>::explorer;
  22. friend class gp::ring_list<T, allocator, copy_allocator, may_contain_self>;
  23. };
  24. class explorer {
  25. node* pos;
  26. public:
  27. explorer(node* v)
  28. : pos{v}
  29. {}
  30. bool operator==(const explorer& oth) const {
  31. return pos == oth.pos;
  32. }
  33. bool operator!=(const explorer& oth) const {
  34. return pos != oth.pos;
  35. }
  36. explorer operator++() {
  37. pos = pos->next;
  38. return pos;
  39. }
  40. explorer operator++(int) {
  41. auto tmp = pos;
  42. pos = pos->next;
  43. return tmp;
  44. }
  45. explorer operator--() {
  46. pos = pos->prev;
  47. return pos;
  48. }
  49. explorer operator--(int) {
  50. auto tmp = pos;
  51. pos = pos->prev;
  52. return tmp;
  53. }
  54. T& operator*() {
  55. return *(pos->value);
  56. }
  57. friend class ring_list;
  58. };
  59. private:
  60. node* any_node;
  61. size_t sz;
  62. typename gp::either<
  63. copy_allocator,
  64. allocator,
  65. gp::reference_wrapper<allocator>
  66. >::type alloc;
  67. void stitch_around(node* n) {
  68. n->prev->next = n->next;
  69. n->next->prev = n->prev;
  70. }
  71. public:
  72. ring_list()
  73. : any_node{nullptr}
  74. , sz{0}
  75. , alloc{}
  76. {}
  77. ring_list(node* initial, allocator& _alloc)
  78. : any_node{initial}
  79. , sz{1}
  80. , alloc{_alloc}
  81. {}
  82. ring_list(allocator& _alloc)
  83. : any_node{nullptr}
  84. , sz{0}
  85. , alloc{_alloc}
  86. {}
  87. template<typename V = T, typename ...Args>
  88. bool insert(Args&&... elem) {
  89. allocator& used_allocator = alloc;
  90. void* mem;
  91. [[unlikely]] if(
  92. nullptr == (mem = used_allocator.allocate(sizeof(V)))
  93. ) return false;
  94. T* p = new(mem) V(elem...);
  95. node* to_insert = nullptr;
  96. [[unlikely]] if(
  97. nullptr == (to_insert = reinterpret_cast<node*>(used_allocator.allocate(sizeof(node))))
  98. ) return false;
  99. to_insert = new(to_insert) node(p);
  100. [[unlikely]] if (any_node == nullptr)
  101. {
  102. any_node = to_insert;
  103. } else {
  104. to_insert->prev = any_node->prev;
  105. to_insert->next = any_node;
  106. to_insert->prev->next = to_insert;
  107. any_node->prev = to_insert;
  108. }
  109. return true;
  110. }
  111. explorer explore() {
  112. return any_node;
  113. }
  114. void remove(explorer value) {
  115. auto v = value.pos;
  116. if(v == any_node) {
  117. if(v->next == v) {
  118. any_node = nullptr;
  119. } else {
  120. any_node = any_node->next;
  121. stitch_around(v);
  122. }
  123. } else {
  124. stitch_around(v);
  125. }
  126. allocator& used_allocator = alloc;
  127. v->value->~T();
  128. gp_config::assertion(used_allocator.deallocate(v->value), "Bad free of value");
  129. v->~node();
  130. gp_config::assertion(used_allocator.deallocate(v), "Bad free of node");
  131. }
  132. ~ring_list()
  133. {
  134. // TODO: Find a less hackish way to resove this issue
  135. if constexpr (!may_contain_self) {
  136. while(any_node) {
  137. remove(explore());
  138. }
  139. }
  140. }
  141. };
  142. }