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.

315 lines
6.4 KiB

4 years ago
  1. #pragma once
  2. #include <gp/containers/buffer.hpp>
  3. #include <gp/utils/allocators/allocator.hpp>
  4. #include <initializer_list>
  5. namespace gp{
  6. /**
  7. * @brief A vector type most similar to that of the standard library
  8. * Always uses polymorphic allocation, no small value optimization
  9. *
  10. * @tparam T
  11. */
  12. template<typename T>
  13. class vector final {
  14. T* ary = nullptr;
  15. size_t sz = 0;
  16. size_t cap = 0;
  17. gp::reference_wrapper<allocator> alloc;
  18. public:
  19. using associated_iterator = pointer_iterator<T, 1>;
  20. using associated_const_iterator = const_pointer_iterator<T, 1>;
  21. using associated_riterator = pointer_iterator<T, -1>;
  22. using associated_const_riterator = const_pointer_iterator<T, -1>;
  23. vector(allocator& v)
  24. : ary()
  25. , alloc(v)
  26. {}
  27. vector(vector& oth)
  28. : alloc(oth.alloc)
  29. {
  30. sz = 0;
  31. cap = 0;
  32. ary = nullptr;
  33. gp_config::assertion(reserve(oth.size()), "could not reserve space on building");
  34. sz = oth.size();
  35. cap = oth.size();
  36. auto it_l = begin();
  37. auto it_o = oth.cbegin();
  38. for(size_t i = 0; i < sz; ++i)
  39. {
  40. new(&*(it_l++)) T(*(it_o++));
  41. }
  42. }
  43. vector(vector&& oth)
  44. : ary(oth.ary)
  45. , sz(oth.sz)
  46. , cap(oth.cap)
  47. , alloc(oth.alloc)
  48. {
  49. oth.ary = nullptr;
  50. }
  51. vector& operator=(vector& oth)
  52. {
  53. gp_config::assertion(reserve(oth.size()), "could not reserve space on assign");
  54. for(size_t i = 0; i < gp::min(sz, oth.sz); ++i)
  55. {
  56. new(ary+i) T(oth[i]);
  57. }
  58. if(sz < oth.sz) {
  59. for(size_t i = sz; i < oth.sz; ++i) {
  60. new(ary+i) T(oth[i]);
  61. }
  62. } else if(sz > oth.sz) {
  63. for(size_t i = oth.sz; i < sz; ++i) {
  64. ary[i]->~T();
  65. }
  66. }
  67. sz = oth.sz;
  68. return *this;
  69. }
  70. vector& operator=(vector&& oth)
  71. {
  72. gp::swap(ary, oth.ary);
  73. gp::swap(alloc, oth.alloc);
  74. gp::swap(sz, oth.sz);
  75. gp::swap(cap, oth.cap);
  76. return *this;
  77. }
  78. constexpr T& operator[] (size_t off)
  79. {
  80. if constexpr (gp_config::has_buffer_bounds)
  81. {
  82. gp_config::assertion(
  83. off < sz,
  84. "Array bounds infringed"
  85. );
  86. }
  87. return ary[off];
  88. }
  89. ~vector()
  90. {
  91. if(ary)
  92. {
  93. for(auto& elem : *this) {
  94. elem.~T();
  95. }
  96. gp_config::assertion(alloc.get().deallocate(ary), "could not deallocate");
  97. }
  98. }
  99. /**
  100. * @brief Ensures the vector can hold at least 1 more element
  101. *
  102. * @return true if it is at least now possible to fit one more element
  103. * @return false if fiting one more element is not possible even now
  104. */
  105. bool grow() {
  106. if(sz == cap) return reserve(1 + sz + (sz >> 1));
  107. return true;
  108. }
  109. /**
  110. * @brief Reserves space so that the capacity is at least equal to the provided value.
  111. *
  112. * This will never shrink the datastructure
  113. *
  114. * @param new_cap the new capacity
  115. * @return true on success
  116. * @return false on failure
  117. */
  118. bool reserve(size_t new_cap) {
  119. if(new_cap <= cap) return true;
  120. size_t new_data_size = new_cap*sizeof(T);
  121. if(alloc.get().try_reallocate(ary, new_data_size)) return true;
  122. if(T* new_ary = (T*)alloc.get().allocate(new_data_size); new_ary) {
  123. auto new_it = new_ary;
  124. for(auto& elem : *this) {
  125. new(new_it++) T(gp::move(elem));
  126. }
  127. if(ary != nullptr) gp_config::assertion(alloc.get().deallocate(ary), "failed to deallocate old range");
  128. ary = new_ary;
  129. cap = new_cap;
  130. return true;
  131. }
  132. return false;
  133. }
  134. constexpr const T& operator[] (size_t off) const
  135. {
  136. if constexpr (gp_config::has_buffer_bounds)
  137. {
  138. gp_config::assertion(
  139. off < sz,
  140. "Array bounds infringed"
  141. );
  142. }
  143. return ary[off];
  144. }
  145. constexpr size_t size() const
  146. {
  147. return sz;
  148. }
  149. constexpr size_t capacity() const
  150. {
  151. return cap;
  152. }
  153. /**
  154. * @brief Adds the provided value to the vector
  155. *
  156. * @param value
  157. * @return true on success
  158. * @return false on failure
  159. */
  160. constexpr bool push_back(T& value) {
  161. if(grow()) {
  162. new(ary+sz) T(value);
  163. sz++;
  164. return true;
  165. }
  166. return false;
  167. }
  168. /**
  169. * @brief Moves the provided value to the vector
  170. *
  171. * @param value
  172. * @return true on success
  173. * @return false on failure
  174. */
  175. constexpr bool push_back(T&& value) {
  176. if(grow()) {
  177. new(ary+sz) T(gp::move(value));
  178. sz++;
  179. return true;
  180. }
  181. return false;
  182. }
  183. /**
  184. * @brief Constructs a new element at the end of the vector
  185. *
  186. * @param value the parameters to be sent to the constructor of T
  187. * @return true on success
  188. * @return false on failure
  189. */
  190. template<typename ...U>
  191. constexpr bool emplace_back(U&&... value) {
  192. if(grow()) {
  193. new(ary+sz) T(gp::forward<U>(value)...);
  194. sz++;
  195. return true;
  196. }
  197. return false;
  198. }
  199. /**
  200. * @brief moves the last element of the vector out if it exists, returning an optional.
  201. *
  202. * @return constexpr gp::optional<T> contains a value if it existed, else it is empty
  203. */
  204. constexpr gp::optional<T> pop_back() {
  205. if(sz == 0) return gp::nullopt;
  206. sz--;
  207. gp::optional<T> ret_val = gp::move(ary[sz]);
  208. ary[sz]->~T();
  209. return gp::move(ret_val);
  210. }
  211. void remove(pointer_iterator<T, 1> it) {
  212. for(auto step = it + 1; step<end(); step++) {
  213. (*it++) = gp::move(*step);
  214. }
  215. *rbegin().~T();
  216. sz -= 1;
  217. }
  218. constexpr pointer_iterator<T, 1> begin()
  219. {
  220. return associated_iterator(&ary[0]);
  221. }
  222. constexpr pointer_iterator<T, 1> end()
  223. {
  224. return associated_iterator(&ary[sz]);
  225. }
  226. constexpr const_pointer_iterator<T, 1> cbegin() const
  227. {
  228. return associated_const_iterator(&ary[0]);
  229. }
  230. constexpr const_pointer_iterator<T, 1> cend() const
  231. {
  232. return associated_const_iterator(&ary[sz]);
  233. }
  234. constexpr pointer_iterator<T, -1> rbegin()
  235. {
  236. return associated_riterator(&ary[sz-1]);
  237. }
  238. constexpr pointer_iterator<T, -1> rend()
  239. {
  240. return associated_riterator(ary-1);
  241. }
  242. constexpr const_pointer_iterator<T, -1> crbegin() const
  243. {
  244. return associated_const_riterator(&ary[sz-1]);
  245. }
  246. constexpr const_pointer_iterator<T, -1> crend() const
  247. {
  248. return associated_const_riterator(ary-1);
  249. }
  250. constexpr bool operator==(const vector& oth) const
  251. {
  252. for(size_t idx = 0; idx<sz; idx++)
  253. {
  254. if(ary[idx] != oth.ary[idx])
  255. {
  256. return false;
  257. }
  258. }
  259. return true;
  260. }
  261. constexpr bool operator!=(const vector& oth) const
  262. {
  263. return !(*this == oth);
  264. }
  265. /**
  266. * @brief Provides a span access to the vector
  267. *
  268. * @return A buffer of the vector.
  269. * It is invalidated by any operation that may change the size of the vector.
  270. */
  271. gp::buffer<T> as_buffer()
  272. {
  273. return gp::buffer<T>{(T*)ary, (T*)ary+sz};
  274. }
  275. };
  276. }