#pragma once #if !defined(MIJIN_CONTAINER_MEMORY_VIEW_HPP_INCLUDED) #define MIJIN_CONTAINER_MEMORY_VIEW_HPP_INCLUDED 1 #include #include #include #include "../debug/assert.hpp" #include "../internal/common.hpp" namespace mijin { // // public defines // // // public constants // // // public types // template concept MemoryViewable = requires(const T& object) { { object.data() } -> std::convertible_to; { object.byteSize() } -> std::convertible_to; }; template concept RWMemoryViewable = MemoryViewable && requires(T& object) { { object.data() } -> std::convertible_to; }; template class MixinMemoryView { public: static constexpr bool WRITABLE = requires(TConcrete& object) { { object.data() } -> std::convertible_to; }; template [[nodiscard]] auto makeSpan(); template [[nodiscard]] auto makeSpan() const; template> [[nodiscard]] std::basic_string_view makeStringView() const ; template [[nodiscard]] auto& dataAt(std::size_t offset) MIJIN_NOEXCEPT; template [[nodiscard]] auto& dataAt(std::size_t offset) const MIJIN_NOEXCEPT; [[nodiscard]] auto bytes() MIJIN_NOEXCEPT { using return_t = mijin::copy_const_t(this)->data())>, std::byte>; return static_cast(static_cast(this)->data()); } [[nodiscard]] auto bytes() const MIJIN_NOEXCEPT { using return_t = mijin::copy_const_t(this)->data())>, std::byte>; return static_cast(static_cast(this)->data()); } private: std::size_t byteSizeImpl() const MIJIN_NOEXCEPT { return static_cast(this)->byteSize(); } }; class MemoryView : public MixinMemoryView { 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 requires(!std::is_const_v) MemoryView(std::span span) MIJIN_NOEXCEPT : data_(span.data()), byteSize_(span.size_bytes()) {} template 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 { 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 ConstMemoryView(std::span span) MIJIN_NOEXCEPT : data_(span.data()), byteSize_(span.size_bytes()) {} template 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 template auto MixinMemoryView::makeSpan() { MIJIN_ASSERT(byteSizeImpl() % sizeof(T) == 0, "Buffer cannot be divided into elements of this type."); using return_t = mijin::copy_const_t; return std::span{ std::bit_cast(bytes()), std::bit_cast(bytes() + byteSizeImpl()) }; } template template auto MixinMemoryView::makeSpan() const { MIJIN_ASSERT(byteSizeImpl() % sizeof(T) == 0, "Buffer cannot be divided into elements of this type."); using return_t = mijin::copy_const_t; return std::span{ std::bit_cast(bytes()), std::bit_cast(bytes() + byteSizeImpl()) }; } template template std::basic_string_view MixinMemoryView::makeStringView() const { MIJIN_ASSERT(byteSizeImpl() % sizeof(TChar) == 0, "Buffer cannot be divided into elements of this char type."); return {std::bit_cast(bytes()), byteSizeImpl() / sizeof(TChar)}; } template template auto& MixinMemoryView::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; return *std::bit_cast(bytes().data() + offset); } template template auto& MixinMemoryView::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; return *std::bit_cast(bytes().data() + offset); } } // namespace mijin #endif // !defined(MIJIN_CONTAINER_MEMORY_VIEW_HPP_INCLUDED)