intial commit

This commit is contained in:
2023-05-29 14:51:44 +02:00
commit da781b87f2
38 changed files with 4842 additions and 0 deletions

View File

@@ -0,0 +1,237 @@
#pragma once
#if !defined(MIJIN_CONTAINER_BOXED_OBJECT_HPP_INCLUDED)
#define MIJIN_CONTAINER_BOXED_OBJECT_HPP_INCLUDED 1
#include <iterator>
#include <memory>
#include "../debug/assert.hpp"
namespace mijin
{
//
// public defines
//
#if !defined(MIJIN_BOXED_OBJECT_DEBUG)
#define MIJIN_BOXED_OBJECT_DEBUG MIJIN_DEBUG
#endif
//
// public constants
//
//
// public types
//
template<typename T>
class BoxedObject
{
private:
union {
T object_;
};
#if MIJIN_BOXED_OBJECT_DEBUG
bool constructed = false;
#endif
public:
BoxedObject() = default;
explicit BoxedObject(T object)
#if MIJIN_BOXED_OBJECT_DEBUG
: constructed(true)
#endif
{
std::construct_at(&object_, std::move(object));
}
BoxedObject(const BoxedObject&) = delete;
BoxedObject(BoxedObject&&) = delete;
#if MIJIN_BOXED_OBJECT_DEBUG
~BoxedObject()
{
MIJIN_ASSERT(!constructed, "BoxedObject::~BoxedObject(): Object has not been destroy prior to destructor!")
}
#endif
BoxedObject& operator=(const BoxedObject&) = delete;
BoxedObject& operator=(BoxedObject&&) = delete;
template<typename... TArgs>
void construct(TArgs&&... args)
{
#if MIJIN_BOXED_OBJECT_DEBUG
MIJIN_ASSERT(!constructed, "BoxedObject::construct(): Attempt to construct an already constructed object!")
constructed = true;
#endif
std::construct_at(&object_, std::forward<TArgs>(args)...);
}
void destroy()
{
#if MIJIN_BOXED_OBJECT_DEBUG
MIJIN_ASSERT(constructed, "BoxedObject::destroy(): Attempt to destroy a not constructed object!")
constructed = false;
#endif
std::destroy_at(&object_);
}
void copyTo(BoxedObject& other) const
{
#if MIJIN_BOXED_OBJECT_DEBUG
MIJIN_ASSERT(constructed, "BoxedObject::copy(): Attempt to copy a not constructed object!")
#endif
other.construct(object_);
}
void moveTo(BoxedObject& other)
{
#if MIJIN_BOXED_OBJECT_DEBUG
MIJIN_ASSERT(constructed, "BoxedObject::copy(): Attempt to copy a not constructed object!")
#endif
other.construct(std::move(object_));
destroy();
}
[[nodiscard]] T& get()
{
#if MIJIN_BOXED_OBJECT_DEBUG
MIJIN_ASSERT(constructed, "BoxedObject::get(): Attempt to access a not constructed object!")
#endif
return object_;
}
[[nodiscard]] const T& get() const
{
#if MIJIN_BOXED_OBJECT_DEBUG
MIJIN_ASSERT(constructed, "BoxedObject::get(): Attempt to access a not constructed object!")
#endif
return object_;
}
};
template<typename T>
class BoxedObjectIterator
{
public:
using difference_type = std::ptrdiff_t;
using value_type = T;
private:
BoxedObject<T>* box_ = nullptr;
#if MIJIN_CHECKED_ITERATORS
BoxedObject<T>* start_ = nullptr;
BoxedObject<T>* end_ = nullptr;
#endif
public:
BoxedObjectIterator() = default;
explicit constexpr BoxedObjectIterator(BoxedObject<T>* box, BoxedObject<T>* start, BoxedObject<T>* end)
: box_(box)
#if MIJIN_CHECKED_ITERATORS
, start_(start), end_(end)
#endif
{}
BoxedObjectIterator(const BoxedObjectIterator&) = default;
BoxedObjectIterator(BoxedObjectIterator&&) noexcept = default;
BoxedObjectIterator& operator=(const BoxedObjectIterator&) = default;
BoxedObjectIterator& operator=(BoxedObjectIterator&&) noexcept = default;
BoxedObjectIterator& operator+=(difference_type diff);
BoxedObjectIterator& operator-=(difference_type diff) { return (*this += -diff); }
constexpr auto operator<=>(const BoxedObjectIterator& other) const noexcept = default;
[[nodiscard]] T& operator*() const;
[[nodiscard]] T* operator->() const;
BoxedObjectIterator& operator++();
BoxedObjectIterator operator++(int);
BoxedObjectIterator& operator--();
BoxedObjectIterator operator--(int);
[[nodiscard]] difference_type operator-(const BoxedObjectIterator& other) const { return box_ - other.box_; }
[[nodiscard]] BoxedObjectIterator operator+(difference_type diff) const;
[[nodiscard]] BoxedObjectIterator operator-(difference_type diff) const { return (*this + -diff); }
[[nodiscard]] T& operator[](difference_type diff) const { return *(*this + diff); }
};
template<typename T>
inline BoxedObjectIterator<T> operator+(std::iter_difference_t<T> diff, const BoxedObjectIterator<T>& iter) {
return iter + diff;
}
static_assert(std::random_access_iterator<BoxedObjectIterator<int>>);
//
// public functions
//
template<typename T>
BoxedObjectIterator<T>& BoxedObjectIterator<T>::operator+=(difference_type diff)
{
#if MIJIN_CHECKED_ITERATORS
MIJIN_ASSERT(diff <= (end_ - box_) && diff >= (box_ - start_), "BoxedObjectIterator::operator+=(): Attempt to iterate out of range.");
#endif
box_ += diff;
return *this;
}
template<typename T>
T& BoxedObjectIterator<T>::operator*() const
{
#if MIJIN_CHECKED_ITERATORS
MIJIN_ASSERT(box_ >= start_ && box < end_, "BoxedObjectIterator::operator->(): Attempt to dereference out-of-range iterator.");
#endif
return box_->get();
}
template<typename T>
BoxedObjectIterator<T>& BoxedObjectIterator<T>::operator++()
{
#if MIJIN_CHECKED_ITERATORS
MIJIN_ASSERT(box_ < end_, "BoxedObjectIterator::operator++(): Attempt to iterator past the end.");
#endif
++box_;
}
template<typename T>
BoxedObjectIterator<T> BoxedObjectIterator<T>::operator++(int)
{
#if MIJIN_CHECKED_ITERATORS
MIJIN_ASSERT(box_ < end_, "BoxedObjectIterator::operator++(int): Attempt to iterator past the end.");
#endif
BoxedObjectIterator copy(*this);
++box_;
return copy;
}
template<typename T>
BoxedObjectIterator<T>& BoxedObjectIterator<T>::operator--()
{
#if MIJIN_CHECKED_ITERATORS
MIJIN_ASSERT(box_ > start_, "BoxedObjectIterator::operator--(): Attempt to iterator past start.");
#endif
--box_;
}
template<typename T>
BoxedObjectIterator<T> BoxedObjectIterator<T>::operator--(int)
{
#if MIJIN_CHECKED_ITERATORS
MIJIN_ASSERT(box_ > start_, "BoxedObjectIterator::operator--(int): Attempt to iterator past start.");
#endif
BoxedObjectIterator copy(*this);
--box_;
return copy;
}
template<typename T>
BoxedObjectIterator<T> BoxedObjectIterator<T>::operator+(difference_type diff) const
{
BoxedObjectIterator copy(*this);
copy += diff;
return copy;
}
} // namespace mijin
#endif // !defined(MIJIN_CONTAINER_BOXED_OBJECT_HPP_INCLUDED)

