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.

372 lines
7.9 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; //< The data
  15. size_t sz = 0; //< the used size of the array
  16. size_t cap = 0; //< the available capacity of the array
  17. gp::reference_wrapper<allocator> alloc; //< the allocator
  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. /**
  24. * @brief Construct a new vector object from an allocator
  25. *
  26. * @param v the allocator
  27. */
  28. vector(allocator& v)
  29. : ary()
  30. , alloc(v)
  31. {}
  32. /**
  33. * @brief Construct a new vector object from another vector, copying it
  34. *
  35. * @param oth the other vector
  36. */
  37. vector(vector& oth)
  38. : alloc(oth.alloc)
  39. {
  40. sz = 0;
  41. cap = 0;
  42. ary = nullptr;
  43. gp_config::assertion(reserve(oth.size()), "could not reserve space on building");
  44. sz = oth.size();
  45. cap = oth.size();
  46. auto it_l = begin();
  47. auto it_o = oth.cbegin();
  48. for(size_t i = 0; i < sz; ++i)
  49. {
  50. new(&*(it_l++)) T(*(it_o++));
  51. }
  52. }
  53. /**
  54. * @brief Construct a new vector object by moving objects out of another vector
  55. *
  56. * @param oth
  57. */
  58. vector(vector&& oth)
  59. : ary(oth.ary)
  60. , sz(oth.sz)
  61. , cap(oth.cap)
  62. , alloc(oth.alloc)
  63. {
  64. oth.sz = 0;
  65. oth.cap = 0;
  66. oth.ary = nullptr;
  67. }
  68. /**
  69. * @brief Copy assignment
  70. *
  71. * @param oth
  72. * @return vector& the reference to this changed vector
  73. */
  74. vector& operator=(vector& oth)
  75. {
  76. gp_config::assertion(reserve(oth.size()), "could not reserve space on assign");
  77. for(size_t i = 0; i < gp::min(sz, oth.sz); ++i)
  78. {
  79. if constexpr (!std::is_trivially_destructible_v<T>) ary[i]->~T();
  80. new(ary+i) T(oth[i]);
  81. }
  82. if(sz < oth.sz) {
  83. for(size_t i = sz; i < oth.sz; ++i) {
  84. if constexpr (!std::is_trivially_destructible_v<T>) ary[i]->~T();
  85. new(ary+i) T(oth[i]);
  86. }
  87. } else if(sz > oth.sz) {
  88. if constexpr (!std::is_trivially_destructible_v<T>)
  89. for(size_t i = oth.sz; i < sz; ++i) {
  90. ary[i]->~T();
  91. }
  92. }
  93. sz = oth.sz;
  94. return *this;
  95. }
  96. /**
  97. * @brief Move assignment
  98. *
  99. * Will not change the allocator of the local vector, is EXPENSIVE if the both vectors have different allocators
  100. *
  101. * @param oth
  102. * @return vector&
  103. */
  104. vector& operator=(vector&& oth)
  105. {
  106. if(&alloc.get() == &oth.alloc.get())
  107. {
  108. gp::swap(ary, oth.ary);
  109. gp::swap(sz, oth.sz);
  110. gp::swap(cap, oth.cap);
  111. } else {
  112. for(auto& elem : *this) {
  113. elem->~T();
  114. }
  115. sz = 0;
  116. if(capacity()<oth.size()) {
  117. reserve(oth.size());
  118. }
  119. size_t idx = 0;
  120. for(auto& elem : oth) {
  121. new(ary+idx) T(gp::move(elem));
  122. ++idx;
  123. elem.~T();
  124. }
  125. gp_config::assertion(alloc.get().deallocate(oth.ary), "could not deallocate");
  126. sz = idx;
  127. oth.sz = 0;
  128. oth.capacity = 0;
  129. oth.ary = nullptr;
  130. }
  131. return *this;
  132. }
  133. constexpr T& operator[] (size_t off)
  134. {
  135. if constexpr (gp_config::has_buffer_bounds)
  136. {
  137. gp_config::assertion(
  138. off < sz,
  139. "Array bounds infringed"
  140. );
  141. }
  142. return ary[off];
  143. }
  144. ~vector()
  145. {
  146. if(ary)
  147. {
  148. if constexpr (!std::is_trivially_destructible_v<T>)
  149. for(auto& elem : *this) {
  150. elem.~T();
  151. }
  152. gp_config::assertion(alloc.get().deallocate(ary), "could not deallocate");
  153. ary = nullptr;
  154. }
  155. }
  156. /**
  157. * @brief Ensures the vector can hold at least 1 more element
  158. *
  159. * @return true if it is at least now possible to fit one more element
  160. * @return false if fiting one more element is not possible even now
  161. */
  162. bool grow() {
  163. if(sz == cap) return reserve(1 + sz + (sz >> 1));
  164. return true;
  165. }
  166. /**
  167. * @brief Reserves space so that the capacity is at least equal to the provided value.
  168. *
  169. * This will never shrink the datastructure
  170. *
  171. * @param new_cap the new capacity
  172. * @return true on success
  173. * @return false on failure
  174. */
  175. bool reserve(size_t new_cap) {
  176. if(new_cap <= cap) return true;
  177. size_t new_data_size = new_cap*sizeof(T);
  178. if(alloc.get().try_reallocate(ary, new_data_size)) return true;
  179. if(T* new_ary = (T*)alloc.get().allocate(new_data_size); new_ary) {
  180. auto new_it = new_ary;
  181. for(auto& elem : *this) {
  182. new(new_it++) T(gp::move(elem));
  183. }
  184. if(ary != nullptr) gp_config::assertion(alloc.get().deallocate(ary), "failed to deallocate old range");
  185. ary = new_ary;
  186. cap = new_cap;
  187. return true;
  188. }
  189. return false;
  190. }
  191. constexpr const T& operator[] (size_t off) const
  192. {
  193. if constexpr (gp_config::has_buffer_bounds)
  194. {
  195. gp_config::assertion(
  196. off < sz,
  197. "Array bounds infringed"
  198. );
  199. }
  200. return ary[off];
  201. }
  202. constexpr size_t size() const
  203. {
  204. return sz;
  205. }
  206. constexpr size_t capacity() const
  207. {
  208. return cap;
  209. }
  210. /**
  211. * @brief Adds the provided value to the vector
  212. *
  213. * @param value
  214. * @return true on success
  215. * @return false on failure
  216. */
  217. constexpr bool push_back(T& value) {
  218. if(grow()) {
  219. new(ary+sz) T(value);
  220. sz++;
  221. return true;
  222. }
  223. return false;
  224. }
  225. /**
  226. * @brief Moves the provided value to the vector
  227. *
  228. * @param value
  229. * @return true on success
  230. * @return false on failure
  231. */
  232. constexpr bool push_back(T&& value) {
  233. if(grow()) {
  234. new(ary+sz) T(gp::move(value));
  235. sz++;
  236. return true;
  237. }
  238. return false;
  239. }
  240. /**
  241. * @brief Constructs a new element at the end of the vector
  242. *
  243. * @param value the parameters to be sent to the constructor of T
  244. * @return true on success
  245. * @return false on failure
  246. */
  247. template<typename ...U>
  248. constexpr bool emplace_back(U&&... value) {
  249. if(grow()) {
  250. new(ary+sz) T(gp::forward<U>(value)...);
  251. sz++;
  252. return true;
  253. }
  254. return false;
  255. }
  256. /**
  257. * @brief moves the last element of the vector out if it exists, returning an optional.
  258. *
  259. * @return constexpr gp::optional<T> contains a value if it existed, else it is empty
  260. */
  261. constexpr gp::optional<T> pop_back() {
  262. if(sz == 0) return gp::nullopt;
  263. sz--;
  264. gp::optional<T> ret_val = gp::move(ary[sz]);
  265. ary[sz]->~T();
  266. return gp::move(ret_val);
  267. }
  268. void remove(pointer_iterator<T, 1> it) {
  269. for(auto step = it + 1; step<end(); step++) {
  270. (*it++) = gp::move(*step);
  271. }
  272. *rbegin().~T();
  273. sz -= 1;
  274. }
  275. constexpr pointer_iterator<T, 1> begin()
  276. {
  277. return associated_iterator(&ary[0]);
  278. }
  279. constexpr pointer_iterator<T, 1> end()
  280. {
  281. return associated_iterator(&ary[sz]);
  282. }
  283. constexpr const_pointer_iterator<T, 1> cbegin() const
  284. {
  285. return associated_const_iterator(&ary[0]);
  286. }
  287. constexpr const_pointer_iterator<T, 1> cend() const
  288. {
  289. return associated_const_iterator(&ary[sz]);
  290. }
  291. constexpr pointer_iterator<T, -1> rbegin()
  292. {
  293. return associated_riterator(&ary[sz-1]);
  294. }
  295. constexpr pointer_iterator<T, -1> rend()
  296. {
  297. return associated_riterator(ary-1);
  298. }
  299. constexpr const_pointer_iterator<T, -1> crbegin() const
  300. {
  301. return associated_const_riterator(&ary[sz-1]);
  302. }
  303. constexpr const_pointer_iterator<T, -1> crend() const
  304. {
  305. return associated_const_riterator(ary-1);
  306. }
  307. constexpr bool operator==(const vector& oth) const
  308. {
  309. for(size_t idx = 0; idx<sz; idx++)
  310. {
  311. if(ary[idx] != oth.ary[idx])
  312. {
  313. return false;
  314. }
  315. }
  316. return true;
  317. }
  318. constexpr bool operator!=(const vector& oth) const
  319. {
  320. return !(*this == oth);
  321. }
  322. /**
  323. * @brief Provides a span access to the vector
  324. *
  325. * @return A buffer of the vector.
  326. * It is invalidated by any operation that may change the size of the vector.
  327. */
  328. gp::buffer<T> as_buffer()
  329. {
  330. return gp::buffer<T>{(T*)ary, (T*)ary+sz};
  331. }
  332. };
  333. }