File: bytes_allocator.h

package info (click to toggle)
dolphin-emu 2512%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 76,328 kB
  • sloc: cpp: 499,023; ansic: 119,674; python: 6,547; sh: 2,338; makefile: 1,093; asm: 726; pascal: 257; javascript: 183; perl: 97; objc: 75; xml: 30
file content (163 lines) | stat: -rw-r--r-- 5,395 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
/**
 * \file libipc/bytes_allocator.h
 * \author mutouyun (orz@orzz.org)
 * \brief A generic polymorphic memory allocator.
 */
#pragma once

#include <type_traits>
#include <array>
#include <limits>   // std::numeric_limits
#include <utility>  // std::forward
#include <tuple>    // std::ignore
#include <cstddef>

#include "libipc/imp/export.h"
#include "libipc/imp/uninitialized.h"
#include "libipc/imp/byte.h"

namespace ipc {
namespace mem {

/// \brief Helper trait for memory resource.

template <typename T, typename = void>
struct has_allocate : std::false_type {};

template <typename T>
struct has_allocate<T, 
  typename std::enable_if<std::is_convertible<
  decltype(std::declval<T &>().allocate(std::declval<std::size_t>(), 
                                        std::declval<std::size_t>())), void *
  >::value>::type> : std::true_type {};

template <typename T, typename = void>
struct has_deallocate : std::false_type {};

template <typename T>
struct has_deallocate<T, 
  decltype(std::declval<T &>().deallocate(std::declval<void *>(), 
                                          std::declval<std::size_t>(), 
                                          std::declval<std::size_t>()))
  > : std::true_type {};

template <typename T>
using is_memory_resource = 
  std::enable_if_t<has_allocate  <T>::value && 
                   has_deallocate<T>::value, bool>;

/**
 * \brief An allocator which exhibits different allocation behavior 
 *        depending upon the memory resource from which it is constructed.
 * 
 * \note Unlike `std::pmr::container_allocator`, it does not 
 *       rely on a specific inheritance relationship and only restricts 
 *       the interface behavior of the incoming memory resource object to 
 *       conform to `std::pmr::memory_resource`.
 * 
 * \see https://en.cppreference.com/w/cpp/memory/memory_resource
 *      https://en.cppreference.com/w/cpp/memory/container_allocator
 */
class LIBIPC_EXPORT bytes_allocator {

  class holder_mr_base {
  public:
    virtual ~holder_mr_base() noexcept = default;
    virtual void *alloc(std::size_t, std::size_t) const = 0;
    virtual void  dealloc(void *, std::size_t, std::size_t) const = 0;
  };

  template <typename MR, typename = bool>
  class holder_mr;

  /**
   * \brief An empty holding class used to calculate a reasonable memory size for the holder.
   * \tparam MR cannot be converted to the type of memory resource
   */
  template <typename MR, typename U>
  class holder_mr : public holder_mr_base {
  protected:
    MR *res_;

  public:
    holder_mr(MR *p_mr) noexcept
      : res_(p_mr) {}

    // [MSVC] error C2259: 'bytes_allocator::holder_mr<void *,bool>': cannot instantiate abstract class.
    void *alloc(std::size_t s, std::size_t a) const override { return nullptr; }
    void dealloc(void *p, std::size_t s, std::size_t a) const override {}
  };

  /**
   * \brief A memory resource pointer holder class for type erasure.
   * \tparam MR memory resource type
   */
  template <typename MR>
  class holder_mr<MR, is_memory_resource<MR>> : public holder_mr<MR, void> {
    using base_t = holder_mr<MR, void>;

  public:
    holder_mr(MR *p_mr) noexcept
      : base_t{p_mr} {}

    void *alloc(std::size_t s, std::size_t a) const override {
      return base_t::res_->allocate(s, a);
    }

    void dealloc(void *p, std::size_t s, std::size_t a) const override {
      base_t::res_->deallocate(p, s, a);
    }
  };

  using void_holder_t = holder_mr<void *>;
  alignas(void_holder_t) std::array<ipc::byte, sizeof(void_holder_t)> holder_;

  holder_mr_base &      get_holder() noexcept;
  holder_mr_base const &get_holder() const noexcept;

  void init_default_resource() noexcept;

public:
  /// \brief Constructs an `bytes_allocator` using the return value of 
  ///       `new_delete_resource::get()` as the underlying memory resource.
  bytes_allocator() noexcept;
  ~bytes_allocator() noexcept;

  bytes_allocator(bytes_allocator const &other) noexcept = default;
  bytes_allocator &operator=(bytes_allocator const &other) & noexcept = default;

  bytes_allocator(bytes_allocator &&other) noexcept = default;
  bytes_allocator &operator=(bytes_allocator &&other) & noexcept = default;

  /// \brief Constructs a `bytes_allocator` from a memory resource pointer.
  /// \note The lifetime of the pointer must be longer than that of bytes_allocator.
  template <typename T, is_memory_resource<T> = true>
  bytes_allocator(T *p_mr) noexcept {
    if (p_mr == nullptr) {
      init_default_resource();
      return;
    }
    std::ignore = ipc::construct<holder_mr<T>>(holder_.data(), p_mr);
  }

  void swap(bytes_allocator &other) noexcept;

  /// \brief Allocate/deallocate memory.
  void *allocate(std::size_t s, std::size_t = alignof(std::max_align_t)) const;
  void  deallocate(void *p, std::size_t s, std::size_t = alignof(std::max_align_t)) const;

  /// \brief Allocates uninitialized memory and constructs an object of type T in the memory.
  template <typename T, typename... A>
  T *construct(A &&...args) const {
    return ipc::construct<T>(allocate(sizeof(T), alignof(T)), std::forward<A>(args)...);
  }

  /// \brief Calls the destructor of the object pointed to by p and deallocates the memory.
  template <typename T>
  void destroy(T *p) const noexcept {
    deallocate(ipc::destroy(p), sizeof(T), alignof(T));
  }
};

} // namespace mem
} // namespace ipc