//# Allocator.h: //# Copyright (C) 2015 //# National Astronomical Observatory of Japan //# 2-21-1, Osawa, Mitaka, Tokyo, 181-8588, Japan. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library is distributed in the hope that it will be useful, but WITHOUT //# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or //# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef CASA_CONTAINERS_ALLOCATOR_H_ #define CASA_CONTAINERS_ALLOCATOR_H_ #include #include #include #include #include #include #include #include #include namespace casacore { //# NAMESPACE CASACORE - BEGIN #ifndef CASA_DEFAULT_ALIGNMENT # define CASA_DEFAULT_ALIGNMENT (32UL) // AVX/AVX2 alignment #endif // // A global enum used by some Array/Block constructors. // // // ArrayInitPolicy is used in functions where an array is allocated/resized. // class ArrayInitPolicy { public: Bool operator ==(ArrayInitPolicy const &other) { return init == other.init; } Bool operator !=(ArrayInitPolicy const &other) { return init != other.init; } private: Bool init; explicit constexpr ArrayInitPolicy(bool v): init(v) {} friend struct ArrayInitPolicies; }; struct ArrayInitPolicies { // Don't initialize elements in the array. (The array will be explicitly filled with values other than the default value.) static constexpr ArrayInitPolicy NO_INIT = ArrayInitPolicy(false); // Initialize all elements in the array with the default value. static constexpr ArrayInitPolicy INIT = ArrayInitPolicy(true); }; template using std11_allocator = std::allocator; template struct casacore_allocator: public std11_allocator { typedef std11_allocator Super; typedef typename Super::size_type size_type; typedef typename Super::difference_type difference_type; typedef typename Super::pointer pointer; typedef typename Super::const_pointer const_pointer; typedef typename Super::reference reference; typedef typename Super::const_reference const_reference; typedef typename Super::value_type value_type; static constexpr size_t alignment = ALIGNMENT; template struct rebind { typedef casacore_allocator other; }; casacore_allocator() throw () { } casacore_allocator(const casacore_allocator&other) noexcept :Super(other) { } template casacore_allocator(const casacore_allocator&) noexcept { } ~casacore_allocator() noexcept { } pointer allocate(size_type elements, const void* = 0) { if (elements > this->max_size()) { throw std::bad_alloc(); } void *memptr = 0; int result = posix_memalign(&memptr, ALIGNMENT, sizeof(T) * elements); if (result != 0) { throw std::bad_alloc(); } return static_cast(memptr); } void deallocate(pointer ptr, size_type) { free(ptr); } }; template inline bool operator==(const casacore_allocator&, const casacore_allocator&) { return true; } template inline bool operator!=(const casacore_allocator&, const casacore_allocator&) { return false; } template struct new_del_allocator: public std11_allocator { typedef std11_allocator Super; typedef typename Super::size_type size_type; typedef typename Super::difference_type difference_type; typedef typename Super::pointer pointer; typedef typename Super::const_pointer const_pointer; typedef typename Super::reference reference; typedef typename Super::const_reference const_reference; typedef typename Super::value_type value_type; template struct rebind { typedef new_del_allocator other; }; new_del_allocator() noexcept { } new_del_allocator(const new_del_allocator&other) noexcept :Super(other) { } template new_del_allocator(const new_del_allocator&) noexcept { } ~new_del_allocator() noexcept { } pointer allocate(size_type elements, const void* = 0) { if (elements > this->max_size()) { throw std::bad_alloc(); } return new T[elements]; } void deallocate(pointer ptr, size_type) { delete[] ptr; } template void construct(U *, Args&&... ) {} // do nothing because new T[] does template void construct(U *ptr, U &&value) { *ptr = value; // because *ptr was already contructed by new[]. } template void construct(U *ptr, U &value) { *ptr = value; // because *ptr was already contructed by new[]. } template void construct(U *ptr, U const &value) { *ptr = value; // because *ptr was already contructed by new[]. } template void destroy(U *) {} // do nothing because delete[] will do. }; template inline bool operator==(const new_del_allocator&, const new_del_allocator&) { return true; } template inline bool operator!=(const new_del_allocator&, const new_del_allocator&) { return false; } template class Block; class Allocator_private { template friend class AbstractAllocator; template friend class BaseAllocator; template friend class Block; template struct BulkAllocator { typedef typename std::allocator::size_type size_type; typedef typename std::allocator::pointer pointer; typedef typename std::allocator::const_pointer const_pointer; typedef typename std::allocator::value_type value_type; virtual pointer allocate(size_type elements, const void*ptr = 0) = 0; virtual void deallocate(pointer ptr, size_type size) = 0; virtual void construct(pointer ptr, size_type n, const_pointer src) = 0; virtual void construct(pointer ptr, size_type n, value_type const &initial_value) = 0; virtual void construct(pointer ptr, size_type n) = 0; virtual void destroy(pointer ptr, size_type n) = 0; virtual std::type_info const &allocator_typeid() const = 0; virtual ~BulkAllocator() {} }; template struct BulkAllocatorImpl: public BulkAllocator { typedef typename Allocator::size_type size_type; typedef typename Allocator::pointer pointer; typedef typename Allocator::const_pointer const_pointer; typedef typename Allocator::value_type value_type; virtual pointer allocate(size_type elements, const void *ptr = 0) override { return allocator.allocate(elements, ptr); } virtual void deallocate(pointer ptr, size_type size) override { allocator.deallocate(ptr, size); } virtual void construct(pointer ptr, size_type n, const_pointer src) override { size_type i = 0; try { for (i = 0; i < n; ++i) { allocator.construct(&ptr[i], src[i]); } } catch (...) { destroy(ptr, i); // rollback constructions throw; } } virtual void construct(pointer ptr, size_type n, value_type const &initial_value) override { size_type i = 0; try { for (i = 0; i < n; ++i) { allocator.construct(&ptr[i], initial_value); } } catch (...) { destroy(ptr, i); // rollback constructions throw; } } virtual void construct(pointer ptr, size_type n) override { size_type i = 0; try { for (i = 0; i < n; ++i) { allocator.construct(&ptr[i]); } } catch (...) { destroy(ptr, i); // rollback constructions throw; } } virtual void destroy(pointer ptr, size_type n) override { for (size_type i = n; i > 0;) { --i; try { allocator.destroy(&ptr[i]); } catch (...) { // Destructor should not raise any exception. } } } virtual std::type_info const &allocator_typeid() const override { return typeid(Allocator); } virtual ~BulkAllocatorImpl() override {} private: static Allocator allocator; }; template static BulkAllocator *get_allocator() { return get_allocator_raw(); } template static BulkAllocatorImpl *get_allocator_raw() { // Because this function gets called from destructors of statically allocated objects that get destructed // after the program finishes, the allocator is constructed in a static storage space and is never // destructed. static typename std::aligned_storage), alignof(BulkAllocatorImpl)>::type storage; static BulkAllocatorImpl* ptr = new (reinterpret_cast*>(&storage)) BulkAllocatorImpl(); return ptr; } // Allocator specifier // // This class is just used to avoid ambiguity between overloaded functions. // template struct AllocSpec { BulkAllocator *allocator; explicit AllocSpec(BulkAllocator *alloc) : allocator(alloc) {} }; }; template Allocator Allocator_private::BulkAllocatorImpl::allocator; template class AbstractAllocator { public: typedef T value_type; virtual ~AbstractAllocator(){} protected: AbstractAllocator(){} friend class Array; friend class Block; virtual Allocator_private::BulkAllocator *getAllocator() const = 0; }; template class BaseAllocator: public AbstractAllocator { public: typedef T value_type; typedef Sub facade_type; virtual ~BaseAllocator() {} protected: BaseAllocator() {} virtual typename Allocator_private::BulkAllocator *getAllocator() const override { return Allocator_private::get_allocator(); } }; // An allocator behaves like operator new[]/delete[]. // Because it is impossible to decouple construction/destruction from allocation/deallocation with this allocator, // it is discouraged to use this allocator. // Use DefaultAllocator or AlignedAllocator as possible. // This allocator is provided only for compatibility for calling // Array::takeStorage(), Block::replaceStorage(), Block(size_t, T *&, Bool) etc. // with a storage allocated by operator new[]. template class NewDelAllocator: public BaseAllocator > { public: typedef new_del_allocator type; // an instance of this allocator. static NewDelAllocator value; protected: NewDelAllocator(){} }; template NewDelAllocator NewDelAllocator::value; // An allocator which allocates aligned memory. template class AlignedAllocator: public BaseAllocator > { public: typedef casacore_allocator type; // an instance of this allocator. static AlignedAllocator value; protected: AlignedAllocator(){} }; template AlignedAllocator AlignedAllocator::value; // An aligned allocator with the default alignment. template class DefaultAllocator: public AlignedAllocator { public: typedef typename AlignedAllocator::type type; // an instance of this allocator. static DefaultAllocator value; protected: DefaultAllocator(){} }; template DefaultAllocator DefaultAllocator::value; // Allocator specifier // // This class is just used to avoid ambiguity between overloaded functions. // template struct AllocSpec { typedef T type; static AllocSpec const value; }; template AllocSpec const AllocSpec::value = AllocSpec(); } //# NAMESPACE CASACORE - END #endif /* CASA_CONTAINERS_ALLOCATOR_H_ */