From b334bbcae408b60909104977271e3f086db16590 Mon Sep 17 00:00:00 2001 From: Brett Date: Sun, 9 Mar 2025 23:04:41 -0400 Subject: [PATCH] minor fixes, all files now use new logging libray, expect things to be broken! --- CMakeLists.txt | 2 +- include/blt/fs/loader.h | 2 +- include/blt/fs/nbt.h | 2 +- include/blt/logging/logging.h | 15 +- include/blt/logging/logging_config.h | 3 +- include/blt/parse/templating.h | 2 +- include/blt/profiling/profiler.h | 4 +- include/blt/profiling/profiler_v2.h | 6 +- include/blt/std/allocator.h | 1861 +++++++++++++------------- include/blt/std/logging.h | 436 ------ include/blt/std/mmap.h | 6 +- src/blt/format/format.cpp | 2 +- src/blt/fs/filesystem.cpp | 1 - src/blt/fs/nbt.cpp | 2 +- src/blt/parse/argparse_v2.cpp | 2 +- src/blt/parse/obj_loader.cpp | 13 +- src/blt/parse/templating.cpp | 2 +- src/blt/profiling/profiler.cpp | 8 +- src/blt/profiling/profiler_v2.cpp | 6 +- src/blt/std/assert.cpp | 18 +- src/blt/std/error.cpp | 108 +- src/blt/std/logging.cpp | 627 --------- src/blt/std/mmap.cpp | 3 +- src/blt/std/simd.cpp | 1 - src/blt/std/system.cpp | 2 +- 25 files changed, 1034 insertions(+), 2100 deletions(-) delete mode 100644 include/blt/std/logging.h delete mode 100644 src/blt/std/logging.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9ad00ae..2b7a3e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.20) include(cmake/color.cmake) -set(BLT_VERSION 5.2.4) +set(BLT_VERSION 5.2.5) set(BLT_TARGET BLT) diff --git a/include/blt/fs/loader.h b/include/blt/fs/loader.h index 991c249..d5eb966 100644 --- a/include/blt/fs/loader.h +++ b/include/blt/fs/loader.h @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include namespace blt::fs diff --git a/include/blt/fs/nbt.h b/include/blt/fs/nbt.h index 2353c42..9d59004 100644 --- a/include/blt/fs/nbt.h +++ b/include/blt/fs/nbt.h @@ -15,7 +15,7 @@ #include "blt/format/format.h" #include "blt/fs/filesystem.h" -#include "blt/std/logging.h" +#include "blt/logging/logging.h" #include "blt/std/memory.h" #include diff --git a/include/blt/logging/logging.h b/include/blt/logging/logging.h index 0d41cc2..b1b75d5 100644 --- a/include/blt/logging/logging.h +++ b/include/blt/logging/logging.h @@ -203,14 +203,21 @@ namespace blt::logging } template - void log(log_level_t level, const char* file, const i32 line, std::string fmt, Args&&... args) + void log(const log_level_t level, const char* file, const i32 line, std::string fmt, Args&&... args) { auto& logger = get_global_logger(); - auto& config = get_global_config(); - auto user_str = logger.log(std::move(fmt), std::forward(args)...); + const auto& config = get_global_config(); + std::string user_str = logger.log(std::move(fmt), std::forward(args)...); + if (!user_str.empty() && user_str.back() == '\n') + user_str.pop_back(); + if (level == log_level_t::NONE) + { + println(user_str); + return; + } auto log_fmt_str = config.generate(user_str, get_thread_name(), level, file, line); if (log_fmt_str) - print(std::move(*log_fmt_str)); + print(*log_fmt_str); } namespace detail diff --git a/include/blt/logging/logging_config.h b/include/blt/logging/logging_config.h index 3859ad7..f5cad5a 100644 --- a/include/blt/logging/logging_config.h +++ b/include/blt/logging/logging_config.h @@ -115,7 +115,8 @@ namespace blt::logging INFO, WARN, ERROR, - FATAL + FATAL, + NONE }; inline constexpr size_t LOG_LEVEL_COUNT = 6; diff --git a/include/blt/parse/templating.h b/include/blt/parse/templating.h index cc9efa5..5f10352 100644 --- a/include/blt/parse/templating.h +++ b/include/blt/parse/templating.h @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include namespace blt diff --git a/include/blt/profiling/profiler.h b/include/blt/profiling/profiler.h index 2320080..74df441 100644 --- a/include/blt/profiling/profiler.h +++ b/include/blt/profiling/profiler.h @@ -11,7 +11,7 @@ #include #include #include -#include +#include #include #include @@ -54,7 +54,7 @@ namespace blt::profiling { profile getProfile(const std::string& profileName); void printProfile( - const std::string& profileName, logging::log_level loggingLevel = logging::log_level::NONE, + const std::string& profileName, logging::log_level_t loggingLevel = logging::log_level_t::NONE, bool averageHistory = false ); diff --git a/include/blt/profiling/profiler_v2.h b/include/blt/profiling/profiler_v2.h index dd080ec..42290af 100644 --- a/include/blt/profiling/profiler_v2.h +++ b/include/blt/profiling/profiler_v2.h @@ -11,7 +11,7 @@ #include #include #include -#include +#include namespace blt { @@ -98,7 +98,7 @@ namespace blt void endInterval(interval_t* interval); void printProfile(profile_t& profiler, std::uint32_t flags = AVERAGE_HISTORY | PRINT_CYCLES | PRINT_THREAD | PRINT_WALL, - sort_by sort = sort_by::CYCLES, blt::logging::log_level log_level = blt::logging::log_level::NONE); + sort_by sort = sort_by::CYCLES, blt::logging::log_level_t log_level = blt::logging::log_level_t::NONE); void writeProfile(std::ostream& stream, profile_t& profiler, std::uint32_t flags = AVERAGE_HISTORY | PRINT_CYCLES | PRINT_THREAD | PRINT_WALL, @@ -113,7 +113,7 @@ namespace blt void endInterval(const std::string& profile_name, const std::string& interval_name); void printProfile(const std::string& profile_name, std::uint32_t flags = AVERAGE_HISTORY | PRINT_CYCLES | PRINT_THREAD | PRINT_WALL, - sort_by sort = sort_by::CYCLES, blt::logging::log_level log_level = blt::logging::log_level::NONE); + sort_by sort = sort_by::CYCLES, blt::logging::log_level_t log_level = blt::logging::log_level_t::NONE); void writeProfile(std::ostream& stream, const std::string& profile_name, std::uint32_t flags = AVERAGE_HISTORY | PRINT_CYCLES | PRINT_THREAD | PRINT_WALL, diff --git a/include/blt/std/allocator.h b/include/blt/std/allocator.h index 03e466e..1f7b8f8 100644 --- a/include/blt/std/allocator.h +++ b/include/blt/std/allocator.h @@ -17,944 +17,949 @@ */ #ifndef BLT_ALLOCATOR_H - - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include "logging.h" - #include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "blt/logging/logging.h" +#include #ifdef __unix__ - - #include + +#include #endif namespace blt { - - template - class allocator_base - { - public: - template - inline void construct(U* p, Args&& ... args) - { - ::new((void*) p) U(std::forward(args)...); - } - - template - inline void destroy(U* p) - { - if (p != nullptr) - p->~U(); - } - - [[nodiscard]] inline size_t max_size() const - { - return std::numeric_limits::max(); - } - - inline const_pointer address(const value_type& val) - { - return std::addressof(val); - } - - inline pointer address(value_type& val) - { - return std::addressof(val); - } - }; - - template - class area_allocator : public allocator_base - { - public: - using value = T; - using type = T; - using value_type = type; - using pointer = type*; - using const_pointer = const type*; - using void_pointer = void*; - using const_void_pointer = const void*; - using reference = value_type&; - using const_reference = const value_type&; - using size_type = size_t; - using difference_type = size_t; - using propagate_on_container_move_assignment = std::false_type; - template - struct rebind - { - typedef blt::area_allocator other; - }; - using allocator_base::allocator_base; - private: - /** - * Stores a view to a region of memory that has been deallocated - * This is a non-owning reference to the memory block - * - * pointer p is the pointer to the beginning of the block of memory - * size_t n is the number of elements that this block can hold - */ - struct pointer_view - { - pointer p; - size_t n; - }; - - /** - * Stores the actual data for allocated blocks. Since we would like to be able to allocate an arbitrary number of items - * we need a way of storing that data. The block storage holds an owning pointer to a region of memory with used elements - * Only up to used has to have their destructors called, which should be handled by the deallocate function - * it is UB to not deallocate memory allocated by this allocator - * - * an internal vector is used to store the regions of memory which have been deallocated. the allocate function will search for - * free blocks with sufficient size in order to maximize memory usage. In the future more advanced methods should be used - * for both faster access to deallocated blocks of sufficient size and to ensure coherent memory. - */ - struct block_storage - { - pointer data; - size_t used = 0; - // TODO: b-tree? - std::vector unallocated_blocks; - }; - - /** - * Stores an index to a pointer_view along with the amount of memory leftover after the allocation - * it also stores the block being allocated to in question. The new inserted leftover should start at old_ptr + size - */ - struct block_view - { - block_storage* blk; - size_t index; - size_t leftover; - - block_view(block_storage* blk, size_t index, size_t leftover): blk(blk), index(index), leftover(leftover) - {} - }; - - /** - * Allocate a new block of memory and push it to the back of blocks. - */ - inline void allocate_block() - { - //BLT_INFO("Allocating a new block of size %d", BLOCK_SIZE); - auto* blk = new block_storage(); - blk->data = static_cast(malloc(sizeof(T) * BLOCK_SIZE)); - blocks.push_back(blk); - } - - /** - * Searches for a free block inside the block storage with sufficient space and returns an optional view to it - * The optional will be empty if no open block can be found. - */ - inline std::optional search_for_block(block_storage* blk, size_t n) - { - for (auto [index, item] : blt::enumerate(blk->unallocated_blocks)) - { - if (item.n >= n) - return block_view{blk, index, item.n - n}; - } - return {}; - } - - /** - * removes the block of memory from the unallocated_blocks storage in the underlying block, inserting a new unallocated block if - * there was any leftover. Returns a pointer to the beginning of the new block. - */ - inline pointer swap_pop_resize_if(const block_view& view, size_t n) - { - pointer_view ptr = view.blk->unallocated_blocks[view.index]; - std::iter_swap(view.blk->unallocated_blocks.begin() + view.index, view.blk->unallocated_blocks.end() - 1); - view.blk->unallocated_blocks.pop_back(); - if (view.leftover > 0) - view.blk->unallocated_blocks.push_back({ptr.p + n, view.leftover}); - return ptr.p; - } - - /** - * Finds the next available unallocated block of memory, or empty if there is none which meet size requirements - */ - inline std::optional find_available_block(size_t n) - { - for (auto* blk : blocks) - { - if (auto view = search_for_block(blk, n)) - return swap_pop_resize_if(view.value(), n); - } - return {}; - } - - /** - * returns a pointer to a block of memory along with an offset into that pointer that the requested block can be found at - */ - inline std::pair getBlock(size_t n) - { - if (auto blk = find_available_block(n)) - return {blk.value(), 0}; - - if (blocks.back()->used + n > BLOCK_SIZE) - allocate_block(); - - auto ptr = std::pair{blocks.back()->data, blocks.back()->used}; - blocks.back()->used += n; - return ptr; - } - - /** - * Calls the constructor on elements if they require construction, otherwise constructor will not be called and this function is useless - * - * ALLOCATORS RETURN UNINIT STORAGE!! THIS HAS BEEN DISABLED. - */ - inline void allocate_in_block(pointer, size_t) - { -// if constexpr (std::is_default_constructible_v && !std::is_trivially_default_constructible_v) -// { -// for (size_t i = 0; i < n; i++) -// new(&begin[i]) T(); -// } - } - - public: - area_allocator() - { - allocate_block(); - } - - area_allocator(const area_allocator& copy) = delete; - - area_allocator(area_allocator&& move) noexcept - { - blocks = move.blocks; - } - - area_allocator& operator=(const area_allocator& copy) = delete; - - area_allocator& operator=(area_allocator&& move) noexcept - { - std::swap(move.blocks, blocks); - } - - [[nodiscard]] pointer allocate(size_t n) - { - if (n > BLOCK_SIZE) - throw std::runtime_error("Requested allocation is too large!"); - - auto block_info = getBlock(n); - - auto* ptr = &block_info.first[block_info.second]; - // call constructors on the objects if they require it - allocate_in_block(ptr, n); - - return ptr; - } - - void deallocate(pointer p, size_t n) noexcept - { - if (p == nullptr) - return; -// for (size_t i = 0; i < n; i++) -// p[i].~T(); - for (auto*& blk : blocks) - { - if (p >= blk->data && p <= (blk->data + BLOCK_SIZE)) - { - blk->unallocated_blocks.push_back(pointer_view{p, n}); - break; - } - } - } - - ~area_allocator() - { - for (auto*& blk : blocks) - { - free(blk->data); - delete blk; - } - } - - private: - std::vector blocks; - }; + template + class allocator_base + { + public: + template + inline void construct(U* p, Args&&... args) + { + ::new((void*) p) U(std::forward(args)...); + } -// template -// class bump_allocator : public allocator_base -// { -// public: -// using value = T; -// using type = T; -// using value_type = type; -// using pointer = type*; -// using const_pointer = const type*; -// using void_pointer = void*; -// using const_void_pointer = const void*; -// using reference = value_type&; -// using const_reference = const value_type&; -// using size_type = size_t; -// using difference_type = size_t; -// using propagate_on_container_move_assignment = std::false_type; -// template -// struct rebind -// { -// typedef blt::bump_allocator other; -// }; -// using allocator_base::allocator_base; -// private: -// pointer buffer_; -// blt::size_t offset_; -// blt::size_t size_; -// public: -// explicit bump_allocator(blt::size_t size): buffer_(static_cast(malloc(size * sizeof(T)))), offset_(0), size_(size) -// {} -// -// template -// explicit bump_allocator(blt::size_t size, Args&& ... defaults): -// buffer_(static_cast(malloc(size * sizeof(type)))), offset_(0), size_(size) -// { -// for (blt::size_t i = 0; i < size_; i++) -// ::new(&buffer_[i]) T(std::forward(defaults)...); -// } -// -// bump_allocator(pointer buffer, blt::size_t size): buffer_(buffer), offset_(0), size_(size) -// {} -// -// bump_allocator(const bump_allocator& copy) = delete; -// -// bump_allocator(bump_allocator&& move) noexcept -// { -// buffer_ = move.buffer_; -// size_ = move.size_; -// offset_ = move.offset_; -// } -// -// bump_allocator& operator=(const bump_allocator& copy) = delete; -// -// bump_allocator& operator=(bump_allocator&& move) noexcept -// { -// std::swap(move.buffer_, buffer_); -// std::swap(move.size_, size_); -// std::swap(move.offset_, offset_); -// } -// -// pointer allocate(blt::size_t n) -// { -// auto nv = offset_ + n; -// if (nv > size_) -// throw std::bad_alloc(); -// pointer b = &buffer_[offset_]; -// offset_ = nv; -// return b; -// } -// -// void deallocate(pointer, blt::size_t) -// {} -// -// ~bump_allocator() -// { -// free(buffer_); -// } -// }; - - /** - * The bump allocator is meant to be a faster area allocator which will only allocate forward through either a supplied buffer or size - * or will create a linked list type data structure of buffered blocks. - * @tparam ALLOC allocator to use for any allocations. In the case of the non-linked variant, this will be used if a size is supplied. The supplied buffer must be allocated with this allocator! - * @tparam linked use a linked list to allocate with the allocator or just use the supplied buffer and throw an exception of we cannot allocate - */ - template typename ALLOC = std::allocator> - class bump_allocator_old; - - template typename ALLOC> - class bump_allocator_old - { - private: - ALLOC allocator; - blt::u8* buffer_; - blt::u8* offset_; - blt::size_t size_; - public: - explicit bump_allocator_old(blt::size_t size): buffer_(static_cast(allocator.allocate(size))), offset_(buffer_), size_(size) - {} - - explicit bump_allocator_old(blt::u8* buffer, blt::size_t size): buffer_(buffer), offset_(buffer), size_(size) - {} - - template - [[nodiscard]] T* allocate() - { - size_t remaining_num_bytes = size_ - static_cast(buffer_ - offset_); - auto pointer = static_cast(offset_); - const auto aligned_address = std::align(alignof(T), sizeof(T), pointer, remaining_num_bytes); - if (aligned_address == nullptr) - throw std::bad_alloc{}; - offset_ = static_cast(aligned_address) + sizeof(T); - return static_cast(aligned_address); - } - - template - [[nodiscard]] T* emplace(Args&& ... args) - { - const auto allocated_memory = allocate(); - return new(allocated_memory) T{std::forward(args)...}; - } - - template - inline void construct(U* p, Args&& ... args) - { - ::new((void*) p) U(std::forward(args)...); - } - - template - inline void destroy(U* p) - { - if (p != nullptr) - p->~U(); - } - - ~bump_allocator_old() - { - allocator.deallocate(buffer_, size_); - } - }; - - template typename ALLOC> - class bump_allocator_old - { - private: - struct block - { - blt::size_t allocated_objects = 0; - blt::u8* buffer = nullptr; - blt::u8* offset = nullptr; - - explicit block(blt::u8* buffer): buffer(buffer), offset(buffer) - {} - }; - - ALLOC allocator; - std::vector> blocks; - blt::size_t size_; - blt::size_t allocations = 0; - blt::size_t deallocations = 0; - - void expand() - { - auto ptr = static_cast(allocator.allocate(size_)); - blocks.push_back(block{ptr}); - allocations++; - } - - template - T* allocate_back() - { - auto& back = blocks.back(); - size_t remaining_bytes = size_ - static_cast(back.offset - back.buffer); - auto pointer = static_cast(back.offset); - const auto aligned_address = std::align(alignof(T), sizeof(T), pointer, remaining_bytes); - if (aligned_address != nullptr) - { - back.offset = static_cast(aligned_address) + sizeof(T); - back.allocated_objects++; - } - - return static_cast(aligned_address); - } - - public: - /** - * @param size of the list blocks - */ - explicit bump_allocator_old(blt::size_t size): size_(size) - { - expand(); - } - - template - [[nodiscard]] T* allocate() - { - if (auto ptr = allocate_back(); ptr == nullptr) - expand(); - else - return ptr; - if (auto ptr = allocate_back(); ptr == nullptr) - throw std::bad_alloc(); - else - return ptr; - } - - template - void deallocate(T* p) - { - auto* ptr = reinterpret_cast(p); - for (auto e : blt::enumerate(blocks)) - { - auto& block = e.second; - if (ptr >= block.buffer && ptr <= block.offset) - { - block.allocated_objects--; - if (block.allocated_objects == 0) - { - std::iter_swap(blocks.begin() + e.first, blocks.end() - 1); - allocator.deallocate(blocks.back().buffer, size_); - blocks.pop_back(); - deallocations++; - } - return; - } - } - } - - template - [[nodiscard]] T* emplace(Args&& ... args) - { - const auto allocated_memory = allocate(); - return new(allocated_memory) T{std::forward(args)...}; - } - - template - inline void construct(U* p, Args&& ... args) - { - ::new((void*) p) U(std::forward(args)...); - } - - template - inline void destroy(U* p) - { - if (p != nullptr) - p->~U(); - } - - ~bump_allocator_old() - { - if (allocations != deallocations) - BLT_WARN("Allocator has blocks which have not been deallocated! Destructors might not have been called!"); - for (auto& v : blocks) - allocator.deallocate(v.buffer, size_); - } - }; - - template - static inline T* allocate_huge_page(blt::size_t BLOCK_SIZE, blt::size_t HUGE_PAGE_SIZE = BLT_2MB_SIZE) - { -#ifdef __unix__ - BLT_ASSERT((BLOCK_SIZE & (HUGE_PAGE_SIZE - 1)) == 0 && "Must be multiple of the huge page size!"); - T* buffer = static_cast(mmap(nullptr, BLOCK_SIZE, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB | MAP_POPULATE, -1, 0)); - // if we fail to allocate a huge page we can try to allocate normally - if (buffer == MAP_FAILED) - { - if constexpr (WARN_ON_FAIL) - { - BLT_WARN_STREAM << "We failed to allocate huge pages\n"; - BLT_WARN_STREAM << handle_mmap_error(); - BLT_WARN_STREAM << "\033[1;31mYou should attempt to enable " - "huge pages as this will allocate normal pages and double the memory usage!\033[22m\n"; - } - blt::size_t bytes = BLOCK_SIZE * 2; - buffer = static_cast(mmap(nullptr, bytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, -1, 0)); - if (buffer == MAP_FAILED) - { - BLT_ERROR_STREAM << "Failed to allocate normal pages\n"; - throw bad_alloc_t(handle_mmap_error()); - } - if constexpr (WARN_ON_FAIL) - { - if (((size_t) buffer & (HUGE_PAGE_SIZE - 1)) != 0) - BLT_ERROR("Pointer is not aligned! %p", buffer); - } - auto* ptr = static_cast(buffer); - auto ptr_size = reinterpret_cast(ptr); - buffer = static_cast(std::align(BLOCK_SIZE, BLOCK_SIZE, ptr, bytes)); - if constexpr (WARN_ON_FAIL) - BLT_ERROR("Offset by %ld pages, resulting: %p", (reinterpret_cast(buffer) - ptr_size) / 4096, buffer); - } - return buffer; -#endif - return malloc(BLOCK_SIZE); - } - - /** - * blt::bump_allocator. Allocates blocks of BLOCK_SIZE with zero reuse. When all objects from a block are fully deallocated the block will be freed - * @tparam BLOCK_SIZE size of block to use. recommended to be multiple of page size or huge page size. - * @tparam USE_HUGE allocate using mmap and huge pages. If this fails it will use mmap to allocate normally. defaults to off because linux has parent huge pages. - * @tparam HUGE_PAGE_SIZE size the system allows huge pages to be. defaults to 2mb - * @tparam WARN_ON_FAIL print warning messages if allocating huge pages fail - */ - template - class bump_allocator - { - // ensure power of two - static_assert(((BLOCK_SIZE & (BLOCK_SIZE - 1)) == 0) && "Must be a power of two!"); - public: - /** - * convert any pointer back into a pointer its block - */ - template - static inline auto to_block(T* p) - { - return reinterpret_cast(reinterpret_cast(p) & static_cast(~(BLOCK_SIZE - 1))); - } - - class stats_t - { - friend bump_allocator; - private: - blt::size_t allocated_blocks = 0; - blt::size_t allocated_bytes = 0; - blt::size_t peak_blocks = 0; - blt::size_t peak_bytes = 0; - protected: - inline void incrementBlocks() - { - allocated_blocks++; - if (allocated_blocks > peak_blocks) - peak_blocks = allocated_blocks; - } - - inline void decrementBlocks() - { - allocated_blocks--; - } - - inline void incrementBytes(blt::size_t bytes) - { - allocated_bytes += bytes; - if (allocated_bytes > peak_bytes) - peak_bytes = allocated_bytes; - } - - inline void decrementBytes(blt::size_t bytes) - { - allocated_bytes -= bytes; - } - - public: - - inline auto getAllocatedBlocks() const - { - return allocated_blocks; - } - - inline auto getAllocatedBytes() const - { - return allocated_bytes; - } - - inline auto getPeakBlocks() const - { - return peak_blocks; - } - - inline auto getPeakBytes() const - { - return peak_bytes; - } - }; - - private: - stats_t stats; - //blt::hashset_t deletes; - - struct block - { - struct block_metadata_t - { - blt::size_t allocated_objects = 0; - block* next = nullptr; - block* prev = nullptr; - blt::u8* offset = nullptr; - } metadata; - blt::u8 buffer[BLOCK_SIZE - sizeof(block_metadata_t)]{}; - - block() - { - metadata.offset = buffer; - } - }; - - // remaining space inside the block after accounting for the metadata - static constexpr blt::size_t BLOCK_REMAINDER = BLOCK_SIZE - sizeof(typename block::block_metadata_t); - - block* base = nullptr; - block* head = nullptr; - - /** - * Handles the allocation of the bytes for the block. - * This function will either use mmap to allocate huge pages if requested - * or use std::align_alloc to create an aligned allocation - * @return pointer to a constructed block - */ - block* allocate_block() - { - block* buffer; -#ifdef __unix__ - if constexpr (USE_HUGE) - { - buffer = allocate_huge_page(BLOCK_SIZE, HUGE_PAGE_SIZE); - } else - buffer = reinterpret_cast(std::aligned_alloc(BLOCK_SIZE, BLOCK_SIZE)); -#else + template + inline void destroy(U* p) + { + if (p != nullptr) + p->~U(); + } + + [[nodiscard]] inline size_t max_size() const + { + return std::numeric_limits::max(); + } + + inline const_pointer address(const value_type& val) + { + return std::addressof(val); + } + + inline pointer address(value_type& val) + { + return std::addressof(val); + } + }; + + template + class area_allocator : public allocator_base + { + public: + using value = T; + using type = T; + using value_type = type; + using pointer = type*; + using const_pointer = const type*; + using void_pointer = void*; + using const_void_pointer = const void*; + using reference = value_type&; + using const_reference = const value_type&; + using size_type = size_t; + using difference_type = size_t; + using propagate_on_container_move_assignment = std::false_type; + + template + struct rebind + { + typedef blt::area_allocator other; + }; + + using allocator_base::allocator_base; + + private: + /** + * Stores a view to a region of memory that has been deallocated + * This is a non-owning reference to the memory block + * + * pointer p is the pointer to the beginning of the block of memory + * size_t n is the number of elements that this block can hold + */ + struct pointer_view + { + pointer p; + size_t n; + }; + + /** + * Stores the actual data for allocated blocks. Since we would like to be able to allocate an arbitrary number of items + * we need a way of storing that data. The block storage holds an owning pointer to a region of memory with used elements + * Only up to used has to have their destructors called, which should be handled by the deallocate function + * it is UB to not deallocate memory allocated by this allocator + * + * an internal vector is used to store the regions of memory which have been deallocated. the allocate function will search for + * free blocks with sufficient size in order to maximize memory usage. In the future more advanced methods should be used + * for both faster access to deallocated blocks of sufficient size and to ensure coherent memory. + */ + struct block_storage + { + pointer data; + size_t used = 0; + // TODO: b-tree? + std::vector unallocated_blocks; + }; + + /** + * Stores an index to a pointer_view along with the amount of memory leftover after the allocation + * it also stores the block being allocated to in question. The new inserted leftover should start at old_ptr + size + */ + struct block_view + { + block_storage* blk; + size_t index; + size_t leftover; + + block_view(block_storage* blk, size_t index, size_t leftover): blk(blk), index(index), leftover(leftover) + {} + }; + + /** + * Allocate a new block of memory and push it to the back of blocks. + */ + inline void allocate_block() + { + //BLT_INFO("Allocating a new block of size %d", BLOCK_SIZE); + auto* blk = new block_storage(); + blk->data = static_cast(malloc(sizeof(T) * BLOCK_SIZE)); + blocks.push_back(blk); + } + + /** + * Searches for a free block inside the block storage with sufficient space and returns an optional view to it + * The optional will be empty if no open block can be found. + */ + inline std::optional search_for_block(block_storage* blk, size_t n) + { + for (auto [index, item] : blt::enumerate(blk->unallocated_blocks)) + { + if (item.n >= n) + return block_view{blk, index, item.n - n}; + } + return {}; + } + + /** + * removes the block of memory from the unallocated_blocks storage in the underlying block, inserting a new unallocated block if + * there was any leftover. Returns a pointer to the beginning of the new block. + */ + inline pointer swap_pop_resize_if(const block_view& view, size_t n) + { + pointer_view ptr = view.blk->unallocated_blocks[view.index]; + std::iter_swap(view.blk->unallocated_blocks.begin() + view.index, view.blk->unallocated_blocks.end() - 1); + view.blk->unallocated_blocks.pop_back(); + if (view.leftover > 0) + view.blk->unallocated_blocks.push_back({ptr.p + n, view.leftover}); + return ptr.p; + } + + /** + * Finds the next available unallocated block of memory, or empty if there is none which meet size requirements + */ + inline std::optional find_available_block(size_t n) + { + for (auto* blk : blocks) + { + if (auto view = search_for_block(blk, n)) + return swap_pop_resize_if(view.value(), n); + } + return {}; + } + + /** + * returns a pointer to a block of memory along with an offset into that pointer that the requested block can be found at + */ + inline std::pair getBlock(size_t n) + { + if (auto blk = find_available_block(n)) + return {blk.value(), 0}; + + if (blocks.back()->used + n > BLOCK_SIZE) + allocate_block(); + + auto ptr = std::pair{blocks.back()->data, blocks.back()->used}; + blocks.back()->used += n; + return ptr; + } + + /** + * Calls the constructor on elements if they require construction, otherwise constructor will not be called and this function is useless + * + * ALLOCATORS RETURN UNINIT STORAGE!! THIS HAS BEEN DISABLED. + */ + inline void allocate_in_block(pointer, size_t) + { + // if constexpr (std::is_default_constructible_v && !std::is_trivially_default_constructible_v) + // { + // for (size_t i = 0; i < n; i++) + // new(&begin[i]) T(); + // } + } + + public: + area_allocator() + { + allocate_block(); + } + + area_allocator(const area_allocator& copy) = delete; + + area_allocator(area_allocator&& move) noexcept + { + blocks = move.blocks; + } + + area_allocator& operator=(const area_allocator& copy) = delete; + + area_allocator& operator=(area_allocator&& move) noexcept + { + std::swap(move.blocks, blocks); + } + + [[nodiscard]] pointer allocate(size_t n) + { + if (n > BLOCK_SIZE) + throw std::runtime_error("Requested allocation is too large!"); + + auto block_info = getBlock(n); + + auto* ptr = &block_info.first[block_info.second]; + // call constructors on the objects if they require it + allocate_in_block(ptr, n); + + return ptr; + } + + void deallocate(pointer p, size_t n) noexcept + { + if (p == nullptr) + return; + // for (size_t i = 0; i < n; i++) + // p[i].~T(); + for (auto*& blk : blocks) + { + if (p >= blk->data && p <= (blk->data + BLOCK_SIZE)) + { + blk->unallocated_blocks.push_back(pointer_view{p, n}); + break; + } + } + } + + ~area_allocator() + { + for (auto*& blk : blocks) + { + free(blk->data); + delete blk; + } + } + + private: + std::vector blocks; + }; + + // template + // class bump_allocator : public allocator_base + // { + // public: + // using value = T; + // using type = T; + // using value_type = type; + // using pointer = type*; + // using const_pointer = const type*; + // using void_pointer = void*; + // using const_void_pointer = const void*; + // using reference = value_type&; + // using const_reference = const value_type&; + // using size_type = size_t; + // using difference_type = size_t; + // using propagate_on_container_move_assignment = std::false_type; + // template + // struct rebind + // { + // typedef blt::bump_allocator other; + // }; + // using allocator_base::allocator_base; + // private: + // pointer buffer_; + // blt::size_t offset_; + // blt::size_t size_; + // public: + // explicit bump_allocator(blt::size_t size): buffer_(static_cast(malloc(size * sizeof(T)))), offset_(0), size_(size) + // {} + // + // template + // explicit bump_allocator(blt::size_t size, Args&& ... defaults): + // buffer_(static_cast(malloc(size * sizeof(type)))), offset_(0), size_(size) + // { + // for (blt::size_t i = 0; i < size_; i++) + // ::new(&buffer_[i]) T(std::forward(defaults)...); + // } + // + // bump_allocator(pointer buffer, blt::size_t size): buffer_(buffer), offset_(0), size_(size) + // {} + // + // bump_allocator(const bump_allocator& copy) = delete; + // + // bump_allocator(bump_allocator&& move) noexcept + // { + // buffer_ = move.buffer_; + // size_ = move.size_; + // offset_ = move.offset_; + // } + // + // bump_allocator& operator=(const bump_allocator& copy) = delete; + // + // bump_allocator& operator=(bump_allocator&& move) noexcept + // { + // std::swap(move.buffer_, buffer_); + // std::swap(move.size_, size_); + // std::swap(move.offset_, offset_); + // } + // + // pointer allocate(blt::size_t n) + // { + // auto nv = offset_ + n; + // if (nv > size_) + // throw std::bad_alloc(); + // pointer b = &buffer_[offset_]; + // offset_ = nv; + // return b; + // } + // + // void deallocate(pointer, blt::size_t) + // {} + // + // ~bump_allocator() + // { + // free(buffer_); + // } + // }; + + /** + * The bump allocator is meant to be a faster area allocator which will only allocate forward through either a supplied buffer or size + * or will create a linked list type data structure of buffered blocks. + * @tparam ALLOC allocator to use for any allocations. In the case of the non-linked variant, this will be used if a size is supplied. The supplied buffer must be allocated with this allocator! + * @tparam linked use a linked list to allocate with the allocator or just use the supplied buffer and throw an exception of we cannot allocate + */ + template typename ALLOC = std::allocator> + class bump_allocator_old; + + template typename ALLOC> + class bump_allocator_old + { + private: + ALLOC allocator; + blt::u8* buffer_; + blt::u8* offset_; + blt::size_t size_; + + public: + explicit bump_allocator_old(blt::size_t size): buffer_(static_cast(allocator.allocate(size))), offset_(buffer_), size_(size) + {} + + explicit bump_allocator_old(blt::u8* buffer, blt::size_t size): buffer_(buffer), offset_(buffer), size_(size) + {} + + template + [[nodiscard]] T* allocate() + { + size_t remaining_num_bytes = size_ - static_cast(buffer_ - offset_); + auto pointer = static_cast(offset_); + const auto aligned_address = std::align(alignof(T), sizeof(T), pointer, remaining_num_bytes); + if (aligned_address == nullptr) + throw std::bad_alloc{}; + offset_ = static_cast(aligned_address) + sizeof(T); + return static_cast(aligned_address); + } + + template + [[nodiscard]] T* emplace(Args&&... args) + { + const auto allocated_memory = allocate(); + return new(allocated_memory) T{std::forward(args)...}; + } + + template + inline void construct(U* p, Args&&... args) + { + ::new((void*) p) U(std::forward(args)...); + } + + template + inline void destroy(U* p) + { + if (p != nullptr) + p->~U(); + } + + ~bump_allocator_old() + { + allocator.deallocate(buffer_, size_); + } + }; + + template typename ALLOC> + class bump_allocator_old + { + private: + struct block + { + blt::size_t allocated_objects = 0; + blt::u8* buffer = nullptr; + blt::u8* offset = nullptr; + + explicit block(blt::u8* buffer): buffer(buffer), offset(buffer) + {} + }; + + ALLOC allocator; + std::vector> blocks; + blt::size_t size_; + blt::size_t allocations = 0; + blt::size_t deallocations = 0; + + void expand() + { + auto ptr = static_cast(allocator.allocate(size_)); + blocks.push_back(block{ptr}); + allocations++; + } + + template + T* allocate_back() + { + auto& back = blocks.back(); + size_t remaining_bytes = size_ - static_cast(back.offset - back.buffer); + auto pointer = static_cast(back.offset); + const auto aligned_address = std::align(alignof(T), sizeof(T), pointer, remaining_bytes); + if (aligned_address != nullptr) + { + back.offset = static_cast(aligned_address) + sizeof(T); + back.allocated_objects++; + } + + return static_cast(aligned_address); + } + + public: + /** + * @param size of the list blocks + */ + explicit bump_allocator_old(blt::size_t size): size_(size) + { + expand(); + } + + template + [[nodiscard]] T* allocate() + { + if (auto ptr = allocate_back(); ptr == nullptr) + expand(); + else + return ptr; + if (auto ptr = allocate_back(); ptr == nullptr) + throw std::bad_alloc(); + else + return ptr; + } + + template + void deallocate(T* p) + { + auto* ptr = reinterpret_cast(p); + for (auto e : blt::enumerate(blocks)) + { + auto& block = e.second; + if (ptr >= block.buffer && ptr <= block.offset) + { + block.allocated_objects--; + if (block.allocated_objects == 0) + { + std::iter_swap(blocks.begin() + e.first, blocks.end() - 1); + allocator.deallocate(blocks.back().buffer, size_); + blocks.pop_back(); + deallocations++; + } + return; + } + } + } + + template + [[nodiscard]] T* emplace(Args&&... args) + { + const auto allocated_memory = allocate(); + return new(allocated_memory) T{std::forward(args)...}; + } + + template + inline void construct(U* p, Args&&... args) + { + ::new((void*) p) U(std::forward(args)...); + } + + template + inline void destroy(U* p) + { + if (p != nullptr) + p->~U(); + } + + ~bump_allocator_old() + { + if (allocations != deallocations) + BLT_WARN("Allocator has blocks which have not been deallocated! Destructors might not have been called!"); + for (auto& v : blocks) + allocator.deallocate(v.buffer, size_); + } + }; + + template + static inline T* allocate_huge_page(blt::size_t BLOCK_SIZE, blt::size_t HUGE_PAGE_SIZE = BLT_2MB_SIZE) + { + #ifdef __unix__ + BLT_ASSERT((BLOCK_SIZE & (HUGE_PAGE_SIZE - 1)) == 0 && "Must be multiple of the huge page size!"); + T* buffer = static_cast(mmap(nullptr, BLOCK_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB | MAP_POPULATE, -1, + 0)); + // if we fail to allocate a huge page we can try to allocate normally + if (buffer == MAP_FAILED) + { + if constexpr (WARN_ON_FAIL) + { + BLT_WARN("We failed to allocate huge pages\n{}{}", handle_mmap_error(), + "\033[1;31mYou should attempt to enable " + "huge pages as this will allocate normal pages and double the memory usage!\033[22m\n"); + } + blt::size_t bytes = BLOCK_SIZE * 2; + buffer = static_cast(mmap(nullptr, bytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, -1, 0)); + if (buffer == MAP_FAILED) + { + BLT_ERROR("Failed to allocate normal pages"); + throw bad_alloc_t(handle_mmap_error()); + } + if constexpr (WARN_ON_FAIL) + { + if (((size_t) buffer & (HUGE_PAGE_SIZE - 1)) != 0) + BLT_ERROR("Pointer is not aligned! %p", buffer); + } + auto* ptr = static_cast(buffer); + auto ptr_size = reinterpret_cast(ptr); + buffer = static_cast(std::align(BLOCK_SIZE, BLOCK_SIZE, ptr, bytes)); + if constexpr (WARN_ON_FAIL) + BLT_ERROR("Offset by %ld pages, resulting: %p", (reinterpret_cast(buffer) - ptr_size) / 4096, buffer); + } + return buffer; + #endif + return malloc(BLOCK_SIZE); + } + + /** + * blt::bump_allocator. Allocates blocks of BLOCK_SIZE with zero reuse. When all objects from a block are fully deallocated the block will be freed + * @tparam BLOCK_SIZE size of block to use. recommended to be multiple of page size or huge page size. + * @tparam USE_HUGE allocate using mmap and huge pages. If this fails it will use mmap to allocate normally. defaults to off because linux has parent huge pages. + * @tparam HUGE_PAGE_SIZE size the system allows huge pages to be. defaults to 2mb + * @tparam WARN_ON_FAIL print warning messages if allocating huge pages fail + */ + template + class bump_allocator + { + // ensure power of two + static_assert(((BLOCK_SIZE & (BLOCK_SIZE - 1)) == 0) && "Must be a power of two!"); + + public: + /** + * convert any pointer back into a pointer its block + */ + template + static inline auto to_block(T* p) + { + return reinterpret_cast(reinterpret_cast(p) & static_cast(~(BLOCK_SIZE - 1))); + } + + class stats_t + { + friend bump_allocator; + + private: + blt::size_t allocated_blocks = 0; + blt::size_t allocated_bytes = 0; + blt::size_t peak_blocks = 0; + blt::size_t peak_bytes = 0; + + protected: + inline void incrementBlocks() + { + allocated_blocks++; + if (allocated_blocks > peak_blocks) + peak_blocks = allocated_blocks; + } + + inline void decrementBlocks() + { + allocated_blocks--; + } + + inline void incrementBytes(blt::size_t bytes) + { + allocated_bytes += bytes; + if (allocated_bytes > peak_bytes) + peak_bytes = allocated_bytes; + } + + inline void decrementBytes(blt::size_t bytes) + { + allocated_bytes -= bytes; + } + + public: + inline auto getAllocatedBlocks() const + { + return allocated_blocks; + } + + inline auto getAllocatedBytes() const + { + return allocated_bytes; + } + + inline auto getPeakBlocks() const + { + return peak_blocks; + } + + inline auto getPeakBytes() const + { + return peak_bytes; + } + }; + + private: + stats_t stats; + //blt::hashset_t deletes; + + struct block + { + struct block_metadata_t + { + blt::size_t allocated_objects = 0; + block* next = nullptr; + block* prev = nullptr; + blt::u8* offset = nullptr; + } metadata; + + blt::u8 buffer[BLOCK_SIZE - sizeof(block_metadata_t)]{}; + + block() + { + metadata.offset = buffer; + } + }; + + // remaining space inside the block after accounting for the metadata + static constexpr blt::size_t BLOCK_REMAINDER = BLOCK_SIZE - sizeof(typename block::block_metadata_t); + + block* base = nullptr; + block* head = nullptr; + + /** + * Handles the allocation of the bytes for the block. + * This function will either use mmap to allocate huge pages if requested + * or use std::align_alloc to create an aligned allocation + * @return pointer to a constructed block + */ + block* allocate_block() + { + block* buffer; + #ifdef __unix__ + if constexpr (USE_HUGE) + { + buffer = allocate_huge_page(BLOCK_SIZE, HUGE_PAGE_SIZE); + } else + buffer = reinterpret_cast(std::aligned_alloc(BLOCK_SIZE, BLOCK_SIZE)); + #else buffer = static_cast(_aligned_malloc(BLOCK_SIZE, BLOCK_SIZE)); -#endif - construct(buffer); -#ifndef BLT_DISABLE_STATS - stats.incrementBlocks(); -#endif - return buffer; - } - - /** - * Allocates a new block and pushes it to the front of the linked listed - */ - void allocate_forward() - { - auto* block = allocate_block(); - if (head == nullptr) - { - base = head = block; - return; - } - block->metadata.prev = head; - head->metadata.next = block; - head = block; - } - - /** - * handles the actual allocation and alignment of memory - * @param bytes number of bytes to allocate - * @param alignment alignment required - * @return aligned pointer - */ - void* allocate_bytes(blt::size_t bytes, blt::size_t alignment) - { - if (head == nullptr) - return nullptr; - blt::size_t remaining_bytes = BLOCK_REMAINDER - static_cast(head->metadata.offset - head->buffer); - auto pointer = static_cast(head->metadata.offset); - return std::align(alignment, bytes, pointer, remaining_bytes); - } - - /** - * allocate an object starting from the next available address - * @tparam T type to allocate for - * @param count number of elements to allocate - * @return nullptr if the object could not be allocated, pointer to the object if it could, pointer to the start if count != 1 - */ - template - T* allocate_object(blt::size_t count) - { - blt::size_t bytes = sizeof(T) * count; - const auto aligned_address = allocate_bytes(bytes, alignof(T)); - if (aligned_address != nullptr) - { - head->metadata.allocated_objects++; - head->metadata.offset = static_cast(aligned_address) + bytes; - } - return static_cast(aligned_address); - } - - /** - * Frees a block - * @param p pointer to the block to free - */ - inline void delete_block(block* p) - { -#ifndef BLT_DISABLE_STATS - stats.decrementBlocks(); -#endif - if constexpr (USE_HUGE) - { - if (munmap(p, BLOCK_SIZE)) - { - BLT_ERROR_STREAM << "FAILED TO DEALLOCATE BLOCK\n"; - throw bad_alloc_t(handle_mmap_error()); - } - } else - free(p); - } - - public: - bump_allocator() = default; - - /** - * Takes an unused size parameter. Purely used for compatibility with the old bump_allocator - */ - explicit bump_allocator(blt::size_t) - {} - - /** - * Allocate bytes for a type - * @tparam T type to allocate - * @param count number of elements to allocate for - * @throws std::bad_alloc - * @return aligned pointer to the beginning of the allocated memory - */ - template - [[nodiscard]] T* allocate(blt::size_t count = 1) - { - if constexpr (sizeof(T) > BLOCK_REMAINDER) - throw std::bad_alloc(); + #endif + construct(buffer); + #ifndef BLT_DISABLE_STATS + stats.incrementBlocks(); + #endif + return buffer; + } -#ifndef BLT_DISABLE_STATS - stats.incrementBytes(sizeof(T) * count); -#endif - - T* ptr = allocate_object(count); - if (ptr != nullptr) - return ptr; - allocate_forward(); - ptr = allocate_object(count); - if (ptr == nullptr) - throw std::bad_alloc(); - return ptr; - } - - /** - * Deallocate a pointer, does not call the destructor - * @tparam T type of pointer - * @param p pointer to deallocate - */ - template - void deallocate(T* p, blt::size_t count = 1) - { - if (p == nullptr) - return; -#ifndef BLT_DISABLE_STATS - stats.decrementBytes(sizeof(T) * count); -#endif -// if (deletes.contains(p)) -// { -// BLT_FATAL("pointer %p has already been freed", p); -// throw std::bad_alloc(); -// }else -// deletes.insert(static_cast(p)); - - auto blk = to_block(p); - blk->metadata.allocated_objects--; - if (blk->metadata.allocated_objects == 0) - { - //BLT_INFO("Deallocating block from %p in (1) %p current head %p, based: %p", p, blk, head, base); - if (blk == base) - { - base = base->metadata.next; - // if they were equal (single allocated block) we also need to move the head forward - if (blk == head) - head = base; - } else if (blk == head) // else, need to make sure the head ptr gets moved back, otherwise we will use a head that has been freed - head = blk->metadata.prev; - else if (blk->metadata.prev != nullptr) // finally if it wasn't the head we need to bridge the gap in the list - blk->metadata.prev->metadata.next = blk->metadata.next; - - //BLT_INFO("Deallocating block from %p in (2) %p current head %p, based: %p", p, blk, head, base); - delete_block(blk); - } - } - - /** - * allocate a type then call its constructor with arguments - * @tparam T type to construct - * @tparam Args type of args to construct with - * @param args args to construct with - * @return aligned pointer to the constructed type - */ - template - [[nodiscard]] T* emplace(Args&& ... args) - { - const auto allocated_memory = allocate(); - return new(allocated_memory) T{std::forward(args)...}; - } - - /** - * allocate an array of count T with argument(s) args and call T's constructor - * @tparam T class to construct - * @tparam Args argument types to supply to construction - * @param count size of the array to allocate in number of elements. Note calling this with count = 0 is equivalent to calling emplace - * @param args the args to supply to construction - * @return aligned pointer to the beginning of the array of T - */ - template - [[nodiscard]] T* emplace_many(blt::size_t count, Args&& ... args) - { - if (count == 0) - return nullptr; - const auto allocated_memory = allocate(count); - for (blt::size_t i = 0; i < count; i++) - new(allocated_memory + i) T{std::forward(args)...}; - return allocated_memory; - } - - /** - * Used to construct a class U with parameters Args - * @tparam U class to construct - * @tparam Args args to use - * @param p pointer to non-constructed memory - * @param args list of arguments to build the class with - */ - template - inline void construct(U* p, Args&& ... args) - { - ::new((void*) p) U(std::forward(args)...); - } - - /** - * Call the destructor for class U with pointer p - * @tparam U class to call destructor on, this will not do anything if the type is std::trivially_destructible - * @param p - */ - template - inline void destroy(U* p) - { - if constexpr (!std::is_trivially_destructible_v) - { - if (p != nullptr) - p->~U(); - } - } - - /** - * Calls destroy on pointer p - * Then calls deallocate on p - * @tparam U class to destroy - * @param p pointer to deallocate - */ - template - inline void destruct(U* p) - { - destroy(p); - deallocate(p); - } - - inline void resetStats() - { - stats = {}; - } - - inline const auto& getStats() const - { - return stats; - } - - ~bump_allocator() - { - block* next = base; - while (next != nullptr) - { - auto* after = next->metadata.next; - delete_block(next); - next = after; - } - } - }; + /** + * Allocates a new block and pushes it to the front of the linked listed + */ + void allocate_forward() + { + auto* block = allocate_block(); + if (head == nullptr) + { + base = head = block; + return; + } + block->metadata.prev = head; + head->metadata.next = block; + head = block; + } + + /** + * handles the actual allocation and alignment of memory + * @param bytes number of bytes to allocate + * @param alignment alignment required + * @return aligned pointer + */ + void* allocate_bytes(blt::size_t bytes, blt::size_t alignment) + { + if (head == nullptr) + return nullptr; + blt::size_t remaining_bytes = BLOCK_REMAINDER - static_cast(head->metadata.offset - head->buffer); + auto pointer = static_cast(head->metadata.offset); + return std::align(alignment, bytes, pointer, remaining_bytes); + } + + /** + * allocate an object starting from the next available address + * @tparam T type to allocate for + * @param count number of elements to allocate + * @return nullptr if the object could not be allocated, pointer to the object if it could, pointer to the start if count != 1 + */ + template + T* allocate_object(blt::size_t count) + { + blt::size_t bytes = sizeof(T) * count; + const auto aligned_address = allocate_bytes(bytes, alignof(T)); + if (aligned_address != nullptr) + { + head->metadata.allocated_objects++; + head->metadata.offset = static_cast(aligned_address) + bytes; + } + return static_cast(aligned_address); + } + + /** + * Frees a block + * @param p pointer to the block to free + */ + inline void delete_block(block* p) + { + #ifndef BLT_DISABLE_STATS + stats.decrementBlocks(); + #endif + if constexpr (USE_HUGE) + { + if (munmap(p, BLOCK_SIZE)) + { + BLT_ERROR("FAILED TO DEALLOCATE BLOCK"); + throw bad_alloc_t(handle_mmap_error()); + } + } else + free(p); + } + + public: + bump_allocator() = default; + + /** + * Takes an unused size parameter. Purely used for compatibility with the old bump_allocator + */ + explicit bump_allocator(blt::size_t) + {} + + /** + * Allocate bytes for a type + * @tparam T type to allocate + * @param count number of elements to allocate for + * @throws std::bad_alloc + * @return aligned pointer to the beginning of the allocated memory + */ + template + [[nodiscard]] T* allocate(blt::size_t count = 1) + { + if constexpr (sizeof(T) > BLOCK_REMAINDER) + throw std::bad_alloc(); + + #ifndef BLT_DISABLE_STATS + stats.incrementBytes(sizeof(T) * count); + #endif + + T* ptr = allocate_object(count); + if (ptr != nullptr) + return ptr; + allocate_forward(); + ptr = allocate_object(count); + if (ptr == nullptr) + throw std::bad_alloc(); + return ptr; + } + + /** + * Deallocate a pointer, does not call the destructor + * @tparam T type of pointer + * @param p pointer to deallocate + */ + template + void deallocate(T* p, blt::size_t count = 1) + { + if (p == nullptr) + return; + #ifndef BLT_DISABLE_STATS + stats.decrementBytes(sizeof(T) * count); + #endif + // if (deletes.contains(p)) + // { + // BLT_FATAL("pointer %p has already been freed", p); + // throw std::bad_alloc(); + // }else + // deletes.insert(static_cast(p)); + + auto blk = to_block(p); + blk->metadata.allocated_objects--; + if (blk->metadata.allocated_objects == 0) + { + //BLT_INFO("Deallocating block from %p in (1) %p current head %p, based: %p", p, blk, head, base); + if (blk == base) + { + base = base->metadata.next; + // if they were equal (single allocated block) we also need to move the head forward + if (blk == head) + head = base; + } else if (blk == head) // else, need to make sure the head ptr gets moved back, otherwise we will use a head that has been freed + head = blk->metadata.prev; + else if (blk->metadata.prev != nullptr) // finally if it wasn't the head we need to bridge the gap in the list + blk->metadata.prev->metadata.next = blk->metadata.next; + + //BLT_INFO("Deallocating block from %p in (2) %p current head %p, based: %p", p, blk, head, base); + delete_block(blk); + } + } + + /** + * allocate a type then call its constructor with arguments + * @tparam T type to construct + * @tparam Args type of args to construct with + * @param args args to construct with + * @return aligned pointer to the constructed type + */ + template + [[nodiscard]] T* emplace(Args&&... args) + { + const auto allocated_memory = allocate(); + return new(allocated_memory) T{std::forward(args)...}; + } + + /** + * allocate an array of count T with argument(s) args and call T's constructor + * @tparam T class to construct + * @tparam Args argument types to supply to construction + * @param count size of the array to allocate in number of elements. Note calling this with count = 0 is equivalent to calling emplace + * @param args the args to supply to construction + * @return aligned pointer to the beginning of the array of T + */ + template + [[nodiscard]] T* emplace_many(blt::size_t count, Args&&... args) + { + if (count == 0) + return nullptr; + const auto allocated_memory = allocate(count); + for (blt::size_t i = 0; i < count; i++) + new(allocated_memory + i) T{std::forward(args)...}; + return allocated_memory; + } + + /** + * Used to construct a class U with parameters Args + * @tparam U class to construct + * @tparam Args args to use + * @param p pointer to non-constructed memory + * @param args list of arguments to build the class with + */ + template + inline void construct(U* p, Args&&... args) + { + ::new((void*) p) U(std::forward(args)...); + } + + /** + * Call the destructor for class U with pointer p + * @tparam U class to call destructor on, this will not do anything if the type is std::trivially_destructible + * @param p + */ + template + inline void destroy(U* p) + { + if constexpr (!std::is_trivially_destructible_v) + { + if (p != nullptr) + p->~U(); + } + } + + /** + * Calls destroy on pointer p + * Then calls deallocate on p + * @tparam U class to destroy + * @param p pointer to deallocate + */ + template + inline void destruct(U* p) + { + destroy(p); + deallocate(p); + } + + inline void resetStats() + { + stats = {}; + } + + inline const auto& getStats() const + { + return stats; + } + + ~bump_allocator() + { + block* next = base; + while (next != nullptr) + { + auto* after = next->metadata.next; + delete_block(next); + next = after; + } + } + }; } #define BLT_ALLOCATOR_H diff --git a/include/blt/std/logging.h b/include/blt/std/logging.h deleted file mode 100644 index 86d6b41..0000000 --- a/include/blt/std/logging.h +++ /dev/null @@ -1,436 +0,0 @@ -/* - * Created by Brett on 20/07/23. - * Licensed under GNU General Public License V3.0 - * See LICENSE file for license detail - */ - -#ifndef BLT_TESTS_LOGGING2_H -#define BLT_TESTS_LOGGING2_H - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace blt::logging -{ - - namespace ansi - { - inline auto ESC(std::string_view str) - { - return std::string("\033") += str; - } - - inline const auto CUR_HOME = ESC("[H"); - - inline auto CUR_MOVE(blt::size_t line, blt::size_t column) - { - return ESC("[{" + std::to_string(line) + "};{" + std::to_string(column) + "}H"); - } - - inline auto CUR_UP(blt::size_t lines) - { - return ESC("[" + std::to_string(lines) + "A"); - } - - inline auto CUR_DOWN(blt::size_t lines) - { - return ESC("[" + std::to_string(lines) + "B"); - } - - inline auto CUR_RIGHT(blt::size_t columns) - { - return ESC("[" + std::to_string(columns) + "C"); - } - - inline auto CUR_LEFT(blt::size_t columns) - { - return ESC("[" + std::to_string(columns) + "D"); - } - - inline auto CUR_BEGIN_NEXT(blt::size_t lines_down) - { - return ESC("[" + std::to_string(lines_down) + "E"); - } - - inline auto CUR_BEGIN_PREV(blt::size_t lines_up) - { - return ESC("[" + std::to_string(lines_up) + "F"); - } - - inline auto CUR_COLUMN(blt::size_t column) - { - return ESC("[" + std::to_string(column) + "G"); - } - - inline auto CUR_POS() - { - return ESC("[6n"); - } - - inline auto CUR_SCROLL_UP() - { - return ESC(" M"); - } - - inline auto CUR_SAVE_DEC() - { - return ESC(" 7"); - } - - inline auto CUR_LOAD_DEC() - { - return ESC(" 8"); - } - - inline auto CUR_SAVE_SCO() - { - return ESC("[s"); - } - - inline auto CUR_LOAD_SCO() - { - return ESC("[u"); - } - - inline auto RESET = ESC("[0m"); - inline auto BOLD = "1"; - inline auto RESET_BOLD = "22"; - inline auto DIM = "2"; - inline auto RESET_DIM = "22"; - inline auto ITALIC = "3"; - inline auto RESET_ITALIC = "23"; - inline auto UNDERLINE = "4"; - inline auto RESET_UNDERLINE = "24"; - inline auto BLINKING = "5"; - inline auto RESET_BLINKING = "25"; - inline auto INVERSE = "7"; - inline auto RESET_INVERSE = "27"; - inline auto HIDDEN = "8"; - inline auto RESET_HIDDEN = "28"; - inline auto STRIKETHROUGH = "9"; - inline auto RESET_STRIKETHROUGH = "29"; - - inline auto COLOR_DEFAULT = "39"; - inline auto BACKGROUND_DEFAULT = "49"; - - inline auto BLACK = "30"; - inline auto RED = "31"; - inline auto GREEN = "32"; - inline auto YELLOW = "33"; - inline auto BLUE = "34"; - inline auto MAGENTA = "35"; - inline auto CYAN = "36"; - inline auto WHITE = "37"; - inline auto BLACK_BACKGROUND = "40"; - inline auto RED_BACKGROUND = "41"; - inline auto GREEN_BACKGROUND = "42"; - inline auto YELLOW_BACKGROUND = "43"; - inline auto BLUE_BACKGROUND = "44"; - inline auto MAGENTA_BACKGROUND = "45"; - inline auto CYAN_BACKGROUND = "46"; - inline auto WHITE_BACKGROUND = "47"; - - inline auto BRIGHT_BLACK = "90"; - inline auto BRIGHT_RED = "91"; - inline auto BRIGHT_GREEN = "92"; - inline auto BRIGHT_YELLOW = "93"; - inline auto BRIGHT_BLUE = "94"; - inline auto BRIGHT_MAGENTA = "95"; - inline auto BRIGHT_CYAN = "96"; - inline auto BRIGHT_WHITE = "97"; - inline auto BRIGHT_BLACK_BACKGROUND = "100"; - inline auto BRIGHT_RED_BACKGROUND = "101"; - inline auto BRIGHT_GREEN_BACKGROUND = "102"; - inline auto BRIGHT_YELLOW_BACKGROUND = "103"; - inline auto BRIGHT_BLUE_BACKGROUND = "104"; - inline auto BRIGHT_MAGENTA_BACKGROUND = "105"; - inline auto BRIGHT_CYAN_BACKGROUND = "106"; - inline auto BRIGHT_WHITE_BACKGROUND = "107"; - - template - inline auto make_color(Args... colors) - { - std::string mode; - ((mode += std::string(colors) + ";"), ...); - return ESC("[" + mode.substr(0, mode.size() - 1) + "m"); - } - } - - enum class log_level - { - // default - NONE, - // low level - TRACE0, TRACE1, TRACE2, TRACE3, - // normal - TRACE, DEBUG, INFO, - // errors - WARN, ERROR, FATAL, - }; - - struct tag_func_param - { - blt::logging::log_level level; - const std::string& file, line, raw_string, formatted_string; - }; - - struct tag - { - // tag without the ${{ or }} - std::string tag; - // function to run: log level, file, line, and raw user input string are provided - std::function func; - }; - - struct log_format - { - /** - * the log output format is the string which will be used to create the log output string - * - * Available tags: - * - ${{YEAR}} // current year - * - ${{MONTH}} // current month - * - ${{DAY}} // current day - * - ${{HOUR}} // current hour - * - ${{MINUTE}} // current minute - * - ${{SECOND}} // current second - * - ${{MS}} // current unix time - * - ${{NS}} // current ns from the high resolution system timer - * - ${{ISO_YEAR}} // ISO formatted 'year-month-day' in a single variable - * - ${{TIME}} // 'hour:minute:second' formatted string in a single variable - * - ${{FULL_TIME}} // 'year-month-day hour:minute:second' in a single variable - * - ${{LF}} // log level color (ANSI color code) - * - ${{ER}} // Error red - * - ${{CNR}} // conditional error red (only outputs if log level is an error!) - * - ${{RC}} // ANSI color reset - * - ${{LOG_LEVEL}} // current log level - * - ${{THREAD_NAME}} // current thread name, NOTE: thread names must be set by calling "setThreadName()" from the thread in question! - * - ${{FILE}} // file from which the macro is invoked - * - ${{LINE}} // line in the from which the macro is invoked - * - ${{RAW_STR}} // raw user string without formatting applied (NOTE: format args are not provided!) - * - ${{STR}} // the user supplied string (format applied!) - */ - std::string logOutputFormat = "\033[94m[${{TIME}}]${{RC}} ${{LF}}[${{LOG_LEVEL}}]${{RC}} \033[35m(${{FILE}}:${{LINE}})${{RC}} ${{CNR}}${{STR}}${{RC}}\n"; - std::string levelNames[11] = {"STDOUT", "TRACE0", "TRACE1", "TRACE2", "TRACE3", "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"}; - std::string levelColors[11] = {"\033[0m", "\033[22;97m", "\033[97m", "\033[97m", "\033[97m", "\033[97m", "\033[36m", "\033[92m", "\033[93m", - "\033[91m", "\033[97;41m"}; - // if true prints the whole path to the file (eg /home/user/.../.../project/src/source.cpp:line#) - bool printFullFileName = false; - // the logging lib will keep track of the largest line found so far and try to keep the spacing accordingly - // this is not thread safe! - bool ensureAlignment = false; - // should we log to file? - bool logToFile = false; - // should we log to console? - bool logToConsole = true; - // where should we log? (empty for current binary directory) should end in a / if not empty! - std::string logFilePath; - // logs to a file called $fileName_$count.log where count is the number of rollover files - // this accepts any of the macros above, using level names and colors should work but isn't supported. - std::string logFileName = "${{ISO_YEAR}}"; - // default limit on file size: 10mb; - size_t logMaxFileSize = 1024 * 1024 * 10; - /** - * Variables below this line should never be changed by the user! - */ - // the current alignment width found (you shouldn't chance this variable!) - size_t currentWidth = 0; - // current number of file roll-overs. you shouldn't change this either. - size_t currentRollover = 0; - std::string lastFile; - }; - - struct logger - { - log_level level; - const char* file; - int line; - }; - - struct empty_logger - { - - }; - - void log_internal(const std::string& format, log_level level, const char* file, int line, std::va_list& args); - - void log_stream_internal(const std::string& str, const logger& logger); - - template - inline std::string to_string_stream(const T& t) - { - std::stringstream stream; - stream << t; - return stream.str(); - } - - template - inline static void log_stream(const T& t, const logger& logger) - { - if constexpr (std::is_arithmetic_v && !std::is_same_v) - { - log_stream_internal(std::to_string(t), logger); - } else if constexpr (std::is_same_v || std::is_same_v) - { - log_stream_internal(t, logger); - } else - { - log_stream_internal(to_string_stream(t), logger); - } - } - - template - inline void log(T t, log_level level, const char* file, int line, ...) - { - std::va_list args; - va_start(args, line); - if constexpr (std::is_arithmetic_v) - { - log_internal(std::to_string(t), level, file, line, args); - } else if constexpr (std::is_same_v) - { - log_internal(t, level, file, line, args); - } else if constexpr (std::is_same_v) - { - log_internal(std::string(t), level, file, line, args); - } else - { - log_internal(to_string_stream(t), level, file, line, args); - } - va_end(args); - } - - template - static inline const blt::logging::logger& operator<<(const blt::logging::logger& out, const T& t) - { - log_stream(t, out); - return out; - } - - template - static inline const blt::logging::empty_logger& operator<<(const blt::logging::empty_logger& out, const T&) - { - return out; - } - - void flush(); - - void newline(); - - void setThreadName(const std::string& name); - - void setLogFormat(const log_format& format); - - void setLogColor(log_level level, const std::string& newFormat); - - void setLogName(log_level level, const std::string& newFormat); - - void setLogOutputFormat(const std::string& newFormat); - - void setLogToFile(bool shouldLogToFile); - - void setLogToConsole(bool shouldLogToConsole); - - void setLogPath(const std::string& path); - - void setLogFileName(const std::string& fileName); - - void setMaxFileSize(size_t fileSize); -} - -#if defined(__clang__) || defined(__llvm__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" -#endif - -#ifdef BLT_DISABLE_LOGGING - #define BLT_LOG(format, level, ...) - #define BLT_LOG_STREAM(level) - #define BLT_TRACE0_STREAM - #define BLT_TRACE1_STREAM - #define BLT_TRACE2_STREAM - #define BLT_TRACE3_STREAM - #define BLT_TRACE_STREAM - #define BLT_DEBUG_STREAM - #define BLT_INFO_STREAM - #define BLT_WARN_STREAM - #define BLT_ERROR_STREAM - #define BLT_FATAL_STREAM - #define BLT_TRACE(format, ...) - #define BLT_DEBUG(format, ...) - #define BLT_INFO(format, ...) - #define BLT_WARN(format, ...) - #define BLT_ERROR(format, ...) - #define BLT_FATAL(format, ...) -#else - #define BLT_NEWLINE() blt::logging::newline() - #define BLT_LOG(format, level, ...) blt::logging::log(format, level, __FILE__, __LINE__, ##__VA_ARGS__) - #define BLT_LOG_STREAM(level) blt::logging::logger{level, __FILE__, __LINE__} - #ifdef BLT_DISABLE_TRACE - #define BLT_TRACE(format, ...) - #define BLT_TRACE0_STREAM blt::logging::empty_logger{} - #define BLT_TRACE1_STREAM blt::logging::empty_logger{} - #define BLT_TRACE2_STREAM blt::logging::empty_logger{} - #define BLT_TRACE3_STREAM blt::logging::empty_logger{} - #define BLT_TRACE_STREAM blt::logging::empty_logger{} - #else - #define BLT_TRACE(format, ...) BLT_LOG(format, blt::logging::log_level::TRACE, ##__VA_ARGS__) - #define BLT_TRACE0_STREAM BLT_LOG_STREAM(blt::logging::log_level::TRACE0) - #define BLT_TRACE1_STREAM BLT_LOG_STREAM(blt::logging::log_level::TRACE1) - #define BLT_TRACE2_STREAM BLT_LOG_STREAM(blt::logging::log_level::TRACE2) - #define BLT_TRACE3_STREAM BLT_LOG_STREAM(blt::logging::log_level::TRACE3) - #define BLT_TRACE_STREAM BLT_LOG_STREAM(blt::logging::log_level::TRACE) - #endif - - #ifdef BLT_DISABLE_DEBUG - #define BLT_DEBUG(format, ...) - #define BLT_DEBUG_STREAM blt::logging::empty_logger{} - #else - #define BLT_DEBUG(format, ...) BLT_LOG(format, blt::logging::log_level::DEBUG, ##__VA_ARGS__) - #define BLT_DEBUG_STREAM BLT_LOG_STREAM(blt::logging::log_level::DEBUG) - #endif - - #ifdef BLT_DISABLE_INFO - #define BLT_INFO(format, ...) - #define BLT_INFO_STREAM blt::logging::empty_logger{} - #else - #define BLT_INFO(format, ...) BLT_LOG(format, blt::logging::log_level::INFO, ##__VA_ARGS__) - #define BLT_INFO_STREAM BLT_LOG_STREAM(blt::logging::log_level::INFO) - #endif - - #ifdef BLT_DISABLE_WARN - #define BLT_WARN(format, ...) - #define BLT_WARN_STREAM blt::logging::empty_logger{} - #else - #define BLT_WARN(format, ...) BLT_LOG(format, blt::logging::log_level::WARN, ##__VA_ARGS__) - #define BLT_WARN_STREAM BLT_LOG_STREAM(blt::logging::log_level::WARN) - #endif - - #ifdef BLT_DISABLE_ERROR - #define BLT_ERROR(format, ...) - #define BLT_ERROR_STREAM blt::logging::empty_logger{} - #else - #define BLT_ERROR(format, ...) BLT_LOG(format, blt::logging::log_level::ERROR, ##__VA_ARGS__) - #define BLT_ERROR_STREAM BLT_LOG_STREAM(blt::logging::log_level::ERROR) - #endif - - #ifdef BLT_DISABLE_FATAL - #define BLT_FATAL(format, ...) - #define BLT_FATAL_STREAM blt::logging::empty_logger{} - #else - #define BLT_FATAL(format, ...) BLT_LOG(format, blt::logging::log_level::FATAL, ##__VA_ARGS__) - #define BLT_FATAL_STREAM BLT_LOG_STREAM(blt::logging::log_level::FATAL) - #endif -#endif - -#if defined(__clang__) || defined(__llvm__) -#pragma clang diagnostic pop -#endif - -#endif //BLT_TESTS_LOGGING2_H diff --git a/include/blt/std/mmap.h b/include/blt/std/mmap.h index e1b7810..c0fb7ee 100644 --- a/include/blt/std/mmap.h +++ b/include/blt/std/mmap.h @@ -19,9 +19,11 @@ #ifndef BLT_MMAP_H #define BLT_MMAP_H -#include #include #include +#include +#include +#include // size of 2mb in bytes inline constexpr blt::size_t BLT_2MB_SIZE = 2048 * 1024; @@ -41,7 +43,7 @@ namespace blt public: bad_alloc_t() = default; - explicit bad_alloc_t(std::string_view str): str(str) + explicit bad_alloc_t(const std::string_view str): str(str) {} explicit bad_alloc_t(std::string str): str(std::move(str)) diff --git a/src/blt/format/format.cpp b/src/blt/format/format.cpp index 3d79234..bf15857 100644 --- a/src/blt/format/format.cpp +++ b/src/blt/format/format.cpp @@ -6,7 +6,7 @@ #include #include #include -#include "blt/std/logging.h" +#include "blt/logging/logging.h" #include "blt/std/assert.h" #include "blt/std/utility.h" #include diff --git a/src/blt/fs/filesystem.cpp b/src/blt/fs/filesystem.cpp index 43b9fb2..9718d9b 100644 --- a/src/blt/fs/filesystem.cpp +++ b/src/blt/fs/filesystem.cpp @@ -17,5 +17,4 @@ */ #include #include -#include diff --git a/src/blt/fs/nbt.cpp b/src/blt/fs/nbt.cpp index 1679977..5ec5c33 100644 --- a/src/blt/fs/nbt.cpp +++ b/src/blt/fs/nbt.cpp @@ -4,7 +4,7 @@ * See LICENSE file for license detail */ #include -#include +#include #include #include diff --git a/src/blt/parse/argparse_v2.cpp b/src/blt/parse/argparse_v2.cpp index d700b28..446ac86 100644 --- a/src/blt/parse/argparse_v2.cpp +++ b/src/blt/parse/argparse_v2.cpp @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/blt/parse/obj_loader.cpp b/src/blt/parse/obj_loader.cpp index 82a3c2d..5433876 100644 --- a/src/blt/parse/obj_loader.cpp +++ b/src/blt/parse/obj_loader.cpp @@ -26,7 +26,7 @@ #include #include "blt/std/assert.h" #include "blt/std/utility.h" -#include +#include namespace blt::parse { @@ -101,24 +101,23 @@ namespace blt::parse return; } - auto elements = blt::string::split(std::string(tokenizer.read_fully()), " "); + auto elements = string::split(tokenizer.read_fully(), " "); BLT_ASSERT(elements.size() >= 2 && "Current line doesn't have enough arguments to process!"); float x = get(elements[0]), y = get(elements[1]); - BLT_DEBUG_STREAM << "Loaded value of (" << x << ", " << y << ")"; + BLT_DEBUG("Loaded value of ({}, {})", x, y); if (elements.size() < 3) { if (type == 't') uvs.push_back(uv_t{x, y}); else - BLT_ERROR("Unable to parse line '%s' type '%c' not recognized for arg count", std::string(tokenizer.read_fully()).c_str(), type); + BLT_ERROR("Unable to parse line '{}' type '{:c}' not recognized for arg count", tokenizer.read_fully(), type); } else { float z = get(elements[2]); - BLT_DEBUG_STREAM << " with z: " << z; + BLT_DEBUG(" with z: {}", z); if (!handle_vertex_and_normals(x, y, z, type)) - BLT_ERROR("Unable to parse line '%s' type '%c' not recognized", std::string(tokenizer.read_fully()).c_str(), type); + BLT_ERROR("Unable to parse line '{}' type '{:c}' not recognized", tokenizer.read_fully(), type); } - BLT_DEBUG_STREAM << "\n"; } bool obj_loader::handle_vertex_and_normals(float x, float y, float z, char type) diff --git a/src/blt/parse/templating.cpp b/src/blt/parse/templating.cpp index b56bef9..6d0f962 100644 --- a/src/blt/parse/templating.cpp +++ b/src/blt/parse/templating.cpp @@ -18,7 +18,7 @@ #include #include #include -#include "blt/std/logging.h" +#include "blt/logging/logging.h" namespace blt { diff --git a/src/blt/profiling/profiler.cpp b/src/blt/profiling/profiler.cpp index b088016..68e5cdb 100644 --- a/src/blt/profiling/profiler.cpp +++ b/src/blt/profiling/profiler.cpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include #include @@ -32,9 +32,9 @@ namespace blt::profiling { difference(difference), name(std::move(name)), total(total) {} }; - inline void println(const std::vector&& lines, logging::log_level level) { + inline void println(const std::vector&& lines, const logging::log_level_t level) { for (const auto& line : lines) - BLT_LOG_STREAM(level) << line << "\n"; + BLT_LOG(level, "{}", line); // auto& logger = logging::getLoggerFromLevel(level); // for (const auto& line : lines) // logger << line << "\n"; @@ -108,7 +108,7 @@ namespace blt::profiling { } void printProfile( - const std::string& profileName, logging::log_level loggingLevel, bool averageHistory + const std::string& profileName, const logging::log_level_t loggingLevel, const bool averageHistory ) { auto& profile = profiles[profileName]; const auto& intervals = profile.intervals; diff --git a/src/blt/profiling/profiler_v2.cpp b/src/blt/profiling/profiler_v2.cpp index 00dd5f7..75e12ee 100644 --- a/src/blt/profiling/profiler_v2.cpp +++ b/src/blt/profiling/profiler_v2.cpp @@ -187,11 +187,11 @@ namespace blt stream << line << "\n"; } - void printProfile(profile_t& profiler, const std::uint32_t flags, sort_by sort, blt::logging::log_level log_level) + void printProfile(profile_t& profiler, const std::uint32_t flags, sort_by sort, blt::logging::log_level_t log_level) { std::stringstream stream; writeProfile(stream, profiler, flags, sort); - BLT_LOG_STREAM(log_level) << stream.str(); + BLT_LOG(log_level, "{}", stream.str()); } profile_t::~profile_t() @@ -237,7 +237,7 @@ namespace blt profiles.erase(profile_name); } - void _internal::printProfile(const std::string& profile_name, std::uint32_t flags, sort_by sort, blt::logging::log_level log_level) + void _internal::printProfile(const std::string& profile_name, std::uint32_t flags, sort_by sort, blt::logging::log_level_t log_level) { if (profiles.find(profile_name) == profiles.end()) return; diff --git a/src/blt/std/assert.cpp b/src/blt/std/assert.cpp index 5ed4440..ebc99e5 100644 --- a/src/blt/std/assert.cpp +++ b/src/blt/std/assert.cpp @@ -3,15 +3,15 @@ * Licensed under GNU General Public License V3.0 * See LICENSE file for license detail */ -#include -#include -#include -#include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include +#include +#include +#include struct abort_exception final : public std::exception { @@ -48,8 +48,8 @@ struct abort_exception final : public std::exception #ifdef IS_GNU_BACKTRACE - #include #include +#include #endif diff --git a/src/blt/std/error.cpp b/src/blt/std/error.cpp index c733e0e..bc658c7 100644 --- a/src/blt/std/error.cpp +++ b/src/blt/std/error.cpp @@ -15,68 +15,52 @@ * along with this program. If not, see . */ #include -#include +#include namespace blt::error { - void print_socket_error() - { - switch (errno) - { - case EINVAL: - BLT_WARN("Invalid argument"); - break; - case EACCES: - BLT_WARN("Permission denied"); - break; - case EPERM: - BLT_WARN("Operation not permitted"); - break; - case EADDRINUSE: - BLT_WARN("Address already in use"); - break; - case EADDRNOTAVAIL: - BLT_WARN("Cannot copy_fast requested address"); - break; - case EAFNOSUPPORT: - BLT_WARN("Address family not supported by protocol"); - break; - case EAGAIN: - BLT_WARN("Try again"); - break; - case EALREADY: - BLT_WARN("Operation already in progress"); - break; - case EBADF: - BLT_WARN("Bad file number"); - break; - case ECONNREFUSED: - BLT_WARN("Connection refused"); - break; - case EFAULT: - BLT_WARN("Bad address"); - break; - case EINPROGRESS: - BLT_WARN("Operation now in progress"); - break; - case EINTR: - BLT_WARN("Interrupted system call"); - break; - case EISCONN: - BLT_WARN("Transport endpoint is already connected"); - break; - case ENETUNREACH: - BLT_WARN("Network is unreachable"); - break; - case ENOTSOCK: - BLT_WARN("Socket operation_t on non-socket"); - break; - case EPROTOTYPE: - BLT_WARN("Protocol wrong type for socket"); - break; - case ETIMEDOUT: - BLT_WARN("Connection timed out"); - break; - } - } -} \ No newline at end of file + void print_socket_error() + { + switch (errno) + { + case EINVAL: BLT_WARN("Invalid argument"); + break; + case EACCES: BLT_WARN("Permission denied"); + break; + case EPERM: BLT_WARN("Operation not permitted"); + break; + case EADDRINUSE: BLT_WARN("Address already in use"); + break; + case EADDRNOTAVAIL: BLT_WARN("Cannot copy_fast requested address"); + break; + case EAFNOSUPPORT: BLT_WARN("Address family not supported by protocol"); + break; + case EAGAIN: BLT_WARN("Try again"); + break; + case EALREADY: BLT_WARN("Operation already in progress"); + break; + case EBADF: BLT_WARN("Bad file number"); + break; + case ECONNREFUSED: BLT_WARN("Connection refused"); + break; + case EFAULT: BLT_WARN("Bad address"); + break; + case EINPROGRESS: BLT_WARN("Operation now in progress"); + break; + case EINTR: BLT_WARN("Interrupted system call"); + break; + case EISCONN: BLT_WARN("Transport endpoint is already connected"); + break; + case ENETUNREACH: BLT_WARN("Network is unreachable"); + break; + case ENOTSOCK: BLT_WARN("Socket operation_t on non-socket"); + break; + case EPROTOTYPE: BLT_WARN("Protocol wrong type for socket"); + break; + case ETIMEDOUT: BLT_WARN("Connection timed out"); + break; + default: + break; + } + } +} diff --git a/src/blt/std/logging.cpp b/src/blt/std/logging.cpp deleted file mode 100644 index 393f8f9..0000000 --- a/src/blt/std/logging.cpp +++ /dev/null @@ -1,627 +0,0 @@ -/* - * Created by Brett on 20/07/23. - * Licensed under GNU General Public License V3.0 - * See LICENSE file for license detail - */ -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#if defined(CXX17_FILESYSTEM) || defined (CXX17_FILESYSTEM_LIBFS) - #include -#elif defined(CXX11_EXP_FILESYSTEM) || defined (CXX11_EXP_FILESYSTEM_LIBFS) - #include -#else -#include -#endif -#include -#include - -template -using hashmap = std::unordered_map; - -namespace blt::logging -{ - /** - * Used to store fast associations between built in tags and their respective values - */ - class tag_map - { - private: - tag* tags; - size_t size; - - [[nodiscard]] static inline size_t hash(const tag& t) - { - size_t h = t.tag[1] * 3 - t.tag[0]; - return h - 100; - } - - // TODO: fix - void expand() - { - auto newSize = size * 2; - auto newTags = new tag[newSize]; - for (size_t i = 0; i < size; i++) - newTags[i] = tags[i]; - delete[] tags; - tags = newTags; - size = newSize; - } - - public: - tag_map(std::initializer_list initial_tags) - { - size_t max = 0; - for (const auto& t : initial_tags) - max = std::max(max, hash(t)); - tags = new tag[(size = max + 1)]; - for (const auto& t : initial_tags) - insert(t); - } - - tag_map(const tag_map& copy) - { - tags = new tag[(size = copy.size)]; - for (size_t i = 0; i < size; i++) - tags[i] = copy.tags[i]; - } - - void insert(const tag& t) - { - auto h = hash(t); - //if (h > size) - // expand(); - if (!tags[h].tag.empty()) - std::cerr << "Tag not empty! " << tags[h].tag << "!!!\n"; - tags[h] = t; - } - - tag& operator[](const std::string& name) const - { - auto h = hash(tag{name, nullptr}); - if (h > size) - std::cerr << "Tag out of bounds"; - return tags[h]; - } - - ~tag_map() - { - delete[] tags; - tags = nullptr; - } - }; - - class LogFileWriter - { - private: - std::string m_path; - std::fstream* output = nullptr; - - public: - explicit LogFileWriter() = default; - - void writeLine(const std::string& path, const std::string& line) - { - if (path != m_path || output == nullptr) - { - clear(); - delete output; - output = new std::fstream(path, std::ios::out | std::ios::app); - if (!output->good()) - { - throw std::runtime_error("Unable to open console filestream!\n"); - } - } - if (!output->good()) - { - std::cerr << "There has been an error in the logging file stream!\n"; - output->clear(); - } - *output << line; - } - - void clear() - { - if (output != nullptr) - { - try - { - output->flush(); - output->close(); - } - catch (std::exception& e) - { - std::cerr << e.what() << "\n"; - } - } - } - - ~LogFileWriter() - { - clear(); - delete(output); - } - }; - -#ifdef WIN32 - #define BLT_NOW() auto t = std::time(nullptr); tm now{}; localtime_s(&now, &t) -#else -#define BLT_NOW() auto t = std::time(nullptr); auto now_ptr = std::localtime(&t); auto& now = *now_ptr -#endif - - //#define BLT_NOW() auto t = std::time(nullptr); tm now; localtime_s(&now, &t); //auto now = std::localtime(&t) -#define BLT_ISO_YEAR(S) auto S = std::to_string(now.tm_year + 1900); \ - S += '-'; \ - S += ensureHasDigits(now.tm_mon+1, 2); \ - S += '-'; \ - S += ensureHasDigits(now.tm_mday, 2); -#define BLT_CUR_TIME(S) auto S = ensureHasDigits(now.tm_hour, 2); \ - S += ':'; \ - S += ensureHasDigits(now.tm_min, 2); \ - S += ':'; \ - S += ensureHasDigits(now.tm_sec, 2); - - static inline std::string ensureHasDigits(int current, int digits) - { - std::string asString = std::to_string(current); - auto length = digits - asString.length(); - if (length <= 0) - return asString; - std::string zeros; - zeros.reserve(length); - for (unsigned int i = 0; i < length; i++) - { - zeros += '0'; - } - return zeros + asString; - } - - log_format loggingFormat{}; - hashmap loggingThreadNames; - hashmap> loggingStreamLines; - LogFileWriter writer; - - const std::unique_ptr tagMap = std::make_unique(tag_map{ - { - "YEAR", [](const tag_func_param&) -> std::string - { - BLT_NOW(); - return std::to_string(now.tm_year); - } - }, - { - "MONTH", [](const tag_func_param&) -> std::string - { - BLT_NOW(); - return ensureHasDigits(now.tm_mon + 1, 2); - } - }, - { - "DAY", [](const tag_func_param&) -> std::string - { - BLT_NOW(); - return ensureHasDigits(now.tm_mday, 2); - } - }, - { - "HOUR", [](const tag_func_param&) -> std::string - { - BLT_NOW(); - return ensureHasDigits(now.tm_hour, 2); - } - }, - { - "MINUTE", [](const tag_func_param&) -> std::string - { - BLT_NOW(); - return ensureHasDigits(now.tm_min, 2); - } - }, - { - "SECOND", [](const tag_func_param&) -> std::string - { - BLT_NOW(); - return ensureHasDigits(now.tm_sec, 2); - } - }, - { - "MS", [](const tag_func_param&) -> std::string - { - return std::to_string(std::chrono::duration_cast( - std::chrono::high_resolution_clock::now().time_since_epoch()).count() - ); - } - }, - { - "NS", [](const tag_func_param&) -> std::string - { - return std::to_string(std::chrono::duration_cast( - std::chrono::high_resolution_clock::now().time_since_epoch()).count() - ); - } - }, - { - "ISO_YEAR", [](const tag_func_param&) -> std::string - { - BLT_NOW(); - BLT_ISO_YEAR(returnStr); - return returnStr; - } - }, - { - "TIME", [](const tag_func_param&) -> std::string - { - BLT_NOW(); - BLT_CUR_TIME(returnStr); - return returnStr; - } - }, - { - "FULL_TIME", [](const tag_func_param&) -> std::string - { - BLT_NOW(); - BLT_ISO_YEAR(ISO); - BLT_CUR_TIME(TIME); - ISO += ' '; - ISO += TIME; - return ISO; - } - }, - { - "LF", [](const tag_func_param& f) -> std::string - { - return loggingFormat.levelColors[(int)f.level]; - } - }, - { - "ER", [](const tag_func_param&) -> std::string - { - return loggingFormat.levelColors[(int)log_level::ERROR]; - } - }, - { - "CNR", [](const tag_func_param& f) -> std::string - { - return f.level >= log_level::ERROR ? loggingFormat.levelColors[(int)log_level::ERROR] : ""; - } - }, - { - "RC", [](const tag_func_param&) -> std::string - { - return "\033[0m"; - } - }, - { - "LOG_LEVEL", [](const tag_func_param& f) -> std::string - { - return loggingFormat.levelNames[(int)f.level]; - } - }, - { - "THREAD_NAME", [](const tag_func_param&) -> std::string - { - if (loggingThreadNames.find(std::this_thread::get_id()) == loggingThreadNames.end()) - return "UNKNOWN"; - return loggingThreadNames[std::this_thread::get_id()]; - } - }, - { - "FILE", [](const tag_func_param& f) -> std::string - { - return f.file; - } - }, - { - "LINE", [](const tag_func_param& f) -> std::string - { - return f.line; - } - }, - { - "RAW_STR", [](const tag_func_param& f) -> std::string - { - return f.raw_string; - } - }, - { - "STR", [](const tag_func_param& f) -> std::string - { - return f.formatted_string; - } - } - }); - - static inline std::vector split(std::string s, const std::string& delim) - { - size_t pos = 0; - std::vector tokens; - while ((pos = s.find(delim)) != std::string::npos) - { - auto token = s.substr(0, pos); - tokens.push_back(token); - s.erase(0, pos + delim.length()); - } - tokens.push_back(s); - return tokens; - } - - inline std::string filename(const std::string& path) - { - if (loggingFormat.printFullFileName) - return path; - auto paths = split(path, "/"); - auto final = paths[paths.size() - 1]; - if (final == "/") - return paths[paths.size() - 2]; - return final; - } - - class string_parser - { - private: - std::string _str; - size_t _pos; - - public: - explicit string_parser(std::string str): _str(std::move(str)), _pos(0) - { - } - - inline char next() - { - return _str[_pos++]; - } - - [[nodiscard]] inline bool has_next() const - { - return _pos < _str.size(); - } - }; - - std::string stripANSI(const std::string& str) - { - string_parser parser(str); - std::string out; - while (parser.has_next()) - { - char c = parser.next(); - if (c == '\033') - { - while (parser.has_next() && parser.next() != 'm'); - } - else - out += c; - } - return out; - } - - void applyCFormatting(const std::string& format, std::string& output, std::va_list& args) - { - // args must be copied because they will be consumed by the first vsnprintf - va_list args_copy; - va_copy(args_copy, args); - - auto buffer_size = std::vsnprintf(nullptr, 0, format.c_str(), args_copy) + 1; - auto* buffer = new char[static_cast(buffer_size)]; - - vsnprintf(buffer, buffer_size, format.c_str(), args); - output = std::string(buffer); - - delete[] buffer; - - va_end(args_copy); - } - - /** - * Checks if the next character in the parser is a tag opening, if not output the buffer to the out string - */ - inline bool tagOpening(string_parser& parser, std::string& out) - { - char c = ' '; - if (parser.has_next() && (c = parser.next()) == '{') - if (parser.has_next() && (c = parser.next()) == '{') - return true; - else - out += c; - else - out += c; - return false; - } - - void parseString(string_parser& parser, std::string& out, const std::string& userStr, log_level level, const char* file, int line) - { - while (parser.has_next()) - { - char c = parser.next(); - std::string nonTag; - if (c == '$' && tagOpening(parser, nonTag)) - { - std::string tag; - while (parser.has_next()) - { - c = parser.next(); - if (c == '}') - break; - tag += c; - } - c = parser.next(); - if (parser.has_next() && c != '}') - { - std::cerr << "Error processing tag, is not closed with two '}'!\n"; - break; - } - if (loggingFormat.ensureAlignment && tag == "STR") - { - auto currentOutputWidth = out.size(); - auto& longestWidth = loggingFormat.currentWidth; - longestWidth = std::max(longestWidth, currentOutputWidth); - // pad with spaces - if (currentOutputWidth != longestWidth) - { - for (size_t i = currentOutputWidth; i < longestWidth; i++) - out += ' '; - } - } - tag_func_param param{ - level, filename({file}), std::to_string(line), userStr, userStr - }; - out += (*tagMap)[tag].func(param); - } - else - { - out += c; - out += nonTag; - } - } - } - - std::string applyFormatString(const std::string& str, log_level level, const char* file, int line) - { - // this can be speedup by preprocessing the string into an easily callable class - // where all the variables are ready to be substituted in one step - // and all static information already entered - string_parser parser(loggingFormat.logOutputFormat); - std::string out; - parseString(parser, out, str, level, file, line); - - return out; - } - - void log_internal(const std::string& format, log_level level, const char* file, int line, std::va_list& args) - { - std::string withoutLn = format; - auto len = withoutLn.length(); - - if (len > 0 && withoutLn[len - 1] == '\n') - withoutLn = withoutLn.substr(0, len - 1); - - std::string out; - - applyCFormatting(withoutLn, out, args); - - if (level == log_level::NONE) - { - std::cout << out << std::endl; - return; - } - - std::string finalFormattedOutput = applyFormatString(out, level, file, line); - - if (loggingFormat.logToConsole) - std::cout << finalFormattedOutput; - - - if (loggingFormat.logToFile) - { - string_parser parser(loggingFormat.logFileName); - std::string fileName; - parseString(parser, fileName, withoutLn, level, file, line); - - auto path = loggingFormat.logFilePath; - if (!path.empty() && path[path.length() - 1] != '/') - path += '/'; - - // if the file has changed (new day in default case) we should reset the rollover count - if (loggingFormat.lastFile != fileName) - { - loggingFormat.currentRollover = 0; - loggingFormat.lastFile = fileName; - } - - path += fileName; - path += '-'; - path += std::to_string(loggingFormat.currentRollover); - path += ".log"; - - if (std::filesystem::exists(path)) - { - auto fileSize = std::filesystem::file_size(path); - - // will start on next file - if (fileSize > loggingFormat.logMaxFileSize) - loggingFormat.currentRollover++; - } - - writer.writeLine(path, stripANSI(finalFormattedOutput)); - } - //std::cout.flush(); - } - - void log_stream_internal(const std::string& str, const logger& logger) - { - auto& s = loggingStreamLines[std::this_thread::get_id()][logger.level]; - // s += str; - for (char c : str) - { - s += c; - if (c == '\n') - { - log(s, logger.level, logger.file, logger.line); - s = ""; - } - } - } - - void setThreadName(const std::string& name) - { - loggingThreadNames[std::this_thread::get_id()] = name; - } - - void setLogFormat(const log_format& format) - { - loggingFormat = format; - } - - void setLogColor(log_level level, const std::string& newFormat) - { - loggingFormat.levelColors[(int)level] = newFormat; - } - - void setLogName(log_level level, const std::string& newFormat) - { - loggingFormat.levelNames[(int)level] = newFormat; - } - - void setLogOutputFormat(const std::string& newFormat) - { - loggingFormat.logOutputFormat = newFormat; - } - - void setLogToFile(bool shouldLogToFile) - { - loggingFormat.logToFile = shouldLogToFile; - } - - void setLogToConsole(bool shouldLogToConsole) - { - loggingFormat.logToConsole = shouldLogToConsole; - } - - void setLogPath(const std::string& path) - { - loggingFormat.logFilePath = path; - } - - void setLogFileName(const std::string& fileName) - { - loggingFormat.logFileName = fileName; - } - - void setMaxFileSize(const size_t fileSize) - { - loggingFormat.logMaxFileSize = fileSize; - } - - void flush() - { - std::cerr.flush(); - std::cout.flush(); - } -} diff --git a/src/blt/std/mmap.cpp b/src/blt/std/mmap.cpp index ef7a978..c65d9b7 100644 --- a/src/blt/std/mmap.cpp +++ b/src/blt/std/mmap.cpp @@ -15,6 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +#include #include #ifdef __unix__ @@ -112,7 +113,7 @@ namespace blt #ifdef __unix__ if (munmap(ptr, bytes)) { - BLT_ERROR_STREAM << "Failed to deallocate\n"; + BLT_ERROR("Failed to deallocate"); throw bad_alloc_t(handle_mmap_error()); } #else diff --git a/src/blt/std/simd.cpp b/src/blt/std/simd.cpp index 1c89acb..85b4e8c 100644 --- a/src/blt/std/simd.cpp +++ b/src/blt/std/simd.cpp @@ -16,7 +16,6 @@ * along with this program. If not, see . */ #include -#include namespace blt { diff --git a/src/blt/std/system.cpp b/src/blt/std/system.cpp index 546bac6..3dbed6e 100644 --- a/src/blt/std/system.cpp +++ b/src/blt/std/system.cpp @@ -4,7 +4,7 @@ * See LICENSE file for license detail */ #include -#include +#include #if !defined(_MSC_VER) && !defined(WIN32) #include /* for struct timeval */