diff --git a/include/boost/json/impl/monotonic_resource.ipp b/include/boost/json/impl/monotonic_resource.ipp index 56728b1e1..7730faaef 100644 --- a/include/boost/json/impl/monotonic_resource.ipp +++ b/include/boost/json/impl/monotonic_resource.ipp @@ -14,8 +14,10 @@ #include #include #include +#include #include +#include namespace boost { namespace json { @@ -128,8 +130,15 @@ do_allocate( return p; } - if(next_size_ < n) - next_size_ = round_pow2(n); + // a new block is only aligned to alignof(block), so an + // over-aligned request may need up to align - alignof(block) + // bytes of padding in addition to n + std::size_t const pad = align > alignof(block) + ? align - alignof(block) : 0; + if(n > max_size() - pad) + throw_exception( std::bad_alloc(), BOOST_CURRENT_LOCATION ); + if(next_size_ < n + pad) + next_size_ = round_pow2(n + pad); auto b = ::new(upstream_->allocate( sizeof(block) + next_size_)) block; b->p = b + 1; diff --git a/test/monotonic_resource.cpp b/test/monotonic_resource.cpp index d682b9350..e5d192fd5 100644 --- a/test/monotonic_resource.cpp +++ b/test/monotonic_resource.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include "checking_resource.hpp" #include "test_suite.hpp" @@ -317,6 +318,29 @@ R"xx({ (void)mr.allocate(1, alignof(core::max_align_t)); } + void + testOverAligned() + { + // an over-aligned request that fills a fresh block needs + // room for the padding in addition to the requested size + for(std::size_t align = 2 * alignof(core::max_align_t); + align <= 4096; align *= 2) + { + monotonic_resource mr; + void* p = mr.allocate(1024, align); + BOOST_TEST(p != nullptr); + BOOST_TEST( + !(reinterpret_cast(p) % align)); + } + // the padding must not make the request overflow + { + monotonic_resource mr; + BOOST_TEST_THROWS( + mr.allocate(std::size_t(-1) - 100, 4096), + std::bad_alloc); + } + } + void run() { @@ -324,6 +348,7 @@ R"xx({ testStorage(); testGeneral(); testAllocation(); + testOverAligned(); } };