Stratax
Scientific computing containers and operations
Loading...
Searching...
No Matches
Buffer.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <algorithm>
4#include <cstring>
5#include <cstddef>
6#include <initializer_list>
7#include <iterator>
8#include <memory>
9#include <utility>
10#include <new>
11#include <limits>
12#include <type_traits>
13
14#include "Config.hpp"
15#include "Exceptions.hpp"
16
17namespace stratax::core {
18
37template<typename T, std::size_t Alignment = config::default_alignment>
38class Buffer {
39public:
40 struct uninitialized_t {};
41 static constexpr uninitialized_t uninitialized{};
42
48 Buffer() noexcept: data_(nullptr), size_(0) {}
49
58 explicit Buffer(std::size_t size): data_(allocate(size)), size_(size)
59 {
60 construct_default(0, size_);
61 }
62
74 Buffer(std::size_t size, uninitialized_t uninitialized): data_(allocate(size)), size_(size)
75 {
76 (void)uninitialized;
77 static_assert(
78 std::is_trivially_destructible_v<T>,
79 "uninitialized Buffer storage is only safe for trivially destructible types"
80 );
81 }
82
91 Buffer(std::size_t size, const T& value): data_(allocate(size)), size_(size)
92 {
93 construct_fill(0, size_, value);
94 }
95
103 Buffer(std::initializer_list<T> list): data_(allocate(list.size())), size_(list.size())
104 {
105 std::size_t i = 0;
106
107 try {
108 for (const T& value : list) {
109 std::construct_at(data_ + i, value);
110 ++i;
111 }
112 } catch (...) {
113 destroy(0, i);
114 deallocate(data_);
115 throw;
116 }
117 }
118
127 Buffer(const Buffer& other)
128 : data_(allocate(other.size_)), size_(other.size_)
129 {
130 if constexpr (std::is_trivially_copyable_v<T>) {
131 if (size_ != 0) {
132 std::memcpy(data_, other.data_, sizeof(T) * size_);
133 }
134 return;
135 }
136
137 std::size_t i = 0;
138
139 try {
140 for (; i < size_; ++i) {
141 std::construct_at(data_ + i, other.data_[i]);
142 }
143 } catch (...) {
144 destroy(0, i);
145 deallocate(data_);
146 throw;
147 }
148 }
149
157 Buffer(Buffer&& other) noexcept
158 : data_(other.data_), size_(other.size_)
159 {
160 other.data_ = nullptr;
161 other.size_ = 0;
162 }
163
174 Buffer& operator=(const Buffer& other)
175 {
176 if (this == &other)
177 return *this;
178
179 Buffer temp(other);
180 swap(temp);
181 return *this;
182 }
183
194 Buffer& operator=(Buffer&& other) noexcept
195 {
196 if (this == &other)
197 return *this;
198
199 destroy(0, size_);
200 deallocate(data_);
201
202 data_ = other.data_;
203 size_ = other.size_;
204
205 other.data_ = nullptr;
206 other.size_ = 0;
207
208 return *this;
209 }
210
218 {
219 destroy(0, size_);
220 deallocate(data_);
221 }
222
232 T& operator[](std::size_t index) noexcept
233 {
234 return data_[index];
235 }
236
246 const T& operator[](std::size_t index) const noexcept
247 {
248 return data_[index];
249 }
250
258 T& front()
259 {
260 if (empty()) {
261 throw Exceptions::IndexError("Cannot access front of an empty buffer.");
262 }
263
264 return data_[0];
265 }
266
274 const T& front() const
275 {
276 if (empty()) {
277 throw Exceptions::IndexError("Cannot access front of an empty buffer.");
278 }
279
280 return data_[0];
281 }
282
290 T& back()
291 {
292 if (empty()) {
293 throw Exceptions::IndexError("Cannot access back of an empty buffer.");
294 }
295
296 return data_[size_ - 1];
297 }
298
306 const T& back() const
307 {
308 if (empty()) {
309 throw Exceptions::IndexError("Cannot access back of an empty buffer.");
310 }
311
312 return data_[size_ - 1];
313 }
314
320 [[nodiscard]] T* data() noexcept
321 {
322 return data_;
323 }
324
330 [[nodiscard]] const T* data() const noexcept
331 {
332 return data_;
333 }
334
340 [[nodiscard]] T* begin() noexcept
341 {
342 return data_;
343 }
344
350 [[nodiscard]] const T* begin() const noexcept
351 {
352 return data_;
353 }
354
360 [[nodiscard]] const T* cbegin() const noexcept
361 {
362 return data_;
363 }
364
370 [[nodiscard]] T* end() noexcept
371 {
372 return data_ + size_;
373 }
374
380 [[nodiscard]] const T* end() const noexcept
381 {
382 return data_ + size_;
383 }
384
390 [[nodiscard]] const T* cend() const noexcept
391 {
392 return data_ + size_;
393 }
394
400 [[nodiscard]] std::reverse_iterator<T*> rbegin() noexcept
401 {
402 return std::reverse_iterator<T*>(end());
403 }
404
410 [[nodiscard]] std::reverse_iterator<const T*> rbegin() const noexcept
411 {
412 return std::reverse_iterator<const T*>(end());
413 }
414
420 [[nodiscard]] std::reverse_iterator<const T*> crbegin() const noexcept
421 {
422 return std::reverse_iterator<const T*>(cend());
423 }
424
430 [[nodiscard]] std::reverse_iterator<T*> rend() noexcept
431 {
432 return std::reverse_iterator<T*>(begin());
433 }
434
440 [[nodiscard]] std::reverse_iterator<const T*> rend() const noexcept
441 {
442 return std::reverse_iterator<const T*>(begin());
443 }
444
450 [[nodiscard]] std::reverse_iterator<const T*> crend() const noexcept
451 {
452 return std::reverse_iterator<const T*>(cbegin());
453 }
454
460 [[nodiscard]] std::size_t size() const noexcept
461 {
462 return size_;
463 }
464
470 [[nodiscard]] bool empty() const noexcept
471 {
472 return size_ == 0;
473 }
474
480 void fill(const T& value)
481 {
482 std::fill_n(data_, size_, value);
483 }
484
490 void swap(Buffer& other) noexcept
491 {
492 std::swap(data_, other.data_);
493 std::swap(size_, other.size_);
494 }
495
496private:
497 T* data_;
498 std::size_t size_;
499
500private:
510 static T* allocate(std::size_t space)
511 {
512 if (space == 0)
513 return nullptr;
514
515 if (space > std::numeric_limits<std::size_t>::max() / sizeof(T)) {
516 throw std::bad_array_new_length();
517 }
518
519 return static_cast<T*>(
520 ::operator new(
521 sizeof(T) * space,
522 std::align_val_t{Alignment}
523 )
524 );
525 }
526
532 static void deallocate(T* ptr) noexcept
533 {
534 if (ptr == nullptr)
535 return;
536
537 ::operator delete(
538 ptr,
539 std::align_val_t{Alignment}
540 );
541 }
542
552 void construct_fill(std::size_t begin, std::size_t end, const T& value)
553 {
554 try {
555 std::uninitialized_fill_n(data_ + begin, end - begin, value);
556 } catch (...) {
557 deallocate(data_);
558 data_ = nullptr;
559 size_ = 0;
560 throw;
561 }
562 }
563
572 void construct_default(std::size_t begin, std::size_t end)
573 {
574 try {
575 std::uninitialized_value_construct_n(data_ + begin, end - begin);
576 } catch (...) {
577 deallocate(data_);
578 data_ = nullptr;
579 size_ = 0;
580 throw;
581 }
582 }
583
590 void destroy(std::size_t begin, std::size_t end) noexcept
591 {
592 if constexpr (!std::is_trivially_destructible_v<T>) {
593 for (std::size_t index = begin; index < end; ++index) {
594 std::destroy_at(data_ + index);
595 }
596 }
597 }
598};
599
600}
Signals an invalid index access.
std::reverse_iterator< const T * > rbegin() const noexcept
Returns a const reverse iterator to the last element.
Definition Buffer.hpp:410
std::size_t size() const noexcept
Returns the number of stored elements.
Definition Buffer.hpp:460
const T & operator[](std::size_t index) const noexcept
Returns a const element reference without bounds checking.
Definition Buffer.hpp:246
const T * data() const noexcept
Returns the raw data pointer as a const pointer.
Definition Buffer.hpp:330
Buffer & operator=(Buffer &&other) noexcept
Replaces this buffer by taking ownership from another buffer.
Definition Buffer.hpp:194
void swap(Buffer &other) noexcept
Swaps the contents of two buffers.
Definition Buffer.hpp:490
Buffer(const Buffer &other)
Creates a copy of another buffer.
Definition Buffer.hpp:127
void fill(const T &value)
Fills every element with the same value.
Definition Buffer.hpp:480
std::reverse_iterator< T * > rbegin() noexcept
Returns a reverse iterator to the last element.
Definition Buffer.hpp:400
Buffer(std::initializer_list< T > list)
Creates a buffer from an initializer list.
Definition Buffer.hpp:103
~Buffer()
Releases the owned storage.
Definition Buffer.hpp:217
Buffer(std::size_t size)
Creates a buffer with default-initialized elements.
Definition Buffer.hpp:58
Buffer(std::size_t size, const T &value)
Creates a buffer and fills every element with a value.
Definition Buffer.hpp:91
const T & back() const
Returns the last element as a const reference.
Definition Buffer.hpp:306
T & back()
Returns the last element.
Definition Buffer.hpp:290
static constexpr uninitialized_t uninitialized
Definition Buffer.hpp:41
bool empty() const noexcept
Returns whether the buffer contains no elements.
Definition Buffer.hpp:470
const T & front() const
Returns the first element as a const reference.
Definition Buffer.hpp:274
std::reverse_iterator< const T * > rend() const noexcept
Returns a const reverse iterator before the first element.
Definition Buffer.hpp:440
const T * end() const noexcept
Returns a const iterator one past the last element.
Definition Buffer.hpp:380
const T * begin() const noexcept
Returns a const iterator to the first element.
Definition Buffer.hpp:350
std::reverse_iterator< const T * > crend() const noexcept
Returns a const reverse iterator before the first element.
Definition Buffer.hpp:450
std::reverse_iterator< const T * > crbegin() const noexcept
Returns a const reverse iterator to the last element.
Definition Buffer.hpp:420
T * end() noexcept
Returns an iterator one past the last element.
Definition Buffer.hpp:370
std::reverse_iterator< T * > rend() noexcept
Returns a reverse iterator before the first element.
Definition Buffer.hpp:430
Buffer(std::size_t size, uninitialized_t uninitialized)
Creates a buffer with allocated but uninitialized storage.
Definition Buffer.hpp:74
Buffer(Buffer &&other) noexcept
Transfers ownership from another buffer.
Definition Buffer.hpp:157
const T * cbegin() const noexcept
Returns a const iterator to the first element.
Definition Buffer.hpp:360
Buffer() noexcept
Creates an empty buffer.
Definition Buffer.hpp:48
T * data() noexcept
Returns the raw data pointer.
Definition Buffer.hpp:320
const T * cend() const noexcept
Returns a const iterator one past the last element.
Definition Buffer.hpp:390
Buffer & operator=(const Buffer &other)
Replaces this buffer with a copy of another buffer.
Definition Buffer.hpp:174
T * begin() noexcept
Returns an iterator to the first element.
Definition Buffer.hpp:340
T & front()
Returns the first element.
Definition Buffer.hpp:258
T & operator[](std::size_t index) noexcept
Returns a mutable element reference without bounds checking.
Definition Buffer.hpp:232