/** This file is a part of rexy's general purpose library Copyright (C) 2020-2022 rexy712 This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef REXY_DEFAULT_ALLOCATOR_HPP #define REXY_DEFAULT_ALLOCATOR_HPP #include //ptrdiff_t, size_t #include //true_type, false_type #include #include //numeric_limits #if __cplusplus >= 202002L #include //allocator #endif #ifdef __cpp_concepts #include "concepts/allocator.hpp" #define REXY_ALLOCATOR_CONCEPT Allocator #else #define REXY_ALLOCATOR_CONCEPT class #endif #include "rexy.hpp" #include "compat/standard.hpp" #include "compat/if_consteval.hpp" #include //declval namespace rexy{ template struct allocator { using pointer = T*; using const_pointer = const T*; using void_pointer = void*; using const_void_pointer = const void*; using value_type = T; using size_type = size_t; using difference_type = ptrdiff_t; using is_always_equal = std::true_type; using propagate_on_container_copy_assignment = std::false_type; using propagate_on_container_move_assignment = std::false_type; using propagate_on_container_swap = std::false_type; template struct rebind{ using other = allocator; }; private: constexpr bool has_overflow(size_type n)const{ return n > max_size(); } public: REXY_CPP20_CONSTEXPR allocator(void) = default; REXY_CPP20_CONSTEXPR allocator(const allocator&) = default; REXY_CPP20_CONSTEXPR allocator(allocator&&) = default; template constexpr allocator(const allocator&)noexcept{} REXY_CPP20_CONSTEXPR ~allocator(void) = default; //'::operator new' is never a constexpr call as of C++23. However, 'std::allocator::allocate' *is* transiently constexpr as of C++20, //even though it directly calls '::operator new' as is stated in the standard. Therefore, when evaluating this call with support for //'if consteval', we can use the 'std::allocator::allocate' constexpr-ness when this is in a constant evaluation context. #if __cplusplus >= 202002L REXY_CPP20_CONSTEXPR pointer allocate(size_type n){ size_type bytes = has_overflow(n) ? std::numeric_limits::max() : n*sizeof(T); REXY_if_consteval{ std::allocator a; const size_type num_items = n / sizeof(value_type); return a.allocate(num_items); }else{ if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){ return reinterpret_cast(::operator new(bytes)); }else{ return reinterpret_cast(::operator new(bytes, static_cast(alignof(T)))); } } //if consteval } REXY_CPP20_CONSTEXPR void deallocate(pointer p, size_type n){ REXY_if_consteval{ std::allocator a; const size_type num_items = n / sizeof(value_type); return a.deallocate(p, num_items); }else{ #if !defined(__cpp_sized_deallocation) //clang does not enable ::operator delete(void* ptr, std::size_t sz) by default for some reason if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){ ::operator delete(p); }else{ ::operator delete(p, static_cast(alignof(T))); } #else //__cpp_sized_deallocation size_type bytes = has_overflow(n) ? std::numeric_limits::max() : n*sizeof(T); if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){ ::operator delete(p, bytes); }else{ ::operator delete(p, bytes, static_cast(alignof(T))); } #endif //__cpp_sized_deallocation } //if consteval } #else //__cplusplus REXY_CPP20_CONSTEXPR pointer allocate(size_type n){ size_type bytes = has_overflow(n) ? std::numeric_limits::max() : n*sizeof(T); if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){ return reinterpret_cast(::operator new(bytes)); }else{ return reinterpret_cast(::operator new(bytes, static_cast(alignof(T)))); } } REXY_CPP20_CONSTEXPR void deallocate(pointer p, size_type n){ #if !defined(__cpp_sized_deallocation) //clang does not enable ::operator delete(void* ptr, std::size_t sz) by default for some reason if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){ ::operator delete(p); }else{ ::operator delete(p, static_cast(alignof(T))); } #else //__cpp_sized_deallocation size_type bytes = has_overflow(n) ? std::numeric_limits::max() : n*sizeof(T); if constexpr(alignof(T) <= __STDCPP_DEFAULT_NEW_ALIGNMENT__){ ::operator delete(p, bytes); }else{ ::operator delete(p, bytes, static_cast(alignof(T))); } #endif //__cpp_sized_deallocation } #endif //__cplusplus constexpr size_type max_size(void)const{ return std::numeric_limits::max()/sizeof(T); } }; template constexpr bool operator==(const allocator&, const allocator&){ return true; } template constexpr bool operator!=(const allocator&, const allocator&){ return false; } template struct is_nothrow_allocator{ static constexpr bool value = noexcept(std::declval().allocate(0)) && noexcept(std::declval().deallocate(nullptr, 0)); }; template static constexpr bool is_nothrow_allocator_v = is_nothrow_allocator::value; } #endif