233 lines
4.6 KiB
C++
233 lines
4.6 KiB
C++
// SPDX-License-Identifier: BSD-2-Clause
|
|
// author: Max Kellermann <max.kellermann@gmail.com>
|
|
|
|
#pragma once
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cassert>
|
|
#include <initializer_list>
|
|
#include <new>
|
|
#include <span>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
/**
|
|
* An array with a maximum size known at compile time. It keeps track
|
|
* of the actual length at runtime.
|
|
*/
|
|
template<class T, std::size_t max>
|
|
class StaticVector {
|
|
using Storage = std::aligned_storage_t<sizeof(T), alignof(T)>;
|
|
using Array = std::array<Storage, max>;
|
|
|
|
public:
|
|
using size_type = typename Array::size_type;
|
|
using value_type = T;
|
|
using reference = T &;
|
|
using const_reference = const T &;
|
|
using pointer = T *;
|
|
using const_pointer = const T *;
|
|
using iterator = pointer;
|
|
using const_iterator = const_pointer;
|
|
|
|
private:
|
|
size_type the_size = 0;
|
|
Array array;
|
|
|
|
static constexpr pointer Launder(Storage *storage) noexcept {
|
|
return std::launder(reinterpret_cast<pointer>(storage));
|
|
}
|
|
|
|
static constexpr const_pointer Launder(const Storage *storage) noexcept {
|
|
return std::launder(reinterpret_cast<const_pointer>(storage));
|
|
}
|
|
|
|
public:
|
|
constexpr StaticVector() noexcept = default;
|
|
|
|
constexpr StaticVector(size_type _size, const_reference value)
|
|
{
|
|
if (_size > max)
|
|
throw std::bad_alloc{};
|
|
|
|
while (_size-- > 0)
|
|
push_back(value);
|
|
}
|
|
|
|
/**
|
|
* Initialise the array with values from the iterator range.
|
|
*/
|
|
template<typename I>
|
|
constexpr StaticVector(I _begin, I _end)
|
|
:the_size(0)
|
|
{
|
|
for (I i = _begin; i != _end; ++i)
|
|
push_back(*i);
|
|
}
|
|
|
|
template<typename U>
|
|
constexpr StaticVector(std::initializer_list<U> init) noexcept
|
|
:the_size(init.size())
|
|
{
|
|
static_assert(init.size() <= max);
|
|
|
|
for (auto &i : init)
|
|
emplace_back(std::move(i));
|
|
}
|
|
|
|
constexpr ~StaticVector() noexcept {
|
|
clear();
|
|
}
|
|
|
|
constexpr operator std::span<const T>() const noexcept {
|
|
return {
|
|
Launder(array.data()),
|
|
the_size,
|
|
};
|
|
}
|
|
|
|
constexpr operator std::span<T>() noexcept {
|
|
return {
|
|
Launder(array.data()),
|
|
the_size,
|
|
};
|
|
}
|
|
|
|
constexpr size_type max_size() const noexcept {
|
|
return max;
|
|
}
|
|
|
|
constexpr size_type size() const noexcept {
|
|
return the_size;
|
|
}
|
|
|
|
constexpr bool empty() const noexcept {
|
|
return the_size == 0;
|
|
}
|
|
|
|
constexpr bool full() const noexcept {
|
|
return the_size == max;
|
|
}
|
|
|
|
constexpr void clear() noexcept {
|
|
if constexpr (std::is_trivially_destructible_v<T>) {
|
|
/* we don't need to call any destructor */
|
|
the_size = 0;
|
|
} else {
|
|
while (!empty())
|
|
pop_back();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns one element. No bounds checking.
|
|
*/
|
|
constexpr reference operator[](size_type i) noexcept {
|
|
assert(i < size());
|
|
|
|
return *Launder(&array[i]);
|
|
}
|
|
|
|
/**
|
|
* Returns one constant element. No bounds checking.
|
|
*/
|
|
constexpr const_reference operator[](size_type i) const noexcept {
|
|
assert(i < size());
|
|
|
|
return *Launder(&array[i]);
|
|
}
|
|
|
|
constexpr reference front() noexcept {
|
|
assert(!empty());
|
|
|
|
return *Launder(&array.front());
|
|
}
|
|
|
|
constexpr const_reference front() const noexcept {
|
|
assert(!empty());
|
|
|
|
return *Launder(&array.front());
|
|
}
|
|
|
|
constexpr reference back() noexcept {
|
|
assert(!empty());
|
|
|
|
return *Launder(&array[the_size - 1]);
|
|
}
|
|
|
|
constexpr const_reference back() const noexcept {
|
|
assert(!empty());
|
|
|
|
return *Launder(&array[the_size - 1]);
|
|
}
|
|
|
|
constexpr iterator begin() noexcept {
|
|
return Launder(&array.front());
|
|
}
|
|
|
|
constexpr const_iterator begin() const noexcept {
|
|
return Launder(&array.front());
|
|
}
|
|
|
|
constexpr iterator end() noexcept {
|
|
return std::next(begin(), the_size);
|
|
}
|
|
|
|
constexpr const_iterator end() const noexcept {
|
|
return std::next(begin(), the_size);
|
|
}
|
|
|
|
/**
|
|
* Return address of start of data segment.
|
|
*/
|
|
constexpr pointer data() noexcept {
|
|
return Launder(array.data());
|
|
}
|
|
|
|
constexpr const_pointer data() const noexcept {
|
|
return Launder(array.data());
|
|
}
|
|
|
|
constexpr reference push_back(const_reference value) {
|
|
return emplace_back(value);
|
|
}
|
|
|
|
constexpr reference push_back(T &&value) {
|
|
return emplace_back(std::move(value));
|
|
}
|
|
|
|
template<typename... Args>
|
|
constexpr reference emplace_back(Args&&... args) {
|
|
if (full())
|
|
throw std::bad_alloc{};
|
|
|
|
::new(&array[the_size]) T(std::forward<Args>(args)...);
|
|
return *Launder(&array[the_size++]);
|
|
}
|
|
|
|
constexpr void pop_front() noexcept {
|
|
assert(!empty());
|
|
|
|
erase(begin());
|
|
}
|
|
|
|
constexpr void pop_back() noexcept {
|
|
assert(!empty());
|
|
|
|
back().~T();
|
|
--the_size;
|
|
}
|
|
|
|
constexpr iterator erase(iterator first, iterator last) noexcept {
|
|
std::size_t n = std::distance(first, last);
|
|
std::move(last, end(), first);
|
|
the_size -= n;
|
|
return first;
|
|
}
|
|
|
|
constexpr iterator erase(iterator pos) noexcept {
|
|
return erase(pos, std::next(pos));
|
|
}
|
|
};
|