// Copyright (c) 2025 GeometryFactory Sarl (France). // // This file is part of CGAL (www.cgal.org). // // $URL$ // $Id$ // SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial // // Author(s) : Laurent Rineau #ifndef CGAL_RANDOM_ALLOCATOR_H #define CGAL_RANDOM_ALLOCATOR_H #include #include #include #include #include #if CGAL_DEBUG_RANDOM_ALLOCATOR # if __has_include() # include # include # if __cpp_lib_format >= 201907L # define CGAL_DEBUG_RANDOM_ALLOCATOR_OKAY 1 # endif # endif # if ! CGAL_DEBUG_RANDOM_ALLOCATOR_OKAY # error "CGAL_DEBUG_RANDOM_ALLOCATOR requires and C++20 std::format" # endif #endif namespace CGAL { // A random allocator that allocates blocks of memory and returns random // locations. To use only for debugging purposes. That allocator is: // - not efficient, // - not thread-safe, // - and increases memory-fragmentation and non-locality. template > class Random_allocator { constexpr static std::size_t minimal_block_size = 1024; constexpr static std::size_t random_size = 64; struct Blocks { struct Block { T* data = nullptr; boost::dynamic_bitset<> available; std::size_t maximal_continuous_free_space = 0; auto size() const { return available.size(); } }; Upstream_allocator alloc; std::vector blocks; // List of allocated blocks void allocate_new_block(std::size_t block_size = minimal_block_size) { auto& block = blocks.emplace_back(); block.data = alloc.allocate(block_size); block.available.resize(block_size, true); block.maximal_continuous_free_space = block_size; } Blocks(const Upstream_allocator& alloc) : alloc(alloc) { allocate_new_block(); } ~Blocks() { for(auto& block : blocks) { alloc.deallocate(block.data, block.size()); } } }; using Block = typename Blocks::Block; using Ptr = std::shared_ptr; Ptr ptr_; std::mt19937 gen; public: using value_type = T; using size_type = std::size_t; using difference_type = std::ptrdiff_t; using pointer = T*; using const_pointer = const T*; using reference = T&; using const_reference = const T&; using allocator_type = Upstream_allocator; Random_allocator(const Upstream_allocator& alloc = {}) noexcept; pointer allocate(size_type n, const void* hint = 0); void deallocate(pointer p, size_type n); template void construct(U* p, Args&&... args); template void destroy(U* p); size_type max_size() const noexcept; template struct rebind { using upstream_traits = std::allocator_traits; using other_upstream_allocator = typename upstream_traits::template rebind_alloc; using other = Random_allocator; }; bool operator==(const Random_allocator&) const noexcept; bool operator!=(const Random_allocator&) const noexcept; }; // Implementation of Random_allocator methods template Random_allocator::Random_allocator(const Upstream_allocator& alloc) noexcept : ptr_(std::make_shared(alloc)), gen(std::random_device()()) { } template typename Random_allocator::pointer Random_allocator::allocate(size_type n, const void* hint) { boost::container::static_vector, random_size> found_spaces; for(auto it = ptr_->blocks.rbegin(), end = ptr_->blocks.rend(); it != end; ++it) { auto& block = *it; if(block.maximal_continuous_free_space < n) continue; auto& available = block.available; const auto block_size = block.size(); size_type found_max_free_space = 0; auto index = available.find_first(); while(index != boost::dynamic_bitset<>::npos) { available.flip(); const auto end_of_free_block = (std::min)(available.find_next(index), block_size); available.flip(); auto free_space = end_of_free_block - index; found_max_free_space = (std::max)(found_max_free_space, free_space); while(free_space > n && found_spaces.size() < found_spaces.capacity()) { found_spaces.emplace_back(std::addressof(block), index); free_space -= n; index += n; } index = block.available.find_next(end_of_free_block); } block.maximal_continuous_free_space = found_max_free_space; if(found_spaces.size() == found_spaces.capacity()) break; } if(!found_spaces.empty()) { std::uniform_int_distribution<> dis(0, found_spaces.size() - 1); auto i = dis(gen); auto [block, index] = found_spaces[i]; block->available.set(index, n, false); #if CGAL_DEBUG_RANDOM_ALLOCATOR std::clog << std::format("CGAL::Random_allocator debug info: n = {}, found_spaces.size() = {}, i = {}," " block id = {}, block size = {}, index = {}\n", n, found_spaces.size(), i, block - ptr_->blocks.data(), block->size(), index); #endif // CGAL_DEBUG_RANDOM_ALLOCATOR return block->data + index; } size_type block_size = (std::max)(n * random_size, minimal_block_size); ptr_->allocate_new_block(block_size); return allocate(n, hint); } template void Random_allocator::deallocate(pointer p, size_type n) { for(auto& block : ptr_->blocks) { if(block.data <= p && p < block.data + block.size()) { block.available.set(p - block.data, n, true); return; } } } template template void Random_allocator::construct(U* p, Args&&... args) { ::new((void*)p) U(std::forward(args)...); } template template void Random_allocator::destroy(U* p) { p->~U(); } template typename Random_allocator::size_type Random_allocator::max_size() const noexcept { return (std::numeric_limits::max)() / sizeof(T); } } // namespace CGAL #endif // CGAL_RANDOM_ALLOCATOR_H