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.

157 lines
3.0 KiB

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