Cuda battery library
Loading...
Searching...
No Matches
vector.hpp
Go to the documentation of this file.
1// Copyright 2021 Pierre Talbot
2
3#ifndef CUDA_BATTERY_VECTOR_HPP
4#define CUDA_BATTERY_VECTOR_HPP
5
6#include "utility.hpp"
7#include "allocator.hpp"
8#include <memory>
9#include <initializer_list>
10#include <vector>
11
12/** \file vector.hpp
13 * A partial implementation of `std::vector`, with additional constructors to convert from `std::vector`.
14 * The allocator is scoped, meaning it will be passed to the constructor of `T` if `T` provides a suited constructor with an allocator.
15*/
16
17namespace battery {
18
19template<class T, class Allocator = standard_allocator>
20class vector {
21
22public:
23 using value_type = T;
24 using allocator_type = Allocator;
26
27private:
28 static constexpr const size_t GROWING_FACTOR = 3;
29 size_t n;
30 size_t cap;
31 allocator_type allocator;
32 value_type* data_;
33
34 CUDA T* allocate() {
35 if(cap > 0) {
36 return static_cast<T*>(allocator.allocate(sizeof(T) * cap));
37 }
38 else {
39 return nullptr;
40 }
41 }
42
43 CUDA void deallocate() {
44 allocator.deallocate(data_);
45 }
46
47 CUDA NI void reallocate(size_t new_cap) {
48 size_t n2 = n;
49 value_type* data2 = data_;
50 cap = new_cap;
51 n = min(new_cap, n); // in case we shrink the initial array.
52 data_ = allocate();
53 for(size_t i = 0; i < n; ++i) {
54 if constexpr(std::is_move_constructible_v<value_type>) {
55 new(&data_[i]) value_type(std::move(data2[i]));
56 }
57 else {
58 new(&data_[i]) value_type(data2[i]);
59 }
60 }
61 if constexpr(!std::is_trivially_destructible_v<value_type>) {
62 // Free the old data array that has been reallocated.
63 for(size_t i = 0; i < n2; ++i) {
64 data2[i].~T();
65 }
66 }
67 allocator.deallocate(data2);
68 }
69
70 // Reallocate memory if the current array is full.
71 CUDA void reallocate() {
72 if(n == cap) {
73 reallocate(max(cap, size_t(1)) * GROWING_FACTOR);
74 }
75 }
76
77 template<class T2, class Allocator2> friend class vector;
78
79 CUDA void inplace_new(size_t i) {
80 if constexpr(std::is_constructible<value_type, const allocator_type&>{}) {
81 new(&data_[i]) value_type(allocator);
82 }
83 else {
84 new(&data_[i]) value_type{};
85 }
86 }
87
88 template <class U>
89 CUDA void inplace_new(size_t i, const U& value) {
90 if constexpr(std::is_constructible<value_type, const U&, const allocator_type&>{}) {
91 new(&data_[i]) value_type(value, allocator);
92 }
93 else {
94 new(&data_[i]) value_type(value);
95 }
96 }
97
98public:
99 /** Allocate an array of size `n` using `allocator`, with `n` default-inserted instances of `T`.
100 * `Allocator` is scoped, meaning it will be passed to the constructor of `T` if `T(const Allocator&)` exists, otherwise `T()` is called. */
101 CUDA NI vector(size_t n, const allocator_type& alloc = allocator_type()):
102 n(n), cap(n), allocator(alloc), data_(allocate())
103 {
104 for(size_t i = 0; i < n; ++i) {
105 inplace_new(i);
106 }
107 }
108
109 /** Default constructor.*/
111 : n(0), cap(0), allocator(alloc), data_(nullptr) {}
112
113 /** Allocate an array of size `n` using `allocator`.
114 Initialize the elements of the array with those of the array `from`.
115 `Allocator` is scoped, meaning it will be passed to the constructor of `T` if `T(const T&, const Allocator&)` exists, otherwise `T(const T&)` is called. */
116 template <class U>
117 CUDA NI vector(const U* from, size_t n, const allocator_type& alloc = allocator_type{})
118 : n(n), cap(n), allocator(alloc), data_(allocate())
119 {
120 for(size_t i = 0; i < n; ++i) {
121 inplace_new(i, from[i]);
122 }
123 }
124
125 /** Copy constructor with an allocator, useful when the vector we copied from was declared using another allocator. */
126 template <class U, class Allocator2>
128 : this_type(from.data(), from.size(), alloc) {}
129
130 /** Copy constructor with different underlying types. */
131 template <class U>
133 : this_type(from, from.allocator) {}
134
135 /** Redefine the copy constructor to be sure it calls a constructor with an allocator. */
136 CUDA vector(const this_type& from)
137 : this_type(from, from.allocator) {}
138
139 /** Initialize of an array of size `n` with each element initialized to `from` using `allocator`.
140 * `Allocator` is scoped, meaning it will be passed to the constructor of `T` if `T(const T&, const Allocator&)` exists, otherwise `T(const T&)` is called. */
141 template <class U>
142 CUDA NI vector(size_t n, const U& from, const allocator_type& alloc = allocator_type{})
143 : n(n), cap(n), allocator(alloc), data_(allocate())
144 {
145 for(size_t i = 0; i < n; ++i) {
146 inplace_new(i, from);
147 }
148 }
149
150 CUDA NI vector(this_type&& other): this_type(other.allocator) {
151 swap(other);
152 }
153
154 CUDA NI vector(std::initializer_list<T> init, const allocator_type& alloc = allocator_type{})
155 : n(init.size()), cap(init.size()), allocator(alloc), data_(allocate())
156 {
157 size_t i = 0;
158 for(const T& v : init) {
159 inplace_new(i, v);
160 ++i;
161 }
162 }
163
164 template <class U, class Alloc2>
165 vector(const std::vector<U, Alloc2>& other, const allocator_type& alloc = allocator_type{})
166 : n(other.size()), cap(other.size()), allocator(alloc), data_(allocate())
167 {
168 for(size_t i = 0; i < n; ++i) {
169 inplace_new(i, other[i]);
170 }
171 }
172
174 if constexpr(!std::is_trivially_destructible_v<value_type>) {
175 for(size_t i = 0; i < n; ++i) {
176 data_[i].~T();
177 }
178 }
179 allocator.deallocate(data_);
180 data_ = nullptr;
181 n = 0;
182 cap = 0;
183 }
184
186 swap(other);
187 return *this;
188 }
189
190private:
191 template <class U, class Allocator2>
192 CUDA this_type& assignment(const vector<U, Allocator2>& other) {
193 reserve(other.size());
194 constexpr bool fast_copy =
195 #ifdef __CUDA_ARCH__
196 std::is_same_v<value_type, U>
197 && std::is_same_v<Allocator2, allocator_type>
198 && std::is_trivially_copyable_v<value_type>;
199 #else
200 std::is_same_v<value_type, U>
201 && std::is_trivially_copyable_v<value_type>;
202 #endif
203
204 if constexpr(fast_copy) {
205 memcpy(data_, other.data_, other.n * sizeof(value_type));
206 }
207 else {
208 for(size_t i = 0; i < other.n; ++i) {
209 if constexpr(!std::is_trivially_destructible_v<value_type>) {
210 if(i < n) {
211 data_[i].~value_type();
212 }
213 }
214 inplace_new(i, other.data_[i]);
215 }
216 }
217 if constexpr(!std::is_trivially_destructible_v<value_type>) {
218 for(size_t i = other.n; i < n; ++i) {
219 data_[i].~value_type();
220 }
221 }
222 n = other.n;
223 return *this;
224 }
225
226public:
228 return assignment(other);
229 }
230
231 /** Beware that this operator does not free the memory of `this`, the capacity remains unchanged. */
232 template <class U, class Allocator2>
234 return assignment(other);
235 }
236
238 return allocator;
239 }
240
242 assert(i < n);
243 return data_[i];
244 }
245
246 CUDA const value_type& operator[](size_t i) const {
247 assert(i < n);
248 return data_[i];
249 }
250
251 CUDA value_type& front() { assert(n > 0); return data_[0]; }
252 CUDA const value_type& front() const { assert(n > 0); return data_[0]; }
253 CUDA value_type& back() { assert(n > 0); return data_[n-1]; }
254 CUDA const value_type& back() const { assert(n > 0); return data_[n-1]; }
255 CUDA value_type* data() { return data_; }
256 CUDA const value_type* data() const { return data_; }
257 CUDA bool empty() const { return n == 0; }
258 CUDA size_t size() const { return n; }
259 CUDA void reserve(size_t new_cap) { if(new_cap > cap) reallocate(new_cap); }
260 CUDA size_t capacity() const { return cap; }
261 CUDA void shrink_to_fit() { if(cap > n) reallocate(n); }
262
263 CUDA NI void clear() {
264 size_t n2 = n;
265 n = 0;
266 for(size_t i = 0; i < n2; ++i) {
267 data_[i].~T();
268 }
269 }
270
271 CUDA void push_back(const T& value) {
272 reallocate();
273 inplace_new(n, value);
274 ++n;
275 }
276
277 CUDA void push_back(T&& value) {
278 reallocate();
279 new(&data_[n]) T(std::forward<T>(value));
280 ++n;
281 }
282
283 template<class... Args>
284 CUDA value_type& emplace_back(Args&&... args) {
285 reallocate();
286 if constexpr(std::is_constructible<value_type, Args&&..., const allocator_type&>{}) {
287 new(&data_[n]) value_type(std::forward<Args>(args)..., allocator);
288 }
289 else {
290 new(&data_[n]) value_type(std::forward<Args>(args)...);
291 }
292 ++n;
293 return data_[n];
294 }
295
296 CUDA void pop_back() {
297 assert(n > 0);
298 data_[n-1].~T();
299 --n;
300 }
301
302 CUDA NI void resize(size_t count) {
303 if(count < n) {
304 if constexpr(!std::is_trivially_destructible_v<value_type>) {
305 for(size_t i = count; i < n; ++i) {
306 data_[i].~T();
307 }
308 }
309 n = count;
310 }
311 else {
312 if(count > cap) {
313 reallocate(count);
314 }
315 for(size_t i = n; i < count; ++i) {
316 inplace_new(i);
317 }
318 n = count;
319 }
320 }
321
322 CUDA void swap(this_type& other) {
323 ::battery::swap(n, other.n);
324 ::battery::swap(cap, other.cap);
325 ::battery::swap(data_, other.data_);
326 ::battery::swap(allocator, other.allocator);
327 }
328
329 CUDA NI void print() const {
330 for(size_t i = 0; i < n; ++i) {
331 ::battery::print(data_[i]);
332 if(i < n - 1) {
333 printf(", ");
334 }
335 }
336 }
337};
338
339template<class T1, class Alloc1, class T2, class Alloc2>
341 if(lhs.size() != rhs.size()) {
342 return false;
343 }
344 else {
345 for(size_t i = 0; i < lhs.size(); ++i) {
346 if(!(lhs[i] == rhs[i])) {
347 return false;
348 }
349 }
350 }
351 return true;
352}
353
354template<class T1, class Alloc1, class T2, class Alloc2>
355CUDA bool operator!=(const vector<T1, Alloc1>& lhs, const vector<T2, Alloc2>& rhs) {
356 return !(lhs == rhs);
357}
358
359
360} // namespace battery
361
362#endif
Definition vector.hpp:20
CUDA size_t capacity() const
Definition vector.hpp:260
CUDA void push_back(const T &value)
Definition vector.hpp:271
CUDA value_type & front()
Definition vector.hpp:251
CUDA void reserve(size_t new_cap)
Definition vector.hpp:259
CUDA NI vector(size_t n, const allocator_type &alloc=allocator_type())
Definition vector.hpp:101
Allocator allocator_type
Definition vector.hpp:24
CUDA const value_type & front() const
Definition vector.hpp:252
CUDA value_type & emplace_back(Args &&... args)
Definition vector.hpp:284
CUDA this_type & operator=(this_type &&other)
Definition vector.hpp:185
CUDA vector(const vector< U, allocator_type > &from)
Definition vector.hpp:132
CUDA value_type & operator[](size_t i)
Definition vector.hpp:241
CUDA NI vector(std::initializer_list< T > init, const allocator_type &alloc=allocator_type{})
Definition vector.hpp:154
vector(const std::vector< U, Alloc2 > &other, const allocator_type &alloc=allocator_type{})
Definition vector.hpp:165
CUDA vector(const vector< U, Allocator2 > &from, const allocator_type &alloc=allocator_type{})
Definition vector.hpp:127
CUDA NI ~vector()
Definition vector.hpp:173
CUDA void shrink_to_fit()
Definition vector.hpp:261
T value_type
Definition vector.hpp:23
CUDA void swap(this_type &other)
Definition vector.hpp:322
CUDA NI vector(this_type &&other)
Definition vector.hpp:150
CUDA NI void resize(size_t count)
Definition vector.hpp:302
CUDA vector(const this_type &from)
Definition vector.hpp:136
CUDA NI vector(size_t n, const U &from, const allocator_type &alloc=allocator_type{})
Definition vector.hpp:142
CUDA const value_type * data() const
Definition vector.hpp:256
CUDA NI this_type & operator=(const vector< U, Allocator2 > &other)
Definition vector.hpp:233
vector< value_type, allocator_type > this_type
Definition vector.hpp:25
CUDA allocator_type get_allocator() const
Definition vector.hpp:237
CUDA void pop_back()
Definition vector.hpp:296
CUDA void push_back(T &&value)
Definition vector.hpp:277
CUDA NI void print() const
Definition vector.hpp:329
CUDA NI void clear()
Definition vector.hpp:263
CUDA const value_type & operator[](size_t i) const
Definition vector.hpp:246
CUDA bool empty() const
Definition vector.hpp:257
CUDA size_t size() const
Definition vector.hpp:258
CUDA NI vector(const U *from, size_t n, const allocator_type &alloc=allocator_type{})
Definition vector.hpp:117
CUDA vector(const allocator_type &alloc=allocator_type{})
Definition vector.hpp:110
CUDA NI this_type & operator=(const this_type &other)
Definition vector.hpp:227
CUDA value_type & back()
Definition vector.hpp:253
CUDA const value_type & back() const
Definition vector.hpp:254
CUDA value_type * data()
Definition vector.hpp:255
Definition algorithm.hpp:10
CUDA INLINE constexpr T min(T a, T b)
Definition utility.hpp:116
CUDA INLINE constexpr T max(T a, T b)
Definition utility.hpp:128
CUDA constexpr void swap(T &a, T &b)
Definition utility.hpp:91
CUDA NI void print(const T &t)
Definition utility.hpp:732
CUDA bool operator==(const string< Alloc1 > &lhs, const string< Alloc2 > &rhs)
Definition string.hpp:110
#define CUDA
Definition utility.hpp:59
#define NI
Definition utility.hpp:62