File: PackedArray.h

package info (click to toggle)
audacity 3.7.7%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 134,800 kB
  • sloc: cpp: 366,277; ansic: 198,323; lisp: 7,761; sh: 3,414; python: 1,501; xml: 1,385; perl: 854; makefile: 125
file content (222 lines) | stat: -rw-r--r-- 7,140 bytes parent folder | download | duplicates (3)
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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
/*!********************************************************************

 Audacity: A Digital Audio Editor

 @file PackedArray.h

 @brief Smart pointer for a header contiguous with an array holding a
 dynamically determined number of elements

 Paul Licameli

 **********************************************************************/

#ifndef __AUDACITY_PACKED_ARRAY__
#define __AUDACITY_PACKED_ARRAY__

#include <type_traits>

namespace PackedArray {

template<typename T> struct Traits;

namespace detail {

//! Primary template of metafunction deducing other things from Traits<T>
template<typename T, typename = void> struct ExtendedTraits : Traits<T> {
   using element_type = typename Traits<T>::element_type;
   using iterated_type = T;
   static iterated_type *begin(T *p) { return p; }
   static element_type *element_ptr(T *p) { return p; }
   static size_t element_offset() { return 0; }
};

//! Partial specialization used when Traits<T>::array_member is defined
template<typename T> struct ExtendedTraits<T, std::void_t<decltype(
   std::declval<T>().*(Traits<T>::array_member)
)>> : Traits<T> {
   using element_type = typename Traits<T>::element_type;
   using member_type = std::remove_reference_t<decltype(
      std::declval<T>().*(Traits<T>::array_member)
   )>;
   // Check restrictions on usage.  The member is expected to have dimension 1
   static_assert(std::extent_v<member_type> == 1);
   using iterated_type = std::remove_extent_t<member_type>;
   // Check that any overlay of a type with a destructor is sensible
   static_assert(sizeof(iterated_type) == sizeof(element_type));

   static iterated_type *begin(T *p) {
      return &(p->*(Traits<T>::array_member))[0];
   }
   static element_type *element_ptr(T *p) {
      return reinterpret_cast<element_type*>(begin(p));
   }
   static size_t element_offset() {
      return reinterpret_cast<size_t>(begin(0));
   }
};

}

//! Primary template used in Deleter that can be specialized
/*!
 Specializations that define a pointer-to-data-member of T called array_member
 are treated specially
 */
template<typename T> struct Traits{
   // Default assumption is that there is no header and no need to overlay
   // the element with a type that performs nontrivial destruction
   struct header_type {};
   using element_type = T;
};

//! Deleter for an array of elements and optional contiguous header structure
/*!
 @tparam Type is the type pointed to, and an explicit specialization of
 Traits<Type> may redefine the nested types for a non-empty header:
  - header_type, which overlays the members of Type except the last member;
   may be non-empty and define a destructor
  - element_type must be such that the last member of Type is Element[1]; may
    define a destructor
 And Traits<Type> may also define a pointer-to-data-member of T called
 array_member
 @tparam BaseDeleter manages the main deallocation
 */
template<typename Type,
   template<typename> typename BaseDeleter = std::default_delete>
struct Deleter : BaseDeleter<Type> {
   using managed_type = Type;
   using base_type = BaseDeleter<managed_type>;
   using traits_type = detail::ExtendedTraits<managed_type>;
   using header_type = typename traits_type::header_type;
   using element_type = typename traits_type::element_type;

   //! Nontrivial, implicit constructor of the deleter takes a size, which
   //! defaults to 0 to allow default contruction of the unique_ptr
   Deleter(size_t size = 0) noexcept
      : mCount{ size
         ? (size - traits_type::element_offset()) / sizeof(element_type)
         : 0
      }
   {}

   void operator()(Type *p) const noexcept(noexcept(
      (void)std::declval<element_type>().~element_type(),
      (void)std::declval<header_type>().~header_type(),
      (void)std::declval<base_type>()(p)
   )) {
      if (!p)
         return;
      // Do nested deallocations for elements by decreasing subscript
      auto pE = traits_type::element_ptr(p) + mCount;
      for (auto count = mCount; count--;)
         (--pE)->~element_type();
      // Do nested deallocations for main structure
      reinterpret_cast<const header_type*>(p)->~header_type();
      // main deallocation
      ((base_type&)*this)(p);
   }

   size_t GetCount() const { return mCount; }
private:
   size_t mCount = 0;
};

//! Smart pointer type that deallocates with Deleter
template<typename Type,
   template<typename> typename BaseDeleter = std::default_delete>
struct Ptr
   : std::unique_ptr<Type, Deleter<Type, BaseDeleter>>
{
   using
      std::unique_ptr<Type, Deleter<Type, BaseDeleter>>::unique_ptr;

   //! Enables subscripting.  Does not check for null!
   auto &operator[](size_t ii) const
   {
      return *(begin(*this) + ii);
   }
};

//! Find out how many elements were allocated with a Ptr
template<typename Type, template<typename> typename BaseDeleter>
inline size_t Count(const Ptr<Type, BaseDeleter> &p)
{
   return p.get_deleter().GetCount();
}

//! Enables range-for
template<typename Type, template<typename> typename BaseDeleter>
inline auto begin(const Ptr<Type, BaseDeleter> &p)
{
   using traits_type = detail::ExtendedTraits<Type>;
   auto ptr = p.get();
   return ptr ? traits_type::begin(ptr) : nullptr;
}

//! Enables range-for
template<typename Type, template<typename> typename BaseDeleter>
inline auto end(const Ptr<Type, BaseDeleter> &p)
{
   auto result = begin(p);
   if (result)
      result += Count(p);
   return result;
}

}

struct PackedArray_t{};
//! Tag argument to distinguish an overload of ::operator new
static constexpr inline PackedArray_t PackedArrayArg;

//! Allocator for objects managed by PackedArray::Ptr enlarges the size request
//! that the compiler makes
inline void *operator new(size_t size, PackedArray_t, size_t enlarged) {
   return ::operator new(std::max(size, enlarged));
}

//! Complementary operator delete in case of exceptions in a new-expression
inline void operator delete(void *p, PackedArray_t, size_t) {
   ::operator delete(p);
}

namespace PackedArray {

//! Allocate a Ptr<Type> holding at least `enlarged` bytes
/*!
 Usage: AllocateBytes<Type>(bytes)(constructor arguments for Type)
 Meant to resemble placement-new syntax with separation of allocator and
 constructor arguments
 */
template<typename Type> auto AllocateBytes(size_t enlarged)
{
   return [enlarged](auto &&...args) {
      return Ptr<Type>{
         safenew(PackedArrayArg, enlarged)
            Type{ std::forward<decltype(args)>(args)... },
         std::max(sizeof(Type), enlarged) // Deleter's constructor argument
      };
   };
}

//! Allocate a Ptr<Type> holding `count` elements
/*!
 Usage: AllocateCount<Type>(count)(constructor arguments for Type)
 Meant to resemble placement-new syntax with separation of allocator and
 constructor arguments

 If Traits<Type> defines a nonempty header, the result always holds
 at least one element
 */
template<typename Type> auto AllocateCount(size_t count)
{
   using traits_type = detail::ExtendedTraits<Type>;
   const auto bytes = traits_type::element_offset()
      + count * sizeof(typename traits_type::element_type);
   return AllocateBytes<Type>(bytes);
}

}

#endif