e4db242ba22edc24065a249e67bc5416f47944b3
[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
82   StlAllocator() : alloc_(nullptr) { }
83   explicit StlAllocator(Alloc* alloc) : alloc_(alloc) { }
84
85   Alloc* alloc() const {
86     return alloc_;
87   }
88
89   template <class U> struct rebind {
90     typedef StlAllocator<Alloc, U> other;
91   };
92
93   bool operator!=(const StlAllocator<Alloc, void>& other) const {
94     return alloc_ != other.alloc_;
95   }
96
97   bool operator==(const StlAllocator<Alloc, void>& other) const {
98     return alloc_ == other.alloc_;
99   }
100
101  private:
102   Alloc* alloc_;
103 };
104
105 template <class Alloc, class T>
106 class StlAllocator {
107  public:
108   typedef T value_type;
109   typedef T* pointer;
110   typedef const T* const_pointer;
111   typedef T& reference;
112   typedef const T& const_reference;
113
114   typedef ptrdiff_t difference_type;
115   typedef size_t size_type;
116
117   StlAllocator() : alloc_(nullptr) { }
118   explicit StlAllocator(Alloc* alloc) : alloc_(alloc) { }
119
120   template <class U> StlAllocator(const StlAllocator<Alloc, U>& other)
121     : alloc_(other.alloc()) { }
122
123   T* allocate(size_t n, const void* hint = nullptr) {
124     return static_cast<T*>(alloc_->allocate(n * sizeof(T)));
125   }
126
127   void deallocate(T* p, size_t n) {
128     alloc_->deallocate(p);
129   }
130
131   size_t max_size() const {
132     return std::numeric_limits<size_t>::max();
133   }
134
135   T* address(T& x) const {
136     return std::addressof(x);
137   }
138
139   const T* address(const T& x) const {
140     return std::addressof(x);
141   }
142
143   template <class... Args>
144   void construct(T* p, Args&&... args) {
145     new (p) T(std::forward<Args>(args)...);
146   }
147
148   void destroy(T* p) {
149     p->~T();
150   }
151
152   Alloc* alloc() const {
153     return alloc_;
154   }
155
156   template <class U> struct rebind {
157     typedef StlAllocator<Alloc, U> other;
158   };
159
160   bool operator!=(const StlAllocator<Alloc, T>& other) const {
161     return alloc_ != other.alloc_;
162   }
163
164   bool operator==(const StlAllocator<Alloc, T>& other) const {
165     return alloc_ == other.alloc_;
166   }
167
168  private:
169   Alloc* alloc_;
170 };
171
172 /*
173  * Helper classes/functions for creating a unique_ptr using a custom allocator
174  *
175  * @author: Marcelo Juchem <marcelo@fb.com>
176  */
177
178 // A deleter implementation based on std::default_delete,
179 // which uses a custom allocator to free memory
180 template <typename Allocator>
181 class allocator_delete {
182   typedef typename std::remove_reference<Allocator>::type allocator_type;
183
184 public:
185   allocator_delete() = default;
186
187   explicit allocator_delete(const allocator_type& allocator):
188     allocator_(allocator)
189   {}
190
191   explicit allocator_delete(allocator_type&& allocator):
192     allocator_(std::move(allocator))
193   {}
194
195   template <typename U>
196   allocator_delete(const allocator_delete<U>& other):
197     allocator_(other.get_allocator())
198   {}
199
200   allocator_type& get_allocator() const {
201     return allocator_;
202   }
203
204   void operator()(typename allocator_type::pointer p) const {
205     if (!p) {
206       return;
207     }
208
209     allocator_.destroy(p);
210     allocator_.deallocate(p, 1);
211   }
212
213 private:
214   mutable allocator_type allocator_;
215 };
216
217 template <typename T, typename Allocator>
218 class is_simple_allocator {
219   FOLLY_CREATE_HAS_MEMBER_FN_TRAITS(has_destroy, destroy);
220
221   typedef typename std::remove_const<
222     typename std::remove_reference<Allocator>::type
223   >::type allocator;
224   typedef typename std::remove_reference<T>::type value_type;
225   typedef value_type* pointer;
226
227 public:
228   constexpr static bool value = !has_destroy<allocator, void(pointer)>::value
229     && !has_destroy<allocator, void(void*)>::value;
230 };
231
232 template <typename T, typename Allocator>
233 struct as_stl_allocator {
234   typedef typename std::conditional<
235     is_simple_allocator<T, Allocator>::value,
236     folly::StlAllocator<
237       typename std::remove_reference<Allocator>::type,
238       typename std::remove_reference<T>::type
239     >,
240     typename std::remove_reference<Allocator>::type
241   >::type type;
242 };
243
244 template <typename T, typename Allocator>
245 typename std::enable_if<
246   is_simple_allocator<T, Allocator>::value,
247   folly::StlAllocator<
248     typename std::remove_reference<Allocator>::type,
249     typename std::remove_reference<T>::type
250   >
251 >::type make_stl_allocator(Allocator&& allocator) {
252   return folly::StlAllocator<
253     typename std::remove_reference<Allocator>::type,
254     typename std::remove_reference<T>::type
255   >(&allocator);
256 }
257
258 template <typename T, typename Allocator>
259 typename std::enable_if<
260   !is_simple_allocator<T, Allocator>::value,
261   typename std::remove_reference<Allocator>::type
262 >::type make_stl_allocator(Allocator&& allocator) {
263   return std::move(allocator);
264 }
265
266 /**
267  * AllocatorUniquePtr: a unique_ptr that supports both STL-style
268  * allocators and SimpleAllocator
269  *
270  * @author: Marcelo Juchem <marcelo@fb.com>
271  */
272
273 template <typename T, typename Allocator>
274 struct AllocatorUniquePtr {
275   typedef std::unique_ptr<T,
276     folly::allocator_delete<
277       typename std::conditional<
278         is_simple_allocator<T, Allocator>::value,
279         folly::StlAllocator<typename std::remove_reference<Allocator>::type, T>,
280         typename std::remove_reference<Allocator>::type
281       >::type
282     >
283   > type;
284 };
285
286 /**
287  * Functions to allocate a unique_ptr / shared_ptr, supporting both
288  * STL-style allocators and SimpleAllocator, analog to std::allocate_shared
289  *
290  * @author: Marcelo Juchem <marcelo@fb.com>
291  */
292
293 template <typename T, typename Allocator, typename ...Args>
294 typename AllocatorUniquePtr<T, Allocator>::type allocate_unique(
295   Allocator&& allocator, Args&&... args
296 ) {
297   auto stlAllocator = folly::make_stl_allocator<T>(
298     std::forward<Allocator>(allocator)
299   );
300   auto p = stlAllocator.allocate(1);
301
302   try {
303     stlAllocator.construct(p, std::forward<Args>(args)...);
304
305     return {p,
306       folly::allocator_delete<decltype(stlAllocator)>(std::move(stlAllocator))
307     };
308   } catch (...) {
309     stlAllocator.deallocate(p, 1);
310     throw;
311   }
312 }
313
314 template <typename T, typename Allocator, typename ...Args>
315 std::shared_ptr<T> allocate_shared(Allocator&& allocator, Args&&... args) {
316   return std::allocate_shared<T>(
317     folly::make_stl_allocator<T>(std::forward<Allocator>(allocator)),
318     std::forward<Args>(args)...
319   );
320 }
321
322 }  // namespace folly
323
324 #endif /* FOLLY_MEMORY_H_ */