BLT/include/blt/std/vector.h

613 lines
20 KiB
C
Raw Normal View History

2024-02-20 15:18:05 -05:00
/*
* <Short Description>
* 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 <https://www.gnu.org/licenses/>.
*/
#ifndef BLT_VECTOR_H
#define BLT_VECTOR_H
#include <iterator>
#include <blt/std/memory_util.h>
2024-02-29 10:14:03 -05:00
#include <blt/std/allocator.h>
2024-02-29 15:07:56 -05:00
#include <blt/compatibility.h>
2024-02-21 13:18:05 -05:00
#include "ranges.h"
2024-02-21 20:24:00 -05:00
#include <stdexcept>
2024-06-30 03:20:47 -04:00
#include <array>
2024-02-20 15:18:05 -05:00
namespace blt
{
template<typename T, size_t MAX_SIZE>
class static_vector
{
private:
2024-06-30 03:20:47 -04:00
std::array<T, MAX_SIZE> buffer_;
2024-02-20 15:18:05 -05:00
size_t size_ = 0;
2024-06-30 03:20:47 -04:00
using value_type = T;
using reference = T&;
using pointer = T*;
using const_reference = const T&;
using const_pointer = const T*;
using iterator = blt::ptr_iterator<T>;
using const_iterator = blt::ptr_iterator<const T>;
2024-02-20 15:18:05 -05:00
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_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;
}
2024-06-30 03:20:47 -04:00
//TODO: replace with placement new / byte data
template<typename... Args>
constexpr inline bool emplace_back(Args&& ... args)
2024-02-20 15:18:05 -05:00
{
2024-06-30 03:20:47 -04:00
if (size_ >= MAX_SIZE)
return false;
buffer_[size_++] = T{std::forward<Args>(args)...};
return true;
2024-02-20 15:18:05 -05:00
}
2024-06-30 03:20:47 -04:00
constexpr inline void pop_back()
2024-02-20 15:18:05 -05:00
{
2024-06-30 03:20:47 -04:00
size_--;
2024-02-20 15:18:05 -05:00
}
constexpr inline void reserve(size_t size)
{
if (size > MAX_SIZE)
size = MAX_SIZE;
size_ = size;
}
2024-06-30 03:20:47 -04:00
[[nodiscard]] constexpr inline bool empty() const
{
return size() == 0;
}
constexpr inline void clear()
{
for (auto& v : *this)
v = {};
size_ = 0;
}
2024-02-20 15:18:05 -05:00
[[nodiscard]] constexpr inline size_t size() const
{
return size_;
}
[[nodiscard]] constexpr inline size_t capacity() const
{
return MAX_SIZE;
}
2024-06-30 03:20:47 -04:00
[[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 pointer operator*()
{
return buffer_;
}
constexpr inline const_pointer operator*() const
2024-02-20 15:18:05 -05:00
{
return buffer_;
}
2024-06-30 03:20:47 -04:00
constexpr inline pointer data()
2024-02-20 15:18:05 -05:00
{
return buffer_;
}
2024-06-30 03:20:47 -04:00
constexpr inline const_pointer data() const
2024-02-20 15:18:05 -05:00
{
return buffer_;
}
2024-06-30 03:20:47 -04:00
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];
}
2024-04-01 08:31:20 -04:00
constexpr inline iterator begin() noexcept
2024-02-20 15:18:05 -05:00
{
return data();
}
2024-04-01 08:31:20 -04:00
constexpr inline iterator end() noexcept
2024-02-20 15:18:05 -05:00
{
return data() + size();
}
constexpr inline const_iterator cbegin() const noexcept
{
return data();
}
constexpr inline const_iterator cend() const noexcept
{
return data() + size();
}
2024-03-07 09:00:19 -05:00
constexpr inline reverse_iterator rbegin() const noexcept
2024-02-20 15:18:05 -05:00
{
return reverse_iterator{end()};
}
2024-03-07 09:00:19 -05:00
constexpr inline reverse_iterator rend() const noexcept
2024-02-20 15:18:05 -05:00
{
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()};
}
2024-06-30 03:20:47 -04:00
template<typename G, std::enable_if_t<std::is_convertible_v<G, T>, 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<typename T1, blt::size_t size1, typename T2, blt::size_t size2, std::enable_if_t<std::is_convertible_v<T1, T2>, bool> = true>
constexpr friend bool operator==(const static_vector<T1, size1>& v1, const static_vector<T2, size2>& v2)
{
if (v1.size() != v2.size())
return false;
2024-06-30 13:26:54 -04:00
for (blt::size_t i = 0; i < v1.size(); i++)
{
if (v1[i] != v2[i])
return false;
2024-06-30 13:26:54 -04:00
}
return true;
}
2024-02-20 15:18:05 -05:00
};
2024-02-21 13:18:05 -05:00
template<typename T, typename ALLOC = std::allocator<T>>
2024-02-20 15:18:05 -05:00
class vector
{
private:
ALLOC allocator;
T* buffer_;
size_t capacity_ = 0;
size_t size_ = 0;
2024-02-21 13:18:05 -05:00
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 = blt::ptr_iterator<T>;
using const_iterator = blt::ptr_iterator<const T>;
2024-02-20 15:18:05 -05:00
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_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_);
}
2024-02-21 13:18:05 -05:00
template<typename G, std::enable_if_t<std::is_convertible_v<G, T>, bool> = true>
2024-02-29 09:10:29 -05:00
constexpr vector(std::initializer_list<G>&& list): capacity_(list.size()), size_(list.size())
2024-02-21 13:18:05 -05:00
{
buffer_ = allocator.allocate(capacity_);
for (auto e : blt::enumerate(list))
buffer_[e.first] = e.second;
}
template<typename G, std::enable_if_t<std::is_same_v<blt::vector<T>, G> || std::is_same_v<std::vector<T>, 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<typename G, std::enable_if_t<std::is_same_v<blt::vector<T>, G> || std::is_same_v<std::vector<T>, G>, bool> = true>
2024-02-29 09:10:29 -05:00
constexpr explicit vector(G&& move): buffer_(move.buffer_), capacity_(move.capacity()), size_(move.size())
2024-02-21 13:18:05 -05:00
{
move.buffer_ = nullptr;
}
2024-02-29 15:07:56 -05:00
BLT_CPP20_CONSTEXPR ~vector()
2024-02-20 15:18:05 -05:00
{
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<typename... Args>
2024-02-21 13:18:05 -05:00
constexpr inline void emplace_back(Args&& ... args)
2024-02-20 15:18:05 -05:00
{
if (size_ >= capacity_)
expand();
2024-02-21 13:18:05 -05:00
new(&buffer_[size_++]) T(std::forward<Args>(args)...);
2024-02-20 15:18:05 -05:00
}
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];
}
2024-02-21 13:18:05 -05:00
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];
}
2024-02-20 15:18:05 -05:00
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)
{
2024-02-20 15:22:15 -05:00
expand(size);
2024-02-20 15:18:05 -05:00
}
[[nodiscard]] constexpr inline size_t size() const
{
return size_;
}
[[nodiscard]] constexpr inline size_t capacity() const
{
return capacity_;
}
2024-02-21 13:55:56 -05:00
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];
}
2024-02-20 15:18:05 -05:00
constexpr inline T* data()
{
return buffer_;
}
constexpr inline T* operator*()
{
return buffer_;
}
constexpr inline T* data() const
{
return buffer_;
}
2024-02-21 13:55:56 -05:00
[[nodiscard]] constexpr inline bool empty() const
{
return size_ == 0;
}
template<typename G, std::enable_if_t<std::is_convertible_v<G, T>, bool> = true>
constexpr iterator insert(const_iterator pos, G&& ref)
{
2024-02-21 15:31:22 -05:00
difference_type loc = pos - buffer_;
2024-02-21 13:55:56 -05:00
if (size_ + 1 >= capacity_)
expand();
2024-02-21 15:31:22 -05:00
for (auto insert = end() - 1; (insert - buffer_) != loc - 1; insert--)
2024-02-21 13:55:56 -05:00
{
auto new_pos = insert + 1;
*new_pos = *insert;
}
2024-02-21 15:31:22 -05:00
buffer_[loc] = ref;
2024-02-21 13:55:56 -05:00
size_++;
2024-02-21 15:31:22 -05:00
return buffer_ + loc;
2024-02-21 13:55:56 -05:00
}
constexpr iterator erase(const_iterator pos)
{
2024-02-21 15:31:22 -05:00
difference_type loc = pos - buffer_;
2024-02-21 13:55:56 -05:00
2024-02-21 15:31:22 -05:00
for (auto fetch = begin() + loc + 1; fetch != end(); fetch++)
2024-02-21 13:55:56 -05:00
{
auto insert = fetch - 1;
*insert = *fetch;
}
size_--;
2024-02-21 15:31:22 -05:00
return buffer_ + loc + 1;
2024-02-21 13:55:56 -05:00
}
constexpr iterator erase(const_iterator first, const_iterator last)
{
2024-02-21 15:31:22 -05:00
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++)
2024-02-21 13:55:56 -05:00
{
2024-02-21 15:31:22 -05:00
*insert = *fetch;
2024-02-21 13:55:56 -05:00
}
2024-02-21 15:31:22 -05:00
size_ -= remove_amount;
return buffer_ + first_pos + 1;
2024-02-21 13:55:56 -05:00
}
2024-02-21 15:31:22 -05:00
constexpr inline iterator begin() const noexcept
2024-02-20 15:18:05 -05:00
{
return data();
}
2024-02-21 15:31:22 -05:00
constexpr inline iterator end() const noexcept
2024-02-20 15:18:05 -05:00
{
return data() + size();
}
constexpr inline const_iterator cbegin() const noexcept
{
return data();
}
constexpr inline const_iterator cend() const noexcept
{
return data() + size();
}
2024-02-21 15:31:22 -05:00
constexpr inline reverse_iterator rbegin() const noexcept
2024-02-20 15:18:05 -05:00
{
return reverse_iterator{end()};
}
2024-02-21 15:31:22 -05:00
constexpr inline reverse_iterator rend() const noexcept
2024-02-20 15:18:05 -05:00
{
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()};
}
};
2024-06-30 13:26:54 -04:00
template<typename T, blt::size_t BUFFER_SIZE = 8 / sizeof(T), typename ALLOC = std::allocator<T>>
class svo_vector
{
public:
2024-06-30 13:54:10 -04:00
constexpr svo_vector() = default;
template<typename G, std::enable_if_t<std::is_convertible_v<G, T>, bool> = true>
constexpr svo_vector(std::initializer_list<G> 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<typename G, std::enable_if_t<std::is_same_v<blt::vector<T>, G> || std::is_same_v<std::vector<T>, 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();
}
}
2024-06-30 13:26:54 -04:00
private:
2024-06-30 13:54:10 -04:00
union buffer_data
{
2024-06-30 13:26:54 -04:00
T* buffer_pointer;
T buffer[BUFFER_SIZE];
2024-06-30 13:54:10 -04:00
} data;
ALLOC alloc;
blt::size_t used = 0;
blt::size_t capacity = BUFFER_SIZE;
2024-06-30 13:26:54 -04:00
};
2024-02-20 15:18:05 -05:00
}
#endif //BLT_VECTOR_H