View File

@@ -0,0 +1,145 @@
#pragma once
#if !defined(MIJIN_CONTAINER_CACHED_ARRAY_HPP_INCLUDED)
#define MIJIN_CONTAINER_CACHED_ARRAY_HPP_INCLUDED 1
#include <algorithm>
#include <array>
#include <cstddef>
#include <limits>
#include <optional>
#include <vector>
#include "../debug/assert.hpp"
namespace mijin
{
//
// public defines
//
//
// public constants
//
//
// public types
//
template<typename T, std::size_t cacheSize = 32>
class CachedArray
{
public:
using element_type = T;
using value_type = std::remove_cv_t<T>;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using pointer = T*;
using const_pointer = const T*;
using reference = T&;
using const_reference = const T&;
using iterator = T*;
using const_iterator = const T*;
static constexpr std::size_t INVALID_INDEX = std::numeric_limits<size_type>::max();
private:
struct CacheEntry
{
size_type index = INVALID_INDEX;
std::size_t useIndex = 0;
std::optional<T> value;
};
mutable std::array<CacheEntry, cacheSize> cache_;
std::vector<std::optional<T>> backbuffer_;
std::size_t nextUseIndex_ = 1;
public:
[[nodiscard]] reference at(size_type index);
[[nodiscard]] const_reference at(size_type index) const;
[[nodiscard]] size_type size() const { return backbuffer_.size(); }
void resize(size_type newSize);
void reserve(size_type elements);
// TODO: make iterable (not that easy if we move from the backbuffer!)
// [[nodiscard]] iterator begin() { return &backbuffer_[0]; }
// [[nodiscard]] const_iterator begin() const { return &backbuffer_[0]; }
// [[nodiscard]] const_iterator cbegin() const { return &backbuffer_[0]; }
// [[nodiscard]] iterator end() { return begin() + size(); }
// [[nodiscard]] const_iterator end() const { return begin() + size(); }
// [[nodiscard]] const_iterator cend() const { return begin() + size(); }
[[nodiscard]] reference operator[](size_type index) { return at(index); }
[[nodiscard]] const_reference operator[](size_type index) const { return at(index); }
private:
// TODO: use deducing this once it is available
template<typename TSelf>
static auto atImpl(TSelf&& self, std::size_t index);
};
//
// public functions
//
template<typename T, std::size_t cacheSize>
auto CachedArray<T, cacheSize>::at(std::size_t index) -> reference
{
return atImpl(*this, index);
}
template<typename T, std::size_t cacheSize>
auto CachedArray<T, cacheSize>::at(std::size_t index) const -> const_reference
{
return atImpl(*this, index);
}
template<typename T, std::size_t cacheSize>
void CachedArray<T, cacheSize>::resize(size_type newSize)
{
if (newSize < size())
{
// invalidate any cache entries that shouldn't exist anymore
for (CacheEntry& entry : cache_)
{
if (entry.index < newSize)
{
entry.value = std::nullopt;
entry.useIndex = 0;
}
}
}
backbuffer_.resize(newSize);
}
template<typename T, std::size_t cacheSize>
template<typename TSelf>
auto CachedArray<T, cacheSize>::atImpl(TSelf&& self, std::size_t index)
{
MIJIN_ASSERT(index < size(), "CachedArray::at(): Attempting to access index out of range.");
// first try the cache
for (CacheEntry& entry : self.cache_)
{
if (entry.index == index)
{
entry.useIndex = self.nextUseIndex_++;
return *entry.value;
}
}
// otherwise copy from backbuffer to cache
auto itCache = std::min_element(self.cache_.begin(), self.cache_.end(), [](const CacheEntry& left, const CacheEntry& right) {
return left.useIndex < right.useIndex;
});
if (itCache->index != INVALID_INDEX)
{
// move back to the backbuffer
self.backbuffer_[itCache->index] = std::move(itCache->value);
}
itCache->index = index;
itCache->value = std::move(self.backbuffer_.at(index));
itCache->useIndex = self.nextUseIndex_++;
return *itCache->value;
}
} // namespace mijin
#endif // !defined(MIJIN_CONTAINER_CACHED_ARRAY_HPP_INCLUDED)

