(root)/
gcc-13.2.0/
libstdc++-v3/
testsuite/
20_util/
monotonic_buffer_resource/
allocate.cc
// Copyright (C) 2018-2023 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
  {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Walloc-size-larger-than="
    // Try to allocate a ridiculous size:
    void* p = mbr.allocate(std::size_t(-2), 1);
#pragma GCC diagnostic pop
    // 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();
}