197 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			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)
 |