View File

@@ -0,0 +1,133 @@
#pragma once
#if !defined(MIJIN_CONTAINER_INLINE_ARRAY_HPP_INCLUDED)
#define MIJIN_CONTAINER_INLINE_ARRAY_HPP_INCLUDED 1
#include "./boxed_object.hpp"
namespace mijin
{
//
// public defines
//
//
// public constants
//
//
// public types
//
template<typename T, std::size_t maxSize>
class InlineArray
{
public:
using element_type = T;
using value_type = std::remove_cv_t<T>;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using pointer = T*;
using const_pointer = const T*;
using reference = T&;
using const_reference = const T&;
using iterator = BoxedObjectIterator<T>;
using const_iterator = BoxedObjectIterator<const T>;
private:
BoxedObject<T> elements_[maxSize]; // NOLINT(modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays)
size_type size_ = 0;
public:
InlineArray() = default;
InlineArray(const InlineArray& other);
InlineArray(InlineArray&& other) noexcept;
~InlineArray();
reference operator[](size_type index);
const_reference operator[](size_type index) const;
[[nodiscard]] iterator begin() { return BoxedObjectIterator<T>(&elements_[0], &elements_[0], &elements_[size_]); }
[[nodiscard]] const_iterator begin() const { return BoxedObjectIterator<const T>(&elements_[0], &elements_[0], &elements_[size_]); }
[[nodiscard]] const_iterator cbegin() const { return BoxedObjectIterator<const T>(&elements_[0], &elements_[0], &elements_[size_]); }
[[nodiscard]] iterator end() { return BoxedObjectIterator<T>(&elements_[size_], &elements_[0], &elements_[size_]); }
[[nodiscard]] const_iterator end() const { return BoxedObjectIterator<const T>(&elements_[size_], &elements_[0], &elements_[size_]); }
[[nodiscard]] const_iterator cend() const { return BoxedObjectIterator<const T>(&elements_[size_], &elements_[0], &elements_[size_]); }
[[nodiscard]] constexpr size_type size() const { return size_; }
[[nodiscard]] constexpr size_type capacity() const { return maxSize; }
[[nodiscard]] constexpr bool empty() const { return size_ == 0; }
[[nodiscard]] constexpr reference at(size_type index) {
MIJIN_ASSERT(index < size_, "InlineArray::at() attempt to access out-of-bounds index.");
return elements_[index].get();
}
[[nodiscard]] constexpr const_reference at(size_type index) const {
MIJIN_ASSERT(index < size_, "InlineArray::at() attempt to access out-of-bounds index.");
return elements_[index].get();
}
void resize(size_type newSize);
void clear() { resize(0); }
};
//
// public functions
//
template<typename T, std::size_t numElements>
InlineArray<T, numElements>::InlineArray(const InlineArray& other) : size_(other.size_)
{
for (size_type idx = 0; idx < size_; ++idx) {
other.elements_[idx].copyTo(elements_[idx]);
}
}
template<typename T, std::size_t numElements>
InlineArray<T, numElements>::InlineArray(InlineArray&& other) noexcept : size_(other.size_)
{
for (size_type idx = 0; idx < size_; ++idx) {
other.elements_[idx].moveTo(elements_[idx]);
}
other.size_ = 0;
}
template<typename T, std::size_t numElements>
InlineArray<T, numElements>::~InlineArray()
{
for (std::size_t idx = 0; idx < size_; ++idx) {
elements_[idx].destroy();
}
}
template<typename T, std::size_t numElements>
auto InlineArray<T, numElements>::operator[](size_type index) -> reference
{
return at(index);
}
template<typename T, std::size_t numElements>
auto InlineArray<T, numElements>::operator[](size_type index) const -> const_reference
{
return at(index);
}
template<typename T, std::size_t numElements>
void InlineArray<T, numElements>::resize(size_type newSize)
{
MIJIN_ASSERT(newSize <= numElements, "InlineArray::resize(): Attempt to resize beyond maximum size.");
if (newSize < size_) {
for (size_type idx = newSize; idx < size_; ++idx) {
elements_[idx].destroy();
}
}
else {
for (size_type idx = size_; idx < newSize; ++idx) {
elements_[idx].construct();
}
}
size_ = newSize;
}
} // namespace mijin
#endif // !defined(MIJIN_CONTAINER_INLINE_ARRAY_HPP_INCLUDED)

