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.

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