/* * * Copyright (C) 2024 Brett Terpstra * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef BLT_VECTOR_H #define BLT_VECTOR_H #include #include #include #include #include "ranges.h" #include #include namespace blt { template class static_vector { private: std::array buffer_; size_t size_ = 0; using value_type = T; using reference = T&; using pointer = T*; using const_reference = const T&; using const_pointer = const T*; using iterator = T*; using const_iterator = const T*; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; public: constexpr static_vector() = default; constexpr inline bool push_back(const T& copy) { if (size_ >= MAX_SIZE) return false; buffer_[size_++] = copy; return true; } constexpr inline bool push_back(T&& move) { if (size_ >= MAX_SIZE) return false; buffer_[size_++] = std::move(move); return true; } //TODO: replace with placement new / byte data template constexpr inline bool emplace_back(Args&& ... args) { if (size_ >= MAX_SIZE) return false; buffer_[size_++] = T{std::forward(args)...}; return true; } constexpr inline void pop_back() { size_--; } constexpr inline void reserve(size_t size) { if (size > MAX_SIZE) size = MAX_SIZE; size_ = size; } [[nodiscard]] constexpr inline bool empty() const { return size() == 0; } constexpr inline void clear() { for (auto& v : *this) v = {}; size_ = 0; } [[nodiscard]] constexpr inline size_t size() const { return size_; } [[nodiscard]] constexpr inline size_t capacity() const { return MAX_SIZE; } [[nodiscard]] constexpr inline blt::size_t max_size() const { return MAX_SIZE; } constexpr inline reference at(size_t index) { if (index >= MAX_SIZE) throw std::runtime_error("Array index " + std::to_string(index) + " out of bounds! (Max size: " + std::to_string(MAX_SIZE) + ')'); return buffer_[index]; } constexpr inline reference operator[](size_t index) { return buffer_[index]; } constexpr inline const_reference operator[](size_t index) const { return buffer_[index]; } constexpr inline reference operator*() { return *buffer_.data(); } constexpr inline const_reference operator*() const { return *buffer_.data(); } constexpr inline pointer data() { return buffer_.data(); } constexpr inline const_pointer data() const { return buffer_.data(); } constexpr inline reference front() { return data()[0]; } constexpr inline const_reference front() const { return data()[0]; } constexpr inline reference back() { return data()[size() - 1]; } constexpr inline const_reference back() const { return data()[size() - 1]; } constexpr inline iterator begin() noexcept { return data(); } constexpr inline iterator end() noexcept { return data() + size(); } constexpr inline const_iterator cbegin() const noexcept { return data(); } constexpr inline const_iterator cend() const noexcept { return data() + size(); } constexpr inline reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; } constexpr inline reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; } constexpr inline const_iterator crbegin() const noexcept { return const_reverse_iterator{cend()}; } constexpr inline reverse_iterator crend() const noexcept { return reverse_iterator{cbegin()}; } template, bool> = true> constexpr iterator insert(const_iterator pos, G&& ref) { blt::ptrdiff_t loc = pos - buffer_; if (size_ + 1 >= capacity()) { BLT_ABORT("Inserting exceeds size of internal buffer!"); } for (auto insert = end() - 1; (insert - buffer_) != loc - 1; insert--) { auto new_pos = insert + 1; *new_pos = *insert; } buffer_[loc] = ref; size_++; return buffer_ + loc; } constexpr iterator erase(const_iterator pos) { blt::ptrdiff_t loc = pos - buffer_; for (auto fetch = begin() + loc + 1; fetch != end(); fetch++) { auto insert = fetch - 1; *insert = *fetch; } size_--; return buffer_ + loc + 1; } constexpr iterator erase(const_iterator first, const_iterator last) { blt::ptrdiff_t first_pos = first - buffer_; blt::ptrdiff_t last_pos = last - buffer_; blt::ptrdiff_t remove_amount = last_pos - first_pos; for (auto fetch = begin() + last_pos, insert = begin() + first_pos; fetch != end(); fetch++, insert++) { *insert = *fetch; } size_ -= remove_amount; return buffer_ + first_pos + 1; } template, bool> = true> constexpr friend bool operator==(const static_vector& v1, const static_vector& v2) { if (v1.size() != v2.size()) return false; for (blt::size_t i = 0; i < v1.size(); i++) { if (v1[i] != v2[i]) return false; } return true; } }; template> class vector { private: ALLOC allocator; T* buffer_; size_t capacity_ = 0; size_t size_ = 0; using value_type = T; using allocator_type = ALLOC; using size_type = size_t; using difference_type = std::ptrdiff_t; using reference = value_type&; using const_reference = const value_type&; using pointer = value_type*; using const_pointer = const pointer; using iterator = T*; using const_iterator = const T*; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; constexpr inline void expand(size_t new_size = 0) { if (new_size == 0) new_size = blt::mem::next_byte_allocation(capacity_); auto new_buffer = allocator.allocate(new_size); for (size_t i = 0; i < size_; i++) new_buffer[i] = buffer_[i]; allocator.deallocate(buffer_, capacity_); buffer_ = new_buffer; capacity_ = new_size; } public: constexpr vector(): capacity_(16) { buffer_ = allocator.allocate(capacity_); } constexpr explicit vector(size_t capacity): capacity_(capacity) { buffer_ = allocator.allocate(capacity_); } template, bool> = true> constexpr vector(std::initializer_list&& list): capacity_(list.size()), size_(list.size()) { buffer_ = allocator.allocate(capacity_); for (auto e : blt::enumerate(list)) buffer_[e.first] = e.second; } template, G> || std::is_same_v, G>, bool> = true> constexpr explicit vector(const G& copy): size_(copy.size()), capacity_(copy.capacity()) { buffer_ = allocator.allocate(capacity_); for (auto e : blt::enumerate(copy)) buffer_[e.first] = e.second; } template, G> || std::is_same_v, G>, bool> = true> constexpr explicit vector(G&& move): buffer_(move.buffer_), capacity_(move.capacity()), size_(move.size()) { move.buffer_ = nullptr; } BLT_CPP20_CONSTEXPR ~vector() { allocator.deallocate(buffer_, capacity_); } constexpr inline void push_back(const T& copy) { if (size_ >= capacity_) expand(); buffer_[size_++] = copy; } constexpr inline void push_back(T&& move) { if (size_ >= capacity_) expand(); buffer_[size_++] = std::move(move); } template constexpr inline void emplace_back(Args&& ... args) { if (size_ >= capacity_) expand(); new(&buffer_[size_++]) T(std::forward(args)...); } constexpr inline T& at(size_t index) { if (index >= capacity_) throw std::runtime_error( "Array index " + std::to_string(index) + " out of bounds! (Max size: " + std::to_string(capacity_) + ')'); return buffer_[index]; } constexpr inline const T& at(size_t index) const { if (index >= capacity_) throw std::runtime_error( "Array index " + std::to_string(index) + " out of bounds! (Max size: " + std::to_string(capacity_) + ')'); return buffer_[index]; } constexpr inline T& operator[](size_t index) { return buffer_[index]; } constexpr inline const T& operator[](size_t index) const { return buffer_[index]; } constexpr inline void reserve(size_t size) { expand(size); } [[nodiscard]] constexpr inline size_t size() const { return size_; } [[nodiscard]] constexpr inline size_t capacity() const { return capacity_; } constexpr inline reference front() { return *buffer_; } constexpr inline const_reference front() const { return *buffer_; } constexpr inline reference back() { return buffer_[size_ - 1]; } constexpr inline const_reference back() const { return buffer_[size_ - 1]; } constexpr inline T* data() { return buffer_; } constexpr inline T* operator*() { return buffer_; } constexpr inline T* data() const { return buffer_; } [[nodiscard]] constexpr inline bool empty() const { return size_ == 0; } template, bool> = true> constexpr iterator insert(const_iterator pos, G&& ref) { difference_type loc = pos - buffer_; if (size_ + 1 >= capacity_) expand(); for (auto insert = end() - 1; (insert - buffer_) != loc - 1; insert--) { auto new_pos = insert + 1; *new_pos = *insert; } buffer_[loc] = ref; size_++; return buffer_ + loc; } constexpr iterator erase(const_iterator pos) { difference_type loc = pos - buffer_; for (auto fetch = begin() + loc + 1; fetch != end(); fetch++) { auto insert = fetch - 1; *insert = *fetch; } size_--; return buffer_ + loc + 1; } constexpr iterator erase(const_iterator first, const_iterator last) { difference_type first_pos = first - buffer_; difference_type last_pos = last - buffer_; difference_type remove_amount = last_pos - first_pos; for (auto fetch = begin() + last_pos, insert = begin() + first_pos; fetch != end(); fetch++, insert++) { *insert = *fetch; } size_ -= remove_amount; return buffer_ + first_pos + 1; } constexpr inline iterator begin() const noexcept { return data(); } constexpr inline iterator end() const noexcept { return data() + size(); } constexpr inline const_iterator cbegin() const noexcept { return data(); } constexpr inline const_iterator cend() const noexcept { return data() + size(); } constexpr inline reverse_iterator rbegin() const noexcept { return reverse_iterator{end()}; } constexpr inline reverse_iterator rend() const noexcept { return reverse_iterator{begin()}; } constexpr inline const_iterator crbegin() const noexcept { return const_reverse_iterator{cend()}; } constexpr inline reverse_iterator crend() const noexcept { return reverse_iterator{cbegin()}; } }; template> class svo_vector { public: constexpr svo_vector() = default; template, bool> = true> constexpr svo_vector(std::initializer_list list) { if (list.size() > BUFFER_SIZE) { // TODO: how to avoid copy here? data.buffer_pointer = alloc.allocate(list.size()); for (const auto& v : blt::enumerate(list)) new (&data.buffer_pointer[v.first]) T(v.second); capacity = list.size(); used = list.size(); } else { for (const auto& v : blt::enumerate(list)) new (&data.buffer[v.first]) T(v.second); used = list.size(); } } template, G> || std::is_same_v, G>, bool> = true> constexpr explicit svo_vector(G&& universal) { if (universal.size() > BUFFER_SIZE) { } else { } } [[nodiscard]] constexpr blt::size_t size() const { return used; } [[nodiscard]] constexpr bool is_heap() const { return used > BUFFER_SIZE; } BLT_CPP20_CONSTEXPR ~svo_vector() { if (is_heap()) { for (blt::size_t i = 0; i < used; i++) data.buffer_pointer[i].~T(); alloc.deallocate(data.buffer_pointer, capacity); } else { for (blt::size_t i = 0; i < used; i++) data.buffer[i].~T(); } } private: union buffer_data { T* buffer_pointer; T buffer[BUFFER_SIZE]; } data; ALLOC alloc; blt::size_t used = 0; blt::size_t capacity = BUFFER_SIZE; }; } #endif //BLT_VECTOR_H