View File

@@ -0,0 +1,90 @@
#pragma once
#if !defined(MIJIN_CONTAINER_MAP_VIEW_HPP_INCLUDED)
#define MIJIN_CONTAINER_MAP_VIEW_HPP_INCLUDED 1
#include <unordered_map>
namespace mijin
{
//
// public defines
//
//
// public constants
//
//
// public types
//
template<typename TKey, typename TValue, typename TMap = std::unordered_map<TKey, TValue>>
struct MapView
{
private:
TMap& map;
public:
MapView() = delete;
MapView(const MapView&) = default;
MapView(MapView&&) noexcept = default;
inline MapView(TMap& map_) : map(map_) {}
MapView& operator=(const MapView&) = default;
MapView& operator=(MapView&&) noexcept = default;
[[nodiscard]] TValue& operator[](const TKey& key)
{
return at(key);
}
[[nodiscard]] const TValue& operator[](const TKey& key) const
{
return at(key);
}
[[nodiscard]] TValue& at(const TKey& key)
{
return map.at(key);
}
[[nodiscard]] const TValue& at(const TKey& key) const
{
return map.at(key);
}
[[nodiscard]] auto begin() const
{
return map.begin();
}
[[nodiscard]] auto begin()
{
return map.begin();
}
[[nodiscard]] auto end()
{
return map.end();
}
[[nodiscard]] auto end() const
{
return map.end();
}
};
// template<typename TKey, typename TValue, template<typename, typename, typename...> typename TMap>
// MapView(TMap<TKey, TValue>& map) -> MapView<TKey, TValue, TMap<TKey, TValue>>;
template<typename TMap>
MapView(TMap& map) -> MapView<typename TMap::key_type, typename TMap::mapped_type, TMap>;
//
// public functions
//
} // namespace mijin
#endif // !defined(MIJIN_CONTAINER_MAP_VIEW_HPP_INCLUDED)

