/ src / common / memory_ref.h
memory_ref.h
  1  // Copyright 2020 Citra Emulator Project
  2  // Licensed under GPLv2 or any later version
  3  // Refer to the license.txt file included.
  4  
  5  #pragma once
  6  
  7  #include <memory>
  8  #include <span>
  9  #include <vector>
 10  #include <boost/serialization/export.hpp>
 11  #include <boost/serialization/shared_ptr.hpp>
 12  #include <boost/serialization/vector.hpp>
 13  #include "common/assert.h"
 14  #include "common/common_types.h"
 15  
 16  /// Abstract host-side memory - for example a static buffer, or local vector
 17  class BackingMem {
 18  public:
 19      virtual ~BackingMem() = default;
 20      virtual u8* GetPtr() = 0;
 21      virtual const u8* GetPtr() const = 0;
 22      virtual std::size_t GetSize() const = 0;
 23  
 24  private:
 25      template <class Archive>
 26      void serialize(Archive&, const unsigned int) {}
 27      friend class boost::serialization::access;
 28  };
 29  
 30  /// Backing memory implemented by a local buffer
 31  class BufferMem : public BackingMem {
 32  public:
 33      BufferMem() = default;
 34      explicit BufferMem(std::size_t size) : data(size) {}
 35  
 36      u8* GetPtr() override {
 37          return data.data();
 38      }
 39  
 40      const u8* GetPtr() const override {
 41          return data.data();
 42      }
 43  
 44      std::size_t GetSize() const override {
 45          return data.size();
 46      }
 47  
 48      std::vector<u8>& Vector() {
 49          return data;
 50      }
 51  
 52      const std::vector<u8>& Vector() const {
 53          return data;
 54      }
 55  
 56  private:
 57      std::vector<u8> data;
 58  
 59      template <class Archive>
 60      void serialize(Archive& ar, const unsigned int) {
 61          ar& boost::serialization::base_object<BackingMem>(*this);
 62          ar& data;
 63      }
 64      friend class boost::serialization::access;
 65  };
 66  
 67  BOOST_CLASS_EXPORT_KEY(BufferMem);
 68  
 69  /**
 70   * A managed reference to host-side memory.
 71   * Fast enough to be used everywhere instead of u8*
 72   * Supports serialization.
 73   */
 74  class MemoryRef {
 75  public:
 76      MemoryRef() = default;
 77      MemoryRef(std::nullptr_t) {}
 78  
 79      MemoryRef(std::shared_ptr<BackingMem> backing_mem_)
 80          : backing_mem(std::move(backing_mem_)), offset(0) {
 81          Init();
 82      }
 83      MemoryRef(std::shared_ptr<BackingMem> backing_mem_, u64 offset_)
 84          : backing_mem(std::move(backing_mem_)), offset(offset_) {
 85          ASSERT(offset <= backing_mem->GetSize());
 86          Init();
 87      }
 88  
 89      explicit operator bool() const {
 90          return cptr != nullptr;
 91      }
 92  
 93      operator u8*() {
 94          return cptr;
 95      }
 96  
 97      u8* GetPtr() {
 98          return cptr;
 99      }
100  
101      operator const u8*() const {
102          return cptr;
103      }
104  
105      const u8* GetPtr() const {
106          return cptr;
107      }
108  
109      std::span<u8> GetWriteBytes(std::size_t size) {
110          return std::span{cptr, std::min(size, csize)};
111      }
112  
113      template <typename T>
114      std::span<const T> GetReadBytes(std::size_t size) const {
115          const auto* cptr_t = reinterpret_cast<T*>(cptr);
116          return std::span{cptr_t, std::min(size, csize) / sizeof(T)};
117      }
118  
119      std::size_t GetSize() const {
120          return csize;
121      }
122  
123      MemoryRef& operator+=(u32 offset_by) {
124          ASSERT(offset_by < csize);
125          offset += offset_by;
126          Init();
127          return *this;
128      }
129  
130      MemoryRef operator+(u32 offset_by) const {
131          ASSERT(offset_by < csize);
132          return MemoryRef(backing_mem, offset + offset_by);
133      }
134  
135  private:
136      std::shared_ptr<BackingMem> backing_mem{};
137      u64 offset{};
138      // Cached values for speed
139      u8* cptr{};
140      std::size_t csize{};
141  
142      void Init() {
143          if (backing_mem) {
144              cptr = backing_mem->GetPtr() + offset;
145              csize = static_cast<std::size_t>(backing_mem->GetSize() - offset);
146          } else {
147              cptr = nullptr;
148              csize = 0;
149          }
150      }
151  
152      template <class Archive>
153      void serialize(Archive& ar, const unsigned int) {
154          ar& backing_mem;
155          ar& offset;
156          Init();
157      }
158      friend class boost::serialization::access;
159  };