09f10f36b351ae79c2aee496b20628c3c8a30304
[folly.git] / folly / Memory.h
1 /*
2  * Copyright 2013 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #ifndef FOLLY_MEMORY_H_
18 #define FOLLY_MEMORY_H_
19
20 #include "folly/Traits.h"
21
22 #include <memory>
23 #include <limits>
24 #include <utility>
25 #include <exception>
26 #include <stdexcept>
27
28 #include <cstddef>
29
30 namespace folly {
31
32 /**
33  * For exception safety and consistency with make_shared. Erase me when
34  * we have std::make_unique().
35  *
36  * @author Louis Brandy (ldbrandy@fb.com)
37  */
38
39 template<typename T, typename... Args>
40 std::unique_ptr<T> make_unique(Args&&... args) {
41   return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
42 }
43
44 /**
45  * Wrap a SimpleAllocator into a STL-compliant allocator.
46  *
47  * The SimpleAllocator must provide two methods:
48  *    void* allocate(size_t size);
49  *    void deallocate(void* ptr);
50  * which, respectively, allocate a block of size bytes (aligned to the maximum
51  * alignment required on your system), throwing std::bad_alloc if the
52  * allocation can't be satisfied, and free a previously allocated block.
53  *
54  * Note that the following allocator resembles the standard allocator
55  * quite well:
56  *
57  * class MallocAllocator {
58  *  public:
59  *   void* allocate(size_t size) {
60  *     void* p = malloc(size);
61  *     if (!p) throw std::bad_alloc();
62  *     return p;
63  *   }
64  *   void deallocate(void* p) {
65  *     free(p);
66  *   }
67  * };
68  *
69  * author: Tudor Bosman <tudorb@fb.com>
70  */
71
72 // This would be so much simpler with std::allocator_traits, but gcc 4.6.2
73 // doesn't support it
74 template <class Alloc, class T> class StlAllocator;
75
76 template <class Alloc> class StlAllocator<Alloc, void> {
77  public:
78   typedef void value_type;
79   typedef void* pointer;
80   typedef const void* const_pointer;
81   template <class U> struct rebind {
82     typedef StlAllocator<Alloc, U> other;
83   };
84 };
85
86 template <class Alloc, class T>
87 class StlAllocator {
88  public:
89   typedef T value_type;
90   typedef T* pointer;
91   typedef const T* const_pointer;
92   typedef T& reference;
93   typedef const T& const_reference;
94
95   typedef ptrdiff_t difference_type;
96   typedef size_t size_type;
97
98   StlAllocator() : alloc_(nullptr) { }
99   explicit StlAllocator(Alloc* alloc) : alloc_(alloc) { }
100
101   template <class U> StlAllocator(const StlAllocator<Alloc, U>& other)
102     : alloc_(other.alloc()) { }
103
104   T* allocate(size_t n, const void* hint = nullptr) {
105     return static_cast<T*>(alloc_->allocate(n * sizeof(T)));
106   }
107
108   void deallocate(T* p, size_t n) {
109     alloc_->deallocate(p);
110   }
111
112   size_t max_size() const {
113     return std::numeric_limits<size_t>::max();
114   }
115
116   T* address(T& x) const {
117     return std::addressof(x);
118   }
119
120   const T* address(const T& x) const {
121     return std::addressof(x);
122   }
123
124   template <class... Args>
125   void construct(T* p, Args&&... args) {
126     new (p) T(std::forward<Args>(args)...);
127   }
128
129   void destroy(T* p) {
130     p->~T();
131   }
132
133   Alloc* alloc() const {
134     return alloc_;
135   }
136
137   template <class U> struct rebind {
138     typedef StlAllocator<Alloc, U> other;
139   };
140
141   bool operator!=(const StlAllocator<Alloc, T>& other) const {
142     return alloc_ != other.alloc_;
143   }
144
145   bool operator==(const StlAllocator<Alloc, T>& other) const {
146     return alloc_ == other.alloc_;
147   }
148
149  private:
150   Alloc* alloc_;
151 };
152
153 /*
154  * Helper classes/functions for creating a unique_ptr using a custom allocator
155  *
156  * @author: Marcelo Juchem <marcelo@fb.com>
157  */
158
159 // A deleter implementation based on std::default_delete,
160 // which uses a custom allocator to free memory
161 template <typename Allocator>
162 class allocator_delete {
163   typedef typename std::remove_reference<Allocator>::type allocator_type;
164
165 public:
166   allocator_delete() = default;
167
168   explicit allocator_delete(const allocator_type& allocator):
169     allocator_(allocator)
170   {}
171
172   explicit allocator_delete(allocator_type&& allocator):
173     allocator_(std::move(allocator))
174   {}
175
176   template <typename U>
177   allocator_delete(const allocator_delete<U>& other):
178     allocator_(other.get_allocator())
179   {}
180
181   allocator_type& get_allocator() const {
182     return allocator_;
183   }
184
185   void operator()(typename allocator_type::pointer p) const {
186     if (!p) {
187       return;
188     }
189
190     allocator_.destroy(p);
191     allocator_.deallocate(p, 1);
192   }
193
194 private:
195   mutable allocator_type allocator_;
196 };
197
198 template <typename T, typename Allocator>
199 class is_simple_allocator {
200   FOLLY_CREATE_HAS_MEMBER_FN_TRAITS(has_destroy, destroy);
201
202   typedef typename std::remove_const<
203     typename std::remove_reference<Allocator>::type
204   >::type allocator;
205   typedef typename std::remove_reference<T>::type value_type;
206   typedef value_type* pointer;
207
208 public:
209   constexpr static bool value = !has_destroy<allocator, void(pointer)>::value
210     && !has_destroy<allocator, void(void*)>::value;
211 };
212
213 template <typename T, typename Allocator>
214 typename std::enable_if<
215   is_simple_allocator<T, Allocator>::value,
216   folly::StlAllocator<
217     typename std::remove_reference<Allocator>::type,
218     typename std::remove_reference<T>::type
219   >
220 >::type make_stl_allocator(Allocator&& allocator) {
221   return folly::StlAllocator<
222     typename std::remove_reference<Allocator>::type,
223     typename std::remove_reference<T>::type
224   >(&allocator);
225 }
226
227 template <typename T, typename Allocator>
228 typename std::enable_if<
229   !is_simple_allocator<T, Allocator>::value,
230   typename std::remove_reference<Allocator>::type
231 >::type make_stl_allocator(Allocator&& allocator) {
232   return std::move(allocator);
233 }
234
235 template <typename T, typename Allocator>
236 struct AllocatorUniquePtr {
237   typedef std::unique_ptr<T,
238     folly::allocator_delete<
239       typename std::conditional<
240         is_simple_allocator<T, Allocator>::value,
241         folly::StlAllocator<typename std::remove_reference<Allocator>::type, T>,
242         typename std::remove_reference<Allocator>::type
243       >::type
244     >
245   > type;
246 };
247
248 template <typename T, typename Allocator, typename ...Args>
249 typename AllocatorUniquePtr<T, Allocator>::type allocate_unique(
250   Allocator&& allocator, Args&&... args
251 ) {
252   auto stlAllocator = folly::make_stl_allocator<T>(
253     std::forward<Allocator>(allocator)
254   );
255   auto p = stlAllocator.allocate(1);
256
257   try {
258     stlAllocator.construct(p, std::forward<Args>(args)...);
259
260     return {p,
261       folly::allocator_delete<decltype(stlAllocator)>(std::move(stlAllocator))
262     };
263   } catch (...) {
264     stlAllocator.deallocate(p, 1);
265     throw;
266   }
267 }
268
269 template <typename T, typename Allocator, typename ...Args>
270 std::shared_ptr<T> allocate_shared(Allocator&& allocator, Args&&... args) {
271   return std::allocate_shared<T>(
272     folly::make_stl_allocator<T>(std::forward<Allocator>(allocator)),
273     std::forward<Args>(args)...
274   );
275 }
276
277 }  // namespace folly
278
279 #endif /* FOLLY_MEMORY_H_ */