756 lines
23 KiB
C++
756 lines
23 KiB
C++
/*
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <cassert>
|
|
#include <cerrno>
|
|
#include <cstddef>
|
|
#include <cstdlib>
|
|
#include <exception>
|
|
#include <limits>
|
|
#include <memory>
|
|
#include <stdexcept>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
#include <folly/ConstexprMath.h>
|
|
#include <folly/Likely.h>
|
|
#include <folly/Traits.h>
|
|
#include <folly/functional/Invoke.h>
|
|
#include <folly/lang/Align.h>
|
|
#include <folly/lang/Exception.h>
|
|
#include <folly/portability/Config.h>
|
|
#include <folly/portability/Malloc.h>
|
|
|
|
namespace folly {
|
|
|
|
/// allocateBytes and deallocateBytes work like a checkedMalloc/free pair,
|
|
/// but take advantage of sized deletion when available
|
|
inline void* allocateBytes(size_t n) {
|
|
return ::operator new(n);
|
|
}
|
|
|
|
inline void deallocateBytes(void* p, size_t n) {
|
|
#if __cpp_sized_deallocation
|
|
return ::operator delete(p, n);
|
|
#else
|
|
(void)n;
|
|
return ::operator delete(p);
|
|
#endif
|
|
}
|
|
|
|
#if _POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600 || \
|
|
(defined(__ANDROID__) && (__ANDROID_API__ > 16)) || \
|
|
(defined(__APPLE__) && \
|
|
(__MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_6 || \
|
|
__IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_3_0)) || \
|
|
defined(__FreeBSD__) || defined(__wasm32__)
|
|
|
|
inline void* aligned_malloc(size_t size, size_t align) {
|
|
// use posix_memalign, but mimic the behaviour of memalign
|
|
void* ptr = nullptr;
|
|
int rc = posix_memalign(&ptr, align, size);
|
|
return rc == 0 ? (errno = 0, ptr) : (errno = rc, nullptr);
|
|
}
|
|
|
|
inline void aligned_free(void* aligned_ptr) {
|
|
free(aligned_ptr);
|
|
}
|
|
|
|
#elif defined(_WIN32)
|
|
|
|
inline void* aligned_malloc(size_t size, size_t align) {
|
|
return _aligned_malloc(size, align);
|
|
}
|
|
|
|
inline void aligned_free(void* aligned_ptr) {
|
|
_aligned_free(aligned_ptr);
|
|
}
|
|
|
|
#else
|
|
|
|
inline void* aligned_malloc(size_t size, size_t align) {
|
|
return memalign(align, size);
|
|
}
|
|
|
|
inline void aligned_free(void* aligned_ptr) {
|
|
free(aligned_ptr);
|
|
}
|
|
|
|
#endif
|
|
|
|
namespace detail {
|
|
template <typename Alloc, size_t kAlign, bool kAllocate>
|
|
void rawOverAlignedImpl(Alloc const& alloc, size_t n, void*& raw) {
|
|
static_assert((kAlign & (kAlign - 1)) == 0, "Align must be a power of 2");
|
|
|
|
using AllocTraits = std::allocator_traits<Alloc>;
|
|
using T = typename AllocTraits::value_type;
|
|
|
|
constexpr bool kCanBypass = std::is_same<Alloc, std::allocator<T>>::value;
|
|
|
|
// BaseType is a type that gives us as much alignment as we need if
|
|
// we can get it naturally, otherwise it is aligned as max_align_t.
|
|
// kBaseAlign is both the alignment and size of this type.
|
|
constexpr size_t kBaseAlign = constexpr_min(kAlign, alignof(max_align_t));
|
|
using BaseType = std::aligned_storage_t<kBaseAlign, kBaseAlign>;
|
|
using BaseAllocTraits =
|
|
typename AllocTraits::template rebind_traits<BaseType>;
|
|
using BaseAlloc = typename BaseAllocTraits::allocator_type;
|
|
static_assert(
|
|
sizeof(BaseType) == kBaseAlign && alignof(BaseType) == kBaseAlign, "");
|
|
|
|
#if __cpp_sized_deallocation
|
|
if (kCanBypass && kAlign == kBaseAlign) {
|
|
// until std::allocator uses sized deallocation, it is worth the
|
|
// effort to bypass it when we are able
|
|
if (kAllocate) {
|
|
raw = ::operator new(n * sizeof(T));
|
|
} else {
|
|
::operator delete(raw, n * sizeof(T));
|
|
}
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (kCanBypass && kAlign > kBaseAlign) {
|
|
// allocating as BaseType isn't sufficient to get alignment, but
|
|
// since we can bypass Alloc we can use something like posix_memalign
|
|
if (kAllocate) {
|
|
raw = aligned_malloc(n * sizeof(T), kAlign);
|
|
} else {
|
|
aligned_free(raw);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// we're not allowed to bypass Alloc, or we don't want to
|
|
BaseAlloc a(alloc);
|
|
|
|
// allocation size is counted in sizeof(BaseType)
|
|
size_t quanta = (n * sizeof(T) + kBaseAlign - 1) / sizeof(BaseType);
|
|
if (kAlign <= kBaseAlign) {
|
|
// rebinding Alloc to BaseType is sufficient to get us the alignment
|
|
// we want, happy path
|
|
if (kAllocate) {
|
|
raw = static_cast<void*>(
|
|
std::addressof(*BaseAllocTraits::allocate(a, quanta)));
|
|
} else {
|
|
BaseAllocTraits::deallocate(
|
|
a,
|
|
std::pointer_traits<typename BaseAllocTraits::pointer>::pointer_to(
|
|
*static_cast<BaseType*>(raw)),
|
|
quanta);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Overaligned and custom allocator, our only option is to
|
|
// overallocate and store a delta to the actual allocation just
|
|
// before the returned ptr.
|
|
//
|
|
// If we give ourselves kAlign extra bytes, then since
|
|
// sizeof(BaseType) divides kAlign we can meet alignment while
|
|
// getting a prefix of one BaseType. If we happen to get a
|
|
// kAlign-aligned block, then we can return a pointer to underlying
|
|
// + kAlign, otherwise there will be at least kBaseAlign bytes in
|
|
// the unused prefix of the first kAlign-aligned block.
|
|
if (kAllocate) {
|
|
char* base = reinterpret_cast<char*>(std::addressof(
|
|
*BaseAllocTraits::allocate(a, quanta + kAlign / sizeof(BaseType))));
|
|
size_t byteDelta =
|
|
kAlign - (reinterpret_cast<uintptr_t>(base) & (kAlign - 1));
|
|
raw = static_cast<void*>(base + byteDelta);
|
|
static_cast<size_t*>(raw)[-1] = byteDelta;
|
|
} else {
|
|
size_t byteDelta = static_cast<size_t*>(raw)[-1];
|
|
char* base = static_cast<char*>(raw) - byteDelta;
|
|
BaseAllocTraits::deallocate(
|
|
a,
|
|
std::pointer_traits<typename BaseAllocTraits::pointer>::pointer_to(
|
|
*reinterpret_cast<BaseType*>(base)),
|
|
quanta + kAlign / sizeof(BaseType));
|
|
}
|
|
}
|
|
} // namespace detail
|
|
|
|
// Works like std::allocator_traits<Alloc>::allocate, but handles
|
|
// over-aligned types. Feel free to manually specify any power of two as
|
|
// the Align template arg. Must be matched with deallocateOverAligned.
|
|
// allocationBytesForOverAligned will give you the number of bytes that
|
|
// this function actually requests.
|
|
template <
|
|
typename Alloc,
|
|
size_t kAlign = alignof(typename std::allocator_traits<Alloc>::value_type)>
|
|
typename std::allocator_traits<Alloc>::pointer allocateOverAligned(
|
|
Alloc const& alloc,
|
|
size_t n) {
|
|
void* raw = nullptr;
|
|
detail::rawOverAlignedImpl<Alloc, kAlign, true>(alloc, n, raw);
|
|
return std::pointer_traits<typename std::allocator_traits<Alloc>::pointer>::
|
|
pointer_to(
|
|
*static_cast<typename std::allocator_traits<Alloc>::value_type*>(
|
|
raw));
|
|
}
|
|
|
|
template <
|
|
typename Alloc,
|
|
size_t kAlign = alignof(typename std::allocator_traits<Alloc>::value_type)>
|
|
void deallocateOverAligned(
|
|
Alloc const& alloc,
|
|
typename std::allocator_traits<Alloc>::pointer ptr,
|
|
size_t n) {
|
|
void* raw = static_cast<void*>(std::addressof(*ptr));
|
|
detail::rawOverAlignedImpl<Alloc, kAlign, false>(alloc, n, raw);
|
|
}
|
|
|
|
template <
|
|
typename Alloc,
|
|
size_t kAlign = alignof(typename std::allocator_traits<Alloc>::value_type)>
|
|
size_t allocationBytesForOverAligned(size_t n) {
|
|
static_assert((kAlign & (kAlign - 1)) == 0, "Align must be a power of 2");
|
|
|
|
using AllocTraits = std::allocator_traits<Alloc>;
|
|
using T = typename AllocTraits::value_type;
|
|
|
|
constexpr size_t kBaseAlign = constexpr_min(kAlign, alignof(max_align_t));
|
|
|
|
if (kAlign > kBaseAlign && std::is_same<Alloc, std::allocator<T>>::value) {
|
|
return n * sizeof(T);
|
|
} else {
|
|
size_t quanta = (n * sizeof(T) + kBaseAlign - 1) / kBaseAlign;
|
|
if (kAlign > kBaseAlign) {
|
|
quanta += kAlign / kBaseAlign;
|
|
}
|
|
return quanta * kBaseAlign;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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)
|
|
*/
|
|
|
|
#if __cplusplus >= 201402L || __cpp_lib_make_unique >= 201304L || \
|
|
(__ANDROID__ && __cplusplus >= 201300L) || _MSC_VER >= 1900
|
|
|
|
/* using override */ using std::make_unique;
|
|
|
|
#else
|
|
|
|
template <typename T, typename... Args>
|
|
typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type
|
|
make_unique(Args&&... args) {
|
|
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
|
}
|
|
|
|
// Allows 'make_unique<T[]>(10)'. (N3690 s20.9.1.4 p3-4)
|
|
template <typename T>
|
|
typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type
|
|
make_unique(const size_t n) {
|
|
return std::unique_ptr<T>(new typename std::remove_extent<T>::type[n]());
|
|
}
|
|
|
|
// Disallows 'make_unique<T[10]>()'. (N3690 s20.9.1.4 p5)
|
|
template <typename T, typename... Args>
|
|
typename std::enable_if<std::extent<T>::value != 0, std::unique_ptr<T>>::type
|
|
make_unique(Args&&...) = delete;
|
|
|
|
#endif
|
|
|
|
/**
|
|
* 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) const {
|
|
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::FooBarAsyncClient;
|
|
*/
|
|
template <typename T, typename D>
|
|
std::shared_ptr<T> to_shared_ptr(std::unique_ptr<T, D>&& ptr) {
|
|
return std::shared_ptr<T>(std::move(ptr));
|
|
}
|
|
|
|
/**
|
|
* to_weak_ptr
|
|
*
|
|
* Make a weak_ptr and return it from a shared_ptr without specifying the
|
|
* template type parameter and letting the compiler deduce it.
|
|
*
|
|
* So you can write this:
|
|
*
|
|
* auto wptr = to_weak_ptr(getSomethingShared<T>());
|
|
*
|
|
* Instead of this:
|
|
*
|
|
* auto wptr = weak_ptr<T>(getSomethingShared<T>());
|
|
*
|
|
* Useful when `T` is long, such as:
|
|
*
|
|
* using T = foobar::FooBarAsyncClient;
|
|
*/
|
|
template <typename T>
|
|
std::weak_ptr<T> to_weak_ptr(const std::shared_ptr<T>& ptr) {
|
|
return std::weak_ptr<T>(ptr);
|
|
}
|
|
|
|
namespace detail {
|
|
template <typename T>
|
|
struct lift_void_to_char {
|
|
using type = T;
|
|
};
|
|
template <>
|
|
struct lift_void_to_char<void> {
|
|
using type = char;
|
|
};
|
|
} // namespace detail
|
|
|
|
/**
|
|
* SysAllocator
|
|
*
|
|
* Resembles std::allocator, the default Allocator, but wraps std::malloc and
|
|
* std::free.
|
|
*/
|
|
template <typename T>
|
|
class SysAllocator {
|
|
private:
|
|
using Self = SysAllocator<T>;
|
|
|
|
public:
|
|
using value_type = T;
|
|
|
|
constexpr SysAllocator() = default;
|
|
|
|
constexpr SysAllocator(SysAllocator const&) = default;
|
|
|
|
template <typename U, std::enable_if_t<!std::is_same<U, T>::value, int> = 0>
|
|
constexpr SysAllocator(SysAllocator<U> const&) noexcept {}
|
|
|
|
T* allocate(size_t count) {
|
|
using lifted = typename detail::lift_void_to_char<T>::type;
|
|
auto const p = std::malloc(sizeof(lifted) * count);
|
|
if (!p) {
|
|
throw_exception<std::bad_alloc>();
|
|
}
|
|
return static_cast<T*>(p);
|
|
}
|
|
void deallocate(T* p, size_t /* count */) {
|
|
std::free(p);
|
|
}
|
|
|
|
friend bool operator==(Self const&, Self const&) noexcept {
|
|
return true;
|
|
}
|
|
friend bool operator!=(Self const&, Self const&) noexcept {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
class DefaultAlign {
|
|
private:
|
|
using Self = DefaultAlign;
|
|
std::size_t align_;
|
|
|
|
public:
|
|
explicit DefaultAlign(std::size_t align) noexcept : align_(align) {
|
|
assert(!(align_ < sizeof(void*)) && bool("bad align: too small"));
|
|
assert(!(align_ & (align_ - 1)) && bool("bad align: not power-of-two"));
|
|
}
|
|
std::size_t operator()(std::size_t align) const noexcept {
|
|
return align_ < align ? align : align_;
|
|
}
|
|
|
|
friend bool operator==(Self const& a, Self const& b) noexcept {
|
|
return a.align_ == b.align_;
|
|
}
|
|
friend bool operator!=(Self const& a, Self const& b) noexcept {
|
|
return a.align_ != b.align_;
|
|
}
|
|
};
|
|
|
|
template <std::size_t Align>
|
|
class FixedAlign {
|
|
private:
|
|
static_assert(!(Align < sizeof(void*)), "bad align: too small");
|
|
static_assert(!(Align & (Align - 1)), "bad align: not power-of-two");
|
|
using Self = FixedAlign<Align>;
|
|
|
|
public:
|
|
constexpr std::size_t operator()(std::size_t align) const noexcept {
|
|
return Align < align ? align : Align;
|
|
}
|
|
|
|
friend bool operator==(Self const&, Self const&) noexcept {
|
|
return true;
|
|
}
|
|
friend bool operator!=(Self const&, Self const&) noexcept {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* AlignedSysAllocator
|
|
*
|
|
* Resembles std::allocator, the default Allocator, but wraps aligned_malloc and
|
|
* aligned_free.
|
|
*
|
|
* Accepts a policy parameter for providing the alignment, which must:
|
|
* * be invocable as std::size_t(std::size_t) noexcept
|
|
* * taking the type alignment and returning the allocation alignment
|
|
* * be noexcept-copy-constructible
|
|
* * have noexcept operator==
|
|
* * have noexcept operator!=
|
|
* * not be final
|
|
*
|
|
* DefaultAlign and FixedAlign<std::size_t>, provided above, are valid policies.
|
|
*/
|
|
template <typename T, typename Align = DefaultAlign>
|
|
class AlignedSysAllocator : private Align {
|
|
private:
|
|
using Self = AlignedSysAllocator<T, Align>;
|
|
|
|
template <typename, typename>
|
|
friend class AlignedSysAllocator;
|
|
|
|
constexpr Align const& align() const {
|
|
return *this;
|
|
}
|
|
|
|
public:
|
|
static_assert(std::is_nothrow_copy_constructible<Align>::value, "");
|
|
static_assert(is_nothrow_invocable_r_v<std::size_t, Align, std::size_t>, "");
|
|
|
|
using value_type = T;
|
|
|
|
using propagate_on_container_copy_assignment = std::true_type;
|
|
using propagate_on_container_move_assignment = std::true_type;
|
|
using propagate_on_container_swap = std::true_type;
|
|
|
|
using Align::Align;
|
|
|
|
// TODO: remove this ctor, which is is no longer required as of under gcc7
|
|
template <
|
|
typename S = Align,
|
|
std::enable_if_t<std::is_default_constructible<S>::value, int> = 0>
|
|
constexpr AlignedSysAllocator() noexcept(noexcept(Align())) : Align() {}
|
|
|
|
constexpr AlignedSysAllocator(AlignedSysAllocator const&) = default;
|
|
|
|
template <typename U, std::enable_if_t<!std::is_same<U, T>::value, int> = 0>
|
|
constexpr AlignedSysAllocator(
|
|
AlignedSysAllocator<U, Align> const& other) noexcept
|
|
: Align(other.align()) {}
|
|
|
|
T* allocate(size_t count) {
|
|
using lifted = typename detail::lift_void_to_char<T>::type;
|
|
auto const a = align()(alignof(lifted));
|
|
auto const p = aligned_malloc(sizeof(lifted) * count, a);
|
|
if (!p) {
|
|
if (FOLLY_UNLIKELY(errno != ENOMEM)) {
|
|
std::terminate();
|
|
}
|
|
throw_exception<std::bad_alloc>();
|
|
}
|
|
return static_cast<T*>(p);
|
|
}
|
|
void deallocate(T* p, size_t /* count */) {
|
|
aligned_free(p);
|
|
}
|
|
|
|
friend bool operator==(Self const& a, Self const& b) noexcept {
|
|
return a.align() == b.align();
|
|
}
|
|
friend bool operator!=(Self const& a, Self const& b) noexcept {
|
|
return a.align() != b.align();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* CxxAllocatorAdaptor
|
|
*
|
|
* A type conforming to C++ concept Allocator, delegating operations to an
|
|
* unowned Inner which has this required interface:
|
|
*
|
|
* void* allocate(std::size_t)
|
|
* void deallocate(void*, std::size_t)
|
|
*
|
|
* Note that Inner is *not* a C++ Allocator.
|
|
*/
|
|
template <typename T, class Inner>
|
|
class CxxAllocatorAdaptor {
|
|
private:
|
|
using Self = CxxAllocatorAdaptor<T, Inner>;
|
|
|
|
template <typename U, typename UAlloc>
|
|
friend class CxxAllocatorAdaptor;
|
|
|
|
std::reference_wrapper<Inner> ref_;
|
|
|
|
public:
|
|
using value_type = T;
|
|
|
|
using propagate_on_container_copy_assignment = std::true_type;
|
|
using propagate_on_container_move_assignment = std::true_type;
|
|
using propagate_on_container_swap = std::true_type;
|
|
|
|
constexpr explicit CxxAllocatorAdaptor(Inner& ref) : ref_(ref) {}
|
|
|
|
constexpr CxxAllocatorAdaptor(CxxAllocatorAdaptor const&) = default;
|
|
|
|
template <typename U, std::enable_if_t<!std::is_same<U, T>::value, int> = 0>
|
|
constexpr CxxAllocatorAdaptor(CxxAllocatorAdaptor<U, Inner> const& other)
|
|
: ref_(other.ref_) {}
|
|
|
|
T* allocate(std::size_t n) {
|
|
using lifted = typename detail::lift_void_to_char<T>::type;
|
|
return static_cast<T*>(ref_.get().allocate(sizeof(lifted) * n));
|
|
}
|
|
void deallocate(T* p, std::size_t n) {
|
|
using lifted = typename detail::lift_void_to_char<T>::type;
|
|
ref_.get().deallocate(p, sizeof(lifted) * n);
|
|
}
|
|
|
|
friend bool operator==(Self const& a, Self const& b) noexcept {
|
|
return std::addressof(a.ref_.get()) == std::addressof(b.ref_.get());
|
|
}
|
|
friend bool operator!=(Self const& a, Self const& b) noexcept {
|
|
return std::addressof(a.ref_.get()) != std::addressof(b.ref_.get());
|
|
}
|
|
};
|
|
|
|
/*
|
|
* allocator_delete
|
|
*
|
|
* A deleter which automatically works with a given allocator.
|
|
*
|
|
* Derives from the allocator to take advantage of the empty base
|
|
* optimization when possible.
|
|
*/
|
|
template <typename Alloc>
|
|
class allocator_delete : private std::remove_reference<Alloc>::type {
|
|
private:
|
|
using allocator_type = typename std::remove_reference<Alloc>::type;
|
|
using allocator_traits = std::allocator_traits<allocator_type>;
|
|
using value_type = typename allocator_traits::value_type;
|
|
using pointer = typename allocator_traits::pointer;
|
|
|
|
public:
|
|
allocator_delete() = default;
|
|
allocator_delete(allocator_delete const&) = default;
|
|
allocator_delete(allocator_delete&&) = default;
|
|
allocator_delete& operator=(allocator_delete const&) = default;
|
|
allocator_delete& operator=(allocator_delete&&) = default;
|
|
|
|
explicit allocator_delete(const allocator_type& alloc)
|
|
: allocator_type(alloc) {}
|
|
|
|
explicit allocator_delete(allocator_type&& alloc)
|
|
: allocator_type(std::move(alloc)) {}
|
|
|
|
template <typename U>
|
|
allocator_delete(const allocator_delete<U>& other)
|
|
: allocator_type(other.get_allocator()) {}
|
|
|
|
allocator_type const& get_allocator() const {
|
|
return *this;
|
|
}
|
|
|
|
void operator()(pointer p) const {
|
|
auto alloc = get_allocator();
|
|
allocator_traits::destroy(alloc, p);
|
|
allocator_traits::deallocate(alloc, p, 1);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* allocate_unique, like std::allocate_shared but for std::unique_ptr
|
|
*/
|
|
template <typename T, typename Alloc, typename... Args>
|
|
std::unique_ptr<T, allocator_delete<Alloc>> allocate_unique(
|
|
Alloc const& alloc,
|
|
Args&&... args) {
|
|
using traits = std::allocator_traits<Alloc>;
|
|
struct DeferCondDeallocate {
|
|
bool& cond;
|
|
Alloc& copy;
|
|
T* p;
|
|
~DeferCondDeallocate() {
|
|
if (FOLLY_UNLIKELY(!cond)) {
|
|
traits::deallocate(copy, p, 1);
|
|
}
|
|
}
|
|
};
|
|
auto copy = alloc;
|
|
auto const p = traits::allocate(copy, 1);
|
|
{
|
|
bool constructed = false;
|
|
DeferCondDeallocate handler{constructed, copy, p};
|
|
traits::construct(copy, p, static_cast<Args&&>(args)...);
|
|
constructed = true;
|
|
}
|
|
return {p, allocator_delete<Alloc>(std::move(copy))};
|
|
}
|
|
|
|
struct SysBufferDeleter {
|
|
void operator()(void* ptr) {
|
|
std::free(ptr);
|
|
}
|
|
};
|
|
using SysBufferUniquePtr = std::unique_ptr<void, SysBufferDeleter>;
|
|
|
|
inline SysBufferUniquePtr allocate_sys_buffer(std::size_t size) {
|
|
auto p = std::malloc(size);
|
|
if (!p) {
|
|
throw_exception<std::bad_alloc>();
|
|
}
|
|
return {p, {}};
|
|
}
|
|
|
|
/**
|
|
* AllocatorHasTrivialDeallocate
|
|
*
|
|
* Unambiguously inherits std::integral_constant<bool, V> for some bool V.
|
|
*
|
|
* Describes whether a C++ Aallocator has trivial, i.e. no-op, deallocate().
|
|
*
|
|
* Also may be used to describe types which may be used with
|
|
* CxxAllocatorAdaptor.
|
|
*/
|
|
template <typename Alloc>
|
|
struct AllocatorHasTrivialDeallocate : std::false_type {};
|
|
|
|
template <typename T, class Alloc>
|
|
struct AllocatorHasTrivialDeallocate<CxxAllocatorAdaptor<T, Alloc>>
|
|
: AllocatorHasTrivialDeallocate<Alloc> {};
|
|
|
|
namespace detail {
|
|
// note that construct and destroy here are methods, not short names for
|
|
// the constructor and destructor
|
|
FOLLY_CREATE_MEMBER_INVOKER(AllocatorConstruct_, construct);
|
|
FOLLY_CREATE_MEMBER_INVOKER(AllocatorDestroy_, destroy);
|
|
|
|
template <typename Void, typename Alloc, typename... Args>
|
|
struct AllocatorCustomizesConstruct_
|
|
: folly::is_invocable<AllocatorConstruct_, Alloc, Args...> {};
|
|
|
|
template <typename Alloc, typename... Args>
|
|
struct AllocatorCustomizesConstruct_<
|
|
void_t<typename Alloc::folly_has_default_object_construct>,
|
|
Alloc,
|
|
Args...> : Negation<typename Alloc::folly_has_default_object_construct> {};
|
|
|
|
template <typename Void, typename Alloc, typename... Args>
|
|
struct AllocatorCustomizesDestroy_
|
|
: folly::is_invocable<AllocatorDestroy_, Alloc, Args...> {};
|
|
|
|
template <typename Alloc, typename... Args>
|
|
struct AllocatorCustomizesDestroy_<
|
|
void_t<typename Alloc::folly_has_default_object_destroy>,
|
|
Alloc,
|
|
Args...> : Negation<typename Alloc::folly_has_default_object_destroy> {};
|
|
} // namespace detail
|
|
|
|
/**
|
|
* AllocatorHasDefaultObjectConstruct
|
|
*
|
|
* AllocatorHasDefaultObjectConstruct<A, T, Args...> unambiguously
|
|
* inherits std::integral_constant<bool, V>, where V will be true iff
|
|
* the effect of std::allocator_traits<A>::construct(a, p, args...) is
|
|
* the same as new (static_cast<void*>(p)) T(args...). If true then
|
|
* any optimizations applicable to object construction (relying on
|
|
* std::is_trivially_copyable<T>, for example) can be applied to objects
|
|
* in an allocator-aware container using an allocation of type A.
|
|
*
|
|
* Allocator types can override V by declaring a type alias for
|
|
* folly_has_default_object_construct. It is helpful to do this if you
|
|
* define a custom allocator type that defines a construct method, but
|
|
* that method doesn't do anything except call placement new.
|
|
*/
|
|
template <typename Alloc, typename T, typename... Args>
|
|
struct AllocatorHasDefaultObjectConstruct
|
|
: Negation<
|
|
detail::AllocatorCustomizesConstruct_<void, Alloc, T*, Args...>> {};
|
|
|
|
template <typename Value, typename T, typename... Args>
|
|
struct AllocatorHasDefaultObjectConstruct<std::allocator<Value>, T, Args...>
|
|
: std::true_type {};
|
|
|
|
/**
|
|
* AllocatorHasDefaultObjectDestroy
|
|
*
|
|
* AllocatorHasDefaultObjectDestroy<A, T> unambiguously inherits
|
|
* std::integral_constant<bool, V>, where V will be true iff the effect
|
|
* of std::allocator_traits<A>::destroy(a, p) is the same as p->~T().
|
|
* If true then optimizations applicable to object destruction (relying
|
|
* on std::is_trivially_destructible<T>, for example) can be applied to
|
|
* objects in an allocator-aware container using an allocator of type A.
|
|
*
|
|
* Allocator types can override V by declaring a type alias for
|
|
* folly_has_default_object_destroy. It is helpful to do this if you
|
|
* define a custom allocator type that defines a destroy method, but that
|
|
* method doesn't do anything except call the object's destructor.
|
|
*/
|
|
template <typename Alloc, typename T>
|
|
struct AllocatorHasDefaultObjectDestroy
|
|
: Negation<detail::AllocatorCustomizesDestroy_<void, Alloc, T*>> {};
|
|
|
|
template <typename Value, typename T>
|
|
struct AllocatorHasDefaultObjectDestroy<std::allocator<Value>, T>
|
|
: std::true_type {};
|
|
|
|
} // namespace folly
|