View File

@@ -0,0 +1,298 @@
#pragma once
#if !defined(MIJIN_CONTAINER_OPTIONAL_HPP_INCLUDED)
#define MIJIN_CONTAINER_OPTIONAL_HPP_INCLUDED 1
#include <cstdint>
#include <utility>
#include "../debug/assert.hpp"
#include "../util/concepts.hpp"
namespace mijin
{
//
// public defines
//
//
// public constants
//
//
// public types
//
namespace impl
{
template<typename T>
struct OptionalStorage
{
alignas(T) std::uint8_t data[sizeof(T)]; // NOLINT(modernize-avoid-c-arrays,cppcoreguidelines-avoid-c-arrays)
std::uint8_t used = 0;
[[nodiscard]]
constexpr bool empty() const noexcept { return !used; }
template<typename... TArgs>
constexpr void emplace(TArgs&&... args) noexcept
{
MIJIN_ASSERT(!used, "Attempting to emplace in an already set OptionalStorage!");
used = 1;
::new (data) T(std::forward<TArgs>(args)...);
}
void clear()
{
MIJIN_ASSERT(used, "Attempting to clear an empty OptionalStorage!");
get().~T();
used = 0;
}
[[nodiscard]] constexpr T& get() noexcept { return *reinterpret_cast<T*>(data); }
[[nodiscard]] constexpr const T& get() const noexcept { return *reinterpret_cast<const T*>(data); }
};
template<pointer_type T>
struct OptionalStorage<T>
{
static constexpr T invalidPointer() noexcept { return reinterpret_cast<T>(1); }
T ptr = invalidPointer();
[[nodiscard]]
constexpr bool empty() const noexcept { return ptr == invalidPointer(); }
void emplace(T value) noexcept
{
ptr = value;
}
void clear()
{
ptr = invalidPointer();
}
[[nodiscard]] T& get() noexcept { return ptr; }
[[nodiscard]] const T& get() const noexcept { return ptr; }
};
template<reference_type T>
struct OptionalStorage<T>
{
using pointer_t = std::remove_reference_t<T>*;
pointer_t ptr = nullptr;
[[nodiscard]]
constexpr bool empty() const noexcept { return ptr == nullptr; }
void emplace(T value) noexcept
{
ptr = &value;
}
void clear()
{
ptr = nullptr;
}
T get() noexcept { return *ptr; }
const T get() const noexcept { return *ptr; } // NOLINT(readability-const-return-type) T already is a reference
};
}
struct NullOptional {};
static constexpr NullOptional NULL_OPTIONAL;
template<typename TValue>
class Optional
{
private:
impl::OptionalStorage<TValue> storage_;
public:
constexpr Optional() = default;
constexpr Optional(NullOptional) noexcept {}
inline Optional(const Optional& other) noexcept;
inline Optional(Optional&& other) noexcept;
inline Optional(TValue value) noexcept;
inline ~Optional() noexcept;
public:
Optional& operator =(const Optional& other) noexcept;
Optional& operator =(Optional&& other) noexcept;
Optional& operator =(TValue value) noexcept;
Optional& operator =(NullOptional) noexcept;
public:
[[nodiscard]]
constexpr bool operator ==(NullOptional) const noexcept { return empty(); }
template<typename TOther>
[[nodiscard]]
constexpr bool operator ==(const Optional<TOther>& other) const noexcept;
template<typename T>
[[nodiscard]]
constexpr bool operator ==(const T& value) const noexcept;
template<typename T>
[[nodiscard]]
constexpr bool operator !=(const T& value) const noexcept { return !(*this == value); }
[[nodiscard]]
constexpr explicit operator bool() const noexcept { return !empty(); }
[[nodiscard]]
constexpr bool operator !() const noexcept { return empty(); }
public:
template<typename... Types>
void emplace(Types&&... params) noexcept;
public:
[[nodiscard]] inline std::remove_reference_t<TValue>& get() noexcept;
[[nodiscard]] inline const std::remove_reference_t<TValue>& get() const noexcept;
[[nodiscard]] constexpr bool empty() const noexcept { return storage_.empty(); }
inline void reset() noexcept;
};
//
// public functions
//
template<typename TValue>
Optional<TValue>::Optional(const Optional& other) noexcept
{
if (other) {
emplace(other.get());
}
}
template<typename TValue>
Optional<TValue>::Optional(Optional&& other) noexcept
{
if (other)
{
emplace(std::move(other.get()));
other.reset();
}
}
template<typename TValue>
Optional<TValue>::Optional(TValue value) noexcept
{
if constexpr (std::is_reference_v<TValue>) {
emplace(value);
}
else {
emplace(std::move(value));
}
}
template<typename TValue>
Optional<TValue>::~Optional() noexcept
{
reset();
}
template<typename TValue>
auto Optional<TValue>::operator =(const Optional& other) noexcept -> Optional&
{
if (&other == this) {
return *this;
}
reset();
if (other) {
emplace(other.get());
}
return *this;
}
template<typename TValue>
auto Optional<TValue>::operator =(Optional&& other) noexcept -> Optional&
{
if (&other == this) {
return *this;
}
reset();
if (other)
{
emplace(std::move(other.get()));
other.reset();
}
return *this;
}
template<typename TValue>
auto Optional<TValue>::operator =(TValue value) noexcept -> Optional&
{
if constexpr (std::is_reference_v<TValue>) {
emplace(value);
}
else {
emplace(std::move(value));
}
return *this;
}
template<typename TValue>
auto Optional<TValue>::operator =(NullOptional) noexcept -> Optional&
{
reset();
return *this;
}
template<typename TValue>
template<typename TOther>
constexpr bool Optional<TValue>::operator ==(const Optional<TOther>& other) const noexcept
{
if (empty())
{
return other.empty();
}
if (!other.empty())
{
return get() == other.get();
}
return false;
}
template<typename TValue>
template<typename T>
constexpr bool Optional<TValue>::operator ==(const T& value) const noexcept
{
if (empty())
{
return false;
}
return get() == value;
}
template<typename TValue>
template<typename... Types>
void Optional<TValue>::emplace(Types&&... params) noexcept
{
reset();
storage_.emplace(std::forward<Types>(params)...);
MIJIN_ASSERT(!empty(), "Something is wrong.");
}
template<typename TValue>
inline std::remove_reference_t<TValue>& Optional<TValue>::get() noexcept
{
MIJIN_ASSERT(!empty(), "Attempting to fetch value from empty Optional!");
return storage_.get();
}
template<typename TValue>
inline const std::remove_reference_t<TValue>& Optional<TValue>::get() const noexcept
{
MIJIN_ASSERT(!empty(), "Attempting to fetch value from empty Optional!");
return storage_.get();
}
template<typename TValue>
inline void Optional<TValue>::reset() noexcept
{
if (empty()) {
return;
}
storage_.clear();
}
} // namespace mijin
#endif // !defined(MIJIN_CONTAINER_OPTIONAL_HPP_INCLUDED)

