forked from boostorg/container
Fixes #107 ("Alignment ignored in resource_adaptor")
This commit is contained in:
@ -1267,7 +1267,8 @@ use [*Boost.Container]? There are several reasons for that:
|
||||
[section:release_notes Release Notes]
|
||||
|
||||
[section:release_notes_boost_1_71_00 Boost 1.71 Release]
|
||||
* [@https://github.com/boostorg/container/issues/88 GitHub #88: ['"Implement C++17 MoveAssignable requirements for self-move assignments"]].
|
||||
* [@https://github.com/boostorg/container/issues/88 GitHub #88: ['"Implement C++17 MoveAssignable requirements for self-move assignments"]].
|
||||
* [@https://github.com/boostorg/container/issues/107 GitHub #107: ['"Alignment ignored in resource_adaptor"]].
|
||||
* [@https://github.com/boostorg/container/pull/109 GitHub #109: ['"Get rid of integer overflow in copy_move_algo.hpp (-fsanitize=integer)"]].
|
||||
* [@https://github.com/boostorg/container/pull/110 GitHub #110: ['"Avoid gcc 9 deprecated copy warnings in new_allocator.hpp"]].
|
||||
* [@https://github.com/boostorg/container/issues/112 GitHub #112: ['"vector::resize() compilation error with msvc-10..12: data is not a member of boost::detail::aligned_storage"]].
|
||||
|
@ -15,14 +15,44 @@
|
||||
# pragma once
|
||||
#endif
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/container/detail/config_begin.hpp>
|
||||
#include <boost/container/detail/workaround.hpp>
|
||||
#include <boost/container/container_fwd.hpp>
|
||||
|
||||
#include <boost/container/pmr/memory_resource.hpp>
|
||||
#include <boost/container/allocator_traits.hpp>
|
||||
#include <boost/intrusive/detail/ebo_functor_holder.hpp>
|
||||
#include <boost/move/utility_core.hpp>
|
||||
#include <boost/move/detail/type_traits.hpp>
|
||||
#include <boost/container/detail/std_fwd.hpp>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace boost {
|
||||
namespace container {
|
||||
|
||||
namespace pmr_dtl {
|
||||
|
||||
template<class T>
|
||||
struct max_allocator_alignment
|
||||
{
|
||||
static const std::size_t value = 1;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct max_allocator_alignment< ::boost::container::new_allocator<T> >
|
||||
{
|
||||
static const std::size_t value = boost::move_detail::alignment_of<boost::move_detail::max_align_t>::value;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct max_allocator_alignment< std::allocator<T> >
|
||||
{
|
||||
static const std::size_t value = boost::move_detail::alignment_of<boost::move_detail::max_align_t>::value;
|
||||
};
|
||||
|
||||
} //namespace pmr_dtl
|
||||
|
||||
namespace pmr {
|
||||
|
||||
//! An instance of resource_adaptor<Allocator> is an adaptor that wraps a memory_resource interface
|
||||
@ -57,7 +87,7 @@ class resource_adaptor_imp
|
||||
void static_assert_if_not_char_allocator() const
|
||||
{
|
||||
//This class can only be used with allocators type char
|
||||
BOOST_STATIC_ASSERT((dtl::is_same<typename Allocator::value_type, char>::value));
|
||||
BOOST_STATIC_ASSERT((boost::container::dtl::is_same<typename Allocator::value_type, char>::value));
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -114,15 +144,25 @@ class resource_adaptor_imp
|
||||
protected:
|
||||
//! <b>Returns</b>: Allocated memory obtained by calling m_alloc.allocate. The size and alignment
|
||||
//! of the allocated memory shall meet the requirements for a class derived from memory_resource.
|
||||
virtual void* do_allocate(size_t bytes, size_t alignment)
|
||||
{ (void)alignment; return this->ebo_alloc_t::get().allocate(bytes); }
|
||||
virtual void* do_allocate(std::size_t bytes, std::size_t alignment)
|
||||
{
|
||||
if (alignment <= priv_guaranteed_allocator_alignment())
|
||||
return this->ebo_alloc_t::get().allocate(bytes);
|
||||
else
|
||||
return this->priv_aligned_alloc(bytes, alignment);
|
||||
}
|
||||
|
||||
//! <b>Requires</b>: p was previously allocated using A.allocate, where A == m_alloc, and not
|
||||
//! subsequently deallocated.
|
||||
//!
|
||||
//! <b>Effects</b>: Returns memory to the allocator using m_alloc.deallocate().
|
||||
virtual void do_deallocate(void* p, size_t bytes, size_t alignment)
|
||||
{ (void)alignment; this->ebo_alloc_t::get().deallocate((char*)p, bytes); }
|
||||
virtual void do_deallocate(void* p, std::size_t bytes, std::size_t alignment)
|
||||
{
|
||||
if (alignment <= priv_guaranteed_allocator_alignment())
|
||||
this->ebo_alloc_t::get().deallocate((char*)p, bytes);
|
||||
else
|
||||
this->priv_aligned_dealloc(p, bytes, alignment);
|
||||
}
|
||||
|
||||
//! Let p be dynamic_cast<const resource_adaptor_imp*>(&other).
|
||||
//!
|
||||
@ -132,6 +172,48 @@ class resource_adaptor_imp
|
||||
const resource_adaptor_imp* p = dynamic_cast<const resource_adaptor_imp*>(&other);
|
||||
return p && p->ebo_alloc_t::get() == this->ebo_alloc_t::get();
|
||||
}
|
||||
|
||||
private:
|
||||
void * priv_aligned_alloc(std::size_t bytes, std::size_t alignment)
|
||||
{
|
||||
//Allocate space for requested bytes, plus alignment, plus bookeeping data
|
||||
void *const p = this->ebo_alloc_t::get().allocate(bytes + priv_extra_bytes_for_overalignment(alignment));
|
||||
|
||||
if (0 != p) {
|
||||
//Obtain the aligned address after the bookeeping data
|
||||
void *const aligned_ptr = (void*)(((std::size_t)p + priv_extra_bytes_for_overalignment(alignment)) & ~(alignment - 1));
|
||||
|
||||
//Store bookeeping data. Use memcpy as the underlying memory might be unaligned for
|
||||
//a pointer (e.g. 2 byte alignment in 32 bit, 4 byte alignment in 64 bit)
|
||||
std::memcpy(priv_bookeeping_addr_from_aligned_ptr(aligned_ptr), &p, sizeof(p));
|
||||
return aligned_ptr;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void priv_aligned_dealloc(void *aligned_ptr, std::size_t bytes, std::size_t alignment)
|
||||
{
|
||||
//Obtain bookeeping data
|
||||
void *p;
|
||||
std::memcpy(&p, priv_bookeeping_addr_from_aligned_ptr(aligned_ptr), sizeof(p));
|
||||
std::size_t s = bytes + priv_extra_bytes_for_overalignment(alignment);
|
||||
this->ebo_alloc_t::get().deallocate((char*)p, s);
|
||||
}
|
||||
|
||||
static BOOST_CONTAINER_FORCEINLINE void *priv_bookeeping_addr_from_aligned_ptr(void *aligned_ptr)
|
||||
{
|
||||
return reinterpret_cast<void*>(reinterpret_cast<std::size_t>(aligned_ptr) - sizeof(void*));
|
||||
}
|
||||
|
||||
BOOST_CONTAINER_FORCEINLINE static std::size_t priv_extra_bytes_for_overalignment(std::size_t alignment)
|
||||
{
|
||||
return alignment - 1 + sizeof(void*);
|
||||
}
|
||||
|
||||
BOOST_CONTAINER_FORCEINLINE static std::size_t priv_guaranteed_allocator_alignment()
|
||||
{
|
||||
return pmr_dtl::max_allocator_alignment<Allocator>::value;
|
||||
}
|
||||
};
|
||||
|
||||
#if !defined(BOOST_NO_CXX11_TEMPLATE_ALIASES) || defined(BOOST_CONTAINER_DOXYGEN_INVOKED)
|
||||
@ -190,4 +272,6 @@ class resource_adaptor
|
||||
} //namespace container {
|
||||
} //namespace boost {
|
||||
|
||||
#include <boost/container/detail/config_end.hpp>
|
||||
|
||||
#endif //BOOST_CONTAINER_PMR_RESOURCE_ADAPTOR_HPP
|
||||
|
@ -11,9 +11,13 @@
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
#include "propagation_test_allocator.hpp"
|
||||
#include "derived_from_memory_resource.hpp"
|
||||
#include <boost/container/new_allocator.hpp>
|
||||
#include <memory>
|
||||
|
||||
using namespace boost::container::pmr;
|
||||
|
||||
static const std::size_t max_alignment_value = boost::move_detail::alignment_of<boost::move_detail::max_align_t>::value;
|
||||
|
||||
void test_default_constructor()
|
||||
{
|
||||
typedef propagation_test_allocator<char, 0> alloc_t;
|
||||
@ -138,23 +142,64 @@ struct derived_from_resource_adaptor_stateful
|
||||
using base_t::do_is_equal;
|
||||
};
|
||||
|
||||
void test_do_allocate()
|
||||
void test_do_allocate_deallocate()
|
||||
{
|
||||
derived_from_resource_adaptor_stateful dra;
|
||||
char dummy = 0;
|
||||
dra.get_allocator().allocate_return = &dummy;
|
||||
void *allocate_ret = dra.do_allocate(998, 1234);
|
||||
BOOST_TEST(allocate_ret == &dummy);
|
||||
BOOST_TEST(dra.get_allocator().allocate_size == 998);
|
||||
}
|
||||
{
|
||||
derived_from_resource_adaptor_stateful dra;
|
||||
char dummy = 0;
|
||||
dra.get_allocator().allocate_return = &dummy;
|
||||
void *allocate_ret = dra.do_allocate(998, 1);
|
||||
BOOST_TEST(allocate_ret == &dummy);
|
||||
BOOST_TEST(dra.get_allocator().allocate_size == 998);
|
||||
}
|
||||
{
|
||||
derived_from_resource_adaptor_stateful dra;
|
||||
char dummy = 0;
|
||||
dra.do_deallocate(&dummy, 1234, 1);
|
||||
BOOST_TEST(dra.get_allocator().deallocate_p == &dummy);
|
||||
BOOST_TEST(dra.get_allocator().deallocate_size == 1234);
|
||||
}
|
||||
{
|
||||
//Overaligned allocation
|
||||
derived_from_resource_adaptor_stateful dra;
|
||||
const std::size_t alignment = max_alignment_value*2u;
|
||||
const std::size_t bytes = alignment/2;
|
||||
char dummy[alignment*2u+sizeof(void*)];
|
||||
dra.get_allocator().allocate_return = dummy;
|
||||
|
||||
void test_do_deallocate()
|
||||
{
|
||||
derived_from_resource_adaptor_stateful dra;
|
||||
char dummy = 0;
|
||||
dra.do_deallocate(&dummy, 1234, 753);
|
||||
BOOST_TEST(dra.get_allocator().deallocate_p == &dummy);
|
||||
BOOST_TEST(dra.get_allocator().deallocate_size == 1234);
|
||||
//First allocate
|
||||
void *allocate_ret = dra.do_allocate(bytes, alignment);
|
||||
BOOST_TEST( (char*)allocate_ret >= (dummy+sizeof(void*)) && (char*)allocate_ret < (dummy + sizeof(dummy)) );
|
||||
BOOST_TEST( (std::size_t(allocate_ret) & (alignment - 1u)) == 0 );
|
||||
BOOST_TEST( dra.get_allocator().allocate_size >= (alignment/2+sizeof(void*)) );
|
||||
|
||||
//Then allocate
|
||||
dra.do_deallocate(allocate_ret, bytes, alignment);
|
||||
BOOST_TEST(dra.get_allocator().deallocate_p == dummy);
|
||||
BOOST_TEST(dra.get_allocator().deallocate_size == dra.get_allocator().allocate_size);
|
||||
}
|
||||
{
|
||||
typedef resource_adaptor< boost::container::new_allocator<int> > new_resource_alloc_t;
|
||||
new_resource_alloc_t ra;
|
||||
boost::container::pmr::memory_resource &mr = ra;
|
||||
|
||||
//new_allocator, low alignment
|
||||
mr.deallocate(mr.allocate(16, 1), 16, 1);
|
||||
|
||||
//new_allocator, high alignment
|
||||
mr.deallocate(mr.allocate(16, max_alignment_value*4u), 16, max_alignment_value*4u);
|
||||
}
|
||||
{
|
||||
typedef resource_adaptor<std ::allocator<int> > new_resource_alloc_t;
|
||||
new_resource_alloc_t ra;
|
||||
boost::container::pmr::memory_resource &mr = ra;
|
||||
|
||||
//std::allocator, low alignment
|
||||
mr.deallocate(mr.allocate(16, 1), 16, 1);
|
||||
|
||||
//std::allocator, high alignment
|
||||
mr.deallocate(mr.allocate(16, max_alignment_value*4u), 16, max_alignment_value*4u);
|
||||
}
|
||||
}
|
||||
|
||||
void test_do_is_equal()
|
||||
@ -183,8 +228,7 @@ int main()
|
||||
test_copy_assign();
|
||||
test_move_assign();
|
||||
test_get_allocator();
|
||||
test_do_allocate();
|
||||
test_do_deallocate();
|
||||
test_do_allocate_deallocate();
|
||||
test_do_is_equal();
|
||||
return ::boost::report_errors();
|
||||
}
|
||||
|
Reference in New Issue
Block a user