1 /**
2  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  * SPDX-License-Identifier: Apache-2.0.
4  */
5 
6 #pragma once
7 
8 #include <aws/core/Core_EXPORTS.h>
9 #include <aws/core/utils/UnreferencedParam.h>
10 #include <aws/core/utils/memory/MemorySystemInterface.h>
11 
12 #include <memory>
13 #include <cstdlib>
14 #include <algorithm>
15 #include <type_traits>
16 
17 struct aws_allocator;
18 
19 namespace Aws
20 {
21     namespace Utils
22     {
23         namespace Memory
24         {
25             /**
26              *InitializeAWSMemory should be called at the very start of your program
27              */
28             AWS_CORE_API void InitializeAWSMemorySystem(MemorySystemInterface& memorySystem);
29 
30             /**
31              * ShutdownAWSMemory should be called the very end of your program
32              */
33             AWS_CORE_API void ShutdownAWSMemorySystem(void);
34 
35             /**
36              * Get the globally install memory system, if it has been installed.
37              */
38             AWS_CORE_API MemorySystemInterface* GetMemorySystem();
39 
40         } // namespace Memory
41     } // namespace Utils
42 
43     /**
44      * ::new, ::delete, ::malloc, ::free, std::make_shared, and std::make_unique should not be used in SDK code
45      *  use these functions instead or Aws::MakeShared
46      */
47     AWS_CORE_API void* Malloc(const char* allocationTag, size_t allocationSize);
48 
49     /**
50      * ::new, ::delete, ::malloc, ::free, std::make_shared, and std::make_unique should not be used in SDK code
51      *  use these functions instead or Aws::MakeShared
52      */
53     AWS_CORE_API void Free(void* memoryPtr);
54 
55     AWS_CORE_API aws_allocator* get_aws_allocator();
56 
57     /**
58      * ::new, ::delete, ::malloc, ::free, std::make_shared, and std::make_unique should not be used in SDK code
59      *  use these functions instead or Aws::MakeShared
60      */
61     template<typename T, typename ...ArgTypes>
New(const char * allocationTag,ArgTypes &&...args)62     T* New(const char* allocationTag, ArgTypes&&... args)
63     {
64         void *rawMemory = Malloc(allocationTag, sizeof(T));
65         // http://stackoverflow.com/questions/6783993/placement-new-and-delete
66         T *constructedMemory = new (rawMemory) T(std::forward<ArgTypes>(args)...);
67         return constructedMemory;
68     }
69 /*
70  * dynamic_cast of all sorts do not work on MSVC if RTTI is turned off.
71  * This means that deleting an object via a pointer to one of its polymorphic base types that do not point to the
72  * address of their concrete class will result in undefined behavior.
73  * Example:
74  * struct Foo : InterfaceA, InterfaceB {};
75  * Aws::Delete(pointerToInterfaceB);
76  */
77 #if defined(_MSC_VER) && !defined(_CPPRTTI)
78     template<typename T>
Delete(T * pointerToT)79     void Delete(T* pointerToT)
80     {
81         if (pointerToT == nullptr)
82         {
83             return;
84         }
85         pointerToT->~T();
86         Free(pointerToT);
87     }
88 #else
89     /**
90      * ::new, ::delete, ::malloc, ::free, std::make_shared, and std::make_unique should not be used in SDK code
91      *  use these functions instead or Aws::MakeShared
92      */
93     template<typename T>
Delete(T * pointerToT)94     typename std::enable_if<!std::is_polymorphic<T>::value>::type Delete(T* pointerToT)
95     {
96         if (pointerToT == nullptr)
97         {
98             return;
99         }
100         //http://stackoverflow.com/questions/6783993/placement-new-and-delete
101         pointerToT->~T();
102         Free(pointerToT);
103     }
104 
105     template<typename T>
Delete(T * pointerToT)106     typename std::enable_if<std::is_polymorphic<T>::value>::type Delete(T* pointerToT)
107     {
108         if (pointerToT == nullptr)
109         {
110             return;
111         }
112         // deal with deleting objects that implement multiple interfaces
113         // see casting to pointer to void in http://en.cppreference.com/w/cpp/language/dynamic_cast
114         // https://stackoverflow.com/questions/8123776/are-there-practical-uses-for-dynamic-casting-to-void-pointer
115         // NOTE: on some compilers, calling the destructor before doing the dynamic_cast affects how calculation of
116         // the address of the most derived class.
117         void* mostDerivedT = dynamic_cast<void*>(pointerToT);
118         pointerToT->~T();
119         Free(mostDerivedT);
120     }
121 #endif // _CPPRTTI
122 
123     template<typename T>
ShouldConstructArrayMembers()124     bool ShouldConstructArrayMembers()
125     {
126         return std::is_class<T>::value;
127     }
128 
129     template<typename T>
ShouldDestroyArrayMembers()130     bool ShouldDestroyArrayMembers()
131     {
132         return !std::is_trivially_destructible<T>::value;
133     }
134 
135     /**
136      * ::new, ::delete, ::malloc, ::free, std::make_shared, and std::make_unique should not be used in SDK code
137      *  use these functions instead or Aws::MakeShared
138      */
139     template<typename T>
NewArray(std::size_t amount,const char * allocationTag)140     T* NewArray(std::size_t amount, const char* allocationTag)
141     {
142         if (amount > 0)
143         {
144             bool constructMembers = ShouldConstructArrayMembers<T>();
145             bool trackMemberCount = ShouldDestroyArrayMembers<T>();
146 
147             // if we need to remember the # of items in the array (because we need to call their destructors) then allocate extra memory and keep the # of items in the extra slot
148             std::size_t allocationSize = amount * sizeof(T);
149 #if defined(_MSC_VER) && _MSC_VER < 1900
150             std::size_t headerSize = (std::max)(sizeof(std::size_t), __alignof(T));
151 #else
152             std::size_t headerSize = (std::max)(sizeof(std::size_t), alignof(T));
153 #endif
154 
155             if (trackMemberCount)
156             {
157                 allocationSize += headerSize;
158             }
159 
160             void* rawMemory = Malloc(allocationTag, allocationSize);
161             T* pointerToT = nullptr;
162 
163             if (trackMemberCount)
164             {
165                 std::size_t* pointerToAmount = reinterpret_cast<std::size_t*>(rawMemory);
166                 *pointerToAmount = amount;
167                 pointerToT = reinterpret_cast<T*>(reinterpret_cast<char*>(pointerToAmount) + headerSize);
168 
169             }
170             else
171             {
172                 pointerToT = reinterpret_cast<T*>(rawMemory);
173             }
174 
175             if (constructMembers)
176             {
177                 for (std::size_t i = 0; i < amount; ++i)
178                 {
179                     new (pointerToT + i) T;
180                 }
181             }
182 
183             return pointerToT;
184         }
185 
186         return nullptr;
187     }
188 
189     /**
190      * ::new, ::delete, ::malloc, ::free, std::make_shared, and std::make_unique should not be used in SDK code
191      *  use these functions instead or Aws::MakeShared
192      */
193     template<typename T>
DeleteArray(T * pointerToTArray)194     void DeleteArray(T* pointerToTArray)
195     {
196         if (pointerToTArray == nullptr)
197         {
198             return;
199         }
200 
201         bool destroyMembers = ShouldDestroyArrayMembers<T>();
202         void* rawMemory = nullptr;
203 
204         if (destroyMembers)
205         {
206 #if defined(_MSC_VER) && _MSC_VER < 1900
207             std::size_t headerSize = (std::max)(sizeof(std::size_t), __alignof(T));
208 #else
209             std::size_t headerSize = (std::max)(sizeof(std::size_t), alignof(T));
210 #endif
211 
212             std::size_t *pointerToAmount = reinterpret_cast<std::size_t*>(reinterpret_cast<char*>(pointerToTArray) - headerSize);
213             std::size_t amount = *pointerToAmount;
214 
215             for (std::size_t i = amount; i > 0; --i)
216             {
217                 (pointerToTArray + i - 1)->~T();
218             }
219             rawMemory = reinterpret_cast<void *>(pointerToAmount);
220         }
221         else
222         {
223             rawMemory = reinterpret_cast<void *>(pointerToTArray);
224         }
225 
226         Free(rawMemory);
227     }
228 
229     /**
230      * modeled from std::default_delete
231      */
232     template<typename T>
233     struct Deleter
234     {
DeleterDeleter235         Deleter() {}
236 
237         template<class U, class = typename std::enable_if<std::is_convertible<U *, T *>::value, void>::type>
DeleterDeleter238         Deleter(const Deleter<U>&)
239         {
240         }
241 
operatorDeleter242         void operator()(T *pointerToT) const
243         {
244             static_assert(0 < sizeof(T), "can't delete an incomplete type");
245             Aws::Delete(pointerToT);
246         }
247     };
248 
249     template< typename T > using UniquePtr = std::unique_ptr< T, Deleter< T > >;
250 
251     /**
252      * ::new, ::delete, ::malloc, ::free, std::make_shared, and std::make_unique should not be used in SDK code
253      *  use these functions instead or Aws::MakeShared
254      */
255     template<typename T, typename ...ArgTypes>
MakeUnique(const char * allocationTag,ArgTypes &&...args)256     UniquePtr<T> MakeUnique(const char* allocationTag, ArgTypes&&... args)
257     {
258         return UniquePtr<T>(Aws::New<T>(allocationTag, std::forward<ArgTypes>(args)...));
259     }
260 
261     template<typename T>
262     struct ArrayDeleter
263     {
ArrayDeleterArrayDeleter264         ArrayDeleter() {}
265 
266         template<class U, class = typename std::enable_if<std::is_convertible<U *, T *>::value, void>::type>
ArrayDeleterArrayDeleter267         ArrayDeleter(const ArrayDeleter<U>&)
268         {
269         }
270 
operatorArrayDeleter271         void operator()(T *pointerToTArray) const
272         {
273             static_assert(0 < sizeof(T), "can't delete an incomplete type");
274             Aws::DeleteArray(pointerToTArray);
275         }
276     };
277 
278     template< typename T > using UniqueArrayPtr = std::unique_ptr< T, ArrayDeleter< T > >;
279 
280     /**
281      * ::new, ::delete, ::malloc, ::free, std::make_shared, and std::make_unique should not be used in SDK code
282      *  use these functions instead or Aws::MakeShared
283      */
284     template<typename T, typename ...ArgTypes>
MakeUniqueArray(std::size_t amount,const char * allocationTag,ArgTypes &&...args)285     UniqueArrayPtr<T> MakeUniqueArray(std::size_t amount, const char* allocationTag, ArgTypes&&... args)
286     {
287         return UniqueArrayPtr<T>(Aws::NewArray<T>(amount, allocationTag, std::forward<ArgTypes>(args)...));
288     }
289 
290 } // namespace Aws
291 
292