View File

@@ -0,0 +1,104 @@
#pragma once
#if !defined(MIJIN_CONTAINER_TYPELESS_BUFFER_HPP_INCLUDED)
#define MIJIN_CONTAINER_TYPELESS_BUFFER_HPP_INCLUDED 1
#include <cstddef>
#include <vector>
#include "../debug/assert.hpp"
namespace mijin
{
//
// public defines
//
//
// public constants
//
//
// public types
//
template<typename T>
class BufferView;
class TypelessBuffer
{
public:
using size_type = std::size_t;
private:
std::vector<std::byte> bytes_;
public:
[[nodiscard]] void* data() { return bytes_.data(); }
[[nodiscard]] const void* data() const { return bytes_.data(); }
[[nodiscard]] size_type byteSize() const { return bytes_.size(); }
void resize(size_type numBytes) { bytes_.resize(numBytes); }
void reserve(size_type numBytes) { bytes_.reserve(numBytes); }
template<typename T>
[[nodiscard]] BufferView<T> makeBufferView() { return BufferView<T>(this); }
};
template<typename T>
class BufferView
{
public:
using element_type = T;
using value_type = std::remove_cv_t<T>;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using pointer = T*;
using const_pointer = const T*;
using reference = T&;
using const_reference = const T&;
using iterator = T*;
using const_iterator = const T*;
private:
class TypelessBuffer* buffer_ = nullptr;
public:
BufferView() = default;
explicit BufferView(class TypelessBuffer* buffer) : buffer_(buffer) {}
BufferView(const BufferView&) = default;
BufferView& operator=(const BufferView&) = default;
[[nodiscard]] inline size_type size() const { return buffer_ ? buffer_->byteSize() / sizeof(T) : 0; }
inline void resize(size_type numElements) {
MIJIN_ASSERT(buffer_, "BufferView::resize(): cannot resize a view without a buffer.");
buffer_->resize(numElements * sizeof(T));
}
inline void reserve(size_type numElements) {
MIJIN_ASSERT(buffer_, "BufferView::resize(): cannot resize a view without a buffer.");
buffer_->reserve(numElements * sizeof(T));
}
[[nodiscard]] inline iterator begin() { return buffer_ ? static_cast<T*>(buffer_->data()) : nullptr; }
[[nodiscard]] inline const_iterator begin() const { return buffer_ ? static_cast<T*>(buffer_->data()) : nullptr; }
[[nodiscard]] inline const_iterator cbegin() const { return begin(); }
[[nodiscard]] inline iterator end() { return buffer_ ? static_cast<T*>(buffer_->data()) + size() : nullptr; }
[[nodiscard]] inline const_iterator end() const { return buffer_ ? static_cast<T*>(buffer_->data()) + size() : nullptr; }
[[nodiscard]] inline const_iterator cend() const { return end(); }
[[nodiscard]] inline reference at(size_type pos) {
MIJIN_ASSERT(pos < size(), "BufferView::at(): pos is out of range.");
return *(begin() + pos);
}
[[nodiscard]] inline const_reference at(size_type pos) const {
MIJIN_ASSERT(pos < size(), "BufferView::at(): pos is out of range.");
return *(begin() + pos);
}
// operators
inline reference operator[](size_type pos) { return at(pos); }
inline const_reference operator[](size_type pos) const { return at(pos); }
};
//
// public functions
//
} // namespace mijin
#endif // !defined(MIJIN_CONTAINER_TYPELESS_BUFFER_HPP_INCLUDED)