mijin2/source/mijin/container/memory_view.hpp

197 lines
5.9 KiB
C++

#pragma once
#if !defined(MIJIN_CONTAINER_MEMORY_VIEW_HPP_INCLUDED)
#define MIJIN_CONTAINER_MEMORY_VIEW_HPP_INCLUDED 1
#include <bit>
#include <span>
#include <string_view>
#include "../debug/assert.hpp"
#include "../internal/common.hpp"
namespace mijin
{
//
// public defines
//
//
// public constants
//
//
// public types
//
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:
void* data_ = nullptr;
std::size_t byteSize_ = 0;
public:
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()) {}
MemoryView& operator=(const MemoryView&) = default;
[[nodiscard]]
void* data() const MIJIN_NOEXCEPT { return data_; }
[[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()) {}
ConstMemoryView& operator=(const ConstMemoryView& other) MIJIN_NOEXCEPT = default;
[[nodiscard]]
const void* data() const MIJIN_NOEXCEPT { return data_; }
[[nodiscard]]
size_type byteSize() const MIJIN_NOEXCEPT { return byteSize_; }
};
//
// public functions
//
template<typename TConcrete>
template<typename T>
auto MixinMemoryView<TConcrete>::makeSpan()
{
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 T>
auto MixinMemoryView<TConcrete>::makeSpan() const
{
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)