Made TypelessBuffer allocator-aware and split some of its functionality into MemoryView type.
This commit is contained in:
@@ -5,10 +5,10 @@
|
||||
#define MIJIN_CONTAINER_MEMORY_VIEW_HPP_INCLUDED 1
|
||||
|
||||
#include <bit>
|
||||
#include <cstddef>
|
||||
#include <span>
|
||||
#include <string_view>
|
||||
#include "../debug/assert.hpp"
|
||||
#include "../util/traits.hpp"
|
||||
#include "../internal/common.hpp"
|
||||
|
||||
namespace mijin
|
||||
{
|
||||
@@ -25,60 +25,170 @@ namespace mijin
|
||||
// public types
|
||||
//
|
||||
|
||||
template<typename TBytes>
|
||||
class MemoryViewBase
|
||||
template<typename T>
|
||||
concept MemoryViewable = requires(const T& object)
|
||||
{
|
||||
{ object.data() } -> std::convertible_to<const void*>;
|
||||
{ object.byteSize() } -> std::convertible_to<std::size_t>;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
concept RWMemoryViewable = MemoryViewable<T> && requires(T& object)
|
||||
{
|
||||
{ object.data() } -> std::convertible_to<void*>;
|
||||
};
|
||||
|
||||
template<typename TConcrete>
|
||||
class MixinMemoryView
|
||||
{
|
||||
public:
|
||||
static constexpr bool WRITABLE = requires(TConcrete& object) { { object.data() } -> std::convertible_to<void*>; };
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
auto makeSpan();
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
auto makeSpan() const;
|
||||
|
||||
template<typename TChar = char, typename TTraits = std::char_traits<TChar>>
|
||||
[[nodiscard]]
|
||||
std::basic_string_view<TChar, TTraits> makeStringView() const ;
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
auto& dataAt(std::size_t offset) MIJIN_NOEXCEPT;
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]]
|
||||
auto& dataAt(std::size_t offset) const MIJIN_NOEXCEPT;
|
||||
|
||||
[[nodiscard]]
|
||||
auto bytes() MIJIN_NOEXCEPT
|
||||
{
|
||||
using return_t = mijin::copy_const_t<std::remove_pointer_t<decltype(static_cast<TConcrete*>(this)->data())>, std::byte>;
|
||||
return static_cast<return_t*>(static_cast<TConcrete*>(this)->data());
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
auto bytes() const MIJIN_NOEXCEPT
|
||||
{
|
||||
using return_t = mijin::copy_const_t<std::remove_pointer_t<decltype(static_cast<const TConcrete*>(this)->data())>, std::byte>;
|
||||
return static_cast<return_t*>(static_cast<const TConcrete*>(this)->data());
|
||||
}
|
||||
private:
|
||||
std::size_t byteSizeImpl() const MIJIN_NOEXCEPT
|
||||
{
|
||||
return static_cast<const TConcrete*>(this)->byteSize();
|
||||
}
|
||||
};
|
||||
|
||||
class MemoryView : public MixinMemoryView<MemoryView>
|
||||
{
|
||||
public:
|
||||
using size_type = std::size_t;
|
||||
private:
|
||||
std::span<TBytes> bytes_;
|
||||
void* data_ = nullptr;
|
||||
std::size_t byteSize_ = 0;
|
||||
public:
|
||||
MemoryViewBase() noexcept = default;
|
||||
MemoryViewBase(const MemoryViewBase&) noexcept = default;
|
||||
MemoryViewBase(copy_const_t<TBytes, void>* data, std::size_t size) noexcept : bytes_(static_cast<TBytes*>(data), size) {}
|
||||
MemoryView() noexcept = default;
|
||||
MemoryView(const MemoryView&) = default;
|
||||
MemoryView(void* data, std::size_t byteSize) MIJIN_NOEXCEPT : data_(data), byteSize_(byteSize) {}
|
||||
template<typename T> requires(!std::is_const_v<T>)
|
||||
MemoryView(std::span<T> span) MIJIN_NOEXCEPT : data_(span.data()), byteSize_(span.size_bytes()) {}
|
||||
template<RWMemoryViewable T>
|
||||
MemoryView(T& memoryViewable) MIJIN_NOEXCEPT : data_(memoryViewable.data()), byteSize_(memoryViewable.byteSize()) {}
|
||||
|
||||
MemoryViewBase& operator=(const MemoryViewBase&) noexcept = default;
|
||||
|
||||
[[nodiscard]] void* data() noexcept { return bytes_.data(); }
|
||||
[[nodiscard]] const void* data() const noexcept { return bytes_.data(); }
|
||||
[[nodiscard]] size_type byteSize() const noexcept { return bytes_.size(); }
|
||||
[[nodiscard]] bool empty() const noexcept { return bytes_.empty(); }
|
||||
MemoryView& operator=(const MemoryView&) = default;
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] std::span<T> makeSpan();
|
||||
[[nodiscard]]
|
||||
void* data() const MIJIN_NOEXCEPT { return data_; }
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] std::span<const T> makeSpan() const;
|
||||
[[nodiscard]]
|
||||
size_type byteSize() const MIJIN_NOEXCEPT { return byteSize_; }
|
||||
};
|
||||
|
||||
class ConstMemoryView : public MixinMemoryView<ConstMemoryView>
|
||||
{
|
||||
public:
|
||||
using size_type = std::size_t;
|
||||
private:
|
||||
const void* data_;
|
||||
std::size_t byteSize_;
|
||||
public:
|
||||
ConstMemoryView() noexcept = default;
|
||||
ConstMemoryView(const ConstMemoryView&) = default;
|
||||
ConstMemoryView(void* data, std::size_t byteSize) MIJIN_NOEXCEPT : data_(data), byteSize_(byteSize) {}
|
||||
template<typename T>
|
||||
ConstMemoryView(std::span<T> span) MIJIN_NOEXCEPT : data_(span.data()), byteSize_(span.size_bytes()) {}
|
||||
template<MemoryViewable T>
|
||||
ConstMemoryView(const T& memoryViewable) MIJIN_NOEXCEPT : data_(memoryViewable.data()), byteSize_(memoryViewable.byteSize()) {}
|
||||
|
||||
[[nodiscard]]
|
||||
const void* data() const MIJIN_NOEXCEPT { return data_; }
|
||||
|
||||
[[nodiscard]]
|
||||
size_type byteSize() const MIJIN_NOEXCEPT { return byteSize_; }
|
||||
};
|
||||
using MemoryView = MemoryViewBase<std::byte>;
|
||||
using ConstMemoryView = MemoryViewBase<const std::byte>;
|
||||
|
||||
//
|
||||
// public functions
|
||||
//
|
||||
|
||||
template<typename TBytes>
|
||||
template<typename TConcrete>
|
||||
template<typename T>
|
||||
std::span<T> MemoryViewBase<TBytes>::makeSpan()
|
||||
auto MixinMemoryView<TConcrete>::makeSpan()
|
||||
{
|
||||
static_assert(!std::is_const_v<TBytes>, "Cannot create writable spans from const memory views.");
|
||||
MIJIN_ASSERT(bytes_.size() % sizeof(T) == 0, "MemoryView cannot be divided into elements of this type.");
|
||||
return {
|
||||
std::bit_cast<T*>(bytes_.data()),
|
||||
std::bit_cast<T*>(bytes_.data() + bytes_.size())
|
||||
MIJIN_ASSERT(byteSizeImpl() % sizeof(T) == 0, "Buffer cannot be divided into elements of this type.");
|
||||
using return_t = mijin::copy_const_t<decltype(*bytes()), T>;
|
||||
return std::span<return_t>{
|
||||
std::bit_cast<return_t*>(bytes()),
|
||||
std::bit_cast<return_t*>(bytes() + byteSizeImpl())
|
||||
};
|
||||
}
|
||||
|
||||
template<typename TBytes>
|
||||
template<typename TConcrete>
|
||||
template<typename T>
|
||||
std::span<const T> MemoryViewBase<TBytes>::makeSpan() const
|
||||
auto MixinMemoryView<TConcrete>::makeSpan() const
|
||||
{
|
||||
MIJIN_ASSERT(bytes_.size() % sizeof(T) == 0, "MemoryView cannot be divided into elements of this type.");
|
||||
return {
|
||||
std::bit_cast<const T*>(bytes_.data()),
|
||||
std::bit_cast<const T*>(bytes_.data() + bytes_.size())
|
||||
MIJIN_ASSERT(byteSizeImpl() % sizeof(T) == 0, "Buffer cannot be divided into elements of this type.");
|
||||
using return_t = mijin::copy_const_t<decltype(*bytes()), T>;
|
||||
return std::span<return_t>{
|
||||
std::bit_cast<return_t*>(bytes()),
|
||||
std::bit_cast<return_t*>(bytes() + byteSizeImpl())
|
||||
};
|
||||
}
|
||||
|
||||
template<typename TConcrete>
|
||||
template<typename TChar, typename TTraits>
|
||||
std::basic_string_view<TChar, TTraits> MixinMemoryView<TConcrete>::makeStringView() const
|
||||
{
|
||||
MIJIN_ASSERT(byteSizeImpl() % sizeof(TChar) == 0, "Buffer cannot be divided into elements of this char type.");
|
||||
return {std::bit_cast<const TChar*>(bytes()), byteSizeImpl() / sizeof(TChar)};
|
||||
}
|
||||
|
||||
template<typename TConcrete>
|
||||
template<typename T>
|
||||
auto& MixinMemoryView<TConcrete>::dataAt(std::size_t offset) MIJIN_NOEXCEPT
|
||||
{
|
||||
MIJIN_ASSERT(offset % alignof(T) == 0, "Offset must be correctly aligned.");
|
||||
MIJIN_ASSERT(offset + sizeof(T) < byteSizeImpl(), "Buffer access out-of-range.");
|
||||
|
||||
using return_t = mijin::copy_const_t<decltype(*bytes()), T>;
|
||||
return *std::bit_cast<return_t*>(bytes().data() + offset);
|
||||
}
|
||||
|
||||
template<typename TConcrete>
|
||||
template<typename T>
|
||||
auto& MixinMemoryView<TConcrete>::dataAt(std::size_t offset) const MIJIN_NOEXCEPT
|
||||
{
|
||||
MIJIN_ASSERT(offset % alignof(T) == 0, "Offset must be correctly aligned.");
|
||||
MIJIN_ASSERT(offset + sizeof(T) < byteSizeImpl(), "Buffer access out-of-range.");
|
||||
|
||||
using return_t = mijin::copy_const_t<decltype(*bytes()), T>;
|
||||
return *std::bit_cast<return_t*>(bytes().data() + offset);
|
||||
}
|
||||
} // namespace mijin
|
||||
|
||||
#endif // !defined(MIJIN_CONTAINER_MEMORY_VIEW_HPP_INCLUDED)
|
||||
|
||||
Reference in New Issue
Block a user