<commit_before>/*!
 * \author ddubois
 * \date 12-Feb-18.
 */

#include "auto_allocating_buffer.hpp"
#include "../../util/logger.hpp"
#include "fmt/format.h"

namespace nova::renderer {
    auto_buffer::auto_buffer(const std::string& name,
                             VmaAllocator allocator,
                             const VkBufferCreateInfo& create_info,
                             const uint64_t alignment,
                             const bool mapped = false)
        : uniform_buffer(name, allocator, create_info, alignment, mapped) {

        chunks.emplace_back(auto_buffer_chunk{VkDeviceSize(0), create_info.size});
    }

    auto_buffer::auto_buffer(auto_buffer&& old) noexcept : uniform_buffer(std::forward<uniform_buffer>(old)) {
        chunks = std::move(old.chunks);
        old.chunks.clear();
    }

    auto_buffer& auto_buffer::operator=(auto_buffer&& old) noexcept {
        uniform_buffer::operator=(std::forward<uniform_buffer>(old));

        chunks = std::move(old.chunks);
        old.chunks.clear();

        return *this;
    }

    VkDescriptorBufferInfo auto_buffer::allocate_space(uint64_t size) {
        size = size > alignment ? size : alignment;
        int32_t index_to_allocate_from = -1;
        if(!chunks.empty()) {
            // Iterate backwards so that inserting or deleting has a minimal cost
            for(int32_t i = static_cast<int32_t>(chunks.size() - 1); i >= 0; --i) {
                if(chunks[static_cast<uint32_t>(i)].range >= size) {
                    index_to_allocate_from = i;
                }
            }
        }

        VkDescriptorBufferInfo ret_val;

        if(index_to_allocate_from == -1) {
            // Whoops, couldn't find anything
            const std::string msg = fmt::
                format(fmt("No big enough slots in the buffer. There's {:d} slots. If there's a lot then you got some fragmentation"),
                       chunks.size());

            NOVA_LOG(ERROR) << msg;
            // Halt execution like a boss
            throw std::runtime_error(msg);
        }

        auto& chunk_to_allocate_from = chunks[static_cast<uint32_t>(index_to_allocate_from)];
        if(chunk_to_allocate_from.range == size) {
            // Easy: unallocate the chunk, return the chunks

            ret_val = VkDescriptorBufferInfo{buffer, chunk_to_allocate_from.offset, chunk_to_allocate_from.range};
            chunks.erase(chunks.begin() + index_to_allocate_from);
            // NOLINTNEXTLINE(cppcoreguidelines-avoid-goto)
            goto end;
        }

        // The chunk is bigger than we need. Allocate at the beginning of it so our iteration algorithm finds the next
        // free chunk nice and early, then shrink it

        ret_val = VkDescriptorBufferInfo{buffer, chunk_to_allocate_from.offset, size};

        chunk_to_allocate_from.offset += size;
        chunk_to_allocate_from.range -= size;

    end:
        return ret_val;
    }

    void auto_buffer::free_allocation(const VkDescriptorBufferInfo& to_free) {
        // This one will be hard...
        // Properly we should try to find an allocated space of our size and merge the two allocations on either side of
        // it... but that's marginally harder (maybe I'll do it) so let's just try to find the first slot it will fit

        const auto to_free_end = to_free.offset + to_free.range;

        auto& first_chunk = chunks[0];
        auto& last_chunk = chunks[chunks.size() - 1];<commit_msg>Remove goto<commit_after>/*!
 * \author ddubois
 * \date 12-Feb-18.
 */

#include "auto_allocating_buffer.hpp"
#include "../../util/logger.hpp"
#include "fmt/format.h"

namespace nova::renderer {
    auto_buffer::auto_buffer(const std::string& name,
                             VmaAllocator allocator,
                             const VkBufferCreateInfo& create_info,
                             const uint64_t alignment,
                             const bool mapped = false)
        : uniform_buffer(name, allocator, create_info, alignment, mapped) {

        chunks.emplace_back(auto_buffer_chunk{VkDeviceSize(0), create_info.size});
    }

    auto_buffer::auto_buffer(auto_buffer&& old) noexcept : uniform_buffer(std::forward<uniform_buffer>(old)) {
        chunks = std::move(old.chunks);
        old.chunks.clear();
    }

    auto_buffer& auto_buffer::operator=(auto_buffer&& old) noexcept {
        uniform_buffer::operator=(std::forward<uniform_buffer>(old));

        chunks = std::move(old.chunks);
        old.chunks.clear();

        return *this;
    }

    VkDescriptorBufferInfo auto_buffer::allocate_space(uint64_t size) {
        size = size > alignment ? size : alignment;
        int32_t index_to_allocate_from = -1;
        if(!chunks.empty()) {
            // Iterate backwards so that inserting or deleting has a minimal cost
            for(int32_t i = static_cast<int32_t>(chunks.size() - 1); i >= 0; --i) {
                if(chunks[static_cast<uint32_t>(i)].range >= size) {
                    index_to_allocate_from = i;
                }
            }
        }

        VkDescriptorBufferInfo ret_val;

        if(index_to_allocate_from == -1) {
            // Whoops, couldn't find anything
            const std::string msg = fmt::
                format(fmt("No big enough slots in the buffer. There's {:d} slots. If there's a lot then you got some fragmentation"),
                       chunks.size());

            NOVA_LOG(ERROR) << msg;
            // Halt execution like a boss
            throw std::runtime_error(msg);
        }

        auto& chunk_to_allocate_from = chunks[static_cast<uint32_t>(index_to_allocate_from)];
        if(chunk_to_allocate_from.range == size) {
            // Easy: unallocate the chunk, return the chunks

            ret_val = VkDescriptorBufferInfo{buffer, chunk_to_allocate_from.offset, chunk_to_allocate_from.range};
            chunks.erase(chunks.begin() + index_to_allocate_from);
            return ret_val;
        }

        // The chunk is bigger than we need. Allocate at the beginning of it so our iteration algorithm finds the next
        // free chunk nice and early, then shrink it

        ret_val = VkDescriptorBufferInfo{buffer, chunk_to_allocate_from.offset, size};

        chunk_to_allocate_from.offset += size;
        chunk_to_allocate_from.range -= size;

        return ret_val;
    }

    void auto_buffer::free_allocation(const VkDescriptorBufferInfo& to_free) {
        // This one will be hard...
        // Properly we should try to find an allocated space of our size and merge the two allocations on either side of
        // it... but that's marginally harder (maybe I'll do it) so let's just try to find the first slot it will fit

        const auto to_free_end = to_free.offset + to_free.range;

        auto& first_chunk = chunks[0];
        auto& last_chunk = chunks[chunks.size() - 1];
{'fraction_non_alphanumeric': 0.10021818181818182, 'fraction_numerical': 0.007272727272727273, 'mean_word_length': 2.2852365026278068, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6955185', 'n_tokens_mistral': 2063, 'n_tokens_neox': 1945, 'n_words': 498}