| /* |
| * Copyright 2015 Facebook, Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #ifndef FOLLY_MEMORY_H_ |
| #define FOLLY_MEMORY_H_ |
| |
| #include <folly/Traits.h> |
| |
| #include <cstddef> |
| #include <cstdlib> |
| #include <exception> |
| #include <limits> |
| #include <memory> |
| #include <stdexcept> |
| #include <utility> |
| |
| namespace folly { |
| |
| /** |
| * For exception safety and consistency with make_shared. Erase me when |
| * we have std::make_unique(). |
| * |
| * @author Louis Brandy (ldbrandy@fb.com) |
| * @author Xu Ning (xning@fb.com) |
| */ |
| |
| template<typename T, typename Dp = std::default_delete<T>, typename... Args> |
| typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T, Dp>>::type |
| make_unique(Args&&... args) { |
| return std::unique_ptr<T, Dp>(new T(std::forward<Args>(args)...)); |
| } |
| |
| // Allows 'make_unique<T[]>(10)'. (N3690 s20.9.1.4 p3-4) |
| template<typename T, typename Dp = std::default_delete<T>> |
| typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T, Dp>>::type |
| make_unique(const size_t n) { |
| return std::unique_ptr<T, Dp>(new typename std::remove_extent<T>::type[n]()); |
| } |
| |
| // Disallows 'make_unique<T[10]>()'. (N3690 s20.9.1.4 p5) |
| template<typename T, typename Dp = std::default_delete<T>, typename... Args> |
| typename std::enable_if< |
| std::extent<T>::value != 0, std::unique_ptr<T, Dp>>::type |
| make_unique(Args&&...) = delete; |
| |
| /** |
| * static_function_deleter |
| * |
| * So you can write this: |
| * |
| * using RSA_deleter = folly::static_function_deleter<RSA, &RSA_free>; |
| * auto rsa = std::unique_ptr<RSA, RSA_deleter>(RSA_new()); |
| * RSA_generate_key_ex(rsa.get(), bits, exponent, nullptr); |
| * rsa = nullptr; // calls RSA_free(rsa.get()) |
| * |
| * This would be sweet as well for BIO, but unfortunately BIO_free has signature |
| * int(BIO*) while we require signature void(BIO*). So you would need to make a |
| * wrapper for it: |
| * |
| * inline void BIO_free_fb(BIO* bio) { CHECK_EQ(1, BIO_free(bio)); } |
| * using BIO_deleter = folly::static_function_deleter<BIO, &BIO_free_fb>; |
| * auto buf = std::unique_ptr<BIO, BIO_deleter>(BIO_new(BIO_s_mem())); |
| * buf = nullptr; // calls BIO_free(buf.get()) |
| */ |
| |
| template <typename T, void(*f)(T*)> |
| struct static_function_deleter { |
| void operator()(T* t) { f(t); } |
| }; |
| |
| /** |
| * to_shared_ptr |
| * |
| * Convert unique_ptr to shared_ptr without specifying the template type |
| * parameter and letting the compiler deduce it. |
| * |
| * So you can write this: |
| * |
| * auto sptr = to_shared_ptr(getSomethingUnique<T>()); |
| * |
| * Instead of this: |
| * |
| * auto sptr = shared_ptr<T>(getSomethingUnique<T>()); |
| * |
| * Useful when `T` is long, such as: |
| * |
| * using T = foobar::cpp2::FooBarServiceAsyncClient; |
| */ |
| template <typename T> |
| std::shared_ptr<T> to_shared_ptr(std::unique_ptr<T>&& ptr) { |
| return std::shared_ptr<T>(std::move(ptr)); |
| } |
| |
| /** |
| * A SimpleAllocator must provide two methods: |
| * |
| * void* allocate(size_t size); |
| * void deallocate(void* ptr); |
| * |
| * which, respectively, allocate a block of size bytes (aligned to the |
| * maximum alignment required on your system), throwing std::bad_alloc |
| * if the allocation can't be satisfied, and free a previously |
| * allocated block. |
| * |
| * SysAlloc resembles the standard allocator. |
| */ |
| class SysAlloc { |
| public: |
| void* allocate(size_t size) { |
| void* p = ::malloc(size); |
| if (!p) throw std::bad_alloc(); |
| return p; |
| } |
| void deallocate(void* p) { |
| ::free(p); |
| } |
| }; |
| |
| /** |
| * StlAllocator wraps a SimpleAllocator into a STL-compliant |
| * allocator, maintaining an instance pointer to the simple allocator |
| * object. The underlying SimpleAllocator object must outlive all |
| * instances of StlAllocator using it. |
| * |
| * But note that if you pass StlAllocator<MallocAllocator,...> to a |
| * standard container it will be larger due to the contained state |
| * pointer. |
| * |
| * @author: Tudor Bosman <tudorb@fb.com> |
| */ |
| |
| // This would be so much simpler with std::allocator_traits, but gcc 4.6.2 |
| // doesn't support it. |
| template <class Alloc, class T> class StlAllocator; |
| |
| template <class Alloc> class StlAllocator<Alloc, void> { |
| public: |
| typedef void value_type; |
| typedef void* pointer; |
| typedef const void* const_pointer; |
| |
| StlAllocator() : alloc_(nullptr) { } |
| explicit StlAllocator(Alloc* a) : alloc_(a) { } |
| |
| Alloc* alloc() const { |
| return alloc_; |
| } |
| |
| template <class U> struct rebind { |
| typedef StlAllocator<Alloc, U> other; |
| }; |
| |
| bool operator!=(const StlAllocator<Alloc, void>& other) const { |
| return alloc_ != other.alloc_; |
| } |
| |
| bool operator==(const StlAllocator<Alloc, void>& other) const { |
| return alloc_ == other.alloc_; |
| } |
| |
| private: |
| Alloc* alloc_; |
| }; |
| |
| template <class Alloc, class T> |
| class StlAllocator { |
| public: |
| typedef T value_type; |
| typedef T* pointer; |
| typedef const T* const_pointer; |
| typedef T& reference; |
| typedef const T& const_reference; |
| |
| typedef ptrdiff_t difference_type; |
| typedef size_t size_type; |
| |
| StlAllocator() : alloc_(nullptr) { } |
| explicit StlAllocator(Alloc* a) : alloc_(a) { } |
| |
| template <class U> StlAllocator(const StlAllocator<Alloc, U>& other) |
| : alloc_(other.alloc()) { } |
| |
| T* allocate(size_t n, const void* hint = nullptr) { |
| return static_cast<T*>(alloc_->allocate(n * sizeof(T))); |
| } |
| |
| void deallocate(T* p, size_t n) { |
| alloc_->deallocate(p); |
| } |
| |
| size_t max_size() const { |
| return std::numeric_limits<size_t>::max(); |
| } |
| |
| T* address(T& x) const { |
| return std::addressof(x); |
| } |
| |
| const T* address(const T& x) const { |
| return std::addressof(x); |
| } |
| |
| template <class... Args> |
| void construct(T* p, Args&&... args) { |
| new (p) T(std::forward<Args>(args)...); |
| } |
| |
| void destroy(T* p) { |
| p->~T(); |
| } |
| |
| Alloc* alloc() const { |
| return alloc_; |
| } |
| |
| template <class U> struct rebind { |
| typedef StlAllocator<Alloc, U> other; |
| }; |
| |
| bool operator!=(const StlAllocator<Alloc, T>& other) const { |
| return alloc_ != other.alloc_; |
| } |
| |
| bool operator==(const StlAllocator<Alloc, T>& other) const { |
| return alloc_ == other.alloc_; |
| } |
| |
| private: |
| Alloc* alloc_; |
| }; |
| |
| /** |
| * Helper function to obtain rebound allocators |
| * |
| * @author: Marcelo Juchem <marcelo@fb.com> |
| */ |
| template <typename T, typename Allocator> |
| typename Allocator::template rebind<T>::other rebind_allocator( |
| Allocator const& allocator |
| ) { |
| return typename Allocator::template rebind<T>::other(allocator); |
| } |
| |
| /* |
| * Helper classes/functions for creating a unique_ptr using a custom |
| * allocator. |
| * |
| * @author: Marcelo Juchem <marcelo@fb.com> |
| */ |
| |
| // Derives from the allocator to take advantage of the empty base |
| // optimization when possible. |
| template <typename Allocator> |
| class allocator_delete |
| : private std::remove_reference<Allocator>::type |
| { |
| typedef typename std::remove_reference<Allocator>::type allocator_type; |
| |
| public: |
| typedef typename Allocator::pointer pointer; |
| |
| allocator_delete() = default; |
| |
| explicit allocator_delete(const allocator_type& allocator) |
| : allocator_type(allocator) |
| {} |
| |
| explicit allocator_delete(allocator_type&& allocator) |
| : allocator_type(std::move(allocator)) |
| {} |
| |
| template <typename U> |
| allocator_delete(const allocator_delete<U>& other) |
| : allocator_type(other.get_allocator()) |
| {} |
| |
| allocator_type& get_allocator() const { |
| return *const_cast<allocator_delete*>(this); |
| } |
| |
| void operator()(pointer p) const { |
| if (!p) return; |
| const_cast<allocator_delete*>(this)->destroy(p); |
| const_cast<allocator_delete*>(this)->deallocate(p, 1); |
| } |
| }; |
| |
| template <typename T, typename Allocator> |
| class is_simple_allocator { |
| FOLLY_CREATE_HAS_MEMBER_FN_TRAITS(has_destroy, destroy); |
| |
| typedef typename std::remove_const< |
| typename std::remove_reference<Allocator>::type |
| >::type allocator; |
| typedef typename std::remove_reference<T>::type value_type; |
| typedef value_type* pointer; |
| |
| public: |
| constexpr static bool value = !has_destroy<allocator, void(pointer)>::value |
| && !has_destroy<allocator, void(void*)>::value; |
| }; |
| |
| template <typename T, typename Allocator> |
| struct as_stl_allocator { |
| typedef typename std::conditional< |
| is_simple_allocator<T, Allocator>::value, |
| folly::StlAllocator< |
| typename std::remove_reference<Allocator>::type, |
| typename std::remove_reference<T>::type |
| >, |
| typename std::remove_reference<Allocator>::type |
| >::type type; |
| }; |
| |
| template <typename T, typename Allocator> |
| typename std::enable_if< |
| is_simple_allocator<T, Allocator>::value, |
| folly::StlAllocator< |
| typename std::remove_reference<Allocator>::type, |
| typename std::remove_reference<T>::type |
| > |
| >::type make_stl_allocator(Allocator&& allocator) { |
| return folly::StlAllocator< |
| typename std::remove_reference<Allocator>::type, |
| typename std::remove_reference<T>::type |
| >(&allocator); |
| } |
| |
| template <typename T, typename Allocator> |
| typename std::enable_if< |
| !is_simple_allocator<T, Allocator>::value, |
| typename std::remove_reference<Allocator>::type |
| >::type make_stl_allocator(Allocator&& allocator) { |
| return std::move(allocator); |
| } |
| |
| /** |
| * AllocatorUniquePtr: a unique_ptr that supports both STL-style |
| * allocators and SimpleAllocator |
| * |
| * @author: Marcelo Juchem <marcelo@fb.com> |
| */ |
| |
| template <typename T, typename Allocator> |
| struct AllocatorUniquePtr { |
| typedef std::unique_ptr<T, |
| folly::allocator_delete< |
| typename std::conditional< |
| is_simple_allocator<T, Allocator>::value, |
| folly::StlAllocator<typename std::remove_reference<Allocator>::type, T>, |
| typename std::remove_reference<Allocator>::type |
| >::type |
| > |
| > type; |
| }; |
| |
| /** |
| * Functions to allocate a unique_ptr / shared_ptr, supporting both |
| * STL-style allocators and SimpleAllocator, analog to std::allocate_shared |
| * |
| * @author: Marcelo Juchem <marcelo@fb.com> |
| */ |
| |
| template <typename T, typename Allocator, typename ...Args> |
| typename AllocatorUniquePtr<T, Allocator>::type allocate_unique( |
| Allocator&& allocator, Args&&... args |
| ) { |
| auto stlAllocator = folly::make_stl_allocator<T>( |
| std::forward<Allocator>(allocator) |
| ); |
| auto p = stlAllocator.allocate(1); |
| |
| try { |
| stlAllocator.construct(p, std::forward<Args>(args)...); |
| |
| return {p, |
| folly::allocator_delete<decltype(stlAllocator)>(std::move(stlAllocator)) |
| }; |
| } catch (...) { |
| stlAllocator.deallocate(p, 1); |
| throw; |
| } |
| } |
| |
| template <typename T, typename Allocator, typename ...Args> |
| std::shared_ptr<T> allocate_shared(Allocator&& allocator, Args&&... args) { |
| return std::allocate_shared<T>( |
| folly::make_stl_allocator<T>(std::forward<Allocator>(allocator)), |
| std::forward<Args>(args)... |
| ); |
| } |
| |
| /** |
| * IsArenaAllocator<T>::value describes whether SimpleAllocator has |
| * no-op deallocate(). |
| */ |
| template <class T> struct IsArenaAllocator : std::false_type { }; |
| |
| } // namespace folly |
| |
| #endif /* FOLLY_MEMORY_H_ */ |