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 #ifdef __CUDA_ARCH__
206 cudaMemcpyAsync(data_, other.data_, other.n * sizeof(value_type), cudaMemcpyDeviceToDevice);
207 #else
208 memcpy(data_, other.data_, other.n * sizeof(value_type));
209 #endif
210 if constexpr(!std::is_trivially_destructible_v<value_type>) {
211 for(size_t i = other.n; i < n; ++i) {
212 data_[i].~value_type();
213 }
214 }
215 n = other.n;
216 return *this;
217 }
218 else {
219 for(size_t i = 0; i < other.n; ++i) {
220 if constexpr(!std::is_trivially_destructible_v<value_type>) {
221 if(i < n) {
222 data_[i].~value_type();
223 }
224 }
225 inplace_new(i, other.data_[i]);
226 }
227 if constexpr(!std::is_trivially_destructible_v<value_type>) {
228 for(size_t i = other.n; i < n; ++i) {
229 data_[i].~value_type();
230 }
231 }
232 n = other.n;
233 return *this;
234 }
235 }
236
237public:
239 return assignment(other);
240 }
241
242 /** Beware that this operator does not free the memory of `this`, the capacity remains unchanged. */
243 template <class U, class Allocator2>
245 return assignment(other);
246 }
247
249 return allocator;
250 }
251
253 assert(i < n);
254 return data_[i];
255 }
256
257 CUDA const value_type& operator[](size_t i) const {
258 assert(i < n);
259 return data_[i];
260 }
261
262 CUDA value_type& front() { assert(n > 0); return data_[0]; }
263 CUDA const value_type& front() const { assert(n > 0); return data_[0]; }
264 CUDA value_type& back() { assert(n > 0); return data_[n-1]; }
265 CUDA const value_type& back() const { assert(n > 0); return data_[n-1]; }
266 CUDA value_type* data() { return data_; }
267 CUDA const value_type* data() const { return data_; }
268 CUDA bool empty() const { return n == 0; }
269 CUDA size_t size() const { return n; }
270 CUDA void reserve(size_t new_cap) { if(new_cap > cap) reallocate(new_cap); }
271 CUDA size_t capacity() const { return cap; }
272 CUDA void shrink_to_fit() { if(cap > n) reallocate(n); }
273
274 CUDA NI void clear() {
275 size_t n2 = n;
276 n = 0;
277 for(size_t i = 0; i < n2; ++i) {
278 data_[i].~T();
279 }
280 }
281
282 CUDA void push_back(const T& value) {
283 reallocate();
284 inplace_new(n, value);
285 ++n;
286 }
287
288 CUDA void push_back(T&& value) {
289 reallocate();
290 new(&data_[n]) T(std::forward<T>(value));
291 ++n;
292 }
293
294 template<class... Args>
295 CUDA value_type& emplace_back(Args&&... args) {
296 reallocate();
297 if constexpr(std::is_constructible<value_type, Args&&..., const allocator_type&>{}) {
298 new(&data_[n]) value_type(std::forward<Args>(args)..., allocator);
299 }
300 else {
301 new(&data_[n]) value_type(std::forward<Args>(args)...);
302 }
303 ++n;
304 return data_[n];
305 }
306
307 CUDA void pop_back() {
308 assert(n > 0);
309 data_[n-1].~T();
310 --n;
311 }
312
313 CUDA NI void resize(size_t count) {
314 if(count < n) {
315 if constexpr(!std::is_trivially_destructible_v<value_type>) {
316 for(size_t i = count; i < n; ++i) {
317 data_[i].~T();
318 }
319 }
320 n = count;
321 }
322 else {
323 if(count > cap) {
324 reallocate(count);
325 }
326 for(size_t i = n; i < count; ++i) {
327 inplace_new(i);
328 }
329 n = count;
330 }
331 }
332
333 CUDA void swap(this_type& other) {
334 ::battery::swap(n, other.n);
335 ::battery::swap(cap, other.cap);
336 ::battery::swap(data_, other.data_);
337 ::battery::swap(allocator, other.allocator);
338 }
339
340 CUDA NI void print() const {
341 for(size_t i = 0; i < n; ++i) {
342 ::battery::print(data_[i]);
343 if(i < n - 1) {
344 printf(", ");
345 }
346 }
347 }
348};
349
350template<class T1, class Alloc1, class T2, class Alloc2>
352 if(lhs.size() != rhs.size()) {
353 return false;
354 }
355 else {
356 for(size_t i = 0; i < lhs.size(); ++i) {
357 if(!(lhs[i] == rhs[i])) {
358 return false;
359 }
360 }
361 }
362 return true;
363}
364
365template<class T1, class Alloc1, class T2, class Alloc2>
366CUDA bool operator!=(const vector<T1, Alloc1>& lhs, const vector<T2, Alloc2>& rhs) {
367 return !(lhs == rhs);
368}
369
370
371} // namespace battery
372
373#endif
Definition vector.hpp:20
CUDA size_t capacity() const
Definition vector.hpp:271
CUDA void push_back(const T &value)
Definition vector.hpp:282
CUDA value_type & front()
Definition vector.hpp:262
CUDA void reserve(size_t new_cap)
Definition vector.hpp:270
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:263
CUDA value_type & emplace_back(Args &&... args)
Definition vector.hpp:295
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:252
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:272
T value_type
Definition vector.hpp:23
CUDA void swap(this_type &other)
Definition vector.hpp:333
CUDA NI vector(this_type &&other)
Definition vector.hpp:150
CUDA NI void resize(size_t count)
Definition vector.hpp:313
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:267
CUDA NI this_type & operator=(const vector< U, Allocator2 > &other)
Definition vector.hpp:244
vector< value_type, allocator_type > this_type
Definition vector.hpp:25
CUDA allocator_type get_allocator() const
Definition vector.hpp:248
CUDA void pop_back()
Definition vector.hpp:307
CUDA void push_back(T &&value)
Definition vector.hpp:288
CUDA NI void print() const
Definition vector.hpp:340
CUDA NI void clear()
Definition vector.hpp:274
CUDA const value_type & operator[](size_t i) const
Definition vector.hpp:257
CUDA bool empty() const
Definition vector.hpp:268
CUDA size_t size() const
Definition vector.hpp:269
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:238
CUDA value_type & back()
Definition vector.hpp:264
CUDA const value_type & back() const
Definition vector.hpp:265
CUDA value_type * data()
Definition vector.hpp:266
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:707
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