blob: 40180223bae34a53b293b55fdfc147a31d2beb18 [file] [log] [blame]
// Copyright (C) 2018-2021 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library 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, 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 General Public License for more details.
// You should have received a copy of the GNU General Public License along
// with this library; see the file COPYING3. If not see
// <http://www.gnu.org/licenses/>.
// { dg-do run { target c++17 } }
// { dg-require-cstdint "" }
#include <memory_resource>
#include <testsuite_allocator.h>
void
test01()
{
__gnu_test::memory_resource r;
// test that it's possible to allocate after each of the constructors
{
std::pmr::monotonic_buffer_resource mr(&r);
auto p = mr.allocate(1024);
VERIFY( p != nullptr );
auto q = mr.allocate(1024);
VERIFY( q != nullptr );
VERIFY( p != q );
}
VERIFY( r.number_of_active_allocations() == 0 );
{
std::pmr::monotonic_buffer_resource mr(128, &r);
auto p = mr.allocate(1024);
VERIFY( p != nullptr );
auto q = mr.allocate(1024);
VERIFY( q != nullptr );
VERIFY( p != q );
}
VERIFY( r.number_of_active_allocations() == 0 );
{
unsigned char buf[64];
std::pmr::monotonic_buffer_resource mr((void*)buf, sizeof(buf), &r);
auto p = mr.allocate(1024);
VERIFY( p != nullptr );
auto q = mr.allocate(1024);
VERIFY( q != nullptr );
VERIFY( p != q );
}
VERIFY( r.number_of_active_allocations() == 0 );
{
std::pmr::monotonic_buffer_resource mr;
auto p = mr.allocate(1024);
VERIFY( p != nullptr );
auto q = mr.allocate(1024);
VERIFY( q != nullptr );
VERIFY( p != q );
}
{
std::pmr::monotonic_buffer_resource mr(64);
auto p = mr.allocate(1024);
VERIFY( p != nullptr );
auto q = mr.allocate(1024);
VERIFY( q != nullptr );
VERIFY( p != q );
}
{
unsigned char buf[64];
std::pmr::monotonic_buffer_resource mr((void*)buf, sizeof(buf));
auto p = mr.allocate(1024);
VERIFY( p != nullptr );
auto q = mr.allocate(1024);
VERIFY( q != nullptr );
VERIFY( p != q );
}
}
void
test02()
{
unsigned char buf[64];
std::pmr::monotonic_buffer_resource mr(buf, sizeof(buf));
auto p = mr.allocate(0);
VERIFY( p != nullptr );
auto q = mr.allocate(0);
VERIFY( q != nullptr );
VERIFY( p != q );
p = mr.allocate(0, 1);
VERIFY( p != nullptr );
q = mr.allocate(0, 1);
VERIFY( q != nullptr );
VERIFY( p != q );
}
void
test03()
{
#if __cpp_exceptions
{
std::pmr::monotonic_buffer_resource mr(std::pmr::null_memory_resource());
bool caught = false;
try
{
(void) mr.allocate(1, 1);
}
catch (const std::bad_alloc&)
{
caught = true;
}
VERIFY( caught );
}
{
unsigned char buf[16];
std::pmr::monotonic_buffer_resource mr(buf, sizeof(buf),
std::pmr::null_memory_resource());
(void) mr.allocate(16, 1);
bool caught = false;
try
{
(void) mr.allocate(1, 1);
}
catch (const std::bad_alloc&)
{
caught = true;
}
VERIFY( caught );
}
#endif
}
void
test04()
{
auto buf = new unsigned char[512];
std::pmr::monotonic_buffer_resource mr(buf, 512,
std::pmr::null_memory_resource());
std::size_t prev_size = 1;
void* prev_ptr = mr.allocate(prev_size, 1);
for (int i = 0; i < 9; ++i)
{
std::size_t size = 1 << i;
void* ptr = mr.allocate(size, 1);
VERIFY( std::size_t((char*)ptr - (char*)prev_ptr) == prev_size );
prev_ptr = ptr;
prev_size = size;
}
}
void
test05()
{
// test that returned pointer is correctly aligned
auto is_aligned = [](void* p, size_t alignment) -> bool {
return (reinterpret_cast<std::uintptr_t>(p) % alignment) == 0;
};
auto buf = new unsigned char[2048];
std::pmr::monotonic_buffer_resource mr(buf+1, 2047);
for (int i = 0; i < 9; ++i)
{
auto p = mr.allocate(1, 1 << i);
VERIFY( is_aligned(p, 1 << i) );
// Make next available byte misaligned:
(void) mr.allocate(1 << i, 1);
}
}
void
test06()
{
// check for geometric progression in buffer sizes from upstream
struct resource : __gnu_test::memory_resource
{
bool allocated = false;
std::size_t last_size = 0;
void*
do_allocate(size_t bytes, size_t align) override
{
allocated = true;
last_size = bytes;
return __gnu_test::memory_resource::do_allocate(bytes, align);
}
};
resource r;
std::pmr::monotonic_buffer_resource mr(32, &r);
std::size_t last_size = 0;
for (int i = 0; i < 100; ++i)
{
(void) mr.allocate(16);
if (r.allocated)
{
VERIFY(r.last_size >= last_size);
last_size = r.last_size;
r.allocated = false;
}
}
}
void
test07()
{
// Custom exception thrown on expected allocation failure.
struct very_bad_alloc : std::bad_alloc { };
struct careful_resource : __gnu_test::memory_resource
{
void* do_allocate(std::size_t bytes, std::size_t alignment)
{
// pmr::monotonic_buffer_resource::do_allocate is not allowed to
// throw an exception when asked for an allocation it can't satisfy.
// The libstdc++ implementation will ask upstream to allocate
// bytes=SIZE_MAX and alignment=bit_floor(SIZE_MAX) instead of throwing.
// Verify that we got those values:
if (bytes != std::numeric_limits<std::size_t>::max())
VERIFY( !"upstream allocation should request maximum number of bytes" );
if (alignment != (1 + std::numeric_limits<std::size_t>::max() / 2))
VERIFY( !"upstream allocation should request maximum alignment" );
// A successful failure:
throw very_bad_alloc();
}
};
careful_resource cr;
std::pmr::monotonic_buffer_resource mbr(&cr);
try
{
// Try to allocate a ridiculous size:
void* p = mbr.allocate(std::size_t(-2), 1);
// Should not reach here!
VERIFY( !"attempt to allocate SIZE_MAX-1 should not have succeeded" );
throw p;
}
catch (const very_bad_alloc&)
{
// Should catch this exception from careful_resource::do_allocate
}
catch (const std::bad_alloc&)
{
VERIFY( !"monotonic_buffer_resource::do_allocate is not allowed to throw" );
}
}
int
main()
{
test01();
test02();
test03();
test04();
test05();
test06();
test07();
}