383 lines
7.0 KiB
C++
383 lines
7.0 KiB
C++
/*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <algorithm>
|
|
#include <iterator>
|
|
#include <initializer_list>
|
|
#include <cstddef>
|
|
#include <cinttypes>
|
|
#include <vector>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <span>
|
|
|
|
namespace OpenVulkano
|
|
{
|
|
/**
|
|
* Heap allocated array. For all these times you need a fixed size data structure, but don't know the size at compile time.
|
|
* @tparam T The type to be used
|
|
*/
|
|
template<typename T>
|
|
class Array
|
|
{
|
|
size_t size;
|
|
T* data;
|
|
|
|
void ClearData()
|
|
{
|
|
if constexpr (!std::is_trivially_destructible_v<T>)
|
|
{
|
|
// Call the destructor on all the members in reverse order
|
|
for (size_t i = size; i > 0;)
|
|
{
|
|
data[--i].~T();
|
|
}
|
|
}
|
|
}
|
|
|
|
static T* MakeBuffer(size_t count)
|
|
{
|
|
return static_cast<T*>(::operator new(sizeof(T) * count));
|
|
}
|
|
|
|
public:
|
|
using iterator = T*;
|
|
using const_iterator = const T*;
|
|
using reverse_iterator = std::reverse_iterator<iterator>;
|
|
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
|
|
|
Array() noexcept: size(0), data(nullptr)
|
|
{}
|
|
|
|
explicit Array(size_t size) : size(size), data(MakeBuffer(size))
|
|
{
|
|
for(size_t i = 0; i < size; i++)
|
|
{
|
|
new (&data[i]) T();
|
|
}
|
|
}
|
|
|
|
Array(const std::initializer_list<T>& list) : size(list.size()), data(MakeBuffer(list.size()))
|
|
{
|
|
int i = 0;
|
|
for(const T& e : list)
|
|
{
|
|
new (&data[i++]) T(e);
|
|
}
|
|
}
|
|
|
|
Array(const Array<T>& other) : size(other.Size()), data(MakeBuffer(other.Size()))
|
|
{
|
|
for(size_t i = 0; i < size; i++)
|
|
{
|
|
new (&data[i]) T(other[i]);
|
|
}
|
|
}
|
|
|
|
Array(Array<T>&& other) noexcept:
|
|
size(other.size), data(other.data)
|
|
{
|
|
other.data = nullptr;
|
|
other.size = 0;
|
|
}
|
|
|
|
explicit Array(const std::vector<T>& vector) : size(vector.size()), data(MakeBuffer(size))
|
|
{
|
|
int i = 0;
|
|
for (const auto& e : vector)
|
|
{
|
|
new (&data[i++]) T(e);
|
|
}
|
|
}
|
|
|
|
Array(size_t size, const T& defaultValue) : size(size), data(MakeBuffer(size))
|
|
{
|
|
Fill(defaultValue);
|
|
}
|
|
|
|
template<typename... ARGS>
|
|
Array(size_t size, ARGS... args) : size(size), data(MakeBuffer(size))
|
|
{
|
|
for(size_t i = 0; i < size; i++)
|
|
{
|
|
new (&data[i]) T(args...);
|
|
}
|
|
}
|
|
|
|
template<typename InputIt, std::enable_if_t<!std::is_same_v<typename std::iterator_traits<InputIt>::value_type, void> &&
|
|
!std::is_convertible_v<InputIt, size_t>, int> = 0>
|
|
Array(InputIt first, InputIt last) : size(std::distance(first, last)), data(MakeBuffer(size))
|
|
{
|
|
size_t i = 0;
|
|
while (first != last)
|
|
{
|
|
new (&data[i++]) T(*first);
|
|
++first;
|
|
}
|
|
}
|
|
|
|
~Array() noexcept
|
|
{
|
|
ClearData();
|
|
::operator delete(data);
|
|
}
|
|
|
|
Array& operator=(const Array<T>& rhs)
|
|
{
|
|
if (this == &rhs) return *this;
|
|
Resize(rhs.Size());
|
|
for(size_t i = 0; i < size; i++)
|
|
{
|
|
new (&data[i]) T(rhs[i]);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
Array& operator=(const std::initializer_list<T>& list)
|
|
{
|
|
Resize(list.size());
|
|
int i = 0;
|
|
for(const T& e : list)
|
|
{
|
|
new (&data[i++]) T(e);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
Array& operator=(Array<T>&& rhs) noexcept
|
|
{
|
|
if (this != &rhs)
|
|
{
|
|
Array(std::move(rhs)).Swap(*this);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
T& operator[](size_t n)
|
|
{
|
|
return data[n];
|
|
}
|
|
|
|
const T& operator[](size_t n) const
|
|
{
|
|
return data[n];
|
|
}
|
|
|
|
operator bool() const noexcept
|
|
{
|
|
return data;
|
|
}
|
|
|
|
operator std::span<T>() const noexcept
|
|
{
|
|
return { data, size };
|
|
}
|
|
|
|
iterator Begin() noexcept
|
|
{
|
|
return data;
|
|
}
|
|
|
|
const_iterator Begin() const noexcept
|
|
{
|
|
return data;
|
|
}
|
|
|
|
iterator begin() noexcept
|
|
{
|
|
return data;
|
|
}
|
|
|
|
const_iterator begin() const noexcept
|
|
{
|
|
return data;
|
|
}
|
|
|
|
iterator End() noexcept
|
|
{
|
|
return Begin() + Size();
|
|
}
|
|
|
|
const_iterator End() const noexcept
|
|
{
|
|
return Begin() + Size();
|
|
}
|
|
|
|
iterator end() noexcept
|
|
{
|
|
return Begin() + Size();
|
|
}
|
|
|
|
const_iterator end() const noexcept
|
|
{
|
|
return Begin() + Size();
|
|
}
|
|
|
|
reverse_iterator ReverseBegin() noexcept
|
|
{
|
|
return reverse_iterator(End());
|
|
}
|
|
|
|
const_reverse_iterator ReverseBegin() const noexcept
|
|
{
|
|
return const_reverse_iterator(End());
|
|
}
|
|
|
|
reverse_iterator ReverseEnd() noexcept
|
|
{
|
|
return reverse_iterator(Begin());
|
|
}
|
|
|
|
const_reverse_iterator ReverseEnd() const noexcept
|
|
{
|
|
return const_reverse_iterator(Begin());
|
|
}
|
|
|
|
const_iterator ConstBegin() const noexcept
|
|
{
|
|
return Begin();
|
|
}
|
|
|
|
const_iterator ConstEnd() const noexcept
|
|
{
|
|
return End();
|
|
}
|
|
|
|
const_reverse_iterator ConstReverseBegin() const noexcept
|
|
{
|
|
return ReverseBegin();
|
|
}
|
|
|
|
const_reverse_iterator ConstReverseEnd() const noexcept
|
|
{
|
|
return ReverseEnd();
|
|
}
|
|
|
|
[[nodiscard]] size_t Size() const noexcept
|
|
{
|
|
return size;
|
|
}
|
|
|
|
[[nodiscard]] size_t MaxSize() const noexcept
|
|
{
|
|
return size;
|
|
}
|
|
|
|
[[nodiscard]] bool Empty() const noexcept
|
|
{
|
|
return Size() == 0;
|
|
}
|
|
|
|
T& At(size_t n)
|
|
{
|
|
if (n >= Size()) throw std::out_of_range("Index " + std::to_string(n) + " is out of bounds.");
|
|
return data[n];
|
|
}
|
|
|
|
const T& At(size_t n) const
|
|
{
|
|
return const_cast<Array*>(this)->At(n);
|
|
}
|
|
|
|
T& Front() noexcept
|
|
{
|
|
return *Begin();
|
|
}
|
|
|
|
const T& Front() const noexcept
|
|
{
|
|
return *ConstBegin();
|
|
}
|
|
|
|
T& Back() noexcept
|
|
{
|
|
return *(End() - 1);
|
|
}
|
|
|
|
const T& Back() const noexcept
|
|
{
|
|
return *(ConstEnd() - 1);
|
|
}
|
|
|
|
T* Data() noexcept
|
|
{
|
|
return data;
|
|
}
|
|
|
|
const T* Data() const noexcept
|
|
{
|
|
return data;
|
|
}
|
|
|
|
void Fill(const T& value) noexcept(noexcept(std::fill(Begin(), End(), value)))
|
|
{
|
|
std::fill(Begin(), End(), value);
|
|
}
|
|
|
|
void Swap(Array<T>& other) noexcept
|
|
{
|
|
std::swap(data, other.data);
|
|
std::swap(size, other.size);
|
|
}
|
|
|
|
std::span<const uint8_t> AsBytes() const noexcept
|
|
{
|
|
return { reinterpret_cast<const uint8_t*>(data), size * sizeof(T) };
|
|
}
|
|
private:
|
|
void Resize(size_t newSize)
|
|
{
|
|
ClearData();
|
|
if (Size() != newSize)
|
|
{
|
|
::operator delete(data);
|
|
data = MakeBuffer(newSize);
|
|
size = newSize;
|
|
}
|
|
}
|
|
};
|
|
|
|
template<typename T>
|
|
inline bool operator==(const Array<T>& lhs, const Array<T>& rhs)
|
|
{
|
|
return lhs.Size() == rhs.Size() && std::equal(lhs.begin(), lhs.end(), rhs.begin());
|
|
}
|
|
|
|
template<typename T>
|
|
inline bool operator!=(const Array<T>& lhs, const Array<T>& rhs)
|
|
{
|
|
return !(lhs == rhs);
|
|
}
|
|
|
|
template<typename T>
|
|
inline bool operator<(const Array<T>& lhs, const Array<T>& rhs)
|
|
{
|
|
return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
|
|
}
|
|
|
|
template<typename T>
|
|
inline bool operator>(const Array<T>& lhs, const Array<T>& rhs)
|
|
{
|
|
return rhs < lhs;
|
|
}
|
|
|
|
template<typename T>
|
|
inline bool operator>=(const Array<T>& lhs, const Array<T>& rhs)
|
|
{
|
|
return !(lhs < rhs);
|
|
}
|
|
|
|
template<typename T>
|
|
inline bool operator<=(const Array<T>& lhs, const Array<T>& rhs)
|
|
{
|
|
return !(rhs < lhs);
|
|
}
|
|
|
|
template<typename InputIt>
|
|
Array(InputIt first, InputIt last) -> Array<typename std::iterator_traits<InputIt>::value_type>;
|
|
} |