Stratax 0.2.0
Loading...
Searching...
No Matches
Buffer.hpp
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:
41 struct uninitialized_t {};
42
44 static constexpr uninitialized_t uninitialized{};
45
47 using iterator = T*;
48
50 using const_iterator = const T*;
51
53 using reverse_iterator = std::reverse_iterator<iterator>;
54
56 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
57
63 Buffer() noexcept: data_(nullptr), size_(0) {}
64
73 explicit Buffer(std::size_t size): data_(allocate(size)), size_(size)
74 {
75 construct_default(0, size_);
76 }
77
89 Buffer(std::size_t size, uninitialized_t uninitialized): data_(allocate(size)), size_(size)
90 {
91 (void)uninitialized;
92 static_assert(
93 std::is_trivially_destructible_v<T>,
94 "uninitialized Buffer storage is only safe for trivially destructible types"
95 );
96 }
97
106 Buffer(std::size_t size, const T& value): data_(allocate(size)), size_(size)
107 {
108 construct_fill(0, size_, value);
109 }
110
118 Buffer(std::initializer_list<T> list): data_(allocate(list.size())), size_(list.size())
119 {
120 std::size_t i = 0;
121
122 try {
123 for (const T& value : list) {
124 std::construct_at(data_ + i, value);
125 ++i;
126 }
127 } catch (...) {
128 destroy(0, i);
129 deallocate(data_);
130 throw;
131 }
132 }
133
142 Buffer(const Buffer& other)
143 : data_(allocate(other.size_)), size_(other.size_)
144 {
145 if constexpr (std::is_trivially_copyable_v<T>) {
146 if (size_ != 0) {
147 std::memcpy(data_, other.data_, sizeof(T) * size_);
148 }
149 return;
150 }
151
152 std::size_t i = 0;
153
154 try {
155 for (; i < size_; ++i) {
156 std::construct_at(data_ + i, other.data_[i]);
157 }
158 } catch (...) {
159 destroy(0, i);
160 deallocate(data_);
161 throw;
162 }
163 }
164
172 Buffer(Buffer&& other) noexcept
173 : data_(other.data_), size_(other.size_)
174 {
175 other.data_ = nullptr;
176 other.size_ = 0;
177 }
178
189 Buffer& operator=(const Buffer& other)
190 {
191 if (this == &other)
192 return *this;
193
194 Buffer temp(other);
195 swap(temp);
196 return *this;
197 }
198
209 Buffer& operator=(Buffer&& other) noexcept
210 {
211 if (this == &other)
212 return *this;
213
214 destroy(0, size_);
215 deallocate(data_);
216
217 data_ = other.data_;
218 size_ = other.size_;
219
220 other.data_ = nullptr;
221 other.size_ = 0;
222
223 return *this;
224 }
225
233 {
234 destroy(0, size_);
235 deallocate(data_);
236 }
237
247 T& operator[](std::size_t index) noexcept
248 {
249 return data_[index];
250 }
251
261 const T& operator[](std::size_t index) const noexcept
262 {
263 return data_[index];
264 }
265
273 T& front()
274 {
275 if (empty()) {
276 throw Exceptions::IndexError("Buffer front cannot be accessed when the buffer is empty.");
277 }
278
279 return data_[0];
280 }
281
289 const T& front() const
290 {
291 if (empty()) {
292 throw Exceptions::IndexError("Buffer front cannot be accessed when the buffer is empty.");
293 }
294
295 return data_[0];
296 }
297
305 T& back()
306 {
307 if (empty()) {
308 throw Exceptions::IndexError("Buffer back cannot be accessed when the buffer is empty.");
309 }
310
311 return data_[size_ - 1];
312 }
313
321 const T& back() const
322 {
323 if (empty()) {
324 throw Exceptions::IndexError("Buffer back cannot be accessed when the buffer is empty.");
325 }
326
327 return data_[size_ - 1];
328 }
329
335 [[nodiscard]] T* data() noexcept
336 {
337 return data_;
338 }
339
345 [[nodiscard]] const T* data() const noexcept
346 {
347 return data_;
348 }
349
355 [[nodiscard]] iterator begin() noexcept
356 {
357 return data_;
358 }
359
365 [[nodiscard]] const_iterator begin() const noexcept
366 {
367 return data_;
368 }
369
375 [[nodiscard]] const_iterator cbegin() const noexcept
376 {
377 return data_;
378 }
379
385 [[nodiscard]] iterator end() noexcept
386 {
387 return empty() ? data_ : data_ + size_;
388 }
389
395 [[nodiscard]] const_iterator end() const noexcept
396 {
397 return empty() ? data_ : data_ + size_;
398 }
399
405 [[nodiscard]] const_iterator cend() const noexcept
406 {
407 return empty() ? data_ : data_ + size_;
408 }
409
415 [[nodiscard]] reverse_iterator rbegin() noexcept
416 {
417 return reverse_iterator(end());
418 }
419
425 [[nodiscard]] const_reverse_iterator rbegin() const noexcept
426 {
427 return const_reverse_iterator(end());
428 }
429
435 [[nodiscard]] const_reverse_iterator crbegin() const noexcept
436 {
438 }
439
445 [[nodiscard]] reverse_iterator rend() noexcept
446 {
447 return reverse_iterator(begin());
448 }
449
455 [[nodiscard]] const_reverse_iterator rend() const noexcept
456 {
458 }
459
465 [[nodiscard]] const_reverse_iterator crend() const noexcept
466 {
468 }
469
475 [[nodiscard]] std::size_t size() const noexcept
476 {
477 return size_;
478 }
479
485 [[nodiscard]] bool empty() const noexcept
486 {
487 return size_ == 0;
488 }
489
495 void fill(const T& value)
496 {
497 std::fill_n(data_, size_, value);
498 }
499
505 void swap(Buffer& other) noexcept
506 {
507 std::swap(data_, other.data_);
508 std::swap(size_, other.size_);
509 }
510
511private:
512 T* data_;
513 std::size_t size_;
514
515private:
525 static T* allocate(std::size_t space)
526 {
527 if (space == 0)
528 return nullptr;
529
530 if (space > std::numeric_limits<std::size_t>::max() / sizeof(T)) {
531 throw std::bad_array_new_length();
532 }
533
534 return static_cast<T*>(
535 ::operator new(
536 sizeof(T) * space,
537 std::align_val_t{Alignment}
538 )
539 );
540 }
541
547 static void deallocate(T* ptr) noexcept
548 {
549 if (ptr == nullptr)
550 return;
551
552 ::operator delete(
553 ptr,
554 std::align_val_t{Alignment}
555 );
556 }
557
567 void construct_fill(std::size_t begin, std::size_t end, const T& value)
568 {
569 try {
570 std::uninitialized_fill_n(data_ + begin, end - begin, value);
571 } catch (...) {
572 deallocate(data_);
573 data_ = nullptr;
574 size_ = 0;
575 throw;
576 }
577 }
578
587 void construct_default(std::size_t begin, std::size_t end)
588 {
589 try {
590 std::uninitialized_value_construct_n(data_ + begin, end - begin);
591 } catch (...) {
592 deallocate(data_);
593 data_ = nullptr;
594 size_ = 0;
595 throw;
596 }
597 }
598
605 void destroy(std::size_t begin, std::size_t end) noexcept
606 {
607 if constexpr (!std::is_trivially_destructible_v<T>) {
608 for (std::size_t index = begin; index < end; ++index) {
609 std::destroy_at(data_ + index);
610 }
611 }
612 }
613}; // class Buffer
614
615} // namespace stratax::core
Signals an invalid index access.
std::size_t size() const noexcept
Definition Buffer.hpp:475
const T & operator[](std::size_t index) const noexcept
Returns a const element reference without bounds checking.
Definition Buffer.hpp:261
std::reverse_iterator< iterator > reverse_iterator
Mutable reverse iterator over contiguous buffer elements.
Definition Buffer.hpp:53
const T * data() const noexcept
Returns the raw data pointer as a const pointer.
Definition Buffer.hpp:345
Buffer & operator=(Buffer &&other) noexcept
Replaces this buffer by taking ownership from another buffer.
Definition Buffer.hpp:209
reverse_iterator rend() noexcept
Returns a reverse iterator before the first element.
Definition Buffer.hpp:445
void swap(Buffer &other) noexcept
Swaps the contents of two buffers.
Definition Buffer.hpp:505
const T * const_iterator
Const iterator over contiguous buffer elements.
Definition Buffer.hpp:50
Buffer(const Buffer &other)
Creates a copy of another buffer.
Definition Buffer.hpp:142
void fill(const T &value)
Fills every element with the same value.
Definition Buffer.hpp:495
Buffer(std::initializer_list< T > list)
Creates a buffer from an initializer list.
Definition Buffer.hpp:118
~Buffer()
Releases the owned storage.
Definition Buffer.hpp:232
Buffer(std::size_t size)
Creates a buffer with default-initialized elements.
Definition Buffer.hpp:73
Buffer(std::size_t size, const T &value)
Creates a buffer and fills every element with a value.
Definition Buffer.hpp:106
const_reverse_iterator rbegin() const noexcept
Returns a const reverse iterator to the last element.
Definition Buffer.hpp:425
const_reverse_iterator crend() const noexcept
Returns a const reverse iterator before the first element.
Definition Buffer.hpp:465
const T & back() const
Returns the last element as a const reference.
Definition Buffer.hpp:321
T & back()
Returns the last element.
Definition Buffer.hpp:305
static constexpr uninitialized_t uninitialized
Definition Buffer.hpp:44
const_iterator cend() const noexcept
Returns a const iterator one past the last element.
Definition Buffer.hpp:405
bool empty() const noexcept
Returns whether the buffer contains no elements.
Definition Buffer.hpp:485
const T & front() const
Returns the first element as a const reference.
Definition Buffer.hpp:289
const_reverse_iterator crbegin() const noexcept
Returns a const reverse iterator to the last element.
Definition Buffer.hpp:435
reverse_iterator rbegin() noexcept
Returns a reverse iterator to the last element.
Definition Buffer.hpp:415
iterator begin() noexcept
Returns an iterator to the first element.
Definition Buffer.hpp:355
const_iterator cbegin() const noexcept
Returns a const iterator to the first element.
Definition Buffer.hpp:375
const_iterator end() const noexcept
Returns a const iterator one past the last element.
Definition Buffer.hpp:395
Buffer(std::size_t size, uninitialized_t uninitialized)
Creates a buffer with allocated but uninitialized storage.
Definition Buffer.hpp:89
Buffer(Buffer &&other) noexcept
Transfers ownership from another buffer.
Definition Buffer.hpp:172
const_iterator begin() const noexcept
Returns a const iterator to the first element.
Definition Buffer.hpp:365
Buffer() noexcept
Creates an empty buffer.
Definition Buffer.hpp:63
T * data() noexcept
Returns the raw data pointer.
Definition Buffer.hpp:335
T * iterator
Mutable iterator over contiguous buffer elements.
Definition Buffer.hpp:47
std::reverse_iterator< const_iterator > const_reverse_iterator
Const reverse iterator over contiguous buffer elements.
Definition Buffer.hpp:56
Buffer & operator=(const Buffer &other)
Replaces this buffer with a copy of another buffer.
Definition Buffer.hpp:189
iterator end() noexcept
Returns an iterator one past the last element.
Definition Buffer.hpp:385
const_reverse_iterator rend() const noexcept
Returns a const reverse iterator before the first element.
Definition Buffer.hpp:455
T & front()
Returns the first element.
Definition Buffer.hpp:273
T & operator[](std::size_t index) noexcept
Returns a mutable element reference without bounds checking.
Definition Buffer.hpp:247
Tag type selecting allocated storage without element initialization.
Definition Buffer.